#define	HAVE_TEST
/*
 *  $Id: io-motif.c,v 1.66 2001/03/09 11:33:29 danny Exp $
 *
 *  This file is part of Oleo, the GNU spreadsheet.
 *
 *  Copyright © 1998-2000 by the Free Software Foundation, Inc.
 *  Written by Danny Backx <danny@gnu.org>.
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

static char rcsid[] = "$Id: io-motif.c,v 1.66 2001/03/09 11:33:29 danny Exp $";

#ifdef	HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef	WITH_DMALLOC
#include <dstdlib.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/param.h>
#include <stdarg.h>

#ifdef	HAVE_LOCALE_H
#include <locale.h>
#endif
#include <libintl.h>

#include <X11/Intrinsic.h>
#include <X11/Shell.h>

#include <Xm/XmAll.h>
#include <Xbae/Matrix.h>
#include <Xbae/Caption.h>

#ifdef	HAVE_XLT
#include <Xlt/SciPlot.h>
#ifndef HAVE_SciPlot_H
#define	HAVE_SciPlot_H
#endif
#else
#ifdef	HAVE_SciPlot_H
#include <SciPlot/SciPlot.h>
#endif
#endif

#ifdef	HAVE_LIBXPM
#include <X11/xpm.h>
#include "oleo_icon.xpm"
#endif

#include "global.h"

#ifdef	HAVE_LIBPLOT
#include <plot.h>
#include "sciplot.h"
#endif
#include "oleo_plot.h"

#ifdef	HAVE_LIBMYSQLCLIENT
#include <mysql/mysql_version.h>
#endif
#include "oleosql.h"

#ifdef	HAVE_LIBCUPS
#include <cups/cups.h>
#endif

#include "utils.h"
#include "io-generic.h"
#include "io-edit.h"
#include "io-utils.h"
#include "io-term.h"
#include "cmd.h"
#include "window.h"
#include "line.h"
#include "font.h"
#include "lists.h"
#include "ir.h"
#include "display.h"
#include "io-abstract.h"
#include "regions.h"
#include "input.h"
#include "info.h"
#include "eval.h"
#include "basic.h"
#include "graph.h"
#include "print.h"
#include "mdi.h"
#include "userpref.h"

#include "io-motif.h"		/* To get warnings when inconsistent */

#ifdef	HAVE_XmHTML_H
#include <XmHTML/XmHTML.h>
#endif

static Widget	w;

/* Fallback resources, application resources */
extern char *fallback[];
#include "appres.h"

GnuSheetAppres	AppRes;

/* Forward declarations */
void CancelTemplate(Widget w, XtPointer client, XtPointer call);
void DestroyTemplate(Widget w, XtPointer client, XtPointer call);
static void FixA0();
static void PopDownSplash();
static void UpdateStatus(void);
void helpUsingCB(Widget w, XtPointer client, XtPointer call);
static void MotifSelectGlobal(Widget);
Widget GscBuildMainWindow(Widget parent);
void DestroyToplevel(Widget w, XtPointer client, XtPointer call);

Widget ConfigureBarChart(Widget parent);
Widget ConfigurePieChart(Widget parent);
Widget ConfigureXYChart(Widget parent);
void ConfigureXYOk(void);
void ConfigureBarOk(void);
void ConfigurePieOk(void);
void ConfigureXYReset(void);
void ConfigureBarReset(void);
void ConfigurePieReset(void);

/*
 * This is used in two places.
 */
#define	CopyRightString							\
	_(								\
	"\nGNU Oleo is free software, you are welcome to\n"		\
	"distribute copies of it. See the file COPYING for the\n"	\
	"conditions.\n\n"						\
	"GNU Oleo comes with ABSOLUTELY NO WARRANTY."			\
	)

/*
 * Conditionally (only when the user wants it) print debugging info
 */
void Debug(char *src, char *fmt, ...)
{
	static char	*srclist = NULL;
	va_list	ap;
	int		printit = 0;

	if (! srclist)
		srclist = getenv("DEBUGSOURCES");

	if (srclist && strstr(src, srclist))
		printit = 1;

	if (printit) {
		va_start(ap, fmt);
		vfprintf(stderr, fmt, ap);
		va_end(ap);
	}
}

void MessageAppend(Boolean beep, char *fmt, ...)
{
	XmTextPosition	pos;
	int		i;
	va_list		ap;
	char		s[256];

	if (fmt == NULL)
		return;

	va_start(ap, fmt);
	vsprintf(s, fmt, ap);
	va_end(ap);

	if (msgtext == NULL) {
		if (Global->MotifGlobal->early_msg_text == NULL) {
			Global->MotifGlobal->early_msg_text = strdup(s);
		} else {
			int l = strlen(Global->MotifGlobal->early_msg_text) + strlen(s) + 1;
			char *x = malloc(l);
			strcpy(x, Global->MotifGlobal->early_msg_text);
			strcat(x, s);
			free(Global->MotifGlobal->early_msg_text);
			Global->MotifGlobal->early_msg_text = x;
		}
		return;
	}

	if ((i = strlen(s)) == 0)
		return;

	pos = XmTextGetLastPosition(msgtext);
	XmTextInsert(msgtext, pos, s);

	if (s[i-1] != '\n') {
		pos = XmTextGetLastPosition(msgtext);
		XmTextInsert(msgtext, pos, "\n");
	}

	XmTextShowPosition(msgtext, XmTextGetLastPosition(msgtext));

	if (beep)
		XBell(XtDisplay(msgtext), 30);
}

/*
 * This just prints a message saying this functionality hasn't been
 * implemented yet.
 */
void none(Widget w, XtPointer client, XtPointer call)
{
	XmString	xms;
	char		*s = NULL;

	MotifSelectGlobal(w);

	XtVaGetValues(w, XmNlabelString, &xms, NULL);
	if (XmStringGetLtoR(xms, XmFONTLIST_DEFAULT_TAG, &s)) {
		MessageAppend(False, "%s: not implemented yet\n", s);
		XtFree(s);
	}
	XmStringFree(xms);
}

void PrintDebug(Widget w, XtPointer client, XtPointer call)
{
	int	i, j;

	for (i=1; i<6; i++) {
		fprintf(stderr, "[%d]\t", i);
		for (j=1; j<6; j++) {
			fprintf(stderr, "%s\t", cell_value_string(i, j, True));
		}
		fprintf(stderr, "\n");
	}
}

/****************************************************************
 *								*
 *		Range Selection Support				*
 *								*
 ****************************************************************/

void FocusCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	ActiveRangeSelectionWidget = w;
}

void LoseFocusCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

#if 0
	if (ActiveRangeSelectionWidget == w)
		ActiveRangeSelectionWidget = NULL;
#endif
}

void SelectCellCB(Widget w, XtPointer client, XtPointer call)
{
	char	*s;
	XbaeMatrixSelectCellCallbackStruct *cbp =
		(XbaeMatrixSelectCellCallbackStruct *)call;
	int		i, j;

	MotifSelectGlobal(w);

#if 0
	fprintf(stderr, "SelectCell(%s, %d %d)\n",
		cbp->params[0], cbp->row, cbp->column);
#endif

	if (Global->MotifGlobal->selectedcells == NULL) {
		/*
		 * There's a memory overrun here somewhere.
		 * Working around it by allocating a couple of extra bytes.
		 */
		Global->MotifGlobal->selectedcells = (Boolean **)XtMalloc(AppRes.rows *
				sizeof(Boolean *));
		for (i=0; i<AppRes.rows; i++)
			Global->MotifGlobal->selectedcells[i] = (Boolean *)
				XtCalloc(10 + AppRes.columns, sizeof(Boolean));
	}

	if (cbp->num_params >= 1 && strcmp(cbp->params[0], "start") == 0) {
		selection_range.lr = cbp->row + 1;
		selection_range.lc = cbp->column + 1;

		for (i=0; i<AppRes.rows; i++)
		    for (j=0; j<=AppRes.columns; j++)
			Global->MotifGlobal->selectedcells[i][j] = False;

		XtVaSetValues(mat, XmNselectedCells, Global->MotifGlobal->selectedcells, NULL);
	} else if (cbp->num_params >= 1 && strcmp(cbp->params[0], "end") == 0) {
		selection_range.hr = cbp->row + 1;
		selection_range.hc = cbp->column + 1;

		Global->MotifGlobal->newcall = True;

		if (ActiveRangeSelectionWidget &&
				XmIsTextField(ActiveRangeSelectionWidget)) {
			s = range_name(&selection_range);
			XmTextFieldSetString(ActiveRangeSelectionWidget, s);
		}
	} else if (cbp->num_params >= 1 && strcmp(cbp->params[0], "move") == 0) {
		/*
		 * Need to figure out which call is the first in a selection.
		 */

		if (Global->MotifGlobal->newcall) {
#if 0
			fprintf(stderr, "SelectCellCB(move): first call\n");
#endif
			/* First call, take the position from the currently selected cell */
			selection_range.lr = curow;
			selection_range.lc = cucol;

			for (i=0; i<AppRes.rows; i++)
			    for (j=0; j<=AppRes.columns; j++)
				Global->MotifGlobal->selectedcells[i][j] = False;

			XtVaSetValues(mat, XmNselectedCells, Global->MotifGlobal->selectedcells, NULL);
		} else {
#if 0
			fprintf(stderr, "SelectCellCB(move): another call\n");
#endif
			/* Motion : change selected area */
			selection_range.hr = cbp->row + 1;
			selection_range.hc = cbp->column + 1;

			for (i=selection_range.lr-1; i<=cbp->row; i++)
			    for (j=selection_range.lc-1; j<=cbp->column; j++)
				Global->MotifGlobal->selectedcells[i][j] = True;

			XtVaSetValues(mat, XmNselectedCells, Global->MotifGlobal->selectedcells, NULL);

			if (ActiveRangeSelectionWidget &&
					XmIsTextField(ActiveRangeSelectionWidget)) {
				s = range_name(&selection_range);
				XmTextFieldSetString(ActiveRangeSelectionWidget, s);
			}
		}

		Global->MotifGlobal->newcall = False;
	}
}

void RegisterRangeSelector(Widget w)
{
	XtAddCallback(w, XmNfocusCallback, FocusCB, NULL);
	XtAddCallback(w, XmNlosingFocusCallback, LoseFocusCB, NULL);
}

/****************************************************************
 *								*
 *		Generic Helper Functions			*
 *								*
 ****************************************************************/
void
RegisterWMClose(Widget w)
{
        Atom    delete_atom;

        delete_atom = XmInternAtom(XtDisplay(w), "WM_DELETE_WINDOW", False);
        XmAddWMProtocols(w, &delete_atom, 1);
        XmSetWMProtocolHooks(w, delete_atom,
		(XtCallbackProc)DestroyToplevel, (XtPointer)1,
                (XtCallbackProc)0, (XtPointer)0);
}

void
PopDownHelpCB(Widget widget, XtPointer client, XtPointer call)
{
	XtPopdown((Widget)client);
}

void MotifUpdateDisplay()
{
        XbaeMatrixRefresh(mat); /* refresh */

	FixA0();

        /*
         * Apparently the refresh above doesn't refresh the cell whose
         * formula we just changed, and which therefore almost certainly
         * needs an update. XbaeMatrixRefreshCell also doesn't work.
         * This does :
         */
        XbaeMatrixSetCell(mat, curow - 1, cucol - 1,
                cell_value_string(curow, cucol, True));
}

/****************************************************************
 *								*
 *			Graphs					*
 *								*
 ****************************************************************/
struct ConfigureWidgets {
	Widget	x,
		a, b, c, d, e, f, g, h, i,
		sa, sb, sc, sd, se, sf, sg, sh, si,
		ta, tb, tc, td, te, tf, tg, th, ti,
		xlog, ylog,
		title, xtitle, ytitle;
};

static void
sciplot_thunk(void *p, CELL *cp, CELLREF r, CELLREF c)
{
	char * str = (char *)char_to_q_char (print_cell (cp));

	MessageAppend(False, "SciPlotThunk(r %d c %d) %s",
		r, c, print_cell(find_cell(r, c)));
}

void UpdateGraph()
{
#ifdef	HAVE_SciPlot_H
	int			i, ii, len, xx, xa, lst;
	int			xlog, ylog;
	double			*vx, *va;
	struct rng		rngx, rnga;
	char			*s, *t;
	extern struct rng	graph_get_data(int);
	enum graph_pair_ordering order = graph_rows_hz;

	xlog = graph_get_logness('x');
	ylog = graph_get_logness('y');

	XtVaSetValues(plot,
			XtNchartType,		XtCARTESIAN,
			XtNxLabel,		graph_get_axis_title('x'),
			XtNyLabel,		graph_get_axis_title('y'),
			XtNxLog,		xlog,
			XtNyLog,		ylog,
		NULL);


	rngx = graph_get_data(0);
	rnga = graph_get_data(1);

	len = rngx.hr - rngx.lr + 1;

	vx = (double *)XtCalloc(len, sizeof(double));
	va = (double *)XtCalloc(len, sizeof(double));

	/* FIX ME */
	/* This code is wrong: only works for vertical set of data */
	for (ii=0, i=rngx.lr; i<=rngx.hr; i++, ii++) {
		s = cell_value_string(i, rngx.lc, True);
		vx[ii] = 0.0;
		sscanf(s, "%lf", &vx[ii]);
		s = cell_value_string(i, rnga.lc, True);
		va[ii] = 0.0;
		sscanf(s, "%lf", &va[ii]);
	}

/* Start test for_pairs_in() */
	for_pairs_in(&rngx, order, (fpi_thunk)sciplot_thunk, NULL);
	for_pairs_in(&rnga, order, (fpi_thunk)sciplot_thunk, NULL);
/* End test for_pairs_in() */

	lst = SciPlotListCreateDouble(plot, 10, vx, va,
			graph_get_data_title(0));
#if 1
    {
	/* Colour test */

	int	fg, bg, lc, pc;

	bg = SciPlotAllocNamedColor(plot, "seagreen");
	fg = SciPlotAllocNamedColor(plot, "yellow");
	lc = SciPlotAllocNamedColor(plot, "orange");
	pc = SciPlotAllocNamedColor(plot, "light blue");

	SciPlotListSetStyle(plot, lst, pc, XtMARKER_CIRCLE,
		lc, XtLINE_DOTTED);
	SciPlotSetForegroundColor(plot, fg);
	SciPlotSetBackgroundColor(plot, bg);
    }
#else
	SciPlotListSetStyle(plot, lst, 1, XtMARKER_CIRCLE,
		1, XtLINE_DOTTED);
#endif

	s = graph_get_title();
	if (s && strlen(s)) {
		XtVaSetValues(plot, XtNplotTitle, s, NULL);
	}

	XtFree((XtPointer)vx);
	XtFree((XtPointer)va);

	SciPlotSetXAutoScale(plot);
	SciPlotSetYAutoScale(plot);

	SciPlotUpdate(plot);
#endif
}

void CreateGraph(Widget w, XtPointer client, XtPointer call)
{
	Widget	f, sw, sep, ok;
	int	i;

	MotifSelectGlobal(w);

	if (gs)
		return;

	gs = XtVaCreatePopupShell("graphShell",
		topLevelShellWidgetClass,
		toplevel,
	NULL);

	f = XtVaCreateManagedWidget("form", xmFormWidgetClass, gs,
		NULL);

#ifdef	HAVE_SciPlot_H
	sw = XtVaCreateManagedWidget("scroll",
		xmScrolledWindowWidgetClass, f,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftOffset,		10,
			XmNrightOffset,		10,
		NULL);

	plot = XtVaCreateManagedWidget("plot",
		sciplotWidgetClass, sw,
			XmNmarginWidth,		20,
			XmNmarginHeight,	20,
			XmNwidth,		600,
			XmNheight,		500,
		NULL);
#else
	/* Fallback if we don't have SciPlot */
	sw = plot = NULL;
#endif

	sep = XtVaCreateManagedWidget("separator",
		xmSeparatorGadgetClass, f,
			XmNorientation,		XmHORIZONTAL,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		sw,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightOffset,		10,
		NULL);

	ok = XtVaCreateManagedWidget("ok",
		xmPushButtonGadgetClass, f,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		sep,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightOffset,		10,
		NULL);
	XtAddCallback(ok, XmNactivateCallback, PopDownHelpCB, gs);

	UpdateGraph();
}

/*
 * Pop up a window with a graph (from SciPlot).
 */
#ifdef	HAVE_SciPlot_H
void ShowGraph(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	CreateGraph(w, client, call);
	XtPopup(gs, XtGrabNone);
}

/*
 * Needs to figure out to which file/printer to print, depending on
 * the callback data passed from the dialog.
 */
void ReallyPrintGraph(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	if (gs == NULL)
		CreateGraph(w, client, call);
	else
		UpdateGraph();
	if (plot)
		SciPlotPSCreateColor(plot, "plot.ps");
}

/*
 * FIX ME
 * This must first pop up a window asking whether to print to a file
 * or a printer. Only after confirmation must ReallyPrintGraph be
 * called.
 */
void PrintGraph(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	ReallyPrintGraph(w, client, call);
}
#endif

/*
 * Show panel of print options
 */
void PrintOptionsOk(Widget w, XtPointer client, XtPointer call)
{
	int	scale;
	float	f;

	MotifSelectGlobal(w);
	XtUnmanageChild(optionsDialog);

	if (Global->MotifGlobal->ZoomScale) {
		XtVaGetValues(Global->MotifGlobal->ZoomScale,
				XmNvalue, &scale,
			NULL);
		f = scale;
	} else
		f = 100.0;

	f = f / 100.0;
	Global->zoom = f;

	MessageAppend(False, "Set zoom to %2.0f%%", 100 * Global->zoom);
}

void PrintOptionsReset(Widget w)
{
	float	f = Global->zoom * 100.0;
	int	s = f;

	if (Global->MotifGlobal->ZoomScale) {
		XtVaSetValues(Global->MotifGlobal->ZoomScale,
				XmNvalue,	s,
			NULL);
	}
}

void PrintOptionsCB(Widget w, XtPointer client, XtPointer call)
{
	Widget		ok, cancel, help, t, rc, nb, b, cb, menu, f;
	XmString	xms;
	Arg		al[5];
	int		ac;

	MotifSelectGlobal(w);

	if (! optionsDialog) {
		ac = 0;
		XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
		optionsDialog = XmCreateTemplateDialog(mw, "configureGraph",
			al, ac);

#if XmVERSION > 1
		nb = XmCreateNotebook(optionsDialog,
			"printOptionsNotebook",
			NULL, 0);
		XtManageChild(nb);

		/* What to show */
		rc = XtVaCreateManagedWidget("showRC", xmRowColumnWidgetClass,
			nb, NULL);

		t = XtVaCreateManagedWidget("showLabels", xmToggleButtonGadgetClass,
				rc,
			NULL);
		t = XtVaCreateManagedWidget("showBorders", xmToggleButtonGadgetClass,
				rc,
			NULL);
		t = XtVaCreateManagedWidget("showRaster", xmToggleButtonGadgetClass,
				rc,
			NULL);

		(void)XtVaCreateManagedWidget("showLabel", xmPushButtonWidgetClass,
				nb,
				XmNnotebookChildType, XmMAJOR_TAB,
			NULL);

		/* Choose a scale for the print */
		f = XtVaCreateManagedWidget("scaleForm", xmFormWidgetClass,
			nb,
			NULL);

		rc = XtVaCreateManagedWidget("scaleRC", xmRowColumnWidgetClass,
			f,
				XmNtopAttachment,	XmATTACH_FORM,
				XmNtopOffset,		10,
				XmNleftAttachment,	XmATTACH_FORM,
			NULL);

		menu = XmCreatePulldownMenu(rc, "optionMenu", NULL, 0);
		ac = 0;
		XtSetArg(al[ac], XmNsubMenuId, menu); ac++;
		xms = XmStringCreateSimple(_("Fit Print Area"));
		XtSetArg(al[ac], XmNlabelString, xms); ac++;
		cb = XmCreateOptionMenu(rc, "optionCB", al, ac);
		XtManageChild(cb);
		XmStringFree(xms);

		b = XtVaCreateManagedWidget("PrintScaleZoom", xmPushButtonGadgetClass,
			menu,
			NULL);
		b = XtVaCreateManagedWidget("PrintScaleAsIs", xmPushButtonGadgetClass,
			menu,
			NULL);
		b = XtVaCreateManagedWidget("PrintScaleColumnsOnOne", xmPushButtonGadgetClass,
			menu,
			NULL);
		b = XtVaCreateManagedWidget("PrintScaleRowsOnOne", xmPushButtonGadgetClass,
			menu,
			NULL);
		b = XtVaCreateManagedWidget("PrintScaleAllOnOne", xmPushButtonGadgetClass,
			menu,
			NULL);

		Global->MotifGlobal->ZoomScale = XtVaCreateManagedWidget("PrintZoomScale",
			xmScaleWidgetClass, f,
				XmNtopAttachment,	XmATTACH_WIDGET,
				XmNtopWidget,		rc,
				XmNtopOffset,		10,
				XmNleftAttachment,	XmATTACH_FORM,
				XmNleftOffset,		10,
				XmNrightAttachment,	XmATTACH_FORM,
				XmNrightOffset,		10,
				XmNorientation,		XmHORIZONTAL,
				XmNminimum,		10,
				XmNmaximum,		400,
				XmNshowValue,		True,
			NULL);

		(void)XtVaCreateManagedWidget("scaleLabel", xmPushButtonWidgetClass,
				nb,
				XmNnotebookChildType, XmMAJOR_TAB,
			NULL);
#endif

		/* Buttons */
		ok = XtVaCreateManagedWidget("ok", xmPushButtonGadgetClass,
			optionsDialog,
			NULL);
		cancel = XtVaCreateManagedWidget("cancel",
			xmPushButtonGadgetClass, optionsDialog,
			NULL);
		help = XtVaCreateManagedWidget("help", xmPushButtonGadgetClass,
			optionsDialog,
			NULL);

		XtAddCallback(ok, XmNactivateCallback, PrintOptionsOk, optionsDialog);
		XtAddCallback(cancel, XmNactivateCallback, CancelTemplate, optionsDialog);

		/* FIX ME need something to call the help system */
	}

	PrintOptionsReset(optionsDialog);
	XtManageChild(optionsDialog);
}

void ConversionError(char *s, char *t)
{
	char *r = XtMalloc(strlen(s) + strlen(t) + 100);

	sprintf(r,
		_("Conversion error: cannot convert '%s' to a %s\n"),
		s, t);
	MessageAppend(True, r);
	XtFree(r);
}

