/*
 *  Copyright (c) 1994, Riley Rainey,  riley@netcon.com
 *
 *  Permission to use, copy, modify and distribute (without charge) this
 *  software, documentation, images, etc. is granted, provided that this 
 *  comment and the author's name is retained.
 *
 *  This software is provided by the author as is, and without any expressed
 *  or implied warranties, including, but not limited to, the implied
 *  warranties of merchantability and fitness for a particular purpose.  In no
 *  event shall the author be liable for any direct, indirect, incidental, or
 *  consequential damages arising in any way out of the use of this software.
 */

#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/MainW.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/TextF.h>
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/MessageB.h>
#include <Xm/Frame.h>
#include <Xm/CascadeB.h>
#include <Xm/BulletinB.h>
#include <Xm/DrawingA.h>
#include <Xm/Separator.h>
#include <Xm/SelectioB.h>
#include <Xm/FileSB.h>
#include <Xm/LabelG.h>
#include <X11/cursorfont.h>

#define DEFINE_STORAGE
#include "gedit.h"
#include "XrMisc.h"

#include "curs_poly"
#include "curs_poly_mask"
#include "curs_marker"
#include "curs_marker_mask"


static unsigned char	grid_dash_list[2] = { 1, 4 };

static XmStringCharSet	charset = (XmStringCharSet) XmSTRING_DEFAULT_CHARSET;

/*
 *  fallback resources
 */

static String	fallback[] = {
	"*.Background:			GhostWhite",
	"*buttons*highlightColor:	GhostWhite",

	"*.fontList:			-*-helvetica-medium-r-*-*-*-140-*",
	"*.rulerFont:			-*-courier-bold-r-*-*-*-140-*",
	"*XmTextField.fontList:		-*-courier-medium-r-*-*-*-140-*",
	"*XmPushButton.shadowThickness:	3",
	"*XmDrawingArea.traversalOn:	false",
	"*.menu_bar*Background:		#4af",
	"*.menu_bar.spacing:		10",
	"*.top_frame.shadowType:	XmSHADOW_ETCHED_OUT",
	"*.bottom_frame.shadowType:	XmSHADOW_ETCHED_OUT",
	"*.twindow.background:		#d8d8d8",
	"*.twindow.foreground:		black",
	"*.bwindow.background:		#e1caac",
	"*.bwindow.foreground:		black",
	"*.main.form.height:		950",
	"*.main.form.width:		950",

	"*set_views.labelString: Set Views ...",
	"*mirror_xz.labelString: Mirror left/right",
	"*mirror_xz.acceleratorText:	Alt-L",
	"*mirror_xz.accelerator:	Meta<Key>l:",
	"*mirror_xz.mnemonic:		l",
	"*mirror_xy.labelString: Mirror top/bottom",
	"*mirror_xy.acceleratorText:	Alt-T",
	"*mirror_xy.accelerator:	Meta<Key>t:",
	"*mirror_xy.mnemonic:		t",
	"*mirror_yz.labelString: Mirror front/back",
	"*mirror_yz.acceleratorText:	Alt-F",
	"*mirror_yz.accelerator:	Meta<Key>f:",
	"*mirror_yz.mnemonic:		f",
	"*rescale.labelString: Rescale Object ...",
	"*rescale.mnemonic:		R",

	"*info.labelString: Aircraft Information ...",
	"*gear.labelString: Landing Gear ...",
	"*stability.labelString: Stability Derivatives ...",
	"*powerplant.labelString: Powerplant ...",

	"*rescale_dialog.dialogTitle: Rescale Object",

	"*open_d.dialogTitle:		Open a File",
	"*open_d.okLabelString:		Open",
	"*open_d.selectionLabelString:	Open which file?",
	"*open_d.autoUnmanage:		true",
	"*open_d*pattern:		*.gdf",

	"*save_as_d.dialogTitle:	Save Work",
	"*save_as_d.selectionLabelString:	File name:",
	"*save_as_d.okLabelString:	Save",
	"*save_as_d.*.fontList:		-*-helvetica-medium-r-*-*-*-120-*",

	"*gedit_format.labelString:	Gedit format",
	"*gedit_format.set:		true",
	"*v_format.labelString:		V format",
	"*rayshade_format.labelString:	ACM Inventory format",

	"*show_grid.visibleWhenOff:	true",
	"*show_ruler.visibleWhenOff:	true",

	"*coodinates*columns:		8",
	"*coodinates*sensitive:		false",

	"*new.labelString:		New",
	"*open.labelString:		Open ...",
	"*save.labelString:		Save",
	"*save_as.labelString:		Save As ...",
	"*exit.labelString:		Exit",

	"*new.acceleratorText:		Alt-N",
	"*new.accelerator:		Meta<Key>n:",
	"*new.mnemonic:			N",
	"*open.acceleratorText:		Alt-O",
	"*open.accelerator:		Meta<Key>o:",
	"*open.mnemonic:		O",
	"*save.acceleratorText:		Alt-S",
	"*save.accelerator:		Meta<Key>s:",
	"*save.mnemonic:		S",
	"*save_as.mnemonic:		A",
	"*exit.acceleratorText:		Alt-E",
	"*exit.accelerator:		Meta<Key>e:",
	"*exit.mnemonic:		E",

	"*cut.labelString:		Cut",
	"*cut.acceleratorText:		Shift-Del",
	"*cut.accelerator:		Shift<Key>Delete:",
	"*cut.mnemonic:			t",
	"*copy.labelString:		Copy",
	"*copy.acceleratorText:		Ctrl-Ins",
	"*copy.accelerator:		Ctrl<Key>Insert:",
	"*copy.mnemonic:		C",
	"*paste.labelString:		Paste",
	"*paste.acceleratorText:	Shift-Ins",
	"*paste.accelerator:		Shift<Key>Insert:",
	"*paste.mnemonic:		P",
	"*clear.labelString:		Clear",
	"*clear.mnemonic:		e",
	"*rotx.labelString:		Rotate 90 about X-Axis",
	
	"*info_dialog*XmLabelGadget.topOffset:		7",
	"*info_dialog*XmLabelGadget.rightOffset:	10",
	"*info_dialog*XmLabelGadget.leftOffset:		10",	
	"*info_dialog*XmTextField.rightOffset:		10",
	"*gear_dialog*XmLabelGadget.topOffset:		7",
	"*gear_dialog*XmLabelGadget.rightOffset:	10",
	"*gear_dialog*XmLabelGadget.leftOffset:		10",
	"*gear_dialog*XmTextField.rightOffset:		10",
	"*powerplant_dialog*XmLabelGadget.topOffset:	7",
	"*powerplant_dialog*XmLabelGadget.rightOffset:	10",
	"*powerplant_dialog*XmLabelGadget.leftOffset:	10",
	"*powerplant_dialog*XmTextField.rightOffset:	10",
	"*stability_dialog*XmLabelGadget.topOffset:	7",
	"*stability_dialog*XmLabelGadget.rightOffset:	10",
	"*stability_dialog*XmLabelGadget.leftOffset:	10",
	"*stability_dialog*XmTextField.rightOffset:	10",

	"*stability_dialog*Calculate.sensitive:	false",
	"*info_dialog*Calculate.sensitive:	false",
	"*info_dialog*Calculate.sensitive:	false",

	NULL
	};