#ifdef	HAVE_LIBPLOT
/*
 * Create a widget tree to configure a graph with.
 *	The result is a widget that isn't managed yet.
 *	(So you can put it in a dialog).
 *
 * Information that's present in traditional oleo graphs :
 * - general :
 *	X Axis		Title, Range
 *	Y Axis		Title, Range
 *	Logarithmic axes
 * - per data set :
 *	Data Set Name
 *	Style		(Linespoints, lines)
 */
Widget CreateConfigureGraph(Widget parent)
{
	Widget	toprc, frame, w, rc, cap;
	struct ConfigureWidgets	*cw;

	MotifSelectGlobal(parent);

	cw = (struct ConfigureWidgets *)XtMalloc(
		sizeof(struct ConfigureWidgets));

	/*
	 * We need to set the userdata on the toplevel widget here.
	 */
	toprc = XtVaCreateWidget("configureGraphRC", xmRowColumnWidgetClass,
		parent,
			XmNuserData,	cw,
		NULL);

	frame = XtVaCreateManagedWidget("dataFrame",
		xmFrameWidgetClass, toprc,
		NULL);

	rc = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass, frame,
			XmNnumColumns,	4,
			XmNorientation,	XmVERTICAL,
			XmNpacking,	XmPACK_COLUMN,
		NULL);
	XtVaCreateManagedWidget("l0", xmLabelGadgetClass, rc,
		NULL);
	XtVaCreateManagedWidget("l1", xmLabelGadgetClass, rc,
		NULL);
	XtVaCreateManagedWidget("l2", xmLabelGadgetClass, rc,
		NULL);
	XtVaCreateManagedWidget("l3", xmLabelGadgetClass, rc,
		NULL);
	XtVaCreateManagedWidget("l4", xmLabelGadgetClass, rc,
		NULL);
	XtVaCreateManagedWidget("l5", xmLabelGadgetClass, rc,
		NULL);

	XtVaCreateManagedWidget("r0", xmLabelGadgetClass, rc,
		NULL);
	cw->x = XtVaCreateManagedWidget("r1", xmTextFieldWidgetClass, rc,
		NULL);
	RegisterRangeSelector(cw->x);
	cw->a = XtVaCreateManagedWidget("r2", xmTextFieldWidgetClass, rc,
		NULL);
	RegisterRangeSelector(cw->a);
	cw->b = XtVaCreateManagedWidget("r3", xmTextFieldWidgetClass, rc,
		NULL);
	RegisterRangeSelector(cw->b);
	cw->c = XtVaCreateManagedWidget("r4", xmTextFieldWidgetClass, rc,
		NULL);
	RegisterRangeSelector(cw->c);
	cw->d = XtVaCreateManagedWidget("r5", xmTextFieldWidgetClass, rc,
		NULL);
	RegisterRangeSelector(cw->d);

	XtVaCreateManagedWidget("s0", xmLabelGadgetClass, rc,
		NULL);
	cw->sa = XtVaCreateManagedWidget("s1", xmToggleButtonGadgetClass, rc,
		NULL);
	cw->sb = XtVaCreateManagedWidget("s2", xmToggleButtonGadgetClass, rc,
		NULL);
	cw->sc = XtVaCreateManagedWidget("s3", xmToggleButtonGadgetClass, rc,
		NULL);
	cw->sd = XtVaCreateManagedWidget("s4", xmToggleButtonGadgetClass, rc,
		NULL);
	cw->se = XtVaCreateManagedWidget("s5", xmToggleButtonGadgetClass, rc,
		NULL);

	XtVaCreateManagedWidget("t0", xmLabelGadgetClass, rc,
		NULL);
	cw->ta = XtVaCreateManagedWidget("t1", xmTextFieldWidgetClass, rc,
		NULL);
	cw->tb = XtVaCreateManagedWidget("t2", xmTextFieldWidgetClass, rc,
		NULL);
	cw->tc = XtVaCreateManagedWidget("t3", xmTextFieldWidgetClass, rc,
		NULL);
	cw->td = XtVaCreateManagedWidget("t4", xmTextFieldWidgetClass, rc,
		NULL);
	cw->te = XtVaCreateManagedWidget("t5", xmTextFieldWidgetClass, rc,
		NULL);

	frame = XtVaCreateManagedWidget("dataFrame",
		xmFrameWidgetClass, toprc,
		NULL);
	rc = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass, frame,
		NULL);
	XtVaCreateManagedWidget("xl", xmLabelGadgetClass, rc,
		NULL);
	cw->xtitle = XtVaCreateManagedWidget("xt", xmTextFieldWidgetClass, rc,
		NULL);
	cw->xlog = XtVaCreateManagedWidget("xlog",
		xmToggleButtonGadgetClass, rc,
		NULL);
	XtVaCreateManagedWidget("yl", xmLabelGadgetClass, rc,
		NULL);
	cw->ytitle = XtVaCreateManagedWidget("yt", xmTextFieldWidgetClass, rc,
		NULL);
	cw->ylog = XtVaCreateManagedWidget("ylog",
		xmToggleButtonGadgetClass, rc,
		NULL);

	cw->title = XtVaCreateManagedWidget("title", xmTextFieldWidgetClass, rc,
		NULL);

	return toprc;
}

/*
 * Ok handler
 *	This reads everything from the widgets in the dialog and copies
 *	the information into variables.
 */
void ConfigureGraphOk(Widget w, XtPointer client, XtPointer call)
{
	Widget			f = (Widget)client;
	struct ConfigureWidgets	*cw = NULL;
	char			*s, *p;
	struct rng		rngx;
	int			r;

	MotifSelectGlobal(w);

/* We should only pop down if everything was read correctly.
 *	XtUnmanageChild(configureGraph);
 */
	Global->return_from_error = 1;
	Global->had_error = 0;

	XtVaGetValues(f, XmNuserData, &cw, NULL);

	if (cw == NULL) {
		MessageAppend(True, _("Cannot find XmNuserData\n"));
		XtUnmanageChild(configureGraph);
		Global->return_from_error = 0;
		return;
	}

/* Plot title */
	s = XmTextFieldGetString(cw->title);
	graph_set_title(s);
#ifdef	FREE_TF_STRING
	XtFree(s);
#endif

/* X Axis */
	s = XmTextFieldGetString(cw->xtitle);
	graph_set_axis_title('x', s);
#ifdef	FREE_TF_STRING
	XtFree(s);
#endif
	graph_set_logness('x', 1, XmToggleButtonGadgetGetState(cw->xlog));

/* Y Axis */
	s = XmTextFieldGetString(cw->ytitle);
	graph_set_axis_title('y', s);
#ifdef	FREE_TF_STRING
	XtFree(s);
#endif
	graph_set_logness('y', 1, XmToggleButtonGadgetGetState(cw->ylog));

	s = XmTextFieldGetString(cw->ta);
	graph_set_data_title(0, s);
#ifdef	FREE_TF_STRING
	XtFree(s);
#endif

/* Data 1 */
	p = s = XmTextFieldGetString(cw->x);
	if (strlen(s)) {
		if ((r = parse_cell_or_range(&p, &rngx)) == 0)
			ConversionError(s, _("range"));
		else if (r & RANGE) {
			graph_set_data(0, &rngx);
		} else {
			rngx.hr = rngx.lr;
			rngx.hc = rngx.lc;
			graph_set_data(0, &rngx);
		}
#ifdef	FREE_TF_STRING
		XtFree(s);
#endif
	}

	s = XmTextFieldGetString(cw->tb);
	graph_set_data_title(1, s);
#ifdef	FREE_TF_STRING
	XtFree(s);
#endif

/* Data 2 */
	p = s = XmTextFieldGetString(cw->a);
	if (strlen(s)) {
		if ((r = parse_cell_or_range(&p, &rngx)) == 0)
			ConversionError(s, _("range"));
		else if (r & RANGE) {
			graph_set_data(1, &rngx);
		} else {
			rngx.hr = rngx.lr;
			rngx.hc = rngx.lc;
			graph_set_data(1, &rngx);
		}
#ifdef	FREE_TF_STRING
		XtFree(s);
#endif
	}

	s = XmTextFieldGetString(cw->tc);
	graph_set_data_title(2, s);
#ifdef	FREE_TF_STRING
	XtFree(s);
#endif

/* Data 3 */
	p = s = XmTextFieldGetString(cw->b);
	if (strlen(s)) {
		if ((r = parse_cell_or_range(&p, &rngx)) == 0)
			ConversionError(s, _("range"));
		else if (r & RANGE) {
			graph_set_data(2, &rngx);
		} else {
			rngx.hr = rngx.lr;
			rngx.hc = rngx.lc;
			graph_set_data(2, &rngx);
		}
#ifdef	FREE_TF_STRING
		XtFree(s);
#endif
	}

	s = XmTextFieldGetString(cw->td);
	graph_set_data_title(3, s);
#ifdef	FREE_TF_STRING
	XtFree(s);
#endif

	p = s = XmTextFieldGetString(cw->c);
	if (strlen(s)) {
		if ((r = parse_cell_or_range(&p, &rngx)) == 0)
			ConversionError(s, _("range"));
		else if (r & RANGE) {
			graph_set_data(3, &rngx);
		} else {
			rngx.hr = rngx.lr;
			rngx.hc = rngx.lc;
			graph_set_data(3, &rngx);
		}
		MessageAppend(False, s);
#ifdef	FREE_TF_STRING
		XtFree(s);
#endif
	}

	p = s = XmTextFieldGetString(cw->d);
	if (strlen(s)) {
		if ((r = parse_cell_or_range(&p, &rngx)) == 0)
			ConversionError(s, _("range"));
		else if (r & RANGE) {
			graph_set_data(4, &rngx);
		} else {
			rngx.hr = rngx.lr;
			rngx.hc = rngx.lc;
			graph_set_data(4, &rngx);
		}
		MessageAppend(False, s);
#ifdef	FREE_TF_STRING
		XtFree(s);
#endif
	}

	ConfigureXYOk();
	ConfigurePieOk();
	ConfigureBarOk();

	/* Pop down */
	if (Global->had_error == 0)
		XtUnmanageChild(configureGraph);

	Global->return_from_error = 0;
}

/*
 * Revert configure graph fields to their current internal values.
 *	This is needed for popping up the dialog after it's been changed by
 *	the user but he's hit Cancel.
 *	Or for values read from a file. (new to 1.6.14)
 */
void ConfigureGraphReset(Widget f)
{
	struct ConfigureWidgets	*cw = NULL;
	struct rng		r;
	char			*s;
	extern struct rng	graph_get_data(int);

	XtVaGetValues(f, XmNuserData, &cw, NULL);

	if (cw == NULL) {
		MessageAppend(True, _("Cannot find XmNuserData\n"));
		return;
	}

#if XmVERSION > 1
	XtVaSetValues(ConfigureGraphNotebook, XmNcurrentPageNumber, 1, NULL);
#endif

	r = graph_get_data(0);
	s = range_name(&r);
	XmTextFieldSetString(cw->x, s);

	r = graph_get_data(1);
	s = range_name(&r);
	XmTextFieldSetString(cw->a, s);

	r = graph_get_data(2);
	s = range_name(&r);
	XmTextFieldSetString(cw->b, s);

	r = graph_get_data(3);
	s = range_name(&r);
	XmTextFieldSetString(cw->c, s);

	r = graph_get_data(4);
	s = range_name(&r);
	XmTextFieldSetString(cw->d, s);

	s = graph_get_title();
	XmTextFieldSetString(cw->title, s);

	s = graph_get_axis_title('x');
	XmTextFieldSetString(cw->xtitle, s);

	s = graph_get_axis_title('y');
	XmTextFieldSetString(cw->ytitle, s);

	s = graph_get_data_title(0);
	XmTextFieldSetString(cw->ta, s);
	s = graph_get_data_title(1);
	XmTextFieldSetString(cw->tb, s);
	s = graph_get_data_title(2);
	XmTextFieldSetString(cw->tc, s);
	s = graph_get_data_title(3);
	XmTextFieldSetString(cw->td, s);
	s = graph_get_data_title(4);
	XmTextFieldSetString(cw->te, s);

	ConfigureXYReset();
	ConfigurePieReset();
	ConfigureBarReset();
}

/*
 * Pop up the dialog for configuring graphs.
 * If necessary, create the widgets first.
 */
void ConfigureGraph(Widget w, XtPointer client, XtPointer call)
{
	Widget		ok, cancel, help, p, fr;
	int		ac;
	Arg		al[5];

	MotifSelectGlobal(w);

	if (! configureGraph) {
		ac = 0;
		XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
		configureGraph = XmCreateTemplateDialog(mw, "configureGraph",
			al, ac);

#if XmVERSION > 1
		ConfigureGraphNotebook = XmCreateNotebook(configureGraph,
			"configureGraphNotebook",
			NULL, 0);
		XtManageChild(ConfigureGraphNotebook);

		/* The data */
		ConfigureGraphInside = CreateConfigureGraph(ConfigureGraphNotebook);
		XtManageChild(ConfigureGraphInside);

		p = XtVaCreateManagedWidget("datatab", xmPushButtonWidgetClass,
			ConfigureGraphNotebook,
				XmNnotebookChildType, XmMAJOR_TAB,
			NULL);

		/* Pie Chart specific stuff */
		fr = ConfigurePieChart(ConfigureGraphNotebook);
		XtManageChild(fr);

		p = XtVaCreateManagedWidget("PieTab", xmPushButtonWidgetClass,
			ConfigureGraphNotebook,
				XmNnotebookChildType, XmMAJOR_TAB,
			NULL);

		/* XY Chart specific stuff */
		fr = ConfigureXYChart(ConfigureGraphNotebook);
		XtManageChild(fr);

		p = XtVaCreateManagedWidget("XYTab", xmPushButtonWidgetClass,
			ConfigureGraphNotebook,
				XmNnotebookChildType, XmMAJOR_TAB,
			NULL);

		/* Bar Chart specific stuff */
		fr = ConfigureBarChart(ConfigureGraphNotebook);
		XtManageChild(fr);

		p = XtVaCreateManagedWidget("BarTab", xmPushButtonWidgetClass,
			ConfigureGraphNotebook,
				XmNnotebookChildType, XmMAJOR_TAB,
			NULL);
#endif

		/* Buttons */
		ok = XtVaCreateManagedWidget("ok", xmPushButtonGadgetClass,
			configureGraph,
			NULL);
		cancel = XtVaCreateManagedWidget("cancel",
			xmPushButtonGadgetClass, configureGraph,
			NULL);
		help = XtVaCreateManagedWidget("help", xmPushButtonGadgetClass,
			configureGraph,
			NULL);

		XtAddCallback(ok, XmNactivateCallback, ConfigureGraphOk, ConfigureGraphInside);
		XtAddCallback(cancel, XmNactivateCallback, CancelTemplate, configureGraph);

		/* FIX ME need something to call the help system */
	}

	ConfigureGraphReset(ConfigureGraphInside);
	XtManageChild(configureGraph);
}
#endif

/*
 * Plotutils
 */

#ifndef	HAVE_LIBPLOT
void NoPlotutils(Widget w, XtPointer client, XtPointer call)
{
	XmString	xms;
	char		*s = NULL;

	MotifSelectGlobal(w);

	XtVaGetValues(w, XmNlabelString, &xms, NULL);
	if (XmStringGetLtoR(xms, XmFONTLIST_DEFAULT_TAG, &s)) {
		MessageAppend(False, "%s: GNU Plotutils not linked in\n", s);
		XtFree(s);
	}
	XmStringFree(xms);
}
#endif

/*
 * RedrawPlotutilsWindow is called when the plotutils widget needs
 * a refresh, which may just be when it's scrolling.
 */
static void RedrawPlotutilsWindow(Widget w, XtPointer client, XtPointer call)
{
	void (*f)(char *, FILE *) = client;

	MotifSelectGlobal(w);

	(*f)("Xdrawable", stdout);
}

/*
 * Show a GNU Plotutils chart in a Motif widget.
 *
 * The client parameter is the function (from plot.c) which will
 * handle the plotting. We pass it to RedrawPlotutilsWindow() too.
 */
void PuShowChart(Widget w, XtPointer client, XtPointer call)
{
#ifdef	HAVE_LIBPLOT
	static Window	window;
	Widget		dlg, d, cancel, help, sw;
	Arg		al[5];
	int		ac;
	void (*f)(char *, FILE *) = client;

	MotifSelectGlobal(w);

	ac = 0;
	XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
	dlg = XmCreateTemplateDialog(toplevel, "printDialog", al, ac);

	ac = 0;
#if 0
	XtSetArg(al[ac], XmNwidth, 500); ac++;
	XtSetArg(al[ac], XmNheight, 500); ac++;
#endif
	XtSetArg(al[ac], XmNwidth, Global->PlotGlobal->img_width + 100); ac++;
	XtSetArg(al[ac], XmNheight, Global->PlotGlobal->img_height + 100); ac++;
	XtSetArg(al[ac], XmNscrollingPolicy, XmAUTOMATIC); ac++;
	sw = XmCreateScrolledWindow(dlg, "scroll", al, ac);
	XtManageChild(sw);

	ac = 0;
	XtSetArg(al[ac], XmNwidth, Global->PlotGlobal->img_width); ac++;
	XtSetArg(al[ac], XmNheight, Global->PlotGlobal->img_height); ac++;
	d = XmCreateDrawingArea(sw, "DrawingArea", al, ac);
	XtManageChild(d);

	XtAddCallback(d, XmNexposeCallback, RedrawPlotutilsWindow, f);

	XtManageChild(dlg);

	window = XtWindow(d);
	if (window == 0) {
		MessageAppend(True, "PuShowChart: 0 window\n");
		return;
	}

	PuX(XtDisplayOfObject(w), window);
	PlotInit();

	cancel = XtVaCreateManagedWidget("dismiss", xmPushButtonGadgetClass, dlg, NULL);
	XtAddCallback(cancel, XmNactivateCallback, DestroyTemplate, dlg);

	help = XtVaCreateManagedWidget("help",
		xmPushButtonGadgetClass, dlg, NULL);
	XtAddCallback(help, XmNactivateCallback, helpUsingCB,
		(XtPointer)"#HelpPlotting");

	(*f)("Xdrawable", stdout);
#else
	NoPlotutils(w, client, call);
#endif
}

/*
 * Printing
 *	need to pop up a dialog to select a file and a GNU PlotUtils plotter
 *	then actually doit
 */
void PuSelectPlotter(Widget w, XtPointer client, XtPointer call)
{
	XmString	xms;
	char		*p;
	int		i;

	MotifSelectGlobal(w);

	PuPlotter = (int)client;

	plotutils_set_device(PuPlotter);

	for (i=0; PlotutilsDeviceArray[i].pus; i++)
		if (PlotutilsDeviceArray[i].t == PuPlotter)
			break;

#if 0
	fprintf(stderr, "PuSelectPlotter(%s)\n", PlotutilsDeviceArray[i].desc);
#endif

	p = malloc(strlen(PlotutilsDeviceArray[i].ext) + 8);
	strcpy(p, "*.");
	strcat(p, PlotutilsDeviceArray[i].ext);
	xms = XmStringCreateSimple(p);
	free(p);

	XtVaSetValues(pufsd, XmNpattern, xms, NULL);
	XmStringFree(xms);
}

Widget CreatePlotterOption(Widget parent, XtCallbackProc f)
{
	Arg		al[10];
	int		ac = 0, r, i;
	XmString	xms;
	Widget		cb, menu, b;

	menu = XmCreatePulldownMenu(parent, "optionMenu", NULL, 0);
	ac = 0;
	XtSetArg(al[ac], XmNsubMenuId, menu); ac++;
	xms = XmStringCreateSimple(_("Plotter Type"));
	XtSetArg(al[ac], XmNlabelString, xms); ac++;
	cb = XmCreateOptionMenu(parent, "optionCB", al, ac);
	XtManageChild(cb);
	XmStringFree(xms);

	for (i=0; PlotutilsDeviceArray[i].pus; i++) {
		b = XtVaCreateManagedWidget(PlotutilsDeviceArray[i].pus, xmPushButtonGadgetClass,
			menu,
			NULL);
		XtAddCallback(b, XmNactivateCallback, f, (XtPointer)PlotutilsDeviceArray[i].t);
	}

	return menu;
}

void PuPrintOk(Widget w, XtPointer client, XtPointer call)
{
#ifdef	HAVE_LIBPLOT
	XmFileSelectionBoxCallbackStruct	*cbp = (XmFileSelectionBoxCallbackStruct *)call;
	char					*fn;

	MotifSelectGlobal(w);

	if (! XmStringGetLtoR(cbp->value, XmFONTLIST_DEFAULT_TAG, &fn)) {
		/* FIX ME */
		return;
	}

	plotutils_set_filename(fn);
	graph_plot();
#endif
}

void PuPrintDialog(Widget w, XtPointer client, XtPointer call)
{
	Arg		al[10];
	int		ac = 0;
	XmString	xms;
	Widget		cb, menu, b;

	MotifSelectGlobal(w);

	ac = 0;
	XtSetArg(al[ac], XmNautoUnmanage, True); ac++;
	xms = XmStringCreateSimple(_("*.oleo"));
	XtSetArg(al[ac], XmNpattern, xms); ac++;

	pufsd = XmCreateFileSelectionDialog(toplevel, "puselectfile",
		al, ac);
	XtDestroyWidget(
		XmFileSelectionBoxGetChild(pufsd, XmDIALOG_HELP_BUTTON));
	XmStringFree(xms);
	XtAddCallback(pufsd, XmNokCallback, PuPrintOk, NULL);

	/* Option menu */
	menu = CreatePlotterOption(pufsd, PuSelectPlotter);
}

void PuPrint(Widget w, XtPointer client, XtPointer call)
{
#ifdef	HAVE_LIBPLOT
	enum graph_type	gt = (enum graph_type)client;

	MotifSelectGlobal(w);

	plotutils_set_graph_type(gt);

	if (! pufsd) PuPrintDialog(w, client, call);
	XtManageChild(pufsd);
#endif
}

#ifdef	HAVE_LIBPLOT
static void TickTypeCB(Widget w, XtPointer client, XtPointer call)
{
	int	n = (int)client;
	int	axis = n / 256;
	int	val = n % 256;
	tick_type_e	v = (tick_type_e)val;
	Widget	tw;

	MotifSelectGlobal(w);

	if (axis == 0) {
		Global->MotifGlobal->xtick = val;
		tw = Global->MotifGlobal->xtickfmt;
	} else {
		Global->MotifGlobal->ytick = val;
		tw = Global->MotifGlobal->ytickfmt;
	}

	switch (v) {
	SP_TICK_DEFAULT:
	SP_TICK_NONE:
#if 0
		fprintf(stderr, "TickTypeCB(axis %d type %d) -> insensitive \n", axis, val);
#endif
		XmTextFieldSetEditable(tw, False);
		break;
	SP_TICK_PRINTF:
	SP_TICK_STRFTIME:
#if 0
		fprintf(stderr, "TickTypeCB(axis %d type %d) -> sensitive \n", axis, val);
#endif
		XmTextFieldSetEditable(tw, True);
		break;
	default: {}
#if 0
		fprintf(stderr, "Huh ? TickTypeCB(axis %d type %d) -> sensitive \n", axis, val);
#endif
	}
#if 0
	fprintf(stderr, "SP_TICK_DEFAULT %d\n", SP_TICK_DEFAULT);
	fprintf(stderr, "SP_TICK_NONE %d\n", SP_TICK_NONE);
	fprintf(stderr, "SP_TICK_PRINTF %d\n", SP_TICK_PRINTF);
	fprintf(stderr, "SP_TICK_STRFTIME %d\n", SP_TICK_STRFTIME);
#endif
}