static XrmOptionDescRec options[] = {
	{"-controllerClass", "*controllerClass", XrmoptionSepArg, "is"},
	};

static XtResource resources[] = {
	{
		XtNselectionColor,
		XtCSelectionColor,
		XtRPixel,
		sizeof(Pixel),
		XtOffset(AppDataPtr, select_pixel),
		XtRString,
		(caddr_t) "firebrick"
	},
	{
		XtNgridColor,
		XtCGridColor,
		XtRPixel,
		sizeof(Pixel),
		XtOffset(AppDataPtr, grid_pixel),
		XtRString,
		(caddr_t) "black"
	},
	{
		XtNlineThickness,
		XtCLineThickness,
		XtRInt,
		sizeof(int),
		XtOffset(AppDataPtr, line_thickness),
		XtRImmediate,
		(caddr_t) 0
	},
	{
		XtNselectionThickness,
		XtCSelectionThickness,
		XtRInt,
		sizeof(int),
		XtOffset(AppDataPtr, selection_thickness),
		XtRImmediate,
		(caddr_t) 2
	},
	{
		XtNboxSize,
		XtCBoxSize,
		XtRInt,
		sizeof(int),
		XtOffset(AppDataPtr, box_size),
		XtRImmediate,
		(caddr_t) 5
	},
	{
		XtNpickSensitivity,
		XtCPickSensitivity,
		XtRInt,
		sizeof(int),
		XtOffset(AppDataPtr, pick_sensitivity),
		XtRImmediate,
		(caddr_t) 64
	},
	{
		XtNbuttonSize,
		XtCButtonSize,
		XtRInt,
		sizeof(int),
		XtOffset(AppDataPtr, button_size),
		XtRImmediate,
		(caddr_t) 32
	},
	{
		XtNcursorForeground,
		XtCCursorForeground,
		XtRPixel,
		sizeof(Pixel),
		XtOffset(AppDataPtr, cursor_foreground),
		XtRString,
		(caddr_t) "black"
	},
	{
		XtNcursorBackground,
		XtCCursorBackground,
		XtRPixel,
		sizeof(Pixel),
		XtOffset(AppDataPtr, cursor_background),
		XtRString,
		(caddr_t) "white"
	},
	{
		XtNshowGrid,
		XtCShowGrid,
		XmRBoolean,
		sizeof(Boolean),
		XtOffset(AppDataPtr, show_grid),
		XtRString,
		(caddr_t) "true"
	},
	{
		XtNshowRuler,
		XtCShowRuler,
		XmRBoolean,
		sizeof(Boolean),
		XtOffset(AppDataPtr, show_ruler),
		XtRString,
		(caddr_t) "false"
	},
	{
		XtNrulerFont,
		XtCFont,
		XtRFontStruct,
		sizeof(XFontStruct *),
		XtOffset(AppDataPtr, ruler_font),
		XtRString,
		(caddr_t) "fixed"
	}

	};

typedef struct {
	Boolean	defined_in_this_version;
	Widget	widget;
	char	*name;
	char	*pixmap_file;
	} button_list_t;

button_list_t button_list[] = {
	{ True,		NULL, "point",		"cursor_%d.xbm" },
	{ False,	NULL, "hand",		"hand_%d.xbm" },
	{ True,		NULL, "polygon",	"polygon_%d.xbm" },
	{ True,		NULL, "circle",		"circle_%d.xbm" },
	{ True,		NULL, "origin",		"origin_%d.xbm" },
	{ True,		NULL, "zoom_out",	"zoom_out_%d.xbm" },
	{ True,		NULL, "zoom_in",	"zoom_in_%d.xbm" }
	};


#define MAX_ARGS	16
#define APP_CLASS	"Gedit"

void	QuitCB(), HelpCB(), ButtonCB(), WindowCB();
void	MenuCB(), FileCB(), ViewCB();
void	InitializeCursors(), SetCursor();
extern	void InitializeTranslations(), DrawWidget(), ClearSelection();
extern	void DetermineObjectExtent();
extern	void CreateMarkerList();
Widget	CreateHelp (), CreateViewsDialog(), CreateRescaleDialog();
Widget  CreateInfoDialog(), CreateGearDialog();
Widget  CreateDerivDialog(), CreatePowerplantDialog();

Widget	help_box;
Widget	view_box = (Widget) NULL;

static	view_info_t t_info, b_info;

main (argc, argv)
int	argc;
char	*argv[];
{

	Widget		toplevel;
	Widget		main_window;
	Widget		menu_bar;
	Widget		menu_pane;
	Widget		button;
	Widget		cascade;
	Widget		top_frame, bottom_frame, main_form, row_col;
	Widget		row_col1, form, index_form;
	int		n, i;
	Arg		args[16];
	char		name[128];
	XmString	label_string;
	XtAppContext	context;
	static int	tw = 1, bw = 2;
	Display		*display;
	Pixmap		pixmap;
	Pixel		fg, bg;
	XGCValues	gcv;
	XmString	string;

	toplevel = XtAppInitialize (&context, APP_CLASS, options,
		XtNumber(options), &argc, argv, fallback, NULL, 0);

	XtGetApplicationResources (toplevel, &app_data, resources,
		XtNumber(resources), NULL, 0);

	n = 0; 
	main_window = XmCreateMainWindow (toplevel, "main", args, n);
	XtManageChild (main_window);

	n = 0;
	menu_bar = XmCreateMenuBar (main_window, "menu_bar", args, n); 
	XtManageChild (menu_bar);

/*
 *  The File menu
 */

	n = 0;
	menu_pane = XmCreatePulldownMenu (menu_bar, "file_menu", args, n);

	n = 0;
	button = XmCreatePushButton (menu_pane, "new", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_NEW);

	n = 0;
	button = XmCreatePushButton (menu_pane, "open", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_OPEN);	
	XmStringFree (string);

	n = 0;
	button = XmCreatePushButton (menu_pane, "save", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_SAVE);
	/* XmStringFree (string); */

	n = 0;
	button = XmCreatePushButton (menu_pane, "save_as", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_SAVE_AS);

	n = 0;
	button = XmCreatePushButton (menu_pane, "exit", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, QuitCB, NULL);

	n = 0;
	XtSetArg (args[n], XmNsubMenuId, menu_pane);  n++;
	XtSetArg (args[n], XmNmnemonic, 'F');  n++;
	cascade = XmCreateCascadeButton (menu_bar, "File", args, n);
	XtManageChild (cascade);

/*
 *  The Edit menu
 */

	n = 0;
	menu_pane = XmCreatePulldownMenu (menu_bar, "edit_menu", args, n);

	n = 0;
	button = XmCreatePushButton (menu_pane, "cut", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_CUT);

	n = 0;
	button = XmCreatePushButton (menu_pane, "copy", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_COPY);

	n = 0;
	button = XmCreatePushButton (menu_pane, "paste", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_PASTE);

	n = 0;
	button = XmCreateSeparator (menu_pane, "separator1", args, n);
	XtManageChild (button);

	n = 0;
	button = XmCreatePushButton (menu_pane, "clear", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_CLEAR);

	n = 0;
	button = XmCreateSeparator (menu_pane, "separator2", args, n);
	XtManageChild (button);

	n = 0;
	button = XmCreatePushButton (menu_pane, "rotx", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_ROTATE_X);

	n = 0;
	button = XmCreatePushButton (menu_pane, "mirror_xz", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_MIRROR_XZ);

	n = 0;
	button = XmCreatePushButton (menu_pane, "mirror_xy", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_MIRROR_XY);

	n = 0;
	button = XmCreatePushButton (menu_pane, "mirror_yz", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_MIRROR_YZ);

	n = 0;
	button = XmCreateSeparator (menu_pane, "separator", args, n);
	XtManageChild (button);

	n = 0;
	button = XmCreatePushButton (menu_pane, "rescale", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_RESCALE);

	n = 0;
	XtSetArg (args[n], XmNsubMenuId, menu_pane);  n++;
	XtSetArg (args[n], XmNmnemonic, 'E');  n++;
	cascade = XmCreateCascadeButton (menu_bar, "Edit", args, n);
	XtManageChild (cascade);

/*
 *  The Layout menu
 */

	n = 0;
	menu_pane = XmCreatePulldownMenu (menu_bar, "layout_menu", args, n);

	n = 0;
	string = XmStringCreateSimple ("Show Grid");
	XtSetArg (args[n], XmNlabelString, string);  n++;
	XtSetArg (args[n], XmNset, app_data.show_grid);  n++;
	XtSetArg (args[n], XmNindicatorType, XmN_OF_MANY);  n++;
	XtSetArg (args[n], XmNradioBehavior, False);  n++;
	button = XmCreateToggleButton (menu_pane, "show_grid", args, n);
	XmStringFree (string);
	XtManageChild (button);	
	XtAddCallback (button, XmNvalueChangedCallback, MenuCB,
		(XtPointer) MENU_GRID);

	n = 0;
	string = XmStringCreateSimple ("Show Ruler");
	XtSetArg (args[n], XmNlabelString, string);  n++;
	XtSetArg (args[n], XmNset, app_data.show_ruler);  n++;
	XtSetArg (args[n], XmNindicatorType, XmN_OF_MANY);  n++;
	XtSetArg (args[n], XmNradioBehavior, False);  n++;
	button = XmCreateToggleButton (menu_pane, "show_ruler", args, n);
	XmStringFree (string);
	XtManageChild (button);	
	XtAddCallback (button, XmNvalueChangedCallback, MenuCB,
		(XtPointer) MENU_RULER);

	n = 0;
	button = XmCreateSeparator (menu_pane, "separator", args, n);
	XtManageChild (button);	

	n = 0;
	button = XmCreatePushButton (menu_pane, "set_views", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_SET_VIEWS);

	n = 0;
	XtSetArg (args[n], XmNsubMenuId, menu_pane);  n++;
	XtSetArg (args[n], XmNmnemonic, 'L');  n++;
	cascade = XmCreateCascadeButton (menu_bar, "Layout", args, n);
	XtManageChild (cascade);

	CreateMarkerList (menu_bar);

	n = 0;
	XtSetArg (args[n], XmNmnemonic, 'H');  n++;
	cascade = XmCreateCascadeButton (menu_bar, "Help", args, n);
	XtManageChild (cascade);
	XtAddCallback (cascade, XmNactivateCallback, HelpCB, NULL);

	n = 0;
	XtSetArg (args[n], XmNmenuHelpWidget, cascade);  n++;
	XtSetValues (menu_bar, args, n);

/*
 *  The Aircraft Performance menu
 */

	n = 0;
	menu_pane = XmCreatePulldownMenu (menu_bar, "perf_menu", args, n);

	n = 0;
	button = XmCreatePushButton (menu_pane, "info", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_INFO);
	
	n = 0;
	button = XmCreatePushButton (menu_pane, "gear", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_GEAR);

	n = 0;
	button = XmCreatePushButton (menu_pane, "stability", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_DERIV);

	n = 0;
	button = XmCreatePushButton (menu_pane, "powerplant", args, n);
	XtManageChild (button);
	XtAddCallback (button, XmNactivateCallback, MenuCB,
		(XtPointer) MENU_PWR);

	n = 0;
	XtSetArg (args[n], XmNsubMenuId, menu_pane);  n++;
	XtSetArg (args[n], XmNmnemonic, 'P');  n++;
	cascade = XmCreateCascadeButton (menu_bar, "Performance", args, n);
	XtManageChild (cascade);

/*
 *  The main window hierarchy
 */

	main_form = XtVaCreateWidget ("form", xmFormWidgetClass,
		main_window,
		NULL);

	XtVaSetValues (main_window,
		XmNworkWindow,	main_form,
		NULL);

	index_form = XtVaCreateWidget ("index", xmFormWidgetClass,
		main_form,
		XmNleftAttachment,	XmATTACH_FORM,
		XmNtopAttachment,	XmATTACH_FORM,
		XmNbottomAttachment,	XmATTACH_FORM,
		NULL);

	row_col = XtVaCreateWidget ("buttons", xmRowColumnWidgetClass,
		index_form,
		XmNleftAttachment,	XmATTACH_FORM,
		XmNtopAttachment,	XmATTACH_FORM,
		NULL);

	XtVaGetValues (row_col,
		XmNforeground,	&fg,
		XmNbackground,	&bg,
		NULL);

	for (i=0; i < XtNumber(button_list); ++i) {

	    if (button_list[i].defined_in_this_version) {
	    
	    	sprintf (name, button_list[i].pixmap_file,
	    		app_data.button_size);

		pixmap = XmGetPixmap (XtScreen (row_col), name, fg, bg);

		button_list[i].widget = XtVaCreateManagedWidget (
			button_list[i].name, xmPushButtonWidgetClass, row_col,
			XmNlabelType,		XmPIXMAP,
			XmNlabelPixmap,		pixmap,
			XmNwidth,		64,
			XmNheight,		64,
			XmNshadowType,		XmSHADOW_ETCHED_OUT,
			NULL);

		XtAddCallback (button_list[i].widget,
			XmNactivateCallback, ButtonCB, (XtPointer) i);
	    }

	}

	XtManageChild (row_col);

	row_col1 = XtVaCreateWidget ("coordinates", xmRowColumnWidgetClass,
		index_form,
		XmNleftAttachment,	XmATTACH_FORM,
		XmNrightAttachment,	XmATTACH_FORM,
		XmNtopAttachment,	XmATTACH_WIDGET,
		XmNtopWidget,		row_col,
		XmNbottomAttachment,	XmATTACH_FORM,
		NULL);

	x_field = XtVaCreateManagedWidget("x",
		xmTextFieldWidgetClass, row_col1,
		XmNcolumns,	   12,
		NULL);

	y_field = XtVaCreateManagedWidget("y",
		xmTextFieldWidgetClass, row_col1,
		XmNcolumns,	   12,
		NULL);

	z_field = XtVaCreateManagedWidget("z",
		xmTextFieldWidgetClass, row_col1,
		XmNcolumns,	   12,
		NULL);

	XtManageChild (row_col1);
	XtManageChild (index_form);

	top_frame = XtVaCreateManagedWidget ("top_frame", xmFrameWidgetClass,
		main_form,
		XmNtopAttachment,	XmATTACH_FORM,
		XmNleftAttachment,	XmATTACH_WIDGET,
		XmNleftWidget,		index_form,
		XmNrightAttachment,	XmATTACH_FORM,
		XmNbottomAttachment,	XmATTACH_POSITION,
		XmNbottomPosition,	50,
		NULL);

	twindow = XmCreateDrawingArea (top_frame, "twindow", args, n);

	XtAddCallback (twindow, XmNexposeCallback, WindowCB, (caddr_t) &tw);
	XtAddCallback (twindow, XmNinputCallback, WindowCB, (caddr_t) &tw);
	XtAddCallback (twindow, XmNresizeCallback, WindowCB, (caddr_t) &tw);

	XtManageChild (twindow);

	bottom_frame = XtVaCreateManagedWidget ("bottom_frame",
		xmFrameWidgetClass, main_form,
		XmNtopAttachment,	XmATTACH_WIDGET,
		XmNtopWidget,		top_frame,
		XmNleftAttachment,	XmATTACH_WIDGET,
		XmNleftWidget,		index_form,
		XmNrightAttachment,	XmATTACH_FORM,
		XmNbottomAttachment,	XmATTACH_FORM,
		NULL);

	n = 0;
	bwindow = XmCreateDrawingArea (bottom_frame, "bwindow", args, n);

	XtAddCallback (bwindow, XmNexposeCallback, WindowCB, (caddr_t) &bw);
	XtAddCallback (bwindow, XmNinputCallback, WindowCB, (caddr_t) &bw);
	XtAddCallback (bwindow, XmNresizeCallback, WindowCB, (caddr_t) &bw);

	XtManageChild (bwindow);
	XtManageChild (main_form);

	InitializeTranslations (context, twindow, bwindow);

/*
 *  File dialogs
 */

	n = 0;
	open_dialog = XmCreateFileSelectionDialog (toplevel, "open_d", args, n);
	XtAddCallback (open_dialog, XmNokCallback, FileCB, (XtPointer) MENU_OPEN);

	n = 0;
	save_as_dialog = XmCreatePromptDialog (toplevel, "save_as_d", args, n);
	XtAddCallback (save_as_dialog, XmNokCallback, FileCB, (XtPointer) MENU_SAVE_AS);

	n = 0;
	row_col = XmCreateRadioBox (save_as_dialog, "file_formats", args, n);
	XtManageChild (row_col);

	save_formats[0] = XtVaCreateManagedWidget ("gedit_format",
		xmToggleButtonWidgetClass, row_col,
		NULL);

	save_formats[1] = XtVaCreateManagedWidget ("v_format",
		xmToggleButtonWidgetClass, row_col,
		NULL);

	save_formats[2] = XtVaCreateManagedWidget ("rayshade_format",
		xmToggleButtonWidgetClass, row_col,
		NULL);

	set_views_dialog = CreateViewsDialog (toplevel);

	rescale_dialog = CreateRescaleDialog (toplevel);

	info_dialog = CreateInfoDialog(toplevel);
	
	gear_dialog = CreateGearDialog(toplevel);

	deriv_dialog = CreateDerivDialog(toplevel);

	powerplant_dialog = CreatePowerplantDialog(toplevel);

/*
 *  We logically cross-connect each of the two drawing areas with a data
 *  structure that is hung off each widget's userData field.
 */

	display = XtDisplay (twindow);
	t_info.flags = 0;
	t_info.other_view = &b_info;
	t_info.layout = VL_NXZ;
	t_info.other_widget = bwindow;
	t_info.other_window = XtWindow (bwindow);
	gcv.graphics_exposures = False;
	gcv.line_style = LineOnOffDash;
	gcv.font = app_data.ruler_font->fid;
	t_info.gc = XCreateGC (display,
		RootWindow(display, DefaultScreen(display)),
		GCGraphicsExposures | GCFont, &gcv);
	t_info.erase_gc = XCreateGC (display,
		RootWindow(display, DefaultScreen(display)),
		GCGraphicsExposures, &gcv);
	t_info.grid_gc = XCreateGC (display,
		RootWindow(display, DefaultScreen(display)),
		GCGraphicsExposures | GCLineStyle, &gcv);
	XSetDashes (display, t_info.grid_gc, 0, grid_dash_list, 2);

	XtVaSetValues (twindow,
		XmNuserData,	&t_info,
		NULL);

	display = XtDisplay (twindow);
	b_info.flags = 0;
	b_info.other_view = &t_info;
	b_info.layout = VL_NXNY;
	b_info.other_widget = twindow;
	b_info.other_window = XtWindow (twindow);
	b_info.gc = XCreateGC (display,
		RootWindow(display, DefaultScreen(display)),
		GCGraphicsExposures | GCFont, &gcv);
	b_info.erase_gc = XCreateGC (display,
		RootWindow(display, DefaultScreen(display)),
		GCGraphicsExposures, &gcv);
	b_info.grid_gc = XCreateGC (display,
		RootWindow(display, DefaultScreen(display)),
		GCGraphicsExposures | GCLineStyle, &gcv);
	XSetDashes (display, b_info.grid_gc, 0, grid_dash_list, 2);

	XtVaSetValues (bwindow,
		XmNuserData,	&b_info,
		NULL);

	help_box = CreateHelp (toplevel);

	edit_state = STATE_POINT;
	cur_polygon = (polygon_t *) NULL;
	polygon_max = 256;
	polygon_list = (polygon_t *) malloc (polygon_max * sizeof (polygon_t));
	sel_polygon = unsel_polygon = clipboard_polygon = -1;
	drag_mode = False;

	for (i=0; i<polygon_max; ++i) {
		polygon_list[i].id = i;
		polygon_list[i].num_points = 0;
		polygon_list[i].next = -1;
	}

	polygon_count = 0;
	tmp_point_max = 128;
	tmp_point = (point_t *) malloc (tmp_point_max * sizeof (point_t));

	pixel_scale = 0.125;
	app_data.box_offset = - app_data.box_size / 2;

	filename_valid = False;
	strcpy (filename, "Untitled");

	desired_view = VIEW_LEFT_TOP;

	XtRealizeWidget (toplevel);

	InitializeCursors ();
	SetCursor (CURSOR_POINT);

	XtAppMainLoop (context);
}