/*
 * A piece of dialog for configuring XY chars
 *	Needs to be managed by the caller.
 */
Widget ConfigureXYChart(Widget parent)
{
	Widget	frame, form, w, te, l, t;
	Arg	al[10];
	int	ac;
	XmString	xms;

	MotifSelectGlobal(parent);

	ac = 0;
	frame = XmCreateFrame(parent, "puselectfile", al, ac);

	w = XtVaCreateManagedWidget("configureXYChartFrameTitle",
			xmLabelGadgetClass,		frame,
			XmNchildType,			XmFRAME_TITLE_CHILD,
			XmNchildVerticalAlignment,	XmALIGNMENT_CENTER,
		NULL);

	form = XtVaCreateManagedWidget("configureXYChartForm", xmFormWidgetClass,
		frame,
		NULL);

	/* X Axis */
	XYxAutoToggle =
	t = XtVaCreateManagedWidget("xAutoToggle", xmToggleButtonGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
		NULL);
	l = XtVaCreateManagedWidget("xMinLabel", xmLabelGadgetClass, form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		t,
			XmNleftOffset,		10,
		NULL);
	XYxMinText =
	te = XtVaCreateManagedWidget("xMinText", xmTextFieldWidgetClass, form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		l,
			XmNleftOffset,		10,
		NULL);
	l = XtVaCreateManagedWidget("xMaxLabel", xmLabelGadgetClass, form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		te,
			XmNleftOffset,		10,
		NULL);
	XYxMaxText =
	te = XtVaCreateManagedWidget("xMaxText", xmTextFieldWidgetClass, form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		l,
			XmNleftOffset,		10,
		NULL);

	/* Y Axis */
	XYyAutoToggle =
	t = XtVaCreateManagedWidget("yAutoToggle", xmToggleButtonGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopWidget,		te,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
		NULL);
	l = XtVaCreateManagedWidget("yMinLabel", xmLabelGadgetClass, form,
			XmNtopAttachment,	XmATTACH_OPPOSITE_WIDGET,
			XmNtopOffset,		0,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		t,
			XmNleftOffset,		10,
		NULL);
	XYyMinText =
	te = XtVaCreateManagedWidget("yMinText", xmTextFieldWidgetClass, form,
			XmNtopAttachment,	XmATTACH_OPPOSITE_WIDGET,
			XmNtopOffset,		0,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		l,
			XmNleftOffset,		10,
		NULL);
	l = XtVaCreateManagedWidget("yMaxLabel", xmLabelGadgetClass, form,
			XmNtopAttachment,	XmATTACH_OPPOSITE_WIDGET,
			XmNtopOffset,		0,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		te,
			XmNleftOffset,		10,
		NULL);
	XYyMaxText =
	te = XtVaCreateManagedWidget("yMaxText", xmTextFieldWidgetClass, form,
			XmNtopAttachment,	XmATTACH_OPPOSITE_WIDGET,
			XmNtopOffset,		0,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		l,
			XmNleftOffset,		10,
		NULL);

	/* Line type */

	/* Lines to offscreen data points */
	lineToOffscreen = XtVaCreateManagedWidget("lineToOffscreen", xmToggleButtonGadgetClass,
		form,
			XmNtopAttachment,       XmATTACH_WIDGET,
			XmNtopWidget,           te,
			XmNtopOffset,           10,
			XmNleftAttachment,      XmATTACH_FORM,
			XmNleftOffset,          10,
		NULL);

	/* Axis tick labels */
	Global->MotifGlobal->XLogToggle = XtVaCreateManagedWidget("xLogToggle",
		xmToggleButtonGadgetClass,
		form,
			XmNtopAttachment,       XmATTACH_WIDGET,
			XmNtopWidget,           lineToOffscreen,
			XmNtopOffset,           10,
			XmNleftAttachment,      XmATTACH_FORM,
			XmNleftOffset,          10,
		NULL);

	Global->MotifGlobal->xticklblmenu = XmCreatePulldownMenu(form, "xticklblmenu", NULL, 0);
	ac=0;
	XtSetArg(al[ac], XmNsubMenuId, Global->MotifGlobal->xticklblmenu); ac++;
	xms = XmStringCreateSimple(_("X Axis Tick Label Type"));
	XtSetArg(al[ac], XmNlabelString, xms); ac++;
	XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
	XtSetArg(al[ac], XmNtopWidget, lineToOffscreen); ac++;
	XtSetArg(al[ac], XmNtopOffset, 10); ac++;
	XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
	XtSetArg(al[ac], XmNleftWidget, Global->MotifGlobal->XLogToggle); ac++;
	XtSetArg(al[ac], XmNleftOffset, 10); ac++;
	Global->MotifGlobal->xticklbloption = XmCreateOptionMenu(form, "xticklbloption", al, ac);
	XtManageChild(Global->MotifGlobal->xticklbloption);

	w = XtVaCreateManagedWidget("xtickdefault", xmPushButtonWidgetClass,
		Global->MotifGlobal->xticklblmenu,
		NULL);
	Global->MotifGlobal->ticktype_w[0][SP_TICK_DEFAULT] = w;
	XtAddCallback(w, XmNactivateCallback, TickTypeCB, (XtPointer)(0x000 | SP_TICK_DEFAULT));
	w = XtVaCreateManagedWidget("xticknone", xmPushButtonWidgetClass,
		Global->MotifGlobal->xticklblmenu,
		NULL);
	Global->MotifGlobal->ticktype_w[0][SP_TICK_NONE] = w;
	XtAddCallback(w, XmNactivateCallback, TickTypeCB, (XtPointer)(0x000 | SP_TICK_NONE));
	w = XtVaCreateManagedWidget("xtickprintf", xmPushButtonWidgetClass,
		Global->MotifGlobal->xticklblmenu,
		NULL);
	Global->MotifGlobal->ticktype_w[0][SP_TICK_PRINTF] = w;
	XtAddCallback(w, XmNactivateCallback, TickTypeCB, (XtPointer)(0x000 | SP_TICK_PRINTF));
	w = XtVaCreateManagedWidget("xtickstrftime", xmPushButtonWidgetClass,
		Global->MotifGlobal->xticklblmenu,
		NULL);
	Global->MotifGlobal->ticktype_w[0][SP_TICK_STRFTIME] = w;
	XtAddCallback(w, XmNactivateCallback, TickTypeCB, (XtPointer)(0x000 | SP_TICK_STRFTIME));

	Global->MotifGlobal->xtickfmt = XtVaCreateManagedWidget("xtickfmt", xmTextFieldWidgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopWidget,		lineToOffscreen,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		Global->MotifGlobal->xticklbloption,
			XmNleftOffset,		10,
		NULL);

	Global->MotifGlobal->YLogToggle = XtVaCreateManagedWidget("yLogToggle",
		xmToggleButtonGadgetClass,
		form,
			XmNtopAttachment,       XmATTACH_WIDGET,
			XmNtopWidget,           Global->MotifGlobal->XLogToggle,
			XmNtopOffset,           10,
			XmNleftAttachment,      XmATTACH_FORM,
			XmNleftOffset,          10,
		NULL);

	Global->MotifGlobal->yticklblmenu = XmCreatePulldownMenu(form, "yticklblmenu", NULL, 0);
	ac=0;
	XtSetArg(al[ac], XmNsubMenuId, Global->MotifGlobal->yticklblmenu); ac++;
	xms = XmStringCreateSimple(_("Y Axis Tick Label Type"));
	XtSetArg(al[ac], XmNlabelString, xms); ac++;
	XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
	XtSetArg(al[ac], XmNtopWidget, Global->MotifGlobal->xtickfmt); ac++;
	XtSetArg(al[ac], XmNtopOffset, 10); ac++;
	XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
	XtSetArg(al[ac], XmNleftWidget, Global->MotifGlobal->YLogToggle); ac++;
	XtSetArg(al[ac], XmNleftOffset, 10); ac++;
	Global->MotifGlobal->yticklbloption = XmCreateOptionMenu(form, "yticklbloption", al, ac);
	XtManageChild(Global->MotifGlobal->yticklbloption);

	w = XtVaCreateManagedWidget("ytickdefault", xmPushButtonWidgetClass,
		Global->MotifGlobal->yticklblmenu,
		NULL);
	Global->MotifGlobal->ticktype_w[1][SP_TICK_DEFAULT] = w;
	XtAddCallback(w, XmNactivateCallback, TickTypeCB, (XtPointer)(0x100 | SP_TICK_DEFAULT));
	w = XtVaCreateManagedWidget("yticknone", xmPushButtonWidgetClass,
		Global->MotifGlobal->yticklblmenu,
		NULL);
	Global->MotifGlobal->ticktype_w[1][SP_TICK_NONE] = w;
	XtAddCallback(w, XmNactivateCallback, TickTypeCB, (XtPointer)(0x100 | SP_TICK_NONE));
	w = XtVaCreateManagedWidget("ytickprintf", xmPushButtonWidgetClass,
		Global->MotifGlobal->yticklblmenu,
		NULL);
	Global->MotifGlobal->ticktype_w[1][SP_TICK_PRINTF] = w;
	XtAddCallback(w, XmNactivateCallback, TickTypeCB, (XtPointer)(0x100 | SP_TICK_PRINTF));
	w = XtVaCreateManagedWidget("ytickstrftime", xmPushButtonWidgetClass,
		Global->MotifGlobal->yticklblmenu,
		NULL);
	Global->MotifGlobal->ticktype_w[1][SP_TICK_STRFTIME] = w;
	XtAddCallback(w, XmNactivateCallback, TickTypeCB, (XtPointer)(0x100 | SP_TICK_STRFTIME));

	Global->MotifGlobal->ytickfmt = XtVaCreateManagedWidget("ytickfmt", xmTextFieldWidgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopWidget,		Global->MotifGlobal->XLogToggle,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		Global->MotifGlobal->xticklbloption,
			XmNleftOffset,		10,
		NULL);

	return frame;
}

void ConfigureXYOk(void)
{
	char	*s;
	int	b;

	s = XmTextFieldGetString(XYxMinText);
	graph_set_axis_lo('x', s);

	s = XmTextFieldGetString(XYxMaxText);
	graph_set_axis_hi('x', s);

	s = XmTextFieldGetString(XYyMinText);
	graph_set_axis_lo('y', s);

	s = XmTextFieldGetString(XYyMaxText);
	graph_set_axis_hi('y', s);

	graph_set_axis_auto(0, XmToggleButtonGadgetGetState(XYxAutoToggle));
	graph_set_axis_auto(1, XmToggleButtonGadgetGetState(XYyAutoToggle));
	graph_set_linetooffscreen(XmToggleButtonGadgetGetState(lineToOffscreen));

	graph_set_axis_ticks(0,
		/* tick label type */	Global->MotifGlobal->xtick,
		/* format string */	XmTextFieldGetString(Global->MotifGlobal->xtickfmt));
	graph_set_axis_ticks(1,
		/* tick label type */	Global->MotifGlobal->ytick,
		/* format string */	XmTextFieldGetString(Global->MotifGlobal->ytickfmt));

	/* FIX ME treat these */
	b = XmToggleButtonGadgetGetState(Global->MotifGlobal->YLogToggle);
	b = XmToggleButtonGadgetGetState(Global->MotifGlobal->YLogToggle);
}

static void TextFieldSetFloat(Widget w, float f)
{
	char	t[32];

	sprintf(t, "%f", f);
	XmTextFieldSetString(w, t);
}

void ConfigureXYReset(void)
{
	XmToggleButtonGadgetSetState(XYxAutoToggle, graph_get_axis_auto(0), False);
	TextFieldSetFloat(XYxMinText, graph_get_axis_lo(0));
	TextFieldSetFloat(XYxMaxText, graph_get_axis_hi(0));
	XmToggleButtonGadgetSetState(XYyAutoToggle, graph_get_axis_auto(1), False);
	TextFieldSetFloat(XYyMinText, graph_get_axis_lo(1));
	TextFieldSetFloat(XYyMaxText, graph_get_axis_hi(1));
	XmToggleButtonGadgetSetState(lineToOffscreen, graph_get_linetooffscreen(), False);

	XtVaSetValues(Global->MotifGlobal->xticklbloption, XmNmenuHistory, 
		Global->MotifGlobal->ticktype_w[0][Global->PlotGlobal->ticktype[0]], NULL);
	XtVaSetValues(Global->MotifGlobal->yticklbloption, XmNmenuHistory, 
		Global->MotifGlobal->ticktype_w[1][Global->PlotGlobal->ticktype[1]], NULL);

	XmTextFieldSetString(Global->MotifGlobal->xtickfmt, graph_get_axis_tickformat(0));
	XmTextFieldSetString(Global->MotifGlobal->ytickfmt, graph_get_axis_tickformat(1));
}

Widget ConfigurePieChart(Widget parent)
{
	Widget	frame, form, w, te, l, t;
	Arg	al[10];
	int	ac;

	ac = 0;
	frame = XmCreateFrame(parent, "configurePieChartFrame", al, ac);

	w = XtVaCreateManagedWidget("configurePieChartFrameTitle",
			xmLabelGadgetClass,		frame,
			XmNchildType,			XmFRAME_TITLE_CHILD,
			XmNchildVerticalAlignment,	XmALIGNMENT_CENTER,
		NULL);

	form = XtVaCreateManagedWidget("configurePieChartForm", xmFormWidgetClass,
		frame,
		NULL);
	t = XtVaCreateManagedWidget("pie3d", xmToggleButtonGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
		NULL);

	return frame;
}

void ConfigurePieOk(void)
{
}

void ConfigurePieReset(void)
{
}

Widget ConfigureBarChart(Widget parent)
{
	Widget	frame, form, w, te, l, t;
	Arg	al[10];
	int	ac;

	ac = 0;
	frame = XmCreateFrame(parent, "configureBarChartFrame", al, ac);

	w = XtVaCreateManagedWidget("configureBarChartFrameTitle",
			xmLabelGadgetClass,		frame,
			XmNchildType,			XmFRAME_TITLE_CHILD,
			XmNchildVerticalAlignment,	XmALIGNMENT_CENTER,
		NULL);

	form = XtVaCreateManagedWidget("configureBarChartForm", xmFormWidgetClass,
		frame,
		NULL);
	t = XtVaCreateManagedWidget("stackToggle", xmToggleButtonGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
		NULL);

	return frame;
}

void ConfigureBarOk(void)
{
}

void ConfigureBarReset(void)
{
}
#endif

/*
 * Print
 */
struct PrintWidgets {
	Widget	rangeTF, fileTF, printerTF, programTF;
	Widget	PrinterToggle, FileToggle, ProgramToggle;
} PrintWidgets;

/*
 * Print the spreadsheet
 */
void ReallyPrintCB(Widget w, XtPointer client, XtPointer call)
{
	/* FIX ME
	 * This is simplistic, we need to print to either a file,
	 * selected by the user from a dialog, or to a printer,
	 * also selected by the user.
	 */
	FILE		*fp = NULL;
	struct rng	rng;
	char		*fn = NULL, *s, *p, *pr = NULL;
	int		r;

	MotifSelectGlobal(w);

	XtUnmanageChild(PrintDialog);

	if (XmToggleButtonGadgetGetState(PrintWidgets.PrinterToggle)) {
		char	cmd[64];

		pr = XmTextFieldGetString(PrintWidgets.printerTF);
		if (pr && strlen(pr)) {
			sprintf(cmd, "lpr -P%s", pr);
		} else if (strlen(AppRes.printer)) {
			sprintf(cmd, "lpr -P%s", AppRes.printer);
			pr = AppRes.printer;
		} else {
			sprintf(cmd, "lpr");
			pr = "[default printer]";
		}

		fp = popen(cmd, "w");
		if (fp == NULL) {
			MessageAppend(True, "Couldn't print to printer %s\n", AppRes.printer);
			return;
		}
	} else if (XmToggleButtonGadgetGetState(PrintWidgets.FileToggle)) {
		fn = XmTextFieldGetString(PrintWidgets.fileTF);

		if (fn)
			fp = fopen(fn, "w");
		if (! fp) {
			MessageAppend(True, "Couldn't write to file %s\n", fn);
#ifdef	FREE_TF_STRING
			XtFree(fn);
#endif
			return;
		}
	} else if (XmToggleButtonGadgetGetState(PrintWidgets.ProgramToggle)) {
		fp = popen(AppRes.program, "w");

		if (fp == NULL) {
			MessageAppend(True, "Couldn't print to program %s\n", AppRes.program);
			return;
		}
	} else {
		MessageAppend(True, _("Internal error: can't find what to print to\n"));
		return;
	}

	p = s = XmTextFieldGetString(PrintWidgets.rangeTF);
	if ((r = parse_cell_or_range(&p, &rng)) == 0) {
		ConversionError(s, _("range"));

		/* FIX ME Which range is the complete spreadsheet ? */
		rng.lr = 1;
		rng.lc = 1;
		rng.hc = 40;
		rng.hr = 80;

	} else if (r & RANGE) {
		;
	} else {
		rng.hr = rng.lr;
		rng.hc = rng.lc;
	}
#ifdef	FREE_TF_STRING
	XtFree(s);
#endif

	print_region_cmd(&rng, fp);
	if (XmToggleButtonGadgetGetState(PrintWidgets.FileToggle))
		fclose(fp);
	else
		pclose(fp);

	if (Global->zoom < 0.90 || Global->zoom > 1.1) {
		int	z = Global->zoom * 100;
		if (fn)
		    MessageAppend(False, "Printed %s to file %s, at %d percent size\n",
			range_name(&rng), fn, z);
		else
		    MessageAppend(False, "Printed %s to printer %s, at %d percent size\n",
			range_name(&rng), pr, z);
	} else if (fn) {
		MessageAppend(False, "Printed %s to file %s\n", range_name(&rng), fn);
	} else {
		MessageAppend(False, "Printed %s to printer %s\n", range_name(&rng), pr);
	}

#ifdef	FREE_TF_STRING
	XtFree(fn);
#endif
}

static void MotifSetPrintType(Widget w, XtPointer client, XtPointer call)
{
	char	*s = (char *)client;

	MotifSelectGlobal(w);

	MessageAppend(False, "Set printer type to %s\n", s);
	PrintSetType(s);
}

static void MotifSetPrintPage(Widget w, XtPointer client, XtPointer call)
{
	int	i = (int) client;

	MotifSelectGlobal(w);

	MessageAppend(False, "Set page format to %s\n", PrintGetPageName(i));
	PrintSetPage(PrintGetPageName(i));
}

/*
 * Select a file to print into
 */
void PrintBrowseFileCBOk(Widget w, XtPointer client, XtPointer call)
{
	char	*fn;
	XmFileSelectionBoxCallbackStruct *cbp = (XmFileSelectionBoxCallbackStruct *)call;

	MotifSelectGlobal(w);

	if (! XmStringGetLtoR(cbp->value, XmFONTLIST_DEFAULT_TAG, &fn))
		return;

	XmTextFieldSetString(PrintWidgets.fileTF, fn);
}

void PrintBrowseFileCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	if (! pfsd) {
		XmString	xms;
		Arg		al[5];
		int		ac = 0;

		xms = XmStringCreateSimple(_("*.out"));
		XtSetArg(al[ac], XmNpattern, xms); ac++;
		XtSetArg(al[ac], XmNautoUnmanage, True); ac++;

		pfsd = XmCreateFileSelectionDialog(toplevel, "selectfile",
			al, ac);
		XtDestroyWidget(
			XmFileSelectionBoxGetChild(pfsd, XmDIALOG_HELP_BUTTON));
		XtAddCallback(pfsd, XmNokCallback, PrintBrowseFileCBOk, NULL);

		XmStringFree(xms);
	}

	XtManageChild(pfsd);
}

void CupsSelectPrinter(Widget w, XtPointer client, XtPointer call)
{
	Widget tf = (Widget)client;

	MotifSelectGlobal(w);

	XmTextFieldSetString(tf, XtName(w));
}

/*
 * The print dialog :
 *	- select where to print to (program, printer, file)
 *	- paper size
 *	- print range
 */