Widget
CreateHelp (parent) 
Widget		parent;
{

	Widget		button;
	Widget		message_box;
	Arg		args[MAX_ARGS];
	register int	n;

	static char	message[BUFSIZ];
	XmString	title_string = NULL;
	XmString	message_string = NULL;
	XmString	button_string = NULL;

	sprintf (message, "\
Gedit:  A Three Dimensional Object Editor\n\
\n\
Copyright (c) 1991  Riley Rainey\n\
\n\n\
\0");

	message_string = XmStringCreateLtoR (message, charset);
	button_string = XmStringCreateLtoR ("Continue", charset);
	title_string = XmStringCreateLtoR ("graphics editor help", charset);

	n = 0;
	XtSetArg (args[n], XmNdialogTitle, title_string);  n++;
	XtSetArg (args[n], XmNokLabelString, button_string);  n++;
	XtSetArg (args[n], XmNmessageString, message_string);  n++;
	message_box = XmCreateMessageDialog (parent, "help_box", args, n);

	button = XmMessageBoxGetChild (message_box, XmDIALOG_CANCEL_BUTTON);
	XtUnmanageChild (button);
	button = XmMessageBoxGetChild (message_box, XmDIALOG_HELP_BUTTON);
	XtUnmanageChild (button);

	if (title_string)
		XtFree (title_string);
	if (message_string)
		XtFree (message_string);
	if (button_string)
		XtFree (button_string);

	return (message_box);
}