Widget MotifCreatePrintDialog(Widget s)
{
	Widget		form, menu, cb, w, frame, radio, form2, cb1, cupsmenu,
			cupsOption;
	int		npages, i, ac, nprinters;
	Arg		al[5];
	XmString	xms;
	int		defaultprint = 0;
	char		**printers;

	MotifSelectGlobal(s);

	ac = 0;
	form = XmCreateForm(s, "printForm", al, ac);

	/* Check value of resource */
#ifdef	HAVE_STRCASECMP
	if (strcasecmp(AppRes.defaultPrintTo, "printer") != 0
	    && strcasecmp(AppRes.defaultPrintTo, "program") != 0
	    && strcasecmp(AppRes.defaultPrintTo, "file") != 0)
#else
	if (strcmp(AppRes.defaultPrintTo, "printer") != 0
	    && strcmp(AppRes.defaultPrintTo, "program") != 0
	    && strcmp(AppRes.defaultPrintTo, "file") != 0)
#endif
	{
		defaultprint = 1;

		MessageAppend(True, _("Value of defaultPrintTo resource is invalid, "
			"should be one of %s, %s, or %s\n"),
			"printer", "file", "program");

	}

	/* Destination */
	frame = XtVaCreateManagedWidget("printDestinationFrame",
			xmFrameWidgetClass, form,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNtopOffset,		10,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
		NULL);

	w = XtVaCreateManagedWidget("printDestinationFrameTitle",
			xmLabelGadgetClass,		frame,
			XmNchildType,			XmFRAME_TITLE_CHILD,
			XmNchildVerticalAlignment,	XmALIGNMENT_CENTER,
		NULL);

	radio = XtVaCreateManagedWidget("printDestinationRadio",
				xmRowColumnWidgetClass, frame,
			XmNradioBehavior,	True,
			XmNradioAlwaysOne,	True,
			XmNnumColumns,		3,
			XmNorientation,		XmHORIZONTAL,
		NULL);

	PrintWidgets.PrinterToggle =
	w = XtVaCreateManagedWidget("printer", xmToggleButtonGadgetClass,
		radio, NULL);
#ifdef	HAVE_STRCASECMP
	if (strcasecmp(AppRes.defaultPrintTo, "printer") == 0 || defaultprint)
#else
	if (strcmp(AppRes.defaultPrintTo, "printer") == 0 || defaultprint)
#endif
		XmToggleButtonGadgetSetState(w, True, False);

	PrintWidgets.printerTF =
	w = XtVaCreateManagedWidget("printerTF", xmTextFieldWidgetClass,
			radio, NULL);
	XmTextFieldSetString(w, AppRes.printer);

	/*
	 * Build an option menu with a list of known printers.
	 *
	 * We can do this if we have CUPS (www.cups.org), otherwise
	 * make the damned thing insensitive.
	 */
	cupsmenu = XmCreatePulldownMenu(radio, "cupsMenu", NULL, 0);
	ac=0;
	XtSetArg(al[ac], XmNsubMenuId, cupsmenu); ac++;
	xms = XmStringCreateSimple(_("Select Printer"));
	XtSetArg(al[ac], XmNlabelString, xms); ac++;
	cupsOption = XmCreateOptionMenu(radio, "printerOption", al, ac);
	XtManageChild(cupsOption);
#ifndef	HAVE_LIBCUPS
	XtSetSensitive(cupsOption, False);
#endif
	XmStringFree(xms);

#ifdef	HAVE_LIBCUPS
	printers = NULL;
	nprinters = cupsGetPrinters(&printers);

	for (i=0; i < nprinters; i++) {
		Widget	b;
		b = XtVaCreateManagedWidget(printers[i],
			xmPushButtonGadgetClass, cupsmenu, NULL);
		XtAddCallback(b, XmNactivateCallback, CupsSelectPrinter,
			(XtPointer)PrintWidgets.printerTF);
		if (i == 0)
			XtVaSetValues(cupsOption, XmNmenuHistory, b, NULL);
	}
	if (nprinters == 0) {
		w = XtVaCreateManagedWidget("No printers available from CUPS",
			xmPushButtonGadgetClass, cupsmenu, NULL);
		XtAddCallback(w, XmNactivateCallback, CupsSelectPrinter,
			(XtPointer)PrintWidgets.printerTF);
	}
#endif

	PrintWidgets.FileToggle =
	w = XtVaCreateManagedWidget("file", xmToggleButtonGadgetClass,
		radio, NULL);
#ifdef	HAVE_STRCASECMP
	if (strcasecmp(AppRes.defaultPrintTo, "file") == 0)
#else
	if (strcmp(AppRes.defaultPrintTo, "file") == 0)
#endif
		XmToggleButtonGadgetSetState(w, True, False);
	PrintWidgets.fileTF =
	w = XtVaCreateManagedWidget("fileTF", xmTextFieldWidgetClass,
			radio, NULL);
	w = XtVaCreateManagedWidget("fileTFBrowse", xmPushButtonGadgetClass,
			radio, NULL);
	XtAddCallback(w, XmNactivateCallback, PrintBrowseFileCB, NULL);

	PrintWidgets.ProgramToggle =
	w = XtVaCreateManagedWidget("program", xmToggleButtonGadgetClass,
		radio, NULL);
#ifdef	HAVE_STRCASECMP
	if (strcasecmp(AppRes.defaultPrintTo, "program") == 0)
#else
	if (strcmp(AppRes.defaultPrintTo, "program") == 0)
#endif
		XmToggleButtonGadgetSetState(w, True, False);
	PrintWidgets.programTF =
	w = XtVaCreateManagedWidget("programTF", xmTextFieldWidgetClass,
			radio, NULL);
	XmTextFieldSetString(w, AppRes.program);

	/* Paper */
	w = frame;
	frame = XtVaCreateManagedWidget("printPaperFrame",
				xmFrameWidgetClass, form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopWidget,		w,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
		NULL);

	form2 = XtVaCreateManagedWidget("printPaperForm",
				xmFormWidgetClass, frame,
		NULL);

	w = XtVaCreateManagedWidget("printPaperFrameTitle",
			xmLabelGadgetClass,		form2,
			XmNchildType,			XmFRAME_TITLE_CHILD,
			XmNchildVerticalAlignment,	XmALIGNMENT_CENTER,
		NULL);

	menu = XmCreatePulldownMenu(form2, "optionMenu", NULL, 0);
	ac = 0;
	XtSetArg(al[ac], XmNsubMenuId, menu); ac++;
	xms = XmStringCreateSimple(_("Paper Format"));
	XtSetArg(al[ac], XmNlabelString, xms); ac++;
	cb1 = cb = XmCreateOptionMenu(form2, "optionCB", al, ac);
	XtManageChild(cb);
	XmStringFree(xms);

	npages = PrintGetNumPageSizes();

	for (i=0; i<npages; i++) {
		ac = 0;
		xms = XmStringCreateSimple(PrintGetPageName(i));
		XtSetArg(al[ac], XmNlabelString, xms); ac++;
		w = XmCreatePushButtonGadget(menu, PrintGetPageName(i), al, ac);
		if (strcmp(AppRes.paper, PrintGetPageName(i)) == 0)
			XtVaSetValues(menu, XmNmenuHistory, w, NULL);
		XtAddCallback(w, XmNactivateCallback,
			MotifSetPrintPage, (XtPointer)i);
		XmStringFree(xms);
		XtManageChild(w);
	}

	/* Print format */
	menu = XmCreatePulldownMenu(form2, "optionMenu", NULL, 0);
	ac = 0;
	XtSetArg(al[ac], XmNsubMenuId, menu); ac++;
	xms = XmStringCreateSimple(_("Printer Language"));
	XtSetArg(al[ac], XmNlabelString, xms); ac++;
	XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
	XtSetArg(al[ac], XmNleftWidget, cb1); ac++;
	XtSetArg(al[ac], XmNleftOffset, 10); ac++;
	cb = XmCreateOptionMenu(form2, "optionCB", al, ac);
	XtManageChild(cb);
	XmStringFree(xms);

	npages = PrintGetNumPageSizes();

	/* Option menu with printer types (PostScript, PCL, Text, .. ) */
	for (i=0; PrintGetType(i); i++) {
		ac = 0;
		xms = XmStringCreateSimple(PrintGetType(i));
		XtSetArg(al[ac], XmNlabelString, xms); ac++;
		w = XmCreatePushButtonGadget(menu, PrintGetType(i), al, ac);
		XtAddCallback(w, XmNactivateCallback,
			MotifSetPrintType, (XtPointer)PrintGetType(i));
		XmStringFree(xms);
		XtManageChild(w);
	}

	/* Range to print */
	w = frame;
	frame = XtVaCreateManagedWidget("printRangeFrame",
				xmFrameWidgetClass, form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopWidget,		w,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
		NULL);

	w = XtVaCreateManagedWidget("printRangeFrameTitle",
			xmLabelGadgetClass,		frame,
			XmNchildType,			XmFRAME_TITLE_CHILD,
			XmNchildVerticalAlignment,	XmALIGNMENT_CENTER,
		NULL);

	PrintWidgets.rangeTF =
	w = XtVaCreateManagedWidget("printRangeTF", xmTextFieldWidgetClass,
		frame,
		NULL);
	RegisterRangeSelector(w);

	return form;
}

void printCB(Widget w, XtPointer client, XtPointer call)
{
	Widget	d, ok, cancel, help;
	Arg	al[5];
	int	ac;

	MotifSelectGlobal(w);

	if (PrintDialog == NULL) {
		ac = 0;
		XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
		PrintDialog = XmCreateTemplateDialog(mw, "printDialog", al, ac);
		d = MotifCreatePrintDialog(PrintDialog);
		XtManageChild(d);

		ok = XtVaCreateManagedWidget("ok", xmPushButtonGadgetClass, PrintDialog,
			NULL);
		XtAddCallback(ok, XmNactivateCallback, ReallyPrintCB, NULL);

		cancel = XtVaCreateManagedWidget("cancel", xmPushButtonGadgetClass, PrintDialog,
			NULL);
		XtAddCallback(cancel, XmNactivateCallback, CancelTemplate, PrintDialog);

		help = XtVaCreateManagedWidget("help",
			xmPushButtonGadgetClass, PrintDialog, NULL);
	}

	XtManageChild(PrintDialog);
}

void SetDefaultFileCB(Widget w, XtPointer client, XtPointer call)
{
	char	*s, *xx;
	Widget	tf, f;

	MotifSelectGlobal(w);

	f = XtParent(w);

	/* Set default file format */

	/* Set separator in List file format */
	if ((tf = XtNameToWidget(f, "*listSeparatorTF")) != NULL) {
		xx = XmTextFieldGetString(tf);
		s = XtMalloc(strlen(xx) + 8);
		strcpy(s, "list ");
		strcat(s, xx);
		list_set_options(1, s);
		XtFree(s);
	}

	XtPopdown(DefaultFileShell);
}

void CancelTemplate(Widget w, XtPointer client, XtPointer call)
{
	Widget	bb = (Widget)client;

	MotifSelectGlobal(w);
	XtUnmanageChild(bb);
}

void DestroyTemplate(Widget w, XtPointer client, XtPointer call)
{
	Widget	bb = (Widget)client;

	MotifSelectGlobal(w);

	XtDestroyWidget(bb);
}

void CancelDialog(Widget w, XtPointer client, XtPointer call)
{
	Widget	shell = (Widget)client;

	MotifSelectGlobal(w);

	XtPopdown(shell);
}

void DefaultFileFormatCB(Widget w, XtPointer client, XtPointer call)
{
	char		*f = (char *)client;

	MotifSelectGlobal(w);

	file_set_default_format(f);
}

void DefaultFileDialogReset(void)
{
	Widget	tw, menu, b;
	char	s[2], ffs[16], *ff;

	MotifSelectGlobal(w);

	s[0] = list_get_separator();
	s[1] = '\0';

	tw = XtNameToWidget(DefaultFileDialog, "*listSeparatorTF");
	if (tw) XmTextFieldSetString(tw, s);

	menu = XtNameToWidget(DefaultFileDialog, "*optionCB");
	ff = file_get_default_format();
	strcpy(ffs, "*");
	if (ff)
		strcat(ffs, ff);
	else
		strcat(ffs, "oleo");

	b = XtNameToWidget(DefaultFileDialog, ffs);
	XtVaSetValues(menu, XmNmenuHistory, b, NULL);
}

Widget CreateFileFormatOption(Widget parent, XtCallbackProc f)
{
	Arg		al[10];
	int		ac = 0, i;
	XmString	xms;
	Widget		cb, menu, b;
	char		*ff;

	menu = XmCreatePulldownMenu(parent, "optionMenu", NULL, 0);
	ac = 0;
	XtSetArg(al[ac], XmNsubMenuId, menu); ac++;
	xms = XmStringCreateSimple(_("File Format"));
	XtSetArg(al[ac], XmNlabelString, xms); ac++;
	cb = XmCreateOptionMenu(parent, "optionCB", al, ac);
	XtManageChild(cb);
	XmStringFree(xms);

	i=1;
	while (1) {
		ff = file_get_format(i);
		if (ff == NULL)
			break;

		b = XtVaCreateManagedWidget(ff, xmPushButtonGadgetClass,
			menu,
			NULL);
		XtAddCallback(b, XmNactivateCallback, f, ff);

		i++;
	}

	return menu;
}

Widget MotifCreateDefaultFileDialog(Widget s)
{
	Widget		form, menu, cb, w, ok, cancel, frame, radio, oldframe;
	int		npages, i, ac;
	Arg		al[5];
	XmString	xms;

	ac = 0;
	form = XmCreateForm(s, "defaultFileForm", al, ac);

	/* List File Separator Character */
	frame = XtVaCreateManagedWidget("listSeparatorFrame",
			xmFrameWidgetClass, form,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNtopOffset,		10,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
		NULL);

	w = XtVaCreateManagedWidget("listSeparatorFrameTitle",
			xmLabelGadgetClass,		frame,
			XmNchildType,			XmFRAME_TITLE_CHILD,
			XmNchildVerticalAlignment,	XmALIGNMENT_CENTER,
		NULL);

	w = XtVaCreateManagedWidget("listSeparatorTF", xmTextFieldWidgetClass,
			frame,
			XmNmaxLength,		1,
		NULL);

	/* Default File Format */
	oldframe = frame;
	frame = XtVaCreateManagedWidget("defaultFormatFrame",
			xmFrameWidgetClass, form,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopWidget,		oldframe,
			XmNleftOffset,		10,
			XmNtopOffset,		10,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
		NULL);

	w = XtVaCreateManagedWidget("defaultFormatFrameTitle",
			xmLabelGadgetClass,		frame,
			XmNchildType,			XmFRAME_TITLE_CHILD,
			XmNchildVerticalAlignment,	XmALIGNMENT_CENTER,
		NULL);

	menu = CreateFileFormatOption(frame, DefaultFileFormatCB);

	/* FIX ME : buttons */
	ok = XtVaCreateManagedWidget("ok", xmPushButtonGadgetClass, form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		frame,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_NONE,
			XmNbottomAttachment,	XmATTACH_FORM,
			XmNbottomOffset,	10,
		NULL);
	XtAddCallback(ok, XmNactivateCallback, SetDefaultFileCB, NULL);

	cancel = XtVaCreateManagedWidget("cancel", xmPushButtonGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		frame,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		ok,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_NONE,
			XmNbottomAttachment,	XmATTACH_FORM,
			XmNbottomOffset,	10,
		NULL);
	XtAddCallback(cancel, XmNactivateCallback, CancelDialog, DefaultFileShell);

	return form;
}

void DefaultFileCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	if (DefaultFileShell == NULL) {
		DefaultFileShell = XtVaCreatePopupShell("defaultFileShell",
				topLevelShellWidgetClass,
				toplevel,
			NULL);
		DefaultFileDialog = MotifCreateDefaultFileDialog(DefaultFileShell);
	}

	DefaultFileDialogReset();

	XtManageChild(DefaultFileDialog);
	XtPopup(DefaultFileShell, XtGrabNone);
}

/****************************************************************
 *								*
 *		GUI stuff					*
 *								*
 ****************************************************************/
/*
 * XbaeMatrix calls this whenever we leave a cell.
 * The cell may have been edited, we can veto the edit by setting the
 *	doit flag to false.
 */
void LeaveCell(Widget w, XtPointer client, XtPointer call)
{
	char	*r;
	CELL	*cp;
	XbaeMatrixLeaveCellCallbackStruct *cbp =
		(XbaeMatrixLeaveCellCallbackStruct *)call;

	MotifSelectGlobal(w);

	cbp->doit = True;

	/*
	 * Check whether this cell has a formula.
	 * Don't change the value if it does.
	 */
	if ((cp = find_cell (curow, cucol)) && cp->cell_formula) {
		Debug(__FILE__, "LeaveCell(%d,%d) has a formula !!\n",
			cbp->row, cbp->column, cbp->value);

		return;
	}

	Debug(__FILE__, "LeaveCell(%d,%d,%s)\n",
		cbp->row, cbp->column, cbp->value);

	/*
	 * Have oleo put the new cell content in place.
	 *
	 * This supposedly returns NULL when all is well, otherwise
	 * an error message.
	 */
	r = new_value(cbp->row + 1, cbp->column + 1, cbp->value);
	if (r) {
		MessageAppend(True, r);
		cbp->doit = False;	/* veto */
	} else {
		Global->modified = 1;

		recalculate(1);

		if (! cp)
		    cp = find_cell (curow, cucol);
		if (cp && cp->cell_formula) {
			UpdateStatus();
#if 0
			XbaeMatrixRefreshCell(mat, curow - 1, cucol - 1);
#endif
			/*
			 * This veto prevents Xbae from displaying the formula
			 * again. By preventing this, it'll display what we
			 * just put in there: the result of formula evaluation.
			 *
			 * Oh boy.
			 */
			cbp->doit = False;	/* veto */
		}
		MotifUpdateDisplay();
	}
}

/*
 * Yuck. Apparently oleo doesn't have an API to change cells.
 * I seem to have to call goto_region, *and* set curow/cucol.
 */
void EnterCell(Widget w, XtPointer client, XtPointer call)
{
	struct	rng	r;
	CELL		*cp;
	XbaeMatrixEnterCellCallbackStruct *cbp =
		(XbaeMatrixEnterCellCallbackStruct *)call;
	char		*dec;

	MotifSelectGlobal(w);

	Debug(__FILE__, "EnterCell (%d, %d)\n", cbp->row, cbp->column);

	/* Tell oleo to move */
	curow = cbp->row + 1;
	cucol = cbp->column + 1;

	r.lr = r.hr = cbp->row + 1;
	r.hc = r.lc = cbp->column + 1;
	goto_region(&r);

	/* Change Formula editor */
#if 1
	if (cp = find_cell (curow, cucol)) {
		dec = decomp (curow, cucol, cp);
		XmTextFieldSetString(formulatf, dec);
	} else {
		XmTextFieldSetString(formulatf, "");
	}
#else
	if ((cp = find_cell (curow, cucol)) && cp->cell_formula) {
		dec = decomp (curow, cucol, cp);
		XmTextFieldSetString(formulatf, dec);
	} else {
		XmTextFieldSetString(formulatf, "");
	}
#endif
}

void ModifyVerify(Widget w, XtPointer client, XtPointer call)
{
	XbaeMatrixModifyVerifyCallbackStruct *cbp =
		(XbaeMatrixModifyVerifyCallbackStruct *)call;

	MotifSelectGlobal(w);

	Debug(__FILE__, "ModifyVerify(%d,%d) '%s'\n",
		cbp->row, cbp->column,
		cbp->prev_text);
}

void WriteCell(Widget w, XtPointer client, XtPointer call)
{
	XbaeMatrixWriteCellCallbackStruct *cbp =
		(XbaeMatrixWriteCellCallbackStruct *)call;

	MotifSelectGlobal(w);

	Debug(__FILE__, "WriteCell(%d, %d, %s)\n",
		cbp->row, cbp->column, cbp->string);
}

void DrawCell(Widget w, XtPointer client, XtPointer call)
{
	/* This needs to be called otherwise WriteCell won't work. */
	XbaeMatrixDrawCellCallbackStruct *cbp =
		(XbaeMatrixDrawCellCallbackStruct *)call;

	MotifSelectGlobal(w);

	cbp->type = XbaeString;
#if 1
	cbp->string = cell_value_string(cbp->row + 1, cbp->column + 1,
		True);
#else
	if (curow == cbp->row + 1 && cucol == cbp->column + 1) {
		cbp->string = cell_value_string(cbp->row + 1, cbp->column + 1,
			True);
	} else {
		cbp->string = print_cell(find_cell(cbp->row + 1,
			cbp->column + 1));
	}
#endif

#if 0
	if (cbp->string && strlen(cbp->string))
		fprintf(stderr, "OleoDrawCell(%d,%d) -> [%s]\n",
			cbp->row, cbp->column, cbp->string);
#endif

#ifdef	VERBOSE
	Debug(__FILE__, "DrawCell(%d, %d, %s)\n",
		cbp->row, cbp->column, cbp->string);
#endif
}

void FormulaCB(Widget w, XtPointer client, XtPointer call)
{
	char	*s, *r;

	MotifSelectGlobal(w);

	s = XmTextFieldGetString(w);
	Debug(__FILE__, "FormulaCB(%s) -> %d,%d\n", s, curow, cucol);

	r = new_value(curow, cucol, s);
	if (r) {
		MessageAppend(True, r);
	} else
		Global->modified = 1;

#ifdef	FREE_TF_STRING
	XtFree(s);
#endif

	recalculate(1);		/* 1 is recalculate all */
	MotifUpdateDisplay();	/* refresh */
}

#ifdef	XmNresizeRowCallback
void ResizeRowCB(Widget w, XtPointer client, XtPointer call)
{
	XbaeMatrixResizeRowCallbackStruct *cbp =
		(XbaeMatrixResizeRowCallbackStruct *)call;

	fprintf(stderr, "Resize row %d to height %d\n",
		cbp->which,
		cbp->row_heights[cbp->which]);
}
#endif

void ResizeColumnCB(Widget w, XtPointer client, XtPointer call)
{
	XbaeMatrixResizeColumnCallbackStruct *cbp =
		(XbaeMatrixResizeColumnCallbackStruct *)call;
	struct rng	rng;
	char		wid[20];

	MotifSelectGlobal(w);

	/* Set a range which is a column */
	rng.lr = 0;
	rng.hr = -1;
	rng.lc = rng.hc = cbp->which + 1;
#if 0
	fprintf(stderr, "Resize column %d to %d, using range %s\n",
		cbp->which,
		cbp->column_widths[cbp->which],
		range_name(&rng));
#endif
	sprintf(wid, "%d", cbp->column_widths[cbp->which]);
	set_region_width(&rng, wid);
}

/****************************************************************
 *								*
 *		File interaction				*
 *								*
 ****************************************************************/

void MotifSetWindowName(const char *s)
{
	char	*t;

	t = XtMalloc(strlen(s) + 32);	/* enough */
	sprintf(t, _("[Oleo: %s]"), s);
	XtVaSetValues(toplevel, XmNtitle, t, NULL);
	XtVaSetValues(toplevel, XmNiconName, t, NULL);

	XtFree(t);
}

void FileFormatCB(Widget w, XtPointer client, XtPointer call)
{
	char		*f = (char *)client, *p;
	XmString	xms;

	MotifSelectGlobal(w);

	p = file_get_pattern(f);
	if (p == NULL)
		p = f;

	strcpy(Global->MotifGlobal->fileformat, f);

	strcpy(Global->MotifGlobal->pattern, "*.");
	strcat(Global->MotifGlobal->pattern, p);

	xms = XmStringCreateSimple(Global->MotifGlobal->pattern);
	XtVaSetValues(fsd, XmNpattern, xms, NULL);
	XmStringFree(xms);
}

void ReallyLoadCB(Widget w, XtPointer client, XtPointer call)
{
	XmFileSelectionBoxCallbackStruct *cbp =
		(XmFileSelectionBoxCallbackStruct *)call;
	FILE	*fp;
	char	*s;

	MotifSelectGlobal(w);

	if (! XmStringGetLtoR(cbp->value, XmFONTLIST_DEFAULT_TAG, &s)) {
		/* handle error */
		return;
	}

	if (Global->modified) {
		/* Handle unsaved changes */
	}

	/* From here it's irreversible */

	ResetColumnWidths();
	XbaeMatrixCancelEdit(mat, True);

	fp = fopen(s, "r");
	if (fp == NULL) {
		/* handle error */
		XtFree(s);
		return;
	}

	FileSetCurrentFileName(s);

	read_file_generic(fp, 0, Global->MotifGlobal->fileformat, s);

	if (fclose(fp) != 0) {
		/* handle error */
		XtFree(s);
		return;
	}
	FixA0();

	/* Force calculate */
	recalculate(1);		/* 1 is all */

	/* Force redisplay */
	MotifUpdateDisplay();

	/* Set the widget as well as the spreadsheet to a default state. */
	Global->modified = 0;
	curow = cucol = 1;
	XbaeMatrixEditCell(mat, 0, 0);

	MotifSetWindowName(s);
	MessageAppend(False, _("Read file '%s'\n"), s);

	XtFree(s);
}

void DefaultFileResetCB(Widget w, XtPointer client, XtPointer call)
{
	Widget		menu, x;
	char		format[32], *f, *p;
	XmString	xms;

	MotifSelectGlobal(w);

	f = file_get_default_format();

	strcpy(format, "*.");
	strcat(format, f ? f : "oleo");

	x = XtNameToWidget(fsd, format);
	menu = XtNameToWidget(fsd, "*optionCB");
	XtVaSetValues(menu, XmNmenuHistory, x, NULL);

	p = file_get_pattern(f ? f : "oleo");
	strcpy(format, "*.");
	strcat(format, p);

	xms = XmStringCreateSimple(format);
	XtVaSetValues(fsd, XmNpattern, xms, NULL);
	XmStringFree(xms);
}

void CreateFSD()
{
	Arg		al[10];
	int		ac = 0;
	XmString	xms;
	Widget		cb, menu, b;

	ac = 0;
	XtSetArg(al[ac], XmNautoUnmanage, True); ac++;
	xms = XmStringCreateSimple(_("*.oleo"));
	XtSetArg(al[ac], XmNpattern, xms); ac++;

	fsd = XmCreateFileSelectionDialog(toplevel, "selectfile",
		al, ac);
	XtDestroyWidget(
		XmFileSelectionBoxGetChild(fsd, XmDIALOG_HELP_BUTTON));
	XmStringFree(xms);

	/* Option menu */
	menu = CreateFileFormatOption(fsd, FileFormatCB);

	XtAddCallback(XtParent(fsd), XmNpopupCallback, DefaultFileResetCB, (XtPointer)menu);
}

void PopupLoad(void)
{
	Arg	al[10];
	int	ac = 0;

	if (fsd == NULL)
		CreateFSD();

	XtRemoveAllCallbacks(fsd, XmNokCallback);
	XtAddCallback(fsd, XmNokCallback, ReallyLoadCB, NULL);

	ac = 0;
	XtSetArg(al[ac], XmNtitle, _("Choose a file to load")); ac++;
	XtSetValues(fsd, al, ac);
	XtManageChild(fsd);
}

void LoadCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	PopupLoad();
}

void NewCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	/* FIX ME Open a new window */
	none(w, client, call);
}

void DestroyToplevel(Widget w, XtPointer client, XtPointer call)
{
#if 0
	fprintf(stderr, "In DestroyToplevel()\n");
#endif

	/*
	 * Calling this solves all the issues...
	 */
	QuitCB(w, client, call);
}

/*
 * MDI : Experimental code to open a second window.
 */

void OpenNewCB(Widget w, XtPointer client, XtPointer call)
{
	Widget	m;
	Display	*d = XtDisplayOfObject(w);
	XtAppContext appc = Global->MotifGlobal->app_c;

	MdiOpen();
	/* Now we've got another Global pointer */
	Global->MotifGlobal->app_c = appc;

	toplevel = XtVaAppCreateShell(PACKAGE, PACKAGE,
		topLevelShellWidgetClass, d, NULL);
	RegisterWMClose(toplevel);
	XtVaSetValues(toplevel, XmNdeleteResponse, XmDO_NOTHING, NULL);

	/* FIX ME do we need to do this again ?? */
	XtVaGetApplicationResources(toplevel, &AppRes,
		resources, num_resources,
		NULL);

	/* Without this we have NULL in cwin. */
	io_init_windows(AppRes.rows, AppRes.columns, 1, 2, 1, 1, 1, 1);

	m = GscBuildMainWindow(toplevel);
	XtManageChild(m);
	XtRealizeWidget(toplevel);

	PopupLoad();
}

void OpenCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	/*
	 * FIX ME
	 *
	 * Figure out when to open a new window :
	 *	- when this window has been modified
	 *	- when this window has an open file
	 */
	if (Global->modified || MdiHasFile())
		OpenNewCB(w, client, call);
	else
		LoadCB(w, client, call);
}

void ReallyCloseCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);
	/*
	 * Destroying the widget tree should take care of calling
	 *	MdiClose(), and therefore also of closing the Global
	 *	structure.
	 * When MdiClose notices this is the last window, it'll also
	 *	exit the application.
	 */
	XtDestroyWidget(toplevel);
}

void CloseCB(Widget w, XtPointer client, XtPointer call)
{
	Arg	al[3];
	int	ac;
	Widget	md;

	MotifSelectGlobal(w);

	if (Global->modified && !option_filter) {
		XmString xms = XmStringCreateLtoR(
			_("There are unsaved changes.\n"
			  "Do you want to quit anyway ?"),
			XmFONTLIST_DEFAULT_TAG);

		ac = 0;
		/* Application resource for message allow i18n */
		XtSetArg(al[ac], XmNmessageString, xms); ac++;

		md = XmCreateQuestionDialog(toplevel, "quitMB", al, ac);
		XtAddCallback(md, XmNokCallback, ReallyCloseCB, NULL);
		XtDestroyWidget(XmMessageBoxGetChild(md,
			XmDIALOG_HELP_BUTTON));
		XtManageChild(md);
		XmStringFree(xms);
		return;
	}

	/* No dialog - just close */
	XtDestroyWidget(toplevel);
}

FILE *OleoOpenForWrite(const char *s)
{
	FILE	*fp;
	int	len;
	char	*fn;

	if (access(s, R_OK) == 0) {
		/* Maybe replace it ? */
		len = strlen(s) + 2 + strlen(".old");
		fn = malloc(len);
		strcpy(fn, s);
		strcat(fn, ".old");
		unlink(fn);
		if (rename(s, fn) < 0) {
			MessageAppend(True,
				_("OleoOpenForWrite: can't move %s to %s"),
				s, fn);
			free(fn);
			return NULL;
		}
		free(fn);
	}

	fp = fopen(s, "w");
	if (fp == NULL) {
		/* handle error */
		MessageAppend(True,
			_("OleoOpenForWrite: couldn't open file '%s' for write"),
			s);
		return NULL;
	}

	return fp;
}

void ReallySave(const char *s)
{
	FILE	*fp;

	fp = OleoOpenForWrite(s);
	if (fp == NULL) {
		/* handle error */
		MessageAppend(True,
			_("ReallySaveCB(%s): couldn't open file for writing"),
			s);
		return;
	}

	write_file_generic(fp, 0, Global->MotifGlobal->fileformat);

	if (fclose(fp) != 0) {
		/* handle error */
		MessageAppend(True, _("ReallySaveCB: file close failed"));
		return;
	}

	Global->modified = 0;
	MessageAppend(False, _("Saved file '%s'\n"), s);
	MotifSetWindowName(s);
}

void ReallySaveCB(Widget w, XtPointer client, XtPointer call)
{
	XmFileSelectionBoxCallbackStruct *cbp =
		(XmFileSelectionBoxCallbackStruct *)call;
	char	*s, *t;

	MotifSelectGlobal(w);

	if (! XmStringGetLtoR(cbp->value, XmFONTLIST_DEFAULT_TAG, &s)) {
		/* handle error */
		MessageAppend(True,
		    _("ReallySaveCB: couldn't figure out file to save into"));
		return;
	}

	ReallySave(s);
	XtFree(s);
}

/*
 * Called from the menu system.
 * Always pop up a FileSB.
 */
void SaveAsCB(Widget w, XtPointer client, XtPointer call)
{
	Arg	al[10];
	int	ac = 0;

	MotifSelectGlobal(w);

	if (fsd == NULL)
		CreateFSD();

	XtRemoveAllCallbacks(fsd, XmNokCallback);
	XtAddCallback(fsd, XmNokCallback, ReallySaveCB, NULL);

	ac = 0;
	XtSetArg(al[ac], XmNtitle, _("Choose a file to save in")); ac++;
	XtSetValues(fsd, al, ac);
	XtManageChild(fsd);
}

/*
 * Called from the menu system.
 * If we don't have a current file, call SaveAs.
 */
void SaveCB(Widget w, XtPointer client, XtPointer call)
{
	char	*s;

	MotifSelectGlobal(w);

	if (s = FileGetCurrentFileName()) {
		ReallySave(s);
		return;
	}

	SaveAsCB(w, client, call);
}

/****************************************************************
 *								*
 *		Build Help Widgets				*
 *								*
 ****************************************************************/
#ifdef	HAVE_XmHTML_H
static void
anchorCB(Widget widget, XtPointer client, XtPointer call)
{
        XmHTMLAnchorCallbackStruct *cbs = (XmHTMLAnchorCallbackStruct *) call;
	char	*anchor;

	MotifSelectGlobal(w);
#if 0
	fprintf(stderr, "AnchorCB(%s)\n", cbs->href);
#endif
        cbs->doit = True;
        cbs->visited = True;

	anchor = strchr(cbs->href, '#');

	XmHTMLAnchorScrollToName(Html, anchor);
}

static void
HelpBuildWindow(void)
{
	Widget	f, sep, ok;

	if (! Html) {
		hd = XtVaCreatePopupShell("helpShell",
				topLevelShellWidgetClass,
				toplevel,
				XmNdeleteResponse, XmUNMAP,
			NULL);

		f = XtVaCreateManagedWidget("form", xmFormWidgetClass, hd,
			NULL);

		ok = XtVaCreateManagedWidget("ok",
			xmPushButtonGadgetClass, f,
				XmNtopAttachment,	XmATTACH_NONE,
				XmNtopOffset,		10,
				XmNtopWidget,		sep,
				XmNleftAttachment,	XmATTACH_FORM,
				XmNrightAttachment,	XmATTACH_FORM,
				XmNleftOffset,		10,
				XmNrightOffset,		10,
				XmNbottomAttachment,	XmATTACH_FORM,
				XmNbottomOffset,	10,
			NULL);
		XtAddCallback(ok, XmNactivateCallback, PopDownHelpCB, hd);

		sep = XtVaCreateManagedWidget("separator",
			xmSeparatorGadgetClass, f,
				XmNorientation,		XmHORIZONTAL,
				XmNleftAttachment,	XmATTACH_FORM,
				XmNrightAttachment,	XmATTACH_FORM,
				XmNleftOffset,		10,
				XmNrightOffset,		10,
				XmNbottomAttachment,	XmATTACH_WIDGET,
				XmNbottomOffset,	10,
				XmNbottomWidget,	ok,
			NULL);

		Html = XtVaCreateManagedWidget("html",
			xmHTMLWidgetClass, f,
				XmNmarginWidth,			20,
				XmNmarginHeight,		20,
				XmNwidth,			600,
				XmNheight,			500,
				XmNenableBadHTMLWarnings,	XmHTML_NONE,
				XmNscrollBarDisplayPolicy,	XmSTATIC,
				XmNtopAttachment,		XmATTACH_FORM,
				XmNleftAttachment,		XmATTACH_FORM,
				XmNrightAttachment,		XmATTACH_FORM,
				XmNleftOffset,			10,
				XmNrightOffset,			10,
				XmNtopOffset,			10,
				XmNbottomAttachment,		XmATTACH_WIDGET,
				XmNbottomOffset,		10,
				XmNbottomWidget,		sep,
			NULL);

		XtAddCallback(Html, XmNactivateCallback,
			(XtCallbackProc)anchorCB, NULL);
	}
}
#endif

/*
 * Load a file into the help window.
 * This probably needs to look at the language.
 */
#define	HELP_FILE_LENGTH	200000

#ifndef	HTMLDIR
#define	HTMLDIR			"/usr/local/share/oleo"
#endif

static void
HelpLoadFile(char *fn, char *anchor)
{
#ifdef	HAVE_XmHTML_H
	char	*buffer = XtMalloc(HELP_FILE_LENGTH);
	FILE	*fp = fopen("oleo.html", "r");
	char	*n;

	if (fn == NULL) {
		XmHTMLTextSetString(Html,
			_("<html><body>"
			  "Can't find the requested help file\n"
			  "</body></html>"));
		return;
	}

	n = XtMalloc(256);
	fp = fopen(fn, "r");
	if (fp == NULL) {
		sprintf(n, "%s/%s/%s",
                        HTMLDIR, 
                        getenv("LANGUAGE") != NULL ? getenv("LANGUAGE") : "",
                        fn);
		fp = fopen(n, "r");
	}

	if (fp == NULL) {
		sprintf(n, "%s/%s/%s",
                        HTMLDIR,
                        getenv("LANG") != NULL ? getenv("LANG") : "",
                        fn);
		fp = fopen(n, "r");
	}

	if (fp == NULL) {
		sprintf(n, "%s/%s", HTMLDIR, fn);
		fp = fopen(n, "r");
	}

	if (fp) {
		fread(buffer, 1, HELP_FILE_LENGTH, fp);
		fclose(fp);
		XmHTMLTextSetString(Html, buffer);
	} else {
		XmHTMLTextSetString(Html,
			_("<html><body>"
			  "Can't find the requested help file\n"
			  "</body></html>"));
                return;
	}
	XtFree(buffer);

	if (anchor)
		MessageAppend(False, _("Help requested on '%s'"), anchor);
	else
		MessageAppend(False, _("Help requested on using Oleo"));

	if (anchor)
		XmHTMLAnchorScrollToName(Html, anchor);
	XtFree(n);
#endif
}

void helpUsingCB(Widget w, XtPointer client, XtPointer call)
{
#ifdef	HAVE_XmHTML_H
	MotifSelectGlobal(w);

	HelpBuildWindow();
	HelpLoadFile("oleo.html", client);
	XtPopup(hd, XtGrabNone);
	XtManageChild(XtParent(Html));		/* LessTif only, harmless otherwise */
#endif
}

void helpAboutCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	/* FIX ME */	versionCB(w, client, call);
}

void helpVersionCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	/* FIX ME */	versionCB(w, client, call);
}

void
MotifUpdateWidth(int col, int wid)
{
	if (Global->MotifGlobal == 0)
		return;

	if (! columnwidths)
		ResetColumnWidths();
	columnwidths[col - 1] = wid;

	if (mat)
		XtVaSetValues(mat,
				XmNcolumnWidths,	columnwidths,
			NULL);
}

void
ResetColumnWidths(void)
{
	int	i;

	if (Global->MotifGlobal == 0)
		return;

	columnwidths = (short *)XtCalloc(AppRes.columns, sizeof(short));
	for (i=0; i<AppRes.columns; i++)
		columnwidths[i] = AppRes.columnWidth;
}

void
SetRowColumnLabels(void)
{
	int	i;
	char	tmp[10];

	rowlabels = (char **)XtCalloc(AppRes.rows, sizeof(char *));
	columnlabels = (char **)XtCalloc(AppRes.columns, sizeof(char *));
	columnmaxlengths = (int *)XtCalloc(AppRes.columns, sizeof(int));

	for (i=0; i<AppRes.rows; i++) {
		sprintf(tmp, Global->a0 ? "%d" : "R%d", i + 1);
		rowlabels[i] = XtNewString(tmp);
	}
	for (i=0; i<AppRes.columns; i++) {
		if (Global->a0)
			columnlabels[i] = XtNewString(col_to_str(i+1));
		else {
			sprintf(tmp, "C%d", i + 1);
			columnlabels[i] = XtNewString(tmp);
		}
		columnmaxlengths[i] = 64000;
	}
}

void
ChangeRowColumnLabels(void)
{
	int	i;
	char	tmp[10];

	for (i=0; i<AppRes.rows; i++) {
		sprintf(tmp, Global->a0 ? "%d" : "R%d", i + 1);
		XtFree(rowlabels[i]);
		rowlabels[i] = XtNewString(tmp);
	}
	for (i=0; i<AppRes.columns; i++) {
		XtFree(columnlabels[i]);
		if (Global->a0)
			columnlabels[i] = XtNewString(col_to_str(i+1));
		else {
			sprintf(tmp, "C%d", i + 1);
			columnlabels[i] = XtNewString(tmp);
		}
		columnmaxlengths[i] = 64000;
	}
}

/****************************************************************
 *								*
 *		Some option settings				*
 *								*
 ****************************************************************/
static void FixA0()
{
	if (Global->MotifGlobal->havea0 == Global->a0)
		return;

	Global->MotifGlobal->havea0 = Global->a0;

	/* Update matrix */
	ChangeRowColumnLabels();
	XtVaSetValues(mat,
			XmNrowLabels,		rowlabels,
			XmNcolumnLabels,	columnlabels,
		NULL);
}

void ToggleA0(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	if (Global->a0)
		Global->a0 = 0;
	else
		Global->a0 = 1;

	FixA0();
}

/****************************************************************
 *								*
 *		Edit Menu					*
 *								*
 ****************************************************************/
void UndoCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	none(w, client, call);
}

void EditInsertCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	/* FIX ME */
		/* Need to figure out whether to insert row or column */
		/* Need to figure out how many rows/columns to treat */
	insert_row(1);
		/* Also need to clear the current cell */
	MotifUpdateDisplay();
}

void EditDeleteCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	/* FIX ME */
		/* Need to figure out whether to delete row or column */
		/* Need to figure out how many rows/columns to treat */
	delete_row(1);
		/* Also need to clear the current cell */
	MotifUpdateDisplay();
}

void EditRecalculateCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	recalculate(1);
	MotifUpdateDisplay();
}

/****************************************************************
 *								*
 *		Copy/Paste stuff				*
 *								*
 ****************************************************************/
/*
 * This, and the other copy/paste functions, must act as follows :
 * - a region is assumed to be selected in the spreadsheet
 * - the region is converted into more than one selections (only on
 *	request though) : text, value/formula group, ...
 * - copying to/from the X selection mechanism
 * - conversion in the other way (preferably the value/formula group,
 *	otherwise text, which can come from other sources as well... )
 *
 * Note that this bypasses the existing oleo copy/paste mechanism.
 */
void CopyCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	none(w, client, call);
}

void CutCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	none(w, client, call);
}

void PasteCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	none(w, client, call);
}

/****************************************************************
 *								*
 *		Oleo move/copy functionality			*
 *								*
 ****************************************************************/
void CreateCopyDialog(char *t, void (*cb)(Widget, XtPointer, XtPointer))
{
	Widget		w, rc, cap;
	Arg		al[3];
	int		ac;
	XmString	ok, cancel;

	if (copyDialog == NULL) {
		ac = 0;
		ok = XmStringCreateSimple(_("OK"));
		cancel = XmStringCreateSimple(_("Cancel"));
		XtSetArg(al[ac], XmNokLabelString, ok); ac++;
		XtSetArg(al[ac], XmNcancelLabelString, cancel); ac++;
		XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
		copyDialog = XmCreateTemplateDialog(toplevel, "copyDialog",
			al, ac);
		XmStringFree(ok);
		XmStringFree(cancel);

		rc = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass,
			copyDialog,
			NULL);

		cap = XtVaCreateManagedWidget("cap1", xbaeCaptionWidgetClass,
			rc,
			NULL);
		w = XtVaCreateManagedWidget("r1", xmTextFieldWidgetClass, cap,
			NULL);
		RegisterRangeSelector(w);
		cap = XtVaCreateManagedWidget("cap2", xbaeCaptionWidgetClass,
			rc,
			NULL);
		w = XtVaCreateManagedWidget("r2", xmTextFieldWidgetClass, cap,
			NULL);
		RegisterRangeSelector(w);
	}

	XtRemoveAllCallbacks(copyDialog, XmNokCallback);
	XtAddCallback(copyDialog, XmNokCallback, cb, NULL);
	XtAddCallback(copyDialog, XmNcancelCallback, CancelTemplate, copyDialog);

	XtVaSetValues(XtParent(copyDialog), XmNtitle, t, NULL);
}

void ReallyCopyRegionCB(Widget w, XtPointer client, XtPointer call)
{
	Widget		ft, tt;
	char		*f, *t, *p;
	struct rng	from, to;
	int		r;

	MotifSelectGlobal(w);

	ft = XtNameToWidget(copyDialog, "*r1");
	tt = XtNameToWidget(copyDialog, "*r2");

	f = XmTextFieldGetString(ft);
	t = XmTextFieldGetString(tt);

	p = f;
	if ((r = parse_cell_or_range(&p, &from)) == 0)
		ConversionError(f, _("range"));
	else if ((r & RANGE) == 0) {
		/* It's a cell address, not a range */
		from.hr = from.lr;
		from.hc = from.lc;
	}

	p = t;
	if ((r = parse_cell_or_range(&p, &to)) == 0)
		ConversionError(t, _("range"));
	else if ((r & RANGE) == 0) {
		/* It's a cell address, not a range */
		to.hr = to.lr;
		to.hc = to.lc;
	}

	copy_region(&from, &to);
	Global->modified = 1;

	recalculate(1);
	MotifUpdateDisplay();

#if 0
	XtFree(f);
	XtFree(t);
#endif
}

void ReallyMoveCB(Widget w, XtPointer client, XtPointer call)
{
	Widget		ft, tt;
	char		*f, *t, *p;
	struct rng	from, to;
	int		r;

	MotifSelectGlobal(w);

	ft = XtNameToWidget(copyDialog, "*r1");
	tt = XtNameToWidget(copyDialog, "*r2");

	f = XmTextFieldGetString(ft);
	t = XmTextFieldGetString(tt);

	MessageAppend(False, "ReallyMoveCB (%s -> %s)!!\n", f, t);

	p = f;
	if ((r = parse_cell_or_range(&p, &from)) == 0)
		ConversionError(f, _("range"));
	else if ((r & RANGE) == 0) {
		/* It's a cell address, not a range */
		from.hr = from.lr;
		from.hc = from.lc;
	}

	p = t;
	if ((r = parse_cell_or_range(&p, &to)) == 0)
		ConversionError(t, _("range"));
	else if ((r & RANGE) == 0) {
		/* It's a cell address, not a range */
		to.hr = to.lr;
		to.hc = to.lc;
	}

	move_region(&from, &to);
	Global->modified = 1;

	recalculate(1);
	MotifUpdateDisplay();
#if 0
	XtFree(f);
	XtFree(t);
#endif
}