Widget
CreateViewsDialog (parent)
Widget parent;
{

	XmString string;
	extern Widget CreateExtendedFormDialog();
	Arg args[10];
	static ActionAreaButton action_items[] = {
        	{ "Ok",     MenuCB,	(XtPointer) MENU_SET_VIEWS_COMPLETE},
        	{ "Cancel", MenuCB,	(XtPointer) MENU_SET_VIEWS_CANCEL},
        	{ "Help",   NULL,	NULL 	},
	};
	Widget	dialog, string_w, form, box;

	dialog = CreateExtendedFormDialog("dialog", parent, &form, action_items,
		XtNumber (action_items), 0); 

	string = XmStringCreateSimple("Select the desired views:");
	string_w = XtVaCreateManagedWidget("label", xmLabelGadgetClass, form,
		XmNlabelString,    string,
		XmNleftAttachment, XmATTACH_FORM,
		XmNtopAttachment,  XmATTACH_FORM,
		NULL);
	XmStringFree(string);

	box = XmVaCreateSimpleRadioBox (form, "box", 0, ViewCB,
		XmVaRADIOBUTTON,
		XmStringCreateSimple("Left side / Top"), NULL, NULL, NULL,
		XmVaRADIOBUTTON,
		XmStringCreateSimple("Front / Top"), NULL, NULL, NULL,
		XmNleftAttachment,	XmATTACH_FORM,
		XmNrightAttachment,	XmATTACH_FORM,
		XmNtopAttachment,	XmATTACH_WIDGET,
		XmNtopWidget,		string_w,
		NULL);

	XtManageChild (box);
	XtManageChild (form);
	return dialog;

}

Widget
CreateRescaleDialog (parent)
Widget parent;
{

	XmString string;
	extern Widget CreateExtendedFormDialog();
	Arg args[10];
	VPoint extent;
	static ActionAreaButton action_items[] = {
        	{ "Apply",     MenuCB,	(XtPointer) MENU_RESCALE_APPLY},
        	{ "Cancel", MenuCB,	(XtPointer) MENU_RESCALE_CANCEL},
        	{ "Help",   NULL,	NULL 	},
	};
	Widget	dialog, string_w, prompt, form;

	dialog = CreateExtendedFormDialog("rescale_dialog", parent, &form,
		action_items, XtNumber (action_items), 0); 

	string = XmStringCreateSimple("Current object dimensions:");
	string_w = XtVaCreateManagedWidget("label", xmLabelGadgetClass, form,
		XmNlabelString,    string,
		XmNleftAttachment, XmATTACH_FORM,
		XmNtopAttachment,  XmATTACH_FORM,
		NULL);
	XmStringFree(string);

	extent_x = XtVaCreateManagedWidget("label_x", xmLabelGadgetClass, form,
		XmNleftAttachment, XmATTACH_FORM,
		XmNtopAttachment,  XmATTACH_WIDGET,
		XmNtopWidget,	   string_w,
		NULL);

	extent_y = XtVaCreateManagedWidget("label_y", xmLabelGadgetClass, form,
		XmNleftAttachment, XmATTACH_FORM,
		XmNtopAttachment,  XmATTACH_WIDGET,
		XmNtopWidget,	   extent_x,
		NULL);

	extent_z = XtVaCreateManagedWidget("label_z", xmLabelGadgetClass, form,
		XmNleftAttachment, XmATTACH_FORM,
		XmNtopAttachment,  XmATTACH_WIDGET,
		XmNtopWidget,	   extent_y,
		NULL);

	string = XmStringCreateSimple("Enter the object scaling factor:");
	prompt = XtVaCreateManagedWidget("label_z", xmLabelGadgetClass, form,
		XmNlabelString,    string,
		XmNleftAttachment, XmATTACH_FORM,
		XmNtopAttachment,  XmATTACH_WIDGET,
		XmNtopWidget,	   extent_z,
		NULL);
	XmStringFree(string);

	rescale_field = XtVaCreateManagedWidget("rescale_field",
		xmTextFieldWidgetClass, form,
		XmNleftAttachment, XmATTACH_FORM,
		XmNtopAttachment,  XmATTACH_WIDGET,
		XmNtopWidget,	   prompt,
		XmNcolumns,	   32,
		NULL);

	XtManageChild (form);
	return dialog;

}

void
ViewCB (w, client_data, call_data)
Widget	w;
caddr_t	client_data;
caddr_t call_data;
{
	register XmToggleButtonCallbackStruct	*cbs =
		(XmToggleButtonCallbackStruct *) call_data;
	register int id = (int) client_data;

	if (cbs->set == False)
		return;

	if (id == 0)
		desired_view = VIEW_LEFT_TOP;
	else
		desired_view = VIEW_FRONT_TOP;

}

void
QuitCB (w, client_data, call_data)
Widget	w;
caddr_t	client_data;
caddr_t call_data;
{

	exit (0);
}

void
HelpCB (w, client_data, call_data)
Widget	w;
caddr_t	client_data;
caddr_t call_data;
{

	XtManageChild (help_box);
}

void
ButtonCB (w, client_data, call_data)
Widget	w;
caddr_t	client_data;
caddr_t call_data;
{

	switch ((int) client_data) {

	case 0:
		edit_state = STATE_POINT;
		SetCursor (CURSOR_POINT);
		break;

	case 1:
		break;

	case 2:
		edit_state = STATE_POLYGON;
		SetCursor (CURSOR_POLY);
		break;

	case 3:
		edit_state = STATE_CIRCLE;
		SetCursor (CURSOR_CIRCLE);
		break;

	case 4:
		edit_state = STATE_MOVE_ORIGIN;
		SetCursor (CURSOR_ORIGIN);
		break;

	case 5:
		RescaleView(twindow, 1 / 1.2);
		XmProcessTraversal (button_list[0].widget, XmTRAVERSE_CURRENT);
		edit_state = STATE_POINT;
		SetCursor (CURSOR_POINT);
		break;

	case 6:
		RescaleView(twindow, 1.2);
		XmProcessTraversal (button_list[0].widget, XmTRAVERSE_CURRENT);
		edit_state = STATE_POINT;
		SetCursor (CURSOR_POINT);
		break;

	default:
		printf ("button %d\n", (int) client_data);

	}
}