void ReallyCopyValuesCB(Widget w, XtPointer client, XtPointer call)
{
	Widget		ft, tt;
	char		*f, *t, *p;
	struct rng	from, to;
	int		r;

	MotifSelectGlobal(w);

	ft = XtNameToWidget(copyDialog, "*r1");
	tt = XtNameToWidget(copyDialog, "*r2");

	f = XmTextFieldGetString(ft);
	t = XmTextFieldGetString(tt);

	p = f;
	if ((r = parse_cell_or_range(&p, &from)) == 0)
		ConversionError(f, _("range"));
	else if ((r & RANGE) == 0) {
		/* It's a cell address, not a range */
		from.hr = from.lr;
		from.hc = from.lc;
	}

	p = t;
	if ((r = parse_cell_or_range(&p, &to)) == 0)
		ConversionError(t, _("range"));
	else if ((r & RANGE) == 0) {
		/* It's a cell address, not a range */
		to.hr = to.lr;
		to.hc = to.lc;
	}

	copy_values_region(&from, &to);
	Global->modified = 1;

	recalculate(1);
	MotifUpdateDisplay();
#if 0
	XtFree(f);
	XtFree(t);
#endif
}

void CopyRegionCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	CreateCopyDialog(_("Copy a region"), ReallyCopyRegionCB);
	XtManageChild(copyDialog);
}

void CopyValuesCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	CreateCopyDialog(_("Copy Values"), ReallyCopyValuesCB);
	XtManageChild(copyDialog);
}

void MoveCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	CreateCopyDialog(_("Move"), ReallyMoveCB);
	XtManageChild(copyDialog);
}

void MarkCellCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

}

void MarkRegionCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

}

void MarkFromDialogCB(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

}

/****************************************************************
 *								*
 *		Formats Dialog					*
 *								*
 ****************************************************************/
static int formats_list[] = {
	/* 0 */		FMT_DEF,
	/* 1 */		FMT_HID,
	/* 2 */		FMT_GPH,
	/* 3 */		FMT_GEN,
	/* 4 */		FMT_DOL,
	/* 5 */		FMT_CMA,
	/* 6 */		FMT_PCT,
	/* 7 */		FMT_DEF,
	/* 8 */		FMT_DEF,
	/* 9 */		FMT_FXT,
	/* 10 */	FMT_DEF,
	/* 11 */	FMT_DATE,
	/* 12 */	FMT_DEF,
	/* 13 */	FMT_DEF,
	/* 14 */	FMT_DEF,
	/* 15 */	FMT_DEF,
	/* 16 */	FMT_DEF
};

/*
 * This is ugly.
 *
 * Yeah gets called whenever the user manipulates the option menu.
 * The OK callback only has to figure out what got selected last
 * (from the global variable) and do its thing.
 * A cleaner but harder solution would have to figure out which
 * button is currently selected and translate that into a format.
 */
void Yeah(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	Global->MotifGlobal->fmt = formats_list[(int) client];
#if 0
	fprintf(stderr, "Yeah %d->%d\n", (int)client, Global->MotifGlobal->fmt);
#endif
}

int	date_format;
void MotifDateFormat(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	date_format = (int) client;
}

void CreateFormatsDialog(Widget p)
{
	Widget		frame, f, tf, l;
	XmString	xms, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10,
			x11, x12, x13, x14, x15;
	extern char	*date_formats[];
	Widget		menu, cb;
	Arg		al[5];
	int		ac, i;

	frame = XtVaCreateManagedWidget("formatsFrame", xmFrameWidgetClass, p,
		NULL);
	l = XtVaCreateManagedWidget("formatsFrameTitle", xmLabelGadgetClass,
		frame,
			XmNchildType,			XmFRAME_TITLE_CHILD,
			XmNchildVerticalAlignment,	XmALIGNMENT_CENTER,
		NULL);

	f = XtVaCreateManagedWidget("formatsForm", xmFormWidgetClass, frame,
		NULL);

	l = XtVaCreateManagedWidget("formatsL", xmLabelGadgetClass, f,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNtopOffset,		10,
		NULL);

	tf = XtVaCreateManagedWidget("formatsTf", xmTextFieldWidgetClass, f,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftWidget,		l,
			XmNleftOffset,		10,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
		NULL);
	RegisterRangeSelector(tf);

	xms = XmStringCreateSimple(_("Formats :"));

	x0 = XmStringCreateSimple(_("Default"));	/* FMT_DEF */
	x1 = XmStringCreateSimple(_("Hidden"));		/* FMT_HID */
	x2 = XmStringCreateSimple(_("Graph"));		/* FMT_GPH */
	x3 = XmStringCreateSimple(_("General"));	/* FMT_GEN */
	x4 = XmStringCreateSimple(_("Dollar"));		/* FMT_DOL */
	x5 = XmStringCreateSimple(_("Comma"));		/* FMT_CMA */
	x6 = XmStringCreateSimple(_("Percent"));	/* FMT_PCT */
	x7 = XmStringCreateSimple(_("Integer"));
	x8 = XmStringCreateSimple(_("Decimal"));
	x9 = XmStringCreateSimple(_("Fixed"));
	x10 = XmStringCreateSimple(_("Exponent"));
	x11 = XmStringCreateSimple(_("Date"));		/* FMT_DATE */
	x12 = XmStringCreateSimple(_("User-1"));
	x13 = XmStringCreateSimple(_("User-2"));
	x14 = XmStringCreateSimple(_("User-3"));
	x15 = XmStringCreateSimple(_("User-4"));

	w = XmVaCreateSimpleOptionMenu(f, "formatsOption",
		xms, /* mnemonic ? */0, /* initial selection */ 0,
		/* callback */ Yeah,
		/* Type, LabelString, Mnemonic, Accelerator, AcceleratorText */
		XmVaPUSHBUTTON, x0, 'D', NULL, NULL,
		XmVaPUSHBUTTON, x1, 'P', NULL, NULL,
		XmVaPUSHBUTTON, x2, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x3, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x4, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x5, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x6, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x7, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x8, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x9, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x10, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x11, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x12, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x13, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x14, ' ', NULL, NULL,
		XmVaPUSHBUTTON, x15, ' ', NULL, NULL,

		XmNtopAttachment,	XmATTACH_WIDGET,
		XmNtopWidget,		tf,
		XmNtopOffset,		10,
		XmNrightAttachment,	XmATTACH_FORM,
		XmNrightOffset,		10,
		XmNleftAttachment,	XmATTACH_FORM,
		XmNleftOffset,		10,
		NULL);
	XtManageChild(w);

	XmStringFree(xms);
	XmStringFree(x0);
	XmStringFree(x1);
	XmStringFree(x2);
	XmStringFree(x3);
	XmStringFree(x4);
	XmStringFree(x5);
	XmStringFree(x6);
	XmStringFree(x7);
	XmStringFree(x8);
	XmStringFree(x9);
	XmStringFree(x10);
	XmStringFree(x11);
	XmStringFree(x12);
	XmStringFree(x13);
	XmStringFree(x14);
	XmStringFree(x15);

	/* Precision */
	l = XtVaCreateManagedWidget("precisionL", xmLabelGadgetClass, f,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		w,
		NULL);
	tf = XtVaCreateManagedWidget("precisionTf", xmTextFieldWidgetClass, f,
		XmNtopAttachment,	XmATTACH_WIDGET,
		XmNtopWidget,		w,
		XmNtopOffset,		10,
		XmNrightAttachment,	XmATTACH_FORM,
		XmNrightOffset,		10,
		XmNleftAttachment,	XmATTACH_WIDGET,
		XmNleftWidget,		l,
		XmNleftOffset,		10,
		NULL);

	/* A dialog for date formats */
	menu = XmCreatePulldownMenu(f, "optionMenu", NULL, 0);
	ac = 0;
	XtSetArg(al[ac], XmNsubMenuId, menu); ac++;
	xms = XmStringCreateSimple(_("Date Format"));
	XtSetArg(al[ac], XmNlabelString, xms); ac++;
	cb = XmCreateOptionMenu(f, "optionCB", al, ac);
	XtVaSetValues(cb,
		XmNtopAttachment,	XmATTACH_WIDGET,
		XmNtopWidget,		tf,
		XmNtopOffset,		10,
		XmNrightAttachment,	XmATTACH_FORM,
		XmNrightOffset,		10,
		XmNleftAttachment,	XmATTACH_FORM,
		XmNleftOffset,		10,
		XmNbottomAttachment,	XmATTACH_FORM,
		XmNbottomOffset,	10,
		NULL);
	XtManageChild(cb);
	XmStringFree(xms);

	for (i=0; date_formats[i]; i++) {
		ac = 0;
		xms = XmStringCreateSimple(date_formats[i]);
		XtSetArg(al[ac], XmNlabelString, xms); ac++;
		w = XmCreatePushButtonGadget(menu, "button", al, ac);
		if (i == 0)
			XtVaSetValues(menu, XmNmenuHistory, w, NULL);
		XtAddCallback(w, XmNactivateCallback,
			MotifDateFormat, (XtPointer)i);
		XmStringFree(xms);
		XtManageChild(w);
	}
}

/*
 * Set formats both on cells/ranges and as default.
 *	client_data = 1 means set the default.
 */
void FormatsDialogOk(Widget w, XtPointer client, XtPointer call)
{
	struct rng	rng;
	Widget		om, b = NULL, tf, pr;
	char		*p, *s = NULL, *prec = NULL;
	int		r, precision;

	MotifSelectGlobal(w);
	XtUnmanageChild(FormatD);

	om = XtNameToWidget(FormatD, "*formatsFrame*formatsOption*formatsOption");
	tf = XtNameToWidget(FormatD, "*formatsFrame*formatsTf");
	pr = XtNameToWidget(FormatD, "*formatsFrame*precisionTf");

	if (om)
		XtVaGetValues(om, XmNmenuHistory, &b, NULL);
	if (tf)
		p = s = XmTextFieldGetString(tf);
	if (pr) {
		prec = XmTextFieldGetString(pr);
		if (prec) precision = atoi(prec);
	}

	/* date */
	if (Global->MotifGlobal->fmt == FMT_DATE)
		precision = date_format;

	if (client) {	/* Set default */
			default_fmt = Global->MotifGlobal->fmt;
			default_prc = precision;

			MessageAppend(False, "Set Default Format %d, precision %d\n",
				default_fmt, default_prc);

			recalculate(1);
			MotifUpdateDisplay();
	} else {
	    if (p) {
		if ((r = parse_cell_or_range(&p, &rng)) == 0)
			ConversionError(s, _("range"));
		else if (r & RANGE) {
			MessageAppend(False, "FormatRegion %s\n",
				range_name(&rng));

			format_region(&rng, Global->MotifGlobal->fmt, -1);
			precision_region(&rng, precision);

			recalculate(1);
			MotifUpdateDisplay();
		} else {
			rng.hr = rng.lr;
			rng.hc = rng.lc;

			format_region(&rng, Global->MotifGlobal->fmt, -1);
			precision_region(&rng, precision);

			recalculate(1);
			MotifUpdateDisplay();
		}
	    }
	}


#ifdef	FREE_TF_STRING
	XtFree(s);
	XtFree(prec);
#endif
}

void FormatsDialogReset(Widget d)
{
}

void FormatsDialog(Widget w, XtPointer client, XtPointer call)
{
	Widget	ok, cancel, help, tf;
	int	c = (int)client, ac;
	Arg	al[5];

	MotifSelectGlobal(w);

	if (! FormatD) {
		ac = 0;
		XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
		FormatD = XmCreateTemplateDialog(mw, "formatsDialog", al, ac);
		CreateFormatsDialog(FormatD);

		ok = XtVaCreateManagedWidget("ok", xmPushButtonGadgetClass, FormatD,
			NULL);
		cancel = XtVaCreateManagedWidget("cancel", xmPushButtonGadgetClass, FormatD,
			NULL);
		help = XtVaCreateManagedWidget("help", xmPushButtonGadgetClass, FormatD,
			NULL);

		XtAddCallback(ok, XmNactivateCallback, FormatsDialogOk,
			(XtPointer)client);	/* Whether or not it's a default setting */
		XtAddCallback(help, XmNactivateCallback, helpUsingCB,
			(XtPointer)"#HelpFormats");
		XtAddCallback(cancel, XmNactivateCallback, CancelTemplate, FormatD);
	}

	tf = XtNameToWidget(FormatD, "*formatsFrame*formatsTf");
	if (tf) XtSetSensitive(tf, 1 - c);

	FormatsDialogReset(FormatD);
	XtManageChild(FormatD);
}

/****************************************************************
 *								*
 *		Generic Dialog Support				*
 *								*
 ****************************************************************/
/*
 * Walk up widget tree until a shell is found.
 * The previous widget is the one returned.
 */
static Widget FindDialog(Widget w)
{
	Widget	p, q;

	p = q = w;
	for (p=q=w; q=p, p=XtParent(p); p != NULL && q != NULL & !XmIsDialogShell(p)) ;

	return q;
}

/****************************************************************
 *								*
 *		User Preferences				*
 *								*
 ****************************************************************/
void
UserPreferencesOk(Widget w, XtPointer client, XtPointer call)
{
	Widget		mft, ft, etf;
	char		*mf, f;
	XmFontList	fl;
	XmFontListEntry	fle;
	XFontSet	fs;
	char		m1[2][80], **missing = (char **)&m1[0];
	char		m2[80], *def = &m2[0];
	int		nmissing = 0;
	Display		*d = XtDisplayOfObject(w);

	MotifSelectGlobal(w);

	XtUnmanageChild(UserPref);

	mft = XtNameToWidget(UserPref, "*matrixfonttf");
	ft = XtNameToWidget(UserPref, "*fonttf");
	etf = XtNameToWidget(UserPref, "*encodingTf");

	if (ft == NULL || mft == NULL || etf == NULL)
		return;	/* Huh ?? */

	mf = XmTextFieldGetString(mft);

	/* set preferences now */
	fs = XCreateFontSet(d, mf, &missing, &nmissing, &def);
	if (fs != NULL) {
		fle = XmFontListEntryCreate(XmFONTLIST_DEFAULT_TAG, XmFONT_IS_FONTSET, fs);
		fl = XmFontListAppendEntry(NULL, fle);
		XtVaSetValues(mat, XmNfontList, fl, NULL);
	/* FIX ME potential leak : can we free fl, fle, fs yet ? */

		OleoSetEncoding(XmTextFieldGetString(etf));
	}

	XtFree(mf);
}

void
UserPreferencesReset(Widget w)
{
	Widget	e = XtNameToWidget(w, "*encodingTf");

	if (e) XmTextFieldSetString(e, OleoGetEncoding());
}

#ifdef	HAVE_XLT_FONTCHOOSER
#include <Xlt/FontChooser.h>
#endif

static void
ChooseFont(Widget w, XtPointer client, XtPointer call)
{
#ifdef	HAVE_XLT_FONTCHOOSER
	char	*s;
	Widget	tf;

	MotifSelectGlobal(w);

	XtVaGetValues(w, XltNfontName, &s, NULL);

	fprintf(stderr, "Selected font '%s'\n", s);

	tf = XtNameToWidget(UserPref, "*matrixfonttf");
	if (tf)
		XmTextFieldSetString(tf, s);
#endif
}

void
FontChooserCB(Widget w, XtPointer client, XtPointer call)
{
#ifdef	HAVE_XLT_FONTCHOOSER
	Widget	tf = (Widget)client, fc;
	Arg	al[5];
	int	ac;

	MotifSelectGlobal(w);

	ac = 0;
	fc = XltCreateFontChooserDialog(toplevel, "font chooser", al, ac);
	XtManageChild(fc);

	XtAddCallback(fc, XmNokCallback, ChooseFont, NULL);
#endif
}

void
CreateUserPreferences(Widget parent)
{
	Widget	form, l, t, b;

	form = XtVaCreateManagedWidget("form", xmFormWidgetClass,
		parent,
		NULL);
	XtManageChild(form);

	l = XtVaCreateManagedWidget("matrixfontlabel", xmLabelGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_NONE,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);

	t = XtVaCreateManagedWidget("matrixfonttf", xmTextFieldWidgetClass,
		form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftOffset,		10,
			XmNleftWidget,		l,
			XmNrightAttachment,	XmATTACH_NONE,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);

	b = XtVaCreateManagedWidget("matrixfontbutton", xmPushButtonGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftOffset,		10,
			XmNleftWidget,		t,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
		NULL);
	XtAddCallback(b, XmNactivateCallback, FontChooserCB, (XtPointer)t);

	l = XtVaCreateManagedWidget("fontlabel", xmLabelGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_WIDGET,
			XmNrightWidget,		t,
			XmNrightOffset,		10,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);

	t = XtVaCreateManagedWidget("fonttf", xmTextFieldWidgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		t,
			XmNrightAttachment,	XmATTACH_OPPOSITE_WIDGET,
			XmNrightWidget,		t,
			XmNleftAttachment,	XmATTACH_OPPOSITE_WIDGET,
			XmNleftWidget,		t,
		NULL);

	b = XtVaCreateManagedWidget("fontbutton", xmPushButtonGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_OPPOSITE_WIDGET,
			XmNtopOffset,		0,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftOffset,		10,
			XmNleftWidget,		t,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
		NULL);
	XtAddCallback(b, XmNactivateCallback, FontChooserCB, (XtPointer)t);

	l = XtVaCreateManagedWidget("encodingLabel", xmLabelGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_WIDGET,
			XmNrightWidget,		t,
			XmNrightOffset,		10,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);

	t = XtVaCreateManagedWidget("encodingTf", xmTextFieldWidgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		t,
			XmNbottomAttachment,	XmATTACH_NONE,
			XmNrightAttachment,	XmATTACH_OPPOSITE_WIDGET,
			XmNrightWidget,		t,
			XmNleftAttachment,	XmATTACH_OPPOSITE_WIDGET,
			XmNleftWidget,		t,
		NULL);
}

void
DoUserPreferences(Widget w, XtPointer client, XtPointer call)
{
	Widget	ok, cancel, help, tf;
	int	c = (int)client, ac;
	Arg	al[5];

	MotifSelectGlobal(w);

	if (! UserPref) {
		ac = 0;
		XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
		UserPref = XmCreateTemplateDialog(mw, "UserPreferences",
			al, ac);
		CreateUserPreferences(UserPref);

		ok = XtVaCreateManagedWidget("ok", xmPushButtonGadgetClass, UserPref,
			NULL);
		cancel = XtVaCreateManagedWidget("cancel", xmPushButtonGadgetClass, UserPref,
			NULL);
		help = XtVaCreateManagedWidget("help", xmPushButtonGadgetClass, UserPref,
			NULL);

		XtAddCallback(ok, XmNactivateCallback, UserPreferencesOk, NULL);
		XtAddCallback(cancel, XmNactivateCallback, CancelTemplate, UserPref);
		XtAddCallback(help, XmNactivateCallback, helpUsingCB,
			(XtPointer)"#HelpUserPreferences");
	}

	UserPreferencesReset(UserPref);
	XtManageChild(UserPref);
}

void
SaveUserPreferences(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	save_preferences();
}

/****************************************************************
 *								*
 *		Database Interaction				*
 *								*
 ****************************************************************/
void
DoMySQLRead(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	none(w, client, call);
}

void
DoMySQLWrite(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	none(w, client, call);
}

void
DoXbaseRead(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	none(w, client, call);
}

void
DoXbaseWrite(Widget w, XtPointer client, XtPointer call)
{
	MotifSelectGlobal(w);

	none(w, client, call);
}

void
MySQLDialogOk(Widget w, XtPointer client, XtPointer call)
{
	Widget	d = FindDialog(w),
		dbhosttf = XtNameToWidget(d, "*dbhosttf"),
		dbnametf = XtNameToWidget(d, "*dbnametf"),
		dbusertf = XtNameToWidget(d, "*dbusertf");
	char	*h, *n, *u;

	MotifSelectGlobal(w);
	XtUnmanageChild(MySQLDialog);

	if (dbhosttf == NULL || dbnametf == NULL)
		return;

	h = XmTextFieldGetString(dbhosttf);
	DatabaseSetHost(h);

	n = XmTextFieldGetString(dbnametf);
	DatabaseSetName(n);

	u = XmTextFieldGetString(dbusertf);
	DatabaseSetUser(u);

	MessageAppend(False, "Database host %s name %s user %s", h, n, u);

	XtFree(h);
	XtFree(n);
	XtFree(u);
}

void
ConfigureMySQLDialogReset(Widget w)
{
	Widget	d = FindDialog(w),
		dbhosttf = XtNameToWidget(d, "*dbhosttf"),
		dbnametf = XtNameToWidget(d, "*dbnametf"),
		dbusertf = XtNameToWidget(d, "*dbusertf");

	if (dbhosttf == NULL || dbnametf == NULL)
		return;

	XmTextFieldSetString(dbhosttf, DatabaseGetHost());
	XmTextFieldSetString(dbnametf, DatabaseGetName());
	XmTextFieldSetString(dbusertf, DatabaseGetUser());
}

void
CreateConfigureMySQLDialog(Widget parent)
{
	Widget	form, l, t;

	form = XtVaCreateManagedWidget("form", xmFormWidgetClass,
		parent,
		NULL);
	XtManageChild(form);

	l = XtVaCreateManagedWidget("dbhostlabel", xmLabelGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_NONE,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);

	t = XtVaCreateManagedWidget("dbhosttf", xmTextFieldWidgetClass,
		form,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNtopOffset,		10,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftOffset,		10,
			XmNleftWidget,		l,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);

	l = XtVaCreateManagedWidget("dbnamelabel", xmLabelGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_NONE,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);

	t = XtVaCreateManagedWidget("dbnametf", xmTextFieldWidgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftOffset,		10,
			XmNleftWidget,		l,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);

	l = XtVaCreateManagedWidget("dbuserlabel", xmLabelGadgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNleftOffset,		10,
			XmNrightAttachment,	XmATTACH_NONE,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);

	t = XtVaCreateManagedWidget("dbusertf", xmTextFieldWidgetClass,
		form,
			XmNtopAttachment,	XmATTACH_WIDGET,
			XmNtopOffset,		10,
			XmNtopWidget,		t,
			XmNleftAttachment,	XmATTACH_WIDGET,
			XmNleftOffset,		10,
			XmNleftWidget,		l,
			XmNrightAttachment,	XmATTACH_FORM,
			XmNrightOffset,		10,
			XmNbottomAttachment,	XmATTACH_NONE,
		NULL);
}

void
ConfigureMySQL(Widget w, XtPointer client, XtPointer call)
{
	Widget	ok, cancel, help, tf;
	int	c = (int)client, ac;
	Arg	al[5];

	MotifSelectGlobal(w);

	if (! MySQLDialog) {
		ac = 0;
		XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
		MySQLDialog = XmCreateTemplateDialog(mw, "mySQLDialog", al, ac);
		CreateConfigureMySQLDialog(MySQLDialog);

		ok = XtVaCreateManagedWidget("ok", xmPushButtonGadgetClass, MySQLDialog,
			NULL);
		cancel = XtVaCreateManagedWidget("cancel", xmPushButtonGadgetClass, MySQLDialog,
			NULL);
		help = XtVaCreateManagedWidget("help", xmPushButtonGadgetClass, MySQLDialog,
			NULL);

		XtAddCallback(ok, XmNactivateCallback, MySQLDialogOk, NULL);
		XtAddCallback(help, XmNactivateCallback, helpUsingCB,
			(XtPointer)"#HelpMySQL");
		XtAddCallback(cancel, XmNactivateCallback, CancelTemplate, MySQLDialog);
	}

	ConfigureMySQLDialogReset(MySQLDialog);
	XtManageChild(MySQLDialog);
}