void
MenuCB (w, client_data, call_data)
Widget	w;
caddr_t	client_data;
caddr_t call_data;
{

	register int	menu_id = (int) client_data;
	extern void	Mirror();
	Boolean		value;
	VPoint		extent;
	XmString	string;
	char		s[64], *factor_s;
	double		factor;

	if (menu_id >= MENU_MARKER && menu_id < MENU_MARKER+32) {
		current_marker = menu_id - MENU_MARKER;
		if (marker_list[current_marker].defined)
			DisplayPoint(&marker_list[current_marker].location);
		SetCursor (CURSOR_MARKER);
		edit_state = STATE_MARKER;
		return;
	}

	switch (menu_id) {

	case MENU_MIRROR_XZ:
	case MENU_MIRROR_XY:
	case MENU_MIRROR_YZ:
		Mirror (menu_id);
		break;

	case MENU_CLEAR:
		ClearSelection();
		break;

	case MENU_COPY:
		CopySelection();
		break;

	case MENU_PASTE:
		PasteSelection();
		break;

	case MENU_ROTATE_X:
		RotateXSelection();
		break;

	case MENU_SAVE:
		if (filename_valid)
			WriteGeditFile (filename);
		else
			XtManageChild (save_as_dialog);
		break;

	case MENU_GRID:
		XtVaGetValues (w,
			XmNset,	&value,
			NULL);

		if (value != app_data.show_grid) {
			app_data.show_grid = value;
			DrawWidget (twindow, False);
			DrawWidget (bwindow, False);
		}
		else {
			app_data.show_grid = value;
		}
		break;

	case MENU_RULER:
		XtVaGetValues (w,
			XmNset,	&value,
			NULL);

		if (value != app_data.show_ruler) {
			app_data.show_ruler = value;
			DrawWidget (twindow, False);
			DrawWidget (bwindow, False);
		}
		else {
			app_data.show_ruler = value;
		}
		break;

	case MENU_OPEN:
		XtManageChild (open_dialog);
		break;

	case MENU_SAVE_AS:
		XtManageChild (save_as_dialog);
		break;

	case MENU_SET_VIEWS:
		XtManageChild (set_views_dialog);
		break;

	case MENU_SET_VIEWS_COMPLETE:
		switch (desired_view) {

		case VIEW_LEFT_TOP:
			t_info.layout = VL_NXZ;
			b_info.layout = VL_NXNY;
			break;

		case VIEW_FRONT_TOP:
			t_info.layout = VL_NYZ;
			b_info.layout = VL_NYX;
			break;
		}

		RescaleView (twindow, 1.0);
		XtUnmanageChild (set_views_dialog);
		break;

	case MENU_SET_VIEWS_CANCEL:
		XtUnmanageChild (set_views_dialog);
		break;

	case MENU_RESCALE:
		DetermineObjectExtent (&extent);

		sprintf (s, "   %s :   %f", "X", extent.x);
		string = XmStringCreateSimple(s);
		XtVaSetValues (extent_x,
			XmNlabelString, string,
			NULL);
		XmStringFree (string);

		sprintf (s, "   %s :   %f", "Y", extent.y);
		string = XmStringCreateSimple(s);
		XtVaSetValues (extent_y,
			XmNlabelString, string,
			NULL);
		XmStringFree (string);

		sprintf (s, "   %s :   %f", "Z", extent.z);
		string = XmStringCreateSimple(s);
		XtVaSetValues (extent_z,
			XmNlabelString, string,
			NULL);
		XmStringFree (string);

		XtManageChild (rescale_dialog);
		break;

	case MENU_RESCALE_APPLY:
		factor_s = XmTextFieldGetString (rescale_field);
		factor = atof(factor_s);
		if (factor != 0.0)
			RescaleObject (factor);
		break;

	case MENU_RESCALE_CANCEL:
		XtUnmanageChild (rescale_dialog);
		break;
		
	case MENU_GEAR:
		XtManageChild (gear_dialog);
		break;

	case MENU_GEAR_CANCEL:
		GetCraftInfo (&c);
		XtUnmanageChild (gear_dialog);
		break;

	case MENU_GEAR_CALCULATE:
		GearCalculate (&c);
		GetCraftInfo (&c);
		break;

	case MENU_INFO:
		XtManageChild (info_dialog);
		break;

	case MENU_INFO_CANCEL:
		GetCraftInfo (&c);
		XtUnmanageChild (info_dialog);
		break;

	case MENU_INFO_CALCULATE:
		GetCraftInfo (&c);
		break;

	case MENU_DERIV:
		XtManageChild (deriv_dialog);
		break;

	case MENU_DERIV_CANCEL:
		GetCraftInfo (&c);
		XtUnmanageChild (deriv_dialog);
		break;

	case MENU_DERIV_CALCULATE:
		GetCraftInfo (&c);
		break;

	case MENU_PWR:
		XtManageChild (powerplant_dialog);
		break;

	case MENU_PWR_CANCEL:
		GetCraftInfo (&c);
		XtUnmanageChild (powerplant_dialog);
		break;

	case MENU_PWR_CALCULATE:
		GetCraftInfo (&c);
		break;
	}

}

void
FileCB (w, client_data, call_data)
Widget	w;
caddr_t	client_data;
caddr_t call_data;
{
	register int	menu_id = (int) client_data, i;
	register XmSelectionBoxCallbackStruct	*p =
		(XmSelectionBoxCallbackStruct *) call_data;
	char	*value;
	Boolean	state;
	extern int	ReadGeditFile(), WriteGeditFile();

	XmStringGetLtoR (p->value, charset, &value);

	switch (menu_id) {

	case MENU_OPEN:
		ReadGeditFile (value);
		break;

	case MENU_SAVE_AS:
		for (i=0; i < 3; ++i) {

			XtVaGetValues (save_formats[i],
				XmNset,		&state,
				NULL);

			if (state) {
				switch (i) {

				case 0:
					WriteGeditFile (value);
					break;
				case 1:
					WriteVFile (value);
					break;
				}

				break;
			}
		}
		break;
	}

	strcpy (filename, value);
	filename_valid = True;

	XtFree (value);
}

void
AllocPixmap (w, width, height)
Widget	w;
Dimension	width, height;
{

	Pixel		bg;
	view_info_t	*p;
	Display		*d;
	int		depth;

	d = XtDisplay (w);

	XtVaGetValues (w,
		XmNbackground,	&bg,
		XmNuserData,	&p,
		XmNdepth,	&depth,
		NULL);

	if (p->flags & VI_PIXMAP_ALLOCATED)
		XFreePixmap (d, p->pixmap);

	p->pixmap = XCreatePixmap (d, RootWindow (d, DefaultScreen(d)),
		width, height, depth);

	XSetForeground (d, p->erase_gc, bg);
	XSetLineAttributes (d, p->erase_gc, app_data.selection_thickness,
		LineSolid, CapButt, JoinMiter);

	XFillRectangle (d, p->pixmap, p->erase_gc, 0, 0, width, height);

	p->width = width;
	p->height = height;
	p->flags |= VI_PIXMAP_ALLOCATED;
}


void
WindowCB (w, client_data, call_data)
Widget	w;
caddr_t	client_data;
caddr_t call_data;
{

	register XmDrawingAreaCallbackStruct	*q;
	view_info_t	*p;

	q = (XmDrawingAreaCallbackStruct *) call_data;

	XtVaGetValues (w,
		XmNuserData,	&p,
		NULL);

	switch (q->reason) {

	case XmCR_EXPOSE:
		if ((p->flags & VI_PIXMAP_ALLOCATED) == 0) {
			XtVaGetValues (w,
				XmNwidth,	&p->width,
				XmNheight,	&p->height,
				NULL);
			AllocPixmap (w, p->width, p->height);
		}
		XCopyArea (XtDisplay(w), p->pixmap, XtWindow(w),
			p->gc, 0, 0, p->width, p->height, 0, 0);
		break;

	case XmCR_RESIZE:
		XtVaGetValues (w,
			XmNwidth,	&p->width,
			XmNheight,	&p->height,
			NULL);
		AllocPixmap (w, p->width, p->height);
		p->origin_x = (p->width + 1) / 2;
		p->origin_y = (p->height + 1) / 2;
		DrawWidget (w, False);
		break;

	}

}

void
InitializeCursors()
{

	unsigned long	fg = 1, bg = 0;
	XColor		colors[2];
	Drawable 	d;
	Display	 	*dpy;
	Pixmap		source, mask;
	Colormap	cmap;

	dpy = XtDisplay (twindow);
	d = RootWindow(dpy, DefaultScreen(dpy));

	XtVaGetValues (twindow,
		XmNcolormap,	&cmap,
		NULL);

	cursors[CURSOR_POINT] = XCreateFontCursor (dpy, XC_left_ptr);

	source = XCreatePixmapFromBitmapData (dpy, d, curs_poly_bits,
		curs_poly_width, curs_poly_height,
		fg, bg, 1);
	mask = XCreatePixmapFromBitmapData (dpy, d, curs_poly_mask_bits,
		curs_poly_width, curs_poly_height,
		fg, bg, 1);

	colors[0].pixel = app_data.cursor_foreground;
	colors[1].pixel = app_data.cursor_background;
	XQueryColors (dpy, cmap, colors, 2);

	cursors[CURSOR_POLY] = XCreatePixmapCursor (dpy, source,
		mask, &colors[0], &colors[1],
		curs_poly_x_hot, curs_poly_y_hot);
	XFreePixmap (dpy, source);
	XFreePixmap (dpy, mask);

	source = XCreatePixmapFromBitmapData (dpy, d, curs_marker_bits,
		curs_marker_width, curs_marker_height,
		fg, bg, 1);
	mask = XCreatePixmapFromBitmapData (dpy, d, curs_marker_mask_bits,
		curs_marker_width, curs_marker_height,
		fg, bg, 1);

	cursors[CURSOR_MARKER] = XCreatePixmapCursor (dpy, source,
		mask, &colors[0], &colors[1],
		curs_marker_x_hot, curs_marker_y_hot);
	XFreePixmap (dpy, source);
	XFreePixmap (dpy, mask);

	cursors[CURSOR_ORIGIN] = XCreateFontCursor (dpy, XC_crosshair);

}

void SetCursor (id)
int	id;
{

	Cursor cursor;

	switch (id) {

	case CURSOR_POINT:
		cursor = cursors[CURSOR_POINT];
		break;

	case CURSOR_POLY_PLANE:
		cursor = cursors[CURSOR_POLY];
		break;

	case CURSOR_CIRCLE:
	case CURSOR_POLY:
		cursor = cursors[CURSOR_POLY];
		break;

	case CURSOR_ORIGIN:
		cursor = cursors[CURSOR_ORIGIN];
		break;

	case CURSOR_MARKER:
		cursor = cursors[CURSOR_MARKER];
		break;
	}

	XDefineCursor (XtDisplay(twindow), XtWindow(twindow), cursor);
	XDefineCursor (XtDisplay(bwindow), XtWindow(bwindow), cursor);
}