void
ExecuteCommandOk(Widget w, XtPointer client, XtPointer call)
{
	XmSelectionBoxCallbackStruct *cbp = (XmSelectionBoxCallbackStruct *)call;
	String	s;

	MotifSelectGlobal(w);

	if (! XmStringGetLtoR(cbp->value, XmFONTLIST_DEFAULT_TAG, &s)) {
		MessageAppend(True, "Couldn't convert XmString into string\n");
		return;
	}

	MessageAppend(False, "Executing command '%s'\n", s);

	execute_command(s);
	XtFree(s);
}

void
ExecuteCommandCB(Widget w, XtPointer client, XtPointer call)
{
	int	ac;
	Arg	al[5];

	MotifSelectGlobal(w);

	if (! ExecuteCommandDialog) {
		ac = 0;
		XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
		ExecuteCommandDialog = XmCreatePromptDialog(mw, "executeCommandDialog", al, ac);

		XtAddCallback(ExecuteCommandDialog, XmNokCallback, ExecuteCommandOk, NULL);
		XtAddCallback(ExecuteCommandDialog, XmNhelpCallback, helpUsingCB,
			(XtPointer)"#HelpExecuteCommand");
		XtAddCallback(ExecuteCommandDialog, XmNcancelCallback, CancelTemplate,
			(XtPointer)ExecuteCommandDialog);
	}

	XtManageChild(ExecuteCommandDialog);
}

/****************************************************************
 *								*
 *		Build Motif GUI					*
 *								*
 ****************************************************************/
/*
 * Build a splash screen
 */

Widget
GscBuildSplash(Widget parent)
{
	Arg		al[10];
	int		ac;
	Pixel		bg, fg;
	Display		*dpy = XtDisplay(parent);
	int		depth;
	Widget		sh, rc, iconlabel, textlabel, form;
	XmString	x1, x2, xms;

#ifdef	HAVE_LIBXPM
	XpmAttributes	attrib;
	Pixmap		mask;
#endif

	sh = XtVaCreatePopupShell("splashShell", xmMenuShellWidgetClass, parent,
#if 0
			XmNallowShellResize,	False,
#endif
			XmNx,			300,
			XmNy,			300,
			XmNwidth,		100,
			XmNheight,		100,
		NULL);

	/* MenuShell wants an RC child */
	rc = XtVaCreateManagedWidget("splash", xmRowColumnWidgetClass, sh, NULL);

	form = XtVaCreateWidget("form", xmFormWidgetClass, rc, NULL);

	ac = 0;

#ifdef	HAVE_LIBXPM
        attrib.closeness = 256;
	attrib.valuemask = 0;
        attrib.valuemask |= XpmCloseness;

        ac = XpmCreatePixmapFromData(XtDisplay(rc),
                XRootWindowOfScreen(XtScreen(rc)),
                oleo_icon,
                &Global->MotifGlobal->oleo_icon_pm,
                &mask,
                &attrib);

        if ( ac < 0 ) {
                fprintf ( stderr, "GBSplash: XpmCreatePixmapFromData failed. ");

                switch ( ac ) {
                case -1:
                        fprintf ( stderr, "XpmOpenFailed failed\n" );
                        XpmFreeAttributes(&attrib);
                        goto skip_xpm_icon;
                break;

                case -2:
                        fprintf ( stderr, "XpmFileInvalid\n" );
                        XpmFreeAttributes(&attrib);
                        goto skip_xpm_icon;
                break;

                case -3:
                        fprintf ( stderr, "XpmNoMemory\n" );
                        goto skip_xpm_icon;
                        XpmFreeAttributes(&attrib);
                break;

                case -4:
                        while ( attrib.closeness <= 10000 ) {
                                attrib.closeness += 256;
                                ac = XpmCreatePixmapFromData(XtDisplay(rc),
		XRootWindowOfScreen(XtScreen(rc)),
		oleo_icon,
		&Global->MotifGlobal->oleo_icon_pm,
		&mask,
		&attrib);
                                if ( ac == -4 ) continue;
                                if ( ac >= 0 ) break;
                                if ( ac == -1 || ac == -2 || ac == -3  ) {
                                        fprintf ( stderr, "secondary error %d\n", ac );
                                        XpmFreeAttributes(&attrib);
                                        goto skip_xpm_icon;
                                }
                        } /* while ( attrib.closeness <= 10000 ) */
                        if ( attrib.closeness > 10000 ) {
                                fprintf ( stderr, "giving up, can't get close colors\n" );
                                XpmFreeAttributes(&attrib);
                                goto skip_xpm_icon;
                        } else
                                fprintf ( stderr, "\n" );
                break;

                default:
                break;
                } /* switch (ac) */
        } /* if ( ac < 0 ) */

        ac = 0;

	XpmFreeAttributes(&attrib);

	XtSetArg(al[ac], XmNlabelPixmap,	Global->MotifGlobal->oleo_icon_pm); ac++;
	XtSetArg(al[ac], XmNlabelType,		XmPIXMAP); ac++;
	XtSetArg(al[ac], XmNshadowThickness,	0); ac++;
	XtSetArg(al[ac], XmNleftAttachment,	XmATTACH_FORM); ac++;
	XtSetArg(al[ac], XmNleftOffset,		0); ac++;
	XtSetArg(al[ac], XmNrightAttachment,	XmATTACH_NONE); ac++;
	XtSetArg(al[ac], XmNtopAttachment,	XmATTACH_FORM); ac++;
	XtSetArg(al[ac], XmNtopOffset,		0); ac++;
	iconlabel = XmCreatePushButtonGadget(form, "iconsplash", al, ac);
	XtManageChild(iconlabel);
	XtAddCallback(iconlabel, XmNactivateCallback, PopDownSplash, NULL);
#endif

        skip_xpm_icon: /* XpmCreatePixmapFromData call failed -- skip it */

	ac = 0;
	x1 = XmStringCreateLtoR(GNU_PACKAGE " " VERSION "\n",
		"large");
	x2 = XmStringCreateLtoR(CopyRightString, "small");
	xms = XmStringConcat(x1, x2);
	XtSetArg(al[ac], XmNlabelString,	xms); ac++;
	XtSetArg(al[ac], XmNlabelType,		XmSTRING); ac++;
	XtSetArg(al[ac], XmNshadowThickness,	0); ac++;
#ifdef	HAVE_LIBXPM
	XtSetArg(al[ac], XmNleftAttachment,	XmATTACH_WIDGET); ac++;
	XtSetArg(al[ac], XmNleftWidget,		iconlabel); ac++;
#else
	XtSetArg(al[ac], XmNleftAttachment,	XmATTACH_FORM); ac++;
#endif
	XtSetArg(al[ac], XmNleftOffset,		0); ac++;
	XtSetArg(al[ac], XmNrightAttachment,	XmATTACH_FORM); ac++;
	XtSetArg(al[ac], XmNrightOffset,	0); ac++;
	XtSetArg(al[ac], XmNtopAttachment,	XmATTACH_FORM); ac++;
	XtSetArg(al[ac], XmNtopOffset,		0); ac++;
	textlabel = XmCreatePushButtonGadget(form, "textsplash", al, ac);
	XtManageChild(textlabel);
	XtAddCallback(textlabel, XmNactivateCallback, PopDownSplash, NULL);

	XmStringFree(x1);
	XmStringFree(x2);
	XmStringFree(xms);

	XtManageChild(form);

	return sh;
}

void Traverse()
{
	if (! XmProcessTraversal(mat, XmTRAVERSE_CURRENT)) {
		fprintf(stderr, _("XmProcessTraversal failed\n"));
	}
}

/*
 * Create the main widget tree.
 */
Widget
GscBuildMainWindow(Widget parent)
{
	int	i;

	/*
	 * Structure
	 */
	mw = XmCreateMainWindow(parent, "mw", NULL, 0);
	XtManageChild(mw);

	mb = XmCreateMenuBar(mw, "menubar", NULL, 0);
	XtManageChild(mb);

	filemenu = XmCreatePulldownMenu(mb, "filemenu", NULL, 0);
	XtManageChild(filemenu);

	XtVaCreateManagedWidget("filecascade", xmCascadeButtonGadgetClass, mb,
			XmNsubMenuId,	filemenu,
		NULL);

	editmenu = XmCreatePulldownMenu(mb, "editmenu", NULL, 0);
	XtManageChild(editmenu);

	XtVaCreateManagedWidget("editcascade", xmCascadeButtonGadgetClass, mb,
			XmNsubMenuId,	editmenu,
		NULL);

	stylemenu = XmCreatePulldownMenu(mb, "stylemenu", NULL, 0);
	XtManageChild(stylemenu);

	XtVaCreateManagedWidget("stylecascade", xmCascadeButtonGadgetClass, mb,
			XmNsubMenuId,	stylemenu,
		NULL);

	optionsmenu = XmCreatePulldownMenu(mb, "optionsmenu", NULL, 0);
	XtManageChild(optionsmenu);

	XtVaCreateManagedWidget("optionscascade", xmCascadeButtonGadgetClass, mb,
			XmNsubMenuId,	optionsmenu,
		NULL);

	graphmenu = XmCreatePulldownMenu(mb, "graphmenu", NULL, 0);
	XtManageChild(graphmenu);

	XtVaCreateManagedWidget("graphcascade", xmCascadeButtonGadgetClass, mb,
			XmNsubMenuId,	graphmenu,
		NULL);

	helpmenu = XmCreatePulldownMenu(mb, "helpmenu", NULL, 0);
	XtManageChild(helpmenu);

	w = XtVaCreateManagedWidget("helpcascade", xmCascadeButtonGadgetClass,
		mb,
			XmNsubMenuId,	helpmenu,
		NULL);
	XtVaSetValues(mb, XmNmenuHelpWidget, w, NULL);

	/*
	 * Row- and column labels
	 */
	if (! columnwidths)
		ResetColumnWidths();
	SetRowColumnLabels();

	/*
	 * The main thing : BaeMatrix
	 *
	 * A couple of application resources determine how big the matrix
	 * should be made.
	 * FIX ME.
	 */
	mat = XtVaCreateManagedWidget("bae", xbaeMatrixWidgetClass, mw,
			XmNrowLabels,		rowlabels,
			XmNcolumnLabels,	columnlabels,
			XmNcolumnWidths,	columnwidths,
			XmNcolumnMaxLengths,	columnmaxlengths,
			XmNrows,		AppRes.rows,
			XmNcolumns,		AppRes.columns,
		NULL);
	XtAddCallback(mat, XmNleaveCellCallback, LeaveCell, NULL);
	XtAddCallback(mat, XmNenterCellCallback, EnterCell, NULL);
	XtAddCallback(mat, XmNmodifyVerifyCallback, ModifyVerify, NULL);
	XtAddCallback(mat, XmNdrawCellCallback, DrawCell, NULL);
	XtAddCallback(mat, XmNwriteCellCallback, WriteCell, NULL);
	XtAddCallback(mat, XmNselectCellCallback, SelectCellCB, NULL);
	XtAddCallback(mat, XmNresizeColumnCallback, ResizeColumnCB, NULL);
#ifdef	XmNresizeRowCallback
	XtAddCallback(mat, XmNresizeRowCallback, ResizeRowCB, NULL);
#endif

	/*
	 * We're building a small combo of two widgets which will represent
	 * one area in the MainWindow : a status area, and a formula editor.
	 */
	w = XtVaCreateManagedWidget("statusformula", xmFormWidgetClass,
		mw,
		NULL);
	statustf = XtVaCreateManagedWidget("status", xmTextFieldWidgetClass,
		w,
			XmNeditable,		False,
			XmNblinkRate,		0,
		NULL);

	formulatf = XtVaCreateManagedWidget("formula", xmTextFieldWidgetClass,
		w,
			XmNleftWidget,		statustf,
			XmNleftOffset,		5,
			XmNleftAttachment,	XmATTACH_WIDGET,
		NULL);
	XtAddCallback(formulatf, XmNactivateCallback, FormulaCB, 0);

	/*
	 * Oleo error messages
	 */
	msgtext = XmCreateScrolledText(mw, "messages", NULL, 0);
	XtVaSetValues(mw,
			XmNmessageWindow,	XtParent(msgtext),
			XmNcommandWindow,	w,
		NULL);
	XtManageChild(msgtext);

	if (Global->MotifGlobal->early_msg_text) {
		XmTextSetString(msgtext, Global->MotifGlobal->early_msg_text);
		free(Global->MotifGlobal->early_msg_text);
		Global->MotifGlobal->early_msg_text = NULL;

		XmTextShowPosition(msgtext, XmTextGetLastPosition(msgtext));
		XBell(XtDisplay(msgtext), 30);
	}

	/*
	 * Menu system contents :
	 *	File Menu.
	 */
	w = XtVaCreateManagedWidget("new", xmPushButtonGadgetClass, filemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, NewCB, NULL);

	w = XtVaCreateManagedWidget("open", xmPushButtonGadgetClass, filemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, OpenCB, NULL);

	w = XtVaCreateManagedWidget("save", xmPushButtonGadgetClass, filemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, SaveCB, NULL);

	w = XtVaCreateManagedWidget("saveas", xmPushButtonGadgetClass, filemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, SaveAsCB, NULL);

	w = XtVaCreateManagedWidget("close", xmPushButtonGadgetClass, filemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, CloseCB, NULL);

	w = XtVaCreateManagedWidget("print", xmPushButtonGadgetClass, filemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, printCB, NULL);

	w = XtVaCreateManagedWidget("quit", xmPushButtonGadgetClass, filemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, QuitCB, NULL);

	/*
	 *	Edit Menu.
	 */
	w = XtVaCreateManagedWidget("undo", xmPushButtonGadgetClass, editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, UndoCB, NULL);

	XtVaCreateManagedWidget("sep1", xmSeparatorGadgetClass, editmenu,
		NULL);

	w = XtVaCreateManagedWidget("copy", xmPushButtonGadgetClass, editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, CopyCB, NULL);

	w = XtVaCreateManagedWidget("cut", xmPushButtonGadgetClass, editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, CutCB, NULL);

	w = XtVaCreateManagedWidget("paste", xmPushButtonGadgetClass, editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, PasteCB, NULL);

	XtVaCreateManagedWidget("sep2", xmSeparatorGadgetClass, editmenu,
		NULL);

	w = XtVaCreateManagedWidget("sort", xmPushButtonGadgetClass, editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	w = XtVaCreateManagedWidget("copyvalues", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, CopyValuesCB, NULL);

	w = XtVaCreateManagedWidget("copyregion", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, CopyRegionCB, NULL);

	w = XtVaCreateManagedWidget("move", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, MoveCB, NULL);

	XtVaCreateManagedWidget("sep3", xmSeparatorGadgetClass, editmenu,
		NULL);

	w = XtVaCreateManagedWidget("insert", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, EditInsertCB, NULL);

	w = XtVaCreateManagedWidget("delete", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, EditDeleteCB, NULL);

	XtVaCreateManagedWidget("sep4", xmSeparatorGadgetClass, editmenu,
		NULL);

	w = XtVaCreateManagedWidget("recalculate", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, EditRecalculateCB, NULL);

	XtVaCreateManagedWidget("sep5", xmSeparatorGadgetClass, editmenu,
		NULL);

	w = XtVaCreateManagedWidget("setregion", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	w = XtVaCreateManagedWidget("markcell", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, MarkCellCB, NULL);

	w = XtVaCreateManagedWidget("markregion", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, MarkRegionCB, NULL);

	w = XtVaCreateManagedWidget("setmark", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, MarkFromDialogCB, NULL);

	w = XtVaCreateManagedWidget("gotocell", xmPushButtonGadgetClass,
		editmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);


	/*
	 *	Style Menu.
	 */
	w = XtVaCreateManagedWidget("format", xmPushButtonGadgetClass,
		stylemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	w = XtVaCreateManagedWidget("alignment", xmPushButtonGadgetClass,
		stylemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	w = XtVaCreateManagedWidget("font", xmPushButtonGadgetClass,
		stylemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	XtVaCreateManagedWidget("sep1", xmSeparatorGadgetClass, stylemenu,
		NULL);

	w = XtVaCreateManagedWidget("width", xmPushButtonGadgetClass,
		stylemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	w = XtVaCreateManagedWidget("height", xmPushButtonGadgetClass,
		stylemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	XtVaCreateManagedWidget("sep2", xmSeparatorGadgetClass, stylemenu,
		NULL);

	w = XtVaCreateManagedWidget("protection", xmPushButtonGadgetClass,
		stylemenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	/*
	 *	Graph Menu.
	 */
	w = XtVaCreateManagedWidget("define", xmPushButtonGadgetClass,
		graphmenu,
		NULL);
#ifdef	HAVE_LIBPLOT
	XtAddCallback(w, XmNactivateCallback, ConfigureGraph, NULL);
#else
	XtAddCallback(w, XmNactivateCallback, NoPlotutils, NULL);
#endif

#ifdef	HAVE_SciPlot_H
	w = XtVaCreateManagedWidget("show", xmPushButtonGadgetClass, graphmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, ShowGraph, NULL);

	w = XtVaCreateManagedWidget("print", xmPushButtonGadgetClass, graphmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, PrintGraph, NULL);
#endif

#ifdef	HAVE_LIBPLOT
	/* GNU Plotutils */
	w = XtVaCreateManagedWidget("pulabel", xmLabelGadgetClass, graphmenu,
		NULL);
	w = XtVaCreateManagedWidget("pushowpie", xmPushButtonGadgetClass,
		graphmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, PuShowChart, PuPieChart);

	w = XtVaCreateManagedWidget("pushowbar", xmPushButtonGadgetClass,
		graphmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, PuShowChart, PuBarChart);
	w = XtVaCreateManagedWidget("pushowxy", xmPushButtonGadgetClass,
		graphmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, PuShowChart, PuXYChart);
	w = XtVaCreateManagedWidget("puprintpie", xmPushButtonGadgetClass,
		graphmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, PuPrint, (XtPointer)GRAPH_PIE);
	w = XtVaCreateManagedWidget("puprintbar", xmPushButtonGadgetClass,
		graphmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, PuPrint, (XtPointer)GRAPH_BAR);
	w = XtVaCreateManagedWidget("puprintxy", xmPushButtonGadgetClass,
		graphmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, PuPrint, (XtPointer)GRAPH_XY);
#endif


	/*
	 *	Options Menu.
	 */
	w = XtVaCreateManagedWidget("a0", xmToggleButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNvalueChangedCallback, ToggleA0, NULL);
	XmToggleButtonGadgetSetState(w, Global->a0, False);

	w = XtVaCreateManagedWidget("edges", xmToggleButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNvalueChangedCallback, none, NULL);
	XmToggleButtonGadgetSetState(w, cwin->flags & WIN_EDGES, False);

	w = XtVaCreateManagedWidget("autorecalc", xmToggleButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNvalueChangedCallback, none, NULL);
	XmToggleButtonGadgetSetState(w, Global->auto_recalc, False);

	w = XtVaCreateManagedWidget("loadhooks", xmToggleButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNvalueChangedCallback, none, NULL);
	XmToggleButtonGadgetSetState(w, UserPreferences.run_load_hooks, False);

	w = XtVaCreateManagedWidget("statusline", xmToggleButtonGadgetClass,
		optionsmenu,
		NULL);
	XmToggleButtonGadgetSetState(w, Global->a0, False);
	XtAddCallback(w, XmNvalueChangedCallback, none, NULL);

	w = XtVaCreateManagedWidget("backup", xmToggleButtonGadgetClass,
		optionsmenu,
		NULL);
	XmToggleButtonGadgetSetState(w, __make_backups, False);
	XtAddCallback(w, XmNvalueChangedCallback, none, NULL);

	w = XtVaCreateManagedWidget("backupcopy", xmToggleButtonGadgetClass,
		optionsmenu,
		NULL);
	XmToggleButtonGadgetSetState(w, __backup_by_copying, False);
	XtAddCallback(w, XmNvalueChangedCallback, none, NULL);

	XtVaCreateManagedWidget("sep1", xmSeparatorGadgetClass, optionsmenu,
		NULL);

	w = XtVaCreateManagedWidget("formats", xmPushButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, FormatsDialog, NULL);

	w = XtVaCreateManagedWidget("defaultformat", xmPushButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, FormatsDialog, (XtPointer) 1);

	w = XtVaCreateManagedWidget("printoptions", xmPushButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, PrintOptionsCB, NULL);

	w = XtVaCreateManagedWidget("defaultfileformat",
		xmPushButtonGadgetClass, optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, DefaultFileCB, NULL);

	XtVaCreateManagedWidget("sep2", xmSeparatorGadgetClass, optionsmenu,
		NULL);

	w = XtVaCreateManagedWidget("setvariable", xmPushButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	w = XtVaCreateManagedWidget("showvariable", xmPushButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	w = XtVaCreateManagedWidget("listvariables", xmPushButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	w = XtVaCreateManagedWidget("showoptions", xmPushButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, none, NULL);

	XtVaCreateManagedWidget("sep3", xmSeparatorGadgetClass, optionsmenu,
		NULL);

	w = XtVaCreateManagedWidget("userpreferences", xmPushButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, DoUserPreferences, NULL);

	w = XtVaCreateManagedWidget("saveuserpreferences", xmPushButtonGadgetClass,
		optionsmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, SaveUserPreferences, NULL);

	/*
	 * Database Access
	 */
	dbmenu = XmCreatePulldownMenu(mb, "dbmenu", NULL, 0);
	XtManageChild(dbmenu);

	w = XtVaCreateManagedWidget("dbcascade", xmCascadeButtonGadgetClass,
		mb,
			XmNsubMenuId,	dbmenu,
		NULL);

#ifdef	HAVE_LIBMYSQLCLIENT
	w = XtVaCreateManagedWidget("configuremysql", xmPushButtonGadgetClass,
		dbmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, ConfigureMySQL, NULL);

	w = XtVaCreateManagedWidget("readmysql", xmPushButtonGadgetClass,
		dbmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, DoMySQLRead, NULL);

	w = XtVaCreateManagedWidget("writemysql", xmPushButtonGadgetClass,
		dbmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, DoMySQLWrite, NULL);
#endif

#if defined(HAVE_LIBMYSQLCLIENT) && defined(HAVE_LIBXBASE)
	w = XtVaCreateManagedWidget("sep", xmSeparatorGadgetClass,
		dbmenu,
		NULL);
#endif
#if (! defined(HAVE_LIBMYSQLCLIENT)) && (! defined(HAVE_LIBXBASE))
	/* This only happens if no db linked in */
	w = XtVaCreateManagedWidget("nodbms", xmLabelGadgetClass,
		dbmenu,
		NULL);
#endif

#ifdef	HAVE_LIBXBASE
	w = XtVaCreateManagedWidget("readxbase", xmPushButtonGadgetClass,
		dbmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, DoXbaseRead, NULL);

	w = XtVaCreateManagedWidget("writexbase", xmPushButtonGadgetClass,
		dbmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, DoXbaseWrite, NULL);
#endif

	/*
	 * Add test code here
	 */
#ifdef	HAVE_TEST
	testmenu = XmCreatePulldownMenu(mb, "testmenu", NULL, 0);
	XtManageChild(testmenu);

	w = XtVaCreateManagedWidget("testcascade", xmCascadeButtonGadgetClass,
		mb,
			XmNsubMenuId,	testmenu,
		NULL);

	w = XtVaCreateManagedWidget("Traverse", xmPushButtonGadgetClass, testmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, Traverse, NULL);

	w = XtVaCreateManagedWidget("OpenNew", xmPushButtonGadgetClass, testmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, OpenNewCB, NULL);

	w = XtVaCreateManagedWidget("ExecuteCommand", xmPushButtonGadgetClass, testmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, ExecuteCommandCB, NULL);

#endif	/* HAVE_TEST */

	/*
	 *	Help Menu.
	 */
	w = XtVaCreateManagedWidget("about", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpAboutCB, NULL);

	w = XtVaCreateManagedWidget("version", xmPushButtonGadgetClass,
		helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpVersionCB, NULL);

	w = XtVaCreateManagedWidget("using", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB, NULL);

	XtVaCreateManagedWidget("sep1", xmSeparatorGadgetClass, helpmenu, NULL);

	w = XtVaCreateManagedWidget("math", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB,
		"#HelpMathFunctions");

	w = XtVaCreateManagedWidget("trig", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB,
		"#HelpTrigonometricFunctions");

	w = XtVaCreateManagedWidget("stats", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB,
		"#HelpStatisticalFunctions");

	w = XtVaCreateManagedWidget("bool", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB,
		"#HelpBooleanFunctions");

	w = XtVaCreateManagedWidget("string", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB,
		"#HelpStringFunctions");

	w = XtVaCreateManagedWidget("struct", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB,
		"#HelpStructuralFunctions");

	w = XtVaCreateManagedWidget("search", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB,
		"#HelpSearchFunctions");

	w = XtVaCreateManagedWidget("bus", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB,
		"#HelpBusinessFunctions");

	w = XtVaCreateManagedWidget("date", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB,
		"#HelpDateFunctions");

	XtVaCreateManagedWidget("sep2", xmSeparatorGadgetClass, helpmenu, NULL);

	w = XtVaCreateManagedWidget("expr", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB, "#HelpExpressions");

	w = XtVaCreateManagedWidget("error", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB, "#HelpErrorValues");

	w = XtVaCreateManagedWidget("format", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB, NULL);

	w = XtVaCreateManagedWidget("option", xmPushButtonGadgetClass, helpmenu,
		NULL);
	XtAddCallback(w, XmNactivateCallback, helpUsingCB, "#HelpOptions");

	return mw;
}

static void
xio_redraw_input_cursor (int on)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_redraw_input_cursor\n");
#endif
}

static void
xio_cellize_cursor (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_cellize_cursor\n");
#endif
	xio_redraw_input_cursor (0);
}

static void
xio_inputize_cursor (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_inputize_cursor\n");
#endif
	xio_redraw_input_cursor (1);
}

/* This redraws the input area without recomputing anything. */
static void
xio_redraw_input (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_redraw_input\n");
#endif
}

static void
xio_fix_input (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_fix_input\n");
#endif
}

static void
xio_move_cursor (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_move_cursor\n");
#endif
}

static void
xio_erase (int len)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_erase\n");
#endif
}

static void
xio_insert (int len)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_insert\n");
#endif
}

static void
xio_over (char * str, int len)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_over\n");
#endif
}

static void
xio_scan_for_input (int blockp)
{
}

static int
xio_input_avail (void)
{
}

static void
xio_wait_for_input (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_wait_for_input ...\n");
#endif
}

static int
xio_read_kbd (char *buffer, int size)
{
	int amt_read = size < Global->MotifGlobal->chars_buffered ? size : Global->MotifGlobal->chars_buffered;
#ifdef	VERBOSE
	Debug(__FILE__, "xio_read_kbd ...\n");
#endif
	if (amt_read)
		bcopy (Global->MotifGlobal->input_buf, (char *) buffer, amt_read);
	Global->MotifGlobal->chars_buffered -= amt_read;
	if (Global->MotifGlobal->chars_buffered)
		bcopy ((char *) Global->MotifGlobal->input_buf + amt_read, (char *) Global->MotifGlobal->input_buf, Global->MotifGlobal->chars_buffered);
	return amt_read;
}

static void
xio_nodelay (int delayp)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_nodelay ...\n");
#endif
}

static int
xio_getch (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_getch ...\n");
#endif
	return 0;
}

static void
xio_clear_input_before (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_clear_input_before\n");
#endif
}

static void
xio_clear_input_after (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_clear_input_after\n");
#endif
}

static void
UpdateStatus(void)
{
	CELL	*cp;
	char	buf[1024];
	char	*ptr, *pos = buf, *dec;

	if (!user_status) {
		Debug(__FILE__, "UpdateStatus: no user_status\n");
		return;
	}

	if (mkrow != NON_ROW) {
		struct rng r;
		*pos++ = '*';
		set_rng (&r, curow, cucol, mkrow, mkcol);
		ptr = range_name (&r);
	} else
		ptr = cell_name (curow, cucol);

	strcpy(buf, ptr);

	/* Put formula in its own widget */
	if ((cp = find_cell (curow, cucol)) && cp->cell_formula) {
		dec = decomp (curow, cucol, cp);
		if (formulatf)
			XmTextFieldSetString(formulatf, dec);
	}

	/* Value */
	ptr = cell_value_string (curow, cucol, True);
	if (ptr) {
		strcat(buf, " [");
		strcat(buf, ptr);
		strcat(buf, "]");
	}

	if (statustf)
		XmTextFieldSetString(statustf, buf);
}

/*
 * Copy the spreadsheet values into Xbae
 * Then do an event loop until there are no more input events.
 */
static void
xio_update_status (void)
{
	XEvent	ev;

	UpdateStatus();


#ifdef	VERBOSEx
	Debug(__FILE__, "xio_update_status(%d,%d)...", curow, cucol);
#endif

	while (XtAppPending(app)) {
		XtAppNextEvent(app, &ev);
		(void) XtDispatchEvent(&ev);
	}

#ifdef	VERBOSEx
	Debug(__FILE__, " done\n");
#endif
}

static int
xio_get_chr (char *prompt)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_get_chr(%s)\n", prompt);
#endif

	MessageAppend(True, prompt);
	return io_getch ();
}

static void
xio_pr_cell_win (struct window *win, CELLREF r, CELLREF c, CELL *cp)
{
#ifdef	VERBOSEx
	Debug(__FILE__, "xio_pr_cell_win\n");
#endif
}

static void
xio_repaint_win (struct window *win)
{
#ifdef	VERBOSEx
	Debug(__FILE__, "xio_repaint_win\n");
#endif
}


static void
xio_repaint (void)
{
#ifdef	VERBOSEx
	Debug(__FILE__, "xio_repaint\n");
#endif
}


static void
xio_hide_cell_cursor (void)
{
#ifdef	VERBOSEx
	Debug(__FILE__, "xio_hide_cell_cursor\n");
#endif
}

static void
xio_display_cell_cursor (void)
{
#ifdef	VERBOSEx
	Debug(__FILE__, "xio_display_cell_cursor\n");
#endif
}

/* Refresh the existing image. */

static void
xio_redisp (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_redisp\n");
#endif
}

extern void
xio_close_display(int e)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_close_display\n");
#endif
	if (toplevel && XtDisplay(toplevel)) {
		XtDestroyWidget(toplevel);
	}

	toplevel = NULL;
}

static void
xio_flush (void)
{
#if 0
	XFlush (theDisplay);
#endif
}


static void
xio_bell (void)
{
#if 0
	XBell (XtDisplay(toplevel), 30);
	XFlush (XtDisplay(toplevel));
#endif
}

extern void
xio_open_display (void)
{
#ifdef	VERBOSE
	Debug(__FILE__, "xio_open_display\n");
#endif
}

int
io_col_to_input_pos (int c)
{
#if 1
  Debug(__FILE__, "io_col_to_input_pos\n");
  return 0;
#else
  struct input_view * iv = &thePort->input_view;
  char * prompt = (iv->expanded_keymap_prompt
		   ? iv->expanded_keymap_prompt
		   : (iv->prompt ? iv->prompt : ""));
  int prompt_wid = iv->prompt_metric (prompt, strlen(prompt));

  c -= prompt_wid;
  {
    int cpos;
    int max = iv->visibility_end - iv->visibility_begin + 1;
    for (cpos = 1; cpos <= max; ++cpos)
      if (iv->input_metric (iv->input_area->buf + iv->visibility_begin, cpos)
	  >= c)
	break;
    return iv->visibility_begin + cpos - 1;
  }
#endif
}

void
set_x_default_point_size (int l)
{
  /* If the display is not open (ie, we're running on a non x screen, */
  /* silently return - allows stuff in startup file to work right.PGA */
  if (!toplevel)
    return;
}

void
xio_command_loop (int i)
{
	XtAppMainLoop(app);
}

void PopDownSplash()
{
	if (SplashShell)
		XtDestroyWidget(SplashShell);
	SplashShell = 0;
}

void MotifGlobalInitialize(void)
{
	if (Global->MotifGlobal)
		return;

	Global->MotifGlobal = (struct MotifGlobalType *)XtMalloc(sizeof(struct MotifGlobalType));
	memset(Global->MotifGlobal, 0, sizeof(struct MotifGlobalType));

	Global->MotifGlobal->input_buf_allocated = 1024;
	strcpy(Global->MotifGlobal->fileformat, "oleo");
	Global->MotifGlobal->havea0 = -1;
	Global->MotifGlobal->selectedcells = NULL;
	Global->MotifGlobal->newcall = True;
	Global->MotifGlobal->needrecalculate = False;
}

void motif_init(int *argc, char **argv)
{
	Display	*dpy;

	MotifGlobalInitialize();

	io_command_loop = xio_command_loop;
	io_open_display = xio_open_display;
	io_redisp = xio_redisp;
	io_repaint = xio_repaint;
	io_repaint_win = xio_repaint_win;
	io_close_display = xio_close_display;
	io_input_avail = xio_input_avail;
	io_scan_for_input = xio_scan_for_input;
	io_wait_for_input = xio_wait_for_input;
	io_read_kbd = xio_read_kbd;
	io_nodelay = xio_nodelay;
	io_getch = xio_getch;
	io_bell = xio_bell;
	io_get_chr = xio_get_chr;
	io_update_status = xio_update_status;
	io_fix_input = xio_fix_input;
	io_move_cursor = xio_move_cursor;
	io_erase = xio_erase;
	io_insert = xio_insert;
	io_over = xio_over;
	io_flush = xio_flush;
	io_clear_input_before = xio_clear_input_before;
	io_clear_input_after = xio_clear_input_after;
	io_pr_cell_win = xio_pr_cell_win;
	io_hide_cell_cursor = xio_hide_cell_cursor;
	io_display_cell_cursor = xio_display_cell_cursor;
	io_cellize_cursor = xio_cellize_cursor;
	io_inputize_cursor = xio_inputize_cursor;

	/*
	 * XAPPLRESDIR is a variable that can be set to determine
	 *	where to find an application resource file.
	 * If it isn't defined yet, let's define one so the odds are
	 *	good for finding our (i18n) application resources.
	 */
#ifndef	APPLRESDIR
#define	APPLRESDIR	HTMLDIR
#endif

	if (getenv("XAPPLRESDIR") == NULL) {
#ifdef	HAVE_PUTENV
		putenv("XAPPLRESDIR=" APPLRESDIR);
#endif
	}

	/*
	 * Open a connection to the display and create a toplevel shell widget.
	 */
	XtToolkitInitialize();
	app = XtCreateApplicationContext();
	XtAppSetFallbackResources(app, fallback);
	dpy = XtOpenDisplay(app, NULL, PACKAGE, PACKAGE,
		/* options */		NULL, 0,
		/* command line */	argc, argv);

	toplevel = XtVaAppCreateShell(PACKAGE, PACKAGE,
		topLevelShellWidgetClass, dpy, NULL);
	RegisterWMClose(toplevel);
	XtVaSetValues(toplevel, XmNdeleteResponse, XmDO_NOTHING, NULL);

	/*
	 * Add converter for tearoffs.
	 */
#if XmVersion >= 1002
	XmRepTypeInstallTearOffModelConverter();
#endif

	XtVaGetApplicationResources(toplevel, &AppRes,
		resources, num_resources,
		NULL);

	if (AppRes.show_version > 0) {
		long	t = AppRes.show_version;

		SplashShell = GscBuildSplash(toplevel);
		XtPopup(SplashShell, XtGrabNone);

		/* Splash remains on screen for configurable time (milliseconds) */
		XtAppAddTimeOut(app, t, PopDownSplash, 0);
	}

	/* Without this we have NULL in cwin. */
	io_init_windows(AppRes.rows, AppRes.columns, 1, 2, 1, 1, 1, 1);
}

void RaiseSplash()
{
	/* This needs to be triggered by a timeout because otherwise it won't work */

	if (SplashShell)
		XRaiseWindow(XtDisplay(SplashShell), XtWindow(SplashShell));
}

void motif_build_gui(void)
{
	RegisterWMClose(toplevel);
	XtVaSetValues(toplevel, XmNdeleteResponse, XmDO_NOTHING, NULL);

	w = GscBuildMainWindow(toplevel);
	XtManageChild(w);
	XtRealizeWidget(toplevel);

#ifdef	HAVE_LIBXPM
	XtVaSetValues(toplevel, XmNiconPixmap, Global->MotifGlobal->oleo_icon_pm, NULL);
#endif

	if (AppRes.show_version > 0) {
		/* 30ms is an arbitrary short value. 1 probably works as well. */
		XtAppAddTimeOut(app, 30L, RaiseSplash, 0);
	}

	if (! XmProcessTraversal(mat, XmTRAVERSE_CURRENT)) {
		fprintf(stderr, _("XmProcessTraversal failed\n"));
	}

	if (Global->MotifGlobal->needrecalculate) {
		recalculate(1);
	}

}

void ReallyQuit(Widget w, XtPointer client, XtPointer call)
{
	kill_oleo();
}

void QuitCB(Widget w, XtPointer client, XtPointer call)
{
	Widget		md;
	Arg		al[4];
	int		ac;

	MotifSelectGlobal(w);

	/* FIX ME this should work on all open files */
	MdiQuit();

	if (Global->modified && !option_filter) {
		XmString xms = XmStringCreateLtoR(
			_("There are unsaved changes.\n"
			  "Do you want to quit anyway ?"),
			XmFONTLIST_DEFAULT_TAG);

		ac = 0;
		/* Application resource for message allow i18n */
		XtSetArg(al[ac], XmNmessageString, xms); ac++;

		md = XmCreateQuestionDialog(toplevel, "quitMB", al, ac);
		XtAddCallback(md, XmNokCallback, ReallyQuit, NULL);
		XtDestroyWidget(XmMessageBoxGetChild(md,
			XmDIALOG_HELP_BUTTON));
		XtManageChild(md);
		XmStringFree(xms);
		return;
	}

	kill_oleo();
}

static char *gpl_string =
#include "gpl.h"
;

void gplCB(Widget w, XtPointer client, XtPointer call)
{
	Widget		x, t, p;
	Arg		al[4];
	int		ac;

	MotifSelectGlobal(w);

	ac = 0;
	XtSetArg(al[ac], XmNautoUnmanage, False); ac++;
	x = XmCreateTemplateDialog(toplevel, "versionD", al, ac);

	ac = 0;
	XtSetArg(al[ac], XmNeditable, False); ac++;
	t = XmCreateScrolledText(x, "gpl", al, ac);
	XtManageChild(t);

	XmTextSetString(t, gpl_string);

	ac = 0;
	p = XmCreatePushButtonGadget(x, "ok", al, ac);
	XtManageChild(p);
	XtAddCallback(p, XmNactivateCallback, DestroyTemplate, x);

	XtManageChild(x);
}

void versionCB(Widget w, XtPointer client, XtPointer call)
{
	Widget		x;
	XmString	xms, xms1, xms2;
	Arg		al[2];
	int		ac;
	char		xbae[64];

	MotifSelectGlobal(w);

	xms1 = XmStringCreateLtoR(GNU_PACKAGE " " VERSION,
		XmFONTLIST_DEFAULT_TAG);
	xms2 = XmStringCreateLtoR(CopyRightString, XmFONTLIST_DEFAULT_TAG);
	xms = XmStringConcat(xms1, xms2);
	XmStringFree(xms1);
	XmStringFree(xms2);

	/* Now add info about libraries used */
	xms1 = xms;
	xms2 = XmStringCreateLtoR("\n\nLibraries used :", XmFONTLIST_DEFAULT_TAG);
	xms = XmStringConcat(xms1, xms2);
	XmStringFree(xms1);
	XmStringFree(xms2);

	xms1 = xms;
	xms2 = XmStringCreateLtoR("\n  " XmVERSION_STRING, XmFONTLIST_DEFAULT_TAG);
	xms = XmStringConcat(xms1, xms2);
	XmStringFree(xms1);
	XmStringFree(xms2);

#if XbaeVersion > 40800
	sprintf(xbae, "\n  %s", XbaeGetVersionTxt());
#else
	sprintf(xbae, "\n  Xbae %d", XbaeVersion);
#endif
	xms1 = xms;
	xms2 = XmStringCreateLtoR(xbae, XmFONTLIST_DEFAULT_TAG);
	xms = XmStringConcat(xms1, xms2);
	XmStringFree(xms1);
	XmStringFree(xms2);

#ifdef	HAVE_XmHTML_H
	xms1 = xms;
	xms2 = XmStringCreateLtoR("\n  " XmHTMLVERSION_STRING, XmFONTLIST_DEFAULT_TAG);
	xms = XmStringConcat(xms1, xms2);
	XmStringFree(xms1);
	XmStringFree(xms2);
#endif

#ifdef	HAVE_SciPlot_H
	sprintf(xbae, "\n  SciPlot %f", _SCIPLOT_WIDGET_VERSION);
	xms1 = xms;
	xms2 = XmStringCreateLtoR(xbae, XmFONTLIST_DEFAULT_TAG);
	xms = XmStringConcat(xms1, xms2);
	XmStringFree(xms1);
	XmStringFree(xms2);
#endif

#ifdef	HAVE_LIBMYSQLCLIENT
	sprintf(xbae, "\n  MySQL %s", MYSQL_SERVER_VERSION);
	xms1 = xms;
	xms2 = XmStringCreateLtoR(xbae, XmFONTLIST_DEFAULT_TAG);
	xms = XmStringConcat(xms1, xms2);
	XmStringFree(xms1);
	XmStringFree(xms2);
#endif

#ifdef	HAVE_LIBCUPS
	sprintf(xbae, "\n  CUPS %f", CUPS_VERSION);
	xms1 = xms;
	xms2 = XmStringCreateLtoR(xbae, XmFONTLIST_DEFAULT_TAG);
	xms = XmStringConcat(xms1, xms2);
	XmStringFree(xms1);
	XmStringFree(xms2);
#endif

#ifdef	HAVE_LIBPLOT
#if defined(PL_LIBPLOT_VER_STRING)
	sprintf(xbae, "\n  GNU PlotUtils (libplot version %s)", PL_LIBPLOT_VER_STRING);
#else
	sprintf(xbae, "\n  GNU PlotUtils (libplot version %s)", LIBPLOT_VERSION);
#endif
	xms1 = xms;
	xms2 = XmStringCreateLtoR(xbae, XmFONTLIST_DEFAULT_TAG);
	xms = XmStringConcat(xms1, xms2);
	XmStringFree(xms1);
	XmStringFree(xms2);
#endif

#if defined(HAVE_LIBXDB)
    {
	extern char	*oleoXdbVersion();
	char	*v = oleoXdbVersion();

	if (v) {
		sprintf(xbae, "\n  XBase library version %s", v);
		xms1 = xms;
		xms2 = XmStringCreateLtoR(xbae, XmFONTLIST_DEFAULT_TAG);
		xms = XmStringConcat(xms1, xms2);
		XmStringFree(xms1);
		XmStringFree(xms2);
	}
    }
#endif

	ac = 0;
	XtSetArg(al[0], XmNmessageString, xms); ac++;
	x = XmCreateMessageDialog(toplevel, "versionD", al, ac);

	XtAddCallback(x, XmNhelpCallback, gplCB, NULL);

	XmStringFree(xms);
	XtManageChild(x);
}

/*
 * These are necessary from io-x11.c
 */
void get_x11_args (int * argc_p, char ** argv)
{
}

void x11_graphics(void)
{
}

void motif_main_loop(void)
{
	XtAppMainLoop(app);
}

/*
 *
 */
static void
MotifSelectGlobal(Widget w)
{
	Widget	p;

	for (p=w; XtParent(p); p=XtParent(p)) ;

	MdiSelectGlobal(offsetof(struct OleoGlobal, MotifGlobal),
		offsetof(struct MotifGlobalType, toplevel_w),
		p);
}

void
ActivateCellWidget(Widget w, XtPointer client, XtPointer call)
{
	char	*cell_name = (char *)client;

	execute_command(cell_name);
}

void
MotifButton(int r, int c, char *lbl, char *cmd)
{
	Widget	button;
	char	*command;

	if (mat == 0) {
		Global->MotifGlobal->needrecalculate = True;
		return;
	}

#if XbaeVersion > 4007
	if (XbaeMatrixGetCellWidget(mat, r-1, c-1) == NULL) {
		button = XtVaCreateManagedWidget(lbl, xmPushButtonWidgetClass, mat, NULL);
		XbaeMatrixSetCellWidget(mat, r-1, c-1, button);

		command = strdup(cmd);
		XtAddCallback(button, XmNactivateCallback, ActivateCellWidget, (XtPointer)command);

		fprintf(stderr, "MotifButton(%d,%d,%s,%s)\n", r-1, c-1, lbl, command);
	}
#endif
}


syntax highlighted by Code2HTML, v. 0.9.1