/* main.c: main program - contains almost the whole user interface */

#include <time.h>

#include <Xm/Xm.h>
#include <Xm/RepType.h>

#include <Xm/Form.h>
#include <Xm/RowColumn.h>
#include <Xm/Frame.h>
#include <Xm/PushB.h>
#include <Xm/Label.h>
#include <Xm/CascadeB.h>
#include <Xm/MessageB.h>
#include <Xm/FileSB.h>
#include <Xm/ToggleB.h>
#include <Xm/BulletinB.h>
#include <Xm/SelectioB.h>
#include <Xm/ScrollBar.h>
#include <Xm/ScrolledW.h>
#include "motif.h"
#include "xmenu.h"
#include "visual.h"

#include "scene.h"
#include "readmgf.h"
#include "readrad.h"

#include "geom.h"
#include "camera.h"
#include "initerror.h"
#include "error.h"
#include "message.h"
#include "raycast.h"
#include "radiance.h"
#include "southwell.h"
#include "gauss_seidel.h"
#include "globals.h"
#include "defaults.h"
#include "render.h"
#include "raycast.h"
#include "canvas.h"
#include "select.h"
#include "surfedit.h"
#include "tvertelim.h"
#include "Memory.h"
#include "formfactor.h"
#include "bases.h"
#include "polygon.h"
#include "monitor.h"

/* the program detects the best X visual to use and creates a suitables
 * standard colormap for use with it automatically. visual.c
 * contains the routijnes to do the choice */
XVisualInfo best_visual;
XStandardColormap std_cmap;

/* the X visual info is not correctly passed from parent to sub menus
 * with Motif 1.2, so we set it explicitely ourselves when creating
 * the submenus */
Arg visargs[3];
int nrvisargs = 3;

/* X application context and some important widgets */
XtAppContext app_context;
Widget topLevel, canvas, statsMessage;

/* making pause = TRUE will halt the radiance computation */
static int pause = TRUE;

/* for statistics about CPU time usage */
static clock_t usecs_total_radiance_propagation, 
               usecs_total_importance_propagation;

/* performs one step of the radiance computation, will be either 
 * DoSouthwellOneStep or DoGaussSeidelOneStep and is set with 
 * SetSolutionMethod */
static int (*ComputeRadianceOneStep)(void);

/* basis to replace the ones being used when loading a new or
 * reloading the current scene - a change of basis is not performed
 * immediately during computations, since it requires available data 
 * to be projected to the new basis ... this is not yet supported. */
static BASIS *newQuadBasis, *newTriBasis;

void SetSolutionMethod(int which)
{
	SolutionMethod = which;
	switch (which)
	{
	case SOUTHWELL:
		ComputeRadianceOneStep = DoSouthwellOneStep;
		break;
	case GAUSS_SEIDEL:
		ComputeRadianceOneStep = DoGaussSeidelOneStep;
		break;
	default:
		Fatal(3, "SetAlgorithm", "Invalid algorithm class");
	}
}

/* for statistics ... */
void ResetTimers(void)
{
	usecs_total = usecs_last_phase = 
		usecs_total_importance_propagation =
		usecs_total_radiance_propagation =
		usecs_last_id_rendering = usecs_ff_computation = 
		usecs_shaftculling = 0;

	iterationr = 0;
}

/* various initializations -- only called once (when starting up the 
 * program) */
static void Init(void)
{
	POINT eyep = DEFAULT_EYEP, 
	      lookp = DEFAULT_LOOKP;
	VECTOR updir = DEFAULT_UPDIR;
	RGB outlinecolor = DEFAULT_OUTLINECOLOR,
	    backgroundcolor = DEFAULT_BACKGROUND,
	    linkcolor = DEFAULT_LINKCOLOR;

	MaterialLib = MaterialListCreate();
	World = GeomListCreate();
	Patches = PatchListCreate();

	CameraSet(&Camera, &eyep, &lookp, &updir, DEFAULT_FOV, 600, 600, 
		  &backgroundcolor);

	RenderSetOptions(DEFAULT_GOURAUDSHADING, 
			 DEFAULT_BACKFACECULLING, 
			 DEFAULT_OUTLINEDRAWING, 
			 &outlinecolor,
			 DEFAULT_SHOWLINKS,
			 &linkcolor);

	SetRenderAmbient(DEFAULT_RENDER_AMBIENT);

	RenderSetMode(DEFAULT_RENDERINGMODE);
	SetTVertexElimination(DEFAULT_TVERTEXELIMINATION);

	SetImportanceDriven(DEFAULT_IMPORTANCE_DRIVEN);
	SetHierarchicalRefinement(DEFAULT_HIERARCHICAL_REFINEMENT);
	SetTolerances(DEFAULT_AEPS,
		      DEFAULT_FEPS,
		      DEFAULT_BEPS,
		      DEFAULT_IEPS);

	SetDemoMode(DEFAULT_DEMOMODE);

#ifdef OLDSTUFF
	SetFormFactorAccuracy(DEFAULT_FF_ACCURACY);
#endif /*OLDSTUFF*/

	SetSolutionMethod(DEFAULT_SOLUTION_METHOD);
	SetShaftcullMode(DEFAULT_SHAFTCULL_MODE);

	working = FALSE; pause = TRUE;
	ResetTimers();

	movieOptions = DEFAULT_MOVIE_OPTIONS;
	moviemode = DEFAULT_MOVIE_MODE;

	RGBSetGamma(DEFAULT_GAMMA);

	FixCubatureRules();

/* default set of basis functions */
	newQuadBasis = &DEFAULT_QUAD_BASIS;
	newTriBasis = &DEFAULT_TRI_BASIS;
	SetBasis(newQuadBasis, newTriBasis); /* just to make sure it is set to a valid basis */

	MGFSetNrQuartCircDivs(DEFAULT_MGF_NQCDIVS);
	MGFSetIgnoreSidedness(DEFAULT_MGF_IGNORE_SIDEDNESS);
}

void ProcessWaitingEvents(void)
{
	XtInputMask mask;

	while ((mask = XtAppPending(app_context))) 
		XtAppProcessEvent(app_context, mask);
}

/* called during radiance computations - checks for events (like the user clicking
 * on some button, or moving the mouse while holding one of the mouse buttons ... ) 
 * makes the program behave much more flexible */
void CheckForEvents(void)
{
	clock_t lastt = 0;

/* checking for events too often slows down the computations too much ... 
 * we check only once each second. */
	if (clock() - lastt < CLOCKS_PER_SEC)
		return;

	ProcessWaitingEvents();

	lastt = clock();
}

void CanvasSetSize(int hres, int vres)
{
	XtVaSetValues(canvas,
		      XmNwidth, hres,
		      XmNheight, vres,
		      NULL);
}

static char *UpdateStats(Widget statsw)
{
	int nrpatches, nrelements, nrinteractions, nrvertices, nrbytes;
	static char buf[1024];

	nrvertices = GetNumberOfVertices();
	nrpatches = PatchListCount(Patches);
	nrelements = GetNumberOfElements();
	nrinteractions = GetNumberOfInteractions();
	nrbytes = GetMemoryUsage();

	sprintf(buf, 
"Statistics after %d iterations:\n\n\
Nr. of vertices: %d\n\
Nr. of patches: %d\n\
Nr. of elements: %d\n\
Nr. of interactions: %d\n\
Memory usage: %d KByte\n\n\
\
Total time computing: %g sec.\n\
Total time computing formfactors: %g sec.\n\
Total time shaftculling: %g sec.\n\
Total time propagating radiance: %g sec.\n\
Total time propagating importance: %g sec.\n\n\
\
Time spent propagating radiance last iteration: %g sec.\n\
Time spent propagating importance last iteration: %g sec.\n\
Time spent ID-rendering last iteration: %g sec.\n",
		iterationr,
		nrvertices,
		nrpatches,
		nrelements,
		nrinteractions,
		nrbytes/1000,

		(float)usecs_total/(float)CLOCKS_PER_SEC,
		(float)usecs_ff_computation/(float)CLOCKS_PER_SEC,
		(float)usecs_shaftculling/(float)CLOCKS_PER_SEC,
		(float)usecs_total_radiance_propagation/(float)CLOCKS_PER_SEC,
		(float)usecs_total_importance_propagation/(float)CLOCKS_PER_SEC,

		(float)usecs_last_radiance_propagation/(float)CLOCKS_PER_SEC,
		(float)usecs_last_importance_propagation/(float)CLOCKS_PER_SEC,
		(float)usecs_last_id_rendering/(float)CLOCKS_PER_SEC);

	if (statsw != (Widget)NULL) 
		XtVaSetValues(statsw,
			      XtVaTypedArg,
			         XmNlabelString, XmRString, buf, strlen(buf)+1,
			      NULL);

	return buf;
}

static int DoOneIteration(void)
{
	int done;
	clock_t t;
	char *stats;

	if (!World)
		return TRUE;

	usecs_last_id_rendering = 0;

	t = clock();

	working = TRUE;
	done = ComputeRadianceOneStep();
	working = FALSE;

	t = clock() - t;
	usecs_last_phase += t;
	usecs_total += t;   

	usecs_total_importance_propagation += usecs_last_importance_propagation;
	usecs_total_radiance_propagation += usecs_last_radiance_propagation;
	stats = UpdateStats(statsMessage);

	EliminateTVertices();

	if (moviemode)
		MovieSaveFrame(movieOptions, iterationr, stats);

	RenderScene();

	return done;
}

/*************************** File menu ******************************************/
/* called when the user clicks the Load button in the file menu */
static void LoadFile(Widget loadButton, XtPointer client_data, XtPointer call_data)
{
	Widget loadBox = (Widget)client_data;

	if (!pause) {
		Warning(NULL, "Interrupt current computations first before loading another scene");
		return;
	}

	XtManageChild(loadBox);
}

/* stolen from the Motif demos */
/* support routine to get normal string from XmString */
char *extract_normal_string(XmString cs)
{
	XmStringContext context;
	XmStringCharSet charset;
	XmStringDirection direction;
	Boolean separator;
	static char *primitive_string;

	XmStringInitContext (&context,cs);
	XmStringGetNextSegment (context,&primitive_string,
				&charset,&direction,&separator);
	XmStringFreeContext (context);
	return ((char *) primitive_string);
}

/* recognized file formats */
#define RAD_FORMAT	1	
#define MGF_FORMAT	2

/* saves the current scene, tries to load the new one, if succesfull, disposes the
 * old one, if not, restores the old scene */
static void ReadScene(char *filename, int format)
{
	MATERIALLIST *oMaterialLib = MaterialLib;
	GEOMLIST *oWorld = World;
	PATCHLIST *oPatches = Patches;
	BASIS *oldQuadBasis, *oldTriBasis;
	int opatchid;

	World = GeomListCreate();
	MaterialLib = MaterialListCreate();

	ErrorReset();
	opatchid = PatchGetNextID();
	PatchSetNextID(1);

/* save the old basis and set the new one to be used */
	oldQuadBasis = quadBasis;
	oldTriBasis = triBasis;
	SetBasis(newQuadBasis, newTriBasis); 

/* parse the input file */
	switch (format) {
	case RAD_FORMAT:
		ReadRad(filename);
		break;
	case MGF_FORMAT:
		ReadMGF(filename);
		break;
	default:
		Fatal(4, "ReadScene", "Invalid file format.\n");
	}

	if (ErrorOccurred() || GeomListCount(World) < 1) {
/* restore the old scene if there were errors while loading the new one */
		SetBasis(oldQuadBasis, oldTriBasis);
		MaterialLib = oMaterialLib;
		World = oWorld;
		Patches = oPatches;
		PatchSetNextID(opatchid);

/* syntax errors should have been reported elsewhere, the only
 * kind of error that still has to be reported is when one loads
 * a valid input file containing no scene, but eg only material
 * descriptions. */
		if (!ErrorOccurred())
			Error(NULL, "Empty world.");
			
		return;
	}

/* dispose the old data */
	PatchListDestroy(oPatches);

	GeomListIterate(oWorld, GeomDestroy);
	GeomListDestroy(oWorld);

	MaterialListIterate(oMaterialLib, MaterialDestroy);
	MaterialListDestroy(oMaterialLib);

/* build the new patch list, this is duplicating already available
 * information and as such potentially dangerous, but we need it
 * so many times, so ... */
	Patches = BuildPatchList(World, PatchListCreate());	
}

static void LoadFileOkCallback(Widget loadBox, XtPointer client_data, XtPointer call_data)
{
	char *filename, *extension;
	FILE *input;
	clock_t t;
	int format;

	filename = extract_normal_string (((XmSelectionBoxCallbackStruct *)call_data)->value);

	if ((input = fopen(filename, "r")) == (FILE *)NULL ||
	     fgetc(input) == EOF) {
		fclose(input);
		Error(NULL, "Can't open file '%s' for reading", filename);
		return;
	}
	fclose(input);

/* determine the file format from its filename extension */	
	if ((extension = strrchr(filename, '.')) == NULL)
		extension = filename;

	if (strcmp(extension+1, "mgf") == 0)
		format = MGF_FORMAT;
	else
		format = RAD_FORMAT;

/* loading a new scene (and disposing the old) may take some time ...
 * inform the user by changing the cursor on the canvas window */
	CanvasPushMode(CANVASMODE_WORKING);
	ReadScene(filename, format);
	CanvasPullMode();

	fclose(stdin);

	if (!ErrorOccurred()) {
		XtUnmanageChild(loadBox);
		ResetTimers();

		t = clock();
		InitRadianceComputation();
		usecs_last_phase = clock() - t;
		usecs_total += usecs_last_phase;

		UpdateStats(statsMessage);

		ComputeAmbientRadiance();
		PatchListIterate(Patches, PatchComputeVertexColors);
		EliminateTVertices();
		RenderScene();
	} 
}

static void LoadFileCancelCallback(Widget loadBox, XtPointer client_data, XtPointer call_data)
{
	XtUnmanageChild(loadBox);
}

/* an X-toolkit work procedure - checks the value of 'pause': if pause==FALSE it will
 * perform one step of the radiance computations, if it is TRUE, it won't and the
 * program will just wait for the user to do something. */
static Boolean ContinueRadianceComputation(XtPointer client_data)
{
	int done = FALSE;

	if (!pause) 
		done = DoOneIteration();
	else
		done = TRUE;

	if (done) 
		CanvasPullMode();

	return done;   
}

/* when the user clicks the 'COmpute' button in the File menu: the radiance computation
 * is started or continued */
static void Compute(Widget w, XtPointer client_data, XtPointer call_data)
{
	pause = FALSE;

	CanvasPushMode(CANVASMODE_WORKING);

	usecs_last_phase = 0;
	XtAppAddWorkProc(app_context, ContinueRadianceComputation, (XtPointer)NULL);
}

/* when the user clicks on the 'One Iteration button': just one ietration of the
 * radiance computation will be done - mainly for debugging purposes */
static void OneIterationCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	CanvasPushMode(CANVASMODE_WORKING);

	usecs_last_phase = 0;
	DoOneIteration();

	CanvasPullMode();
}

/* when the user clicks on Pause button in the FIle menu ... */
static void Pause(Widget w, XtPointer client_data, XtPointer call_data)
{
	pause = TRUE;
}

static void ShowStats(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget statsBox = (Widget)client_data;

	UpdateStats(statsMessage);
	XtManageChild(statsBox);
}

static void StatsOKCallback(Widget statsBox, XtPointer client_data, XtPointer call_data)
{
	XtUnmanageChild(statsBox);
}

static void Save(Widget filenamePromptBox, XtPointer client_data, XtPointer call_data)
{
	char *filename;
	FILE *fp;

	filename = extract_normal_string (((XmSelectionBoxCallbackStruct *)call_data)->value);

	if ((fp = fopen(filename, "w")) == (FILE *)NULL) {
		Error(NULL, "Can't open file '%s' for reading", filename);
		return;
	}

	XtUnmanageChild(filenamePromptBox);	

	working = TRUE;
	CanvasPushMode(CANVASMODE_WORKING);

	SaveScreen(fp);
	fclose(fp);

	CanvasPullMode();
	working = FALSE;
}

static void PromptForFilename(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget filenamePromptBox = (Widget)client_data;

	XtManageChild(filenamePromptBox);
}

static void EditMessageOKCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget editMessageDialog = (Widget)client_data;

	XtUnmanageChild(editMessageDialog);
	EditSurface();
}	

static void SurfEdit(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget editMessageDialog = (Widget)client_data;

	if (!pause) {
		Warning(NULL, "Interrupt current computations first before editing surfaces");
		return;
	}

	XtManageChild(editMessageDialog);
}

/* when the user clicks the 'Exit' button in the File menu ... */
static void Exit(Widget w, XtPointer client_data, XtPointer call_data)
{
	exit(0);
}

/* creates the File menu */
static void CreateFileMenu(Widget menuBar)
{
	Widget fileMenu, fileButton, exitButton,
	       loadButton, loadBox, computeButton, pauseButton, saveButton,
	       filenamePromptBox, statsButton, statsBox,
	       surfeditButton, editMessageDialog, temp;

/* File button in menubar */
	fileButton = XtVaCreateManagedWidget("fileButton",
					     xmCascadeButtonWidgetClass,
					     menuBar,
					     NULL);

/* File pulldown menu: actually a Shell widget and RowColumn Wwidget combo -
 * note that we need to explicitely specify what visual to use (at least with Motif 1.2) */
	fileMenu = XmCreatePulldownMenu(menuBar, "fileMenu", visargs, nrvisargs);

/* specify which menu the File Button will pop up */
	XtVaSetValues(fileButton,
		      XmNsubMenuId, fileMenu,
		      NULL);	

/* note that the fileMenu widget is intentionally NOT managed here */

/* ------------------ Load button ------------------- */
	loadButton = XtVaCreateManagedWidget("loadButton",
					     xmPushButtonWidgetClass,
					     fileMenu,
					     NULL);

	loadBox = XmCreateFileSelectionDialog(loadButton, "loadBox", visargs, nrvisargs);

	temp = XmFileSelectionBoxGetChild(loadBox, XmDIALOG_HELP_BUTTON);
	XtUnmanageChild(temp);

	XtAddCallback(loadBox, XmNokCallback, LoadFileOkCallback, (XtPointer)NULL);
	XtAddCallback(loadBox, XmNcancelCallback, LoadFileCancelCallback, (XtPointer)NULL);

	XtAddCallback(loadButton, XmNactivateCallback, LoadFile, (XtPointer)loadBox);

/* ------------------- Compute button ---------------- */
	computeButton = XtVaCreateManagedWidget("computeButton",
						xmPushButtonWidgetClass,
						fileMenu,
						NULL);

	XtAddCallback(computeButton, XmNactivateCallback, Compute, (XtPointer)NULL);

/* ------------------- Pause button ---------------- */
	pauseButton = XtVaCreateManagedWidget("pauseButton",
					      xmPushButtonWidgetClass,
					      fileMenu,
					      NULL);

	XtAddCallback(pauseButton, XmNactivateCallback, Pause, (XtPointer)NULL);

/* ------------------- Edit Surfaces button */
	surfeditButton = XtVaCreateManagedWidget("surfeditButton",
						 xmPushButtonWidgetClass,
						 fileMenu,
						 NULL);

	editMessageDialog = XmCreateMessageDialog(topLevel, "editMessageDialog", visargs, nrvisargs);
	temp = XmMessageBoxGetChild(editMessageDialog, XmDIALOG_HELP_BUTTON);
	XtUnmanageChild(temp);

	XtAddCallback(editMessageDialog, XmNokCallback, EditMessageOKCallback, (XtPointer)editMessageDialog);

	XtAddCallback(surfeditButton, XmNactivateCallback, SurfEdit, (XtPointer)editMessageDialog);

/* ------------------- Statistics button ---------------- */
	statsButton = XtVaCreateManagedWidget("statsButton",
					      xmPushButtonWidgetClass,
					      fileMenu,
					      NULL);

/* create a message dialog to show some statistics about the computation */
	statsBox = XmCreateMessageDialog(statsButton, "statsBox", visargs, nrvisargs);

	statsMessage = XtVaCreateManagedWidget("statsMessage",
					       xmLabelWidgetClass,
					       statsBox,
					       NULL);

	XtAddCallback(statsBox, XmNokCallback, StatsOKCallback, (XtPointer)NULL);

/* don't show the CANCEL and HELP button */
	temp = XmMessageBoxGetChild(statsBox, XmDIALOG_CANCEL_BUTTON);
	XtUnmanageChild(temp);
	temp = XmMessageBoxGetChild(statsBox, XmDIALOG_HELP_BUTTON);
	XtUnmanageChild(temp);

	XtAddCallback(statsButton, XmNactivateCallback, ShowStats, (XtPointer)statsBox);

/* ------------------- Save screen button ---------------- */
	saveButton = XtVaCreateManagedWidget("saveButton",
					      xmPushButtonWidgetClass,
					      fileMenu,
					      NULL);

	filenamePromptBox = XmCreatePromptDialog(saveButton, "filenamePromptBox", visargs, nrvisargs);
	XtAddCallback(filenamePromptBox, XmNokCallback, Save, (XtPointer)NULL);

	temp = XmSelectionBoxGetChild(filenamePromptBox, XmDIALOG_HELP_BUTTON);
	XtUnmanageChild(temp);

	XtAddCallback(saveButton, XmNactivateCallback, PromptForFilename, (XtPointer)filenamePromptBox);

/* ------------------- Exit button ------------------- */
	exitButton = XtVaCreateManagedWidget("exitButton",
					     xmPushButtonWidgetClass,
					     fileMenu,
					     NULL);
	XtAddCallback(exitButton, XmNactivateCallback, Exit, (XtPointer)NULL);
}

/*************************** Render menu ******************************************/
static void SetRenderingModeCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	int mode = (int)client_data;
	int set = ((XmToggleButtonCallbackStruct *)call_data)->set;

	if (set == XmSET) {
		RenderSetMode(mode);
		/* if (!working) */ RenderScene();
	}
}

static void SetGouraudShadingCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	RenderSetGouraudShading(((XmToggleButtonCallbackStruct *)call_data)->set == XmSET);
	/* if (!working) */ RenderScene();
}

static void SetRenderAmbientCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	SetRenderAmbient(((XmToggleButtonCallbackStruct *)call_data)->set == XmSET);

	ComputeAmbientRadiance();
	PatchListIterate(Patches, PatchComputeVertexColors);
	EliminateTVertices();
	RenderScene();
}

static void SetBackfaceCullingCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	RenderSetBackfaceCulling(((XmToggleButtonCallbackStruct *)call_data)->set == XmSET);
	/* if (!working) */ RenderScene();
}

static void SetOutlineDrawingCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	RenderSetOutlineDrawing(((XmToggleButtonCallbackStruct *)call_data)->set == XmSET);
	/* if (!working) */ RenderScene();
}

static void TVEliminationCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	SetTVertexElimination(((XmToggleButtonCallbackStruct *)call_data)->set == XmSET);

	EliminateTVertices();
	/* if (!working) */ RenderScene();
}

static void ShowOutlineColorBox(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget outlineColorBox = (Widget)client_data;

	XtManageChild(outlineColorBox);
}

static void OutlineColorOKCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget outlineColorBox = XtParent(w);
	RGB *outlinecolor = (RGB *)client_data;

	XtUnmanageChild(outlineColorBox);

	RenderSetOutlineColor(outlinecolor);
	/* if (!working) */ RenderScene();
}

static void OutlineColorCancelCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget outlineColorBox = XtParent(w);
	XtUnmanageChild(outlineColorBox);
}

static void SetShowLinksCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	RenderShowLinks(((XmToggleButtonCallbackStruct *)call_data)->set);
	/* if (!working) */ RenderScene();
}

static void ShowLinkColorBox(Widget help, XtPointer client_data, XtPointer call_data)
{
	Widget linkColorBox = (Widget)client_data;

	XtManageChild(linkColorBox);
}

static void LinkColorOKCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget linkColorBox = XtParent(w);
	RGB *linkcolor = (RGB *)client_data;

	XtUnmanageChild(linkColorBox);

	RenderSetLinkColor(linkcolor);
	/* if (!working) */ RenderScene();
}

static void LinkColorCancelCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget linkColorBox = XtParent(w);
	XtUnmanageChild(linkColorBox);
}

static void CreateRenderMenu(Widget menuBar)
{
	Widget renderButton, renderMenu, whatToRenderButton, whatToRenderMenu,
	       reflectivityButton, radianceButton, unshotRadianceButton, 
	       importanceButton, unshotImportanceButton, patchIdButton,
	       gouraudButton, backfacecullButton, drawoutlineButton,
	       showlinksButton, TVelimButton, ambientButton;

	renderButton = XtVaCreateManagedWidget("renderButton",
					       xmCascadeButtonWidgetClass,
					       menuBar,
					       NULL);

/* render menu: a Shell- and  RowColumn widget combo */
	renderMenu = XmCreatePulldownMenu(menuBar, "renderMenu", visargs, nrvisargs);

/* specify what menu to pop up when the Render button is clicked */
	XtVaSetValues(renderButton,
		      XmNsubMenuId, renderMenu,
		      NULL);

/* -------------------- What To Render button --------------------- */
	whatToRenderButton = XtVaCreateManagedWidget("whatToRenderButton",
						     xmCascadeButtonWidgetClass,
						     renderMenu,
						     NULL);

	whatToRenderMenu = XmCreatePulldownMenu(renderMenu, "whatToRenderMenu", visargs, nrvisargs);

	XtVaSetValues(whatToRenderButton,
		      XmNsubMenuId, whatToRenderMenu,
		      NULL);

	XtVaSetValues(whatToRenderMenu,
		      XmNradioBehavior, True,
		      NULL);

	reflectivityButton = XtVaCreateManagedWidget("reflectivityButton",
						 xmToggleButtonWidgetClass,
						 whatToRenderMenu,
						 XmNset, DEFAULT_RENDERINGMODE == RENDER_REFLECTIVITY ? True : False,
						 NULL);

	XtAddCallback(reflectivityButton, XmNvalueChangedCallback, SetRenderingModeCallback, (XtPointer)RENDER_REFLECTIVITY);

	radianceButton = XtVaCreateManagedWidget("radianceButton",
						 xmToggleButtonWidgetClass,
						 whatToRenderMenu,
						 XmNset, DEFAULT_RENDERINGMODE == RENDER_RADIANCE ? True : False,
						 NULL);

	XtAddCallback(radianceButton, XmNvalueChangedCallback, SetRenderingModeCallback, (XtPointer)RENDER_RADIANCE);
						
	unshotRadianceButton = XtVaCreateManagedWidget("unshotRadianceButton",
						       xmToggleButtonWidgetClass,
						       whatToRenderMenu,
						       XmNset, DEFAULT_RENDERINGMODE == RENDER_UNSHOT_RADIANCE ? True : False,
						       NULL);

	XtAddCallback(unshotRadianceButton, XmNvalueChangedCallback, SetRenderingModeCallback, (XtPointer)RENDER_UNSHOT_RADIANCE);
						

	importanceButton = XtVaCreateManagedWidget("importanceButton",
						 xmToggleButtonWidgetClass,
						 whatToRenderMenu,
						 XmNset, DEFAULT_RENDERINGMODE == RENDER_IMPORTANCE ? True : False,
						 NULL);

	XtAddCallback(importanceButton, XmNvalueChangedCallback, SetRenderingModeCallback, (XtPointer)RENDER_IMPORTANCE);
						
	unshotImportanceButton = XtVaCreateManagedWidget("unshotImportanceButton",
						       xmToggleButtonWidgetClass,
						       whatToRenderMenu,
						       XmNset, DEFAULT_RENDERINGMODE == RENDER_UNSHOT_IMPORTANCE ? True : False,
						       NULL);

	XtAddCallback(unshotImportanceButton, XmNvalueChangedCallback, SetRenderingModeCallback, (XtPointer)RENDER_UNSHOT_IMPORTANCE);
						
	patchIdButton = XtVaCreateManagedWidget("patchIdButton",
						 xmToggleButtonWidgetClass,
						 whatToRenderMenu,
						 XmNset, DEFAULT_RENDERINGMODE == RENDER_ID ? True : False,
						 NULL);

	XtAddCallback(patchIdButton, XmNvalueChangedCallback, SetRenderingModeCallback, (XtPointer)RENDER_ID);
						

/* ------------------- Ambient button ------------------- */
	ambientButton = XtVaCreateManagedWidget("ambientButton",
						xmToggleButtonWidgetClass,
						renderMenu,
						XmNset, DEFAULT_RENDER_AMBIENT,
						NULL);

	XtAddCallback(ambientButton, XmNvalueChangedCallback, SetRenderAmbientCallback, (XtPointer)NULL);

/* ------------------- Gouraud Shading button ------------------- */
	gouraudButton = XtVaCreateManagedWidget("gouraudButton",
						xmToggleButtonWidgetClass,
						renderMenu,
						XmNset, DEFAULT_GOURAUDSHADING,
						NULL);

	XtAddCallback(gouraudButton, XmNvalueChangedCallback, SetGouraudShadingCallback, (XtPointer)NULL);

/* ----------------------- TV-elimination button -------------------- */
	TVelimButton = XtVaCreateManagedWidget("TVelimButton",
					       xmToggleButtonWidgetClass,
					       renderMenu,
					       XmNset, DEFAULT_TVERTEXELIMINATION,
					       NULL);
	XtAddCallback(TVelimButton, XmNvalueChangedCallback, TVEliminationCallback, (XtPointer)NULL);

/* -------------------- Backfaceculling button ----------------------*/
	backfacecullButton = XtVaCreateManagedWidget("backfacecullButton",
						     xmToggleButtonWidgetClass,
						     renderMenu,
						     XmNset, DEFAULT_BACKFACECULLING,
						     NULL);

	XtAddCallback(backfacecullButton, XmNvalueChangedCallback, SetBackfaceCullingCallback, (XtPointer)NULL);

/* --------------------- Outline Drawing button -------------------- */
	drawoutlineButton = XtVaCreateManagedWidget("drawoutlineButton",
						     xmToggleButtonWidgetClass,
						     renderMenu,
						     XmNset, DEFAULT_OUTLINEDRAWING,
						     NULL);

	XtAddCallback(drawoutlineButton, XmNvalueChangedCallback, SetOutlineDrawingCallback, (XtPointer)NULL);	

/* --------------------- Show Links button -------------------- */
	showlinksButton = XtVaCreateManagedWidget("showlinksButton",
						  xmToggleButtonWidgetClass,
						  renderMenu,
						  XmNset, DEFAULT_SHOWLINKS,
						  NULL);

	XtAddCallback(showlinksButton, XmNvalueChangedCallback, SetShowLinksCallback, (XtPointer)NULL);	
}

/***************** Post-radiosity pass menu *********************************/
static void (*PostPassRoutine)(GEOMLIST *, CAMERA *, FILE *);

static void DoPostPass(Widget filenamePromptBox, XtPointer client_data, XtPointer call_data)
{
	char *filename;
	FILE *fp;

	filename = extract_normal_string (((XmSelectionBoxCallbackStruct *)call_data)->value);

	if ((fp = fopen(filename, "w")) == (FILE *)NULL) {
		Error(NULL, "Can't open file '%s' for reading", filename);
		return;
	}

	XtUnmanageChild(filenamePromptBox);

	CanvasPushMode(CANVASMODE_RENDER);
	PostPassRoutine(World, &Camera, fp);
	CanvasPullMode();
}

static void RayCastCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget filenamePromptBox = (Widget)client_data;

	PostPassRoutine = RayCast;
	XtManageChild(filenamePromptBox);
}

static void CreatePostPassMenu(Widget menuBar)
{
	Widget postPassButton, postPassMenu, raycastButton,
	       filenamePromptBox, temp;

	postPassButton = XtVaCreateManagedWidget("postPassButton",
					       xmCascadeButtonWidgetClass,
					       menuBar,
					       NULL);

	postPassMenu = XmCreatePulldownMenu(menuBar, "postPassMenu", visargs, nrvisargs);

	XtVaSetValues(postPassButton,
		      XmNsubMenuId, postPassMenu,
		      NULL);

/* -------------------- Dialog asking for a filename --------------------------- */
	filenamePromptBox = XmCreatePromptDialog(postPassMenu, "filenamePromptBox", visargs, nrvisargs);
	XtAddCallback(filenamePromptBox, XmNokCallback, DoPostPass, (XtPointer)NULL);

	temp = XmSelectionBoxGetChild(filenamePromptBox, XmDIALOG_HELP_BUTTON);
	XtUnmanageChild(temp);

/* --------------------- Raycast button ------------------------- */
	raycastButton = XtVaCreateManagedWidget("raycastButton",
						xmPushButtonWidgetClass,
						postPassMenu,
						NULL);

	XtAddCallback(raycastButton, XmNactivateCallback, RayCastCallback, (XtPointer)filenamePromptBox);
}


/*************************** Algorithm menu ******************************************/

/* ++++++++++++++++++++++++ Solution Method submenu ++++++++++++++++++ */
static void SetSolutionMethodCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	int set = ((XmToggleButtonCallbackStruct *)call_data)->set;
	if (set == XmSET)
		SetSolutionMethod((int)client_data);
}

static void CreateSolutionMethodMenu(Widget methodMenu)
{
	Widget SouthwellButton, GaussSeidelButton;

	XtVaSetValues(methodMenu,
		      XmNradioBehavior, True,
		      NULL);

	SouthwellButton = XtVaCreateManagedWidget("SouthwellButton",
						  xmToggleButtonWidgetClass,
						  methodMenu,
						  XmNset, DEFAULT_SOLUTION_METHOD == SOUTHWELL ? TRUE : FALSE,
						  NULL);
	XtAddCallback(SouthwellButton, XmNvalueChangedCallback, SetSolutionMethodCallback, (XtPointer)SOUTHWELL);

	GaussSeidelButton = XtVaCreateManagedWidget("GaussSeidelButton",
						    xmToggleButtonWidgetClass,
						    methodMenu,
						    XmNset, DEFAULT_SOLUTION_METHOD == GAUSS_SEIDEL ? TRUE : FALSE,
						    NULL);
	XtAddCallback(GaussSeidelButton, XmNvalueChangedCallback, SetSolutionMethodCallback, (XtPointer)GAUSS_SEIDEL);
}

/* ++++++++++++++++++++++++ formfactors submenu ++++++++++++++++++ */

/* + + + + + + + + + + + +  Cubature rules subsubmenu + + + + + + + + + + */
#define CUBA_RCV_MENU	  1	/* rule to be used for receiving or remitting patches */
#define CUBA_EMT_MENU	  2
#define CUBA_DEG_3	  3
#define CUBA_DEG_5	  5
#define CUBA_DEG_7	  7
#define CUBA_DEG_3P	 33	/* product rules */
#define CUBA_DEG_5P	 55
#define CUBA_DEG_7P	 77

static void FFCubaRulesCallback(Widget ruleButton, XtPointer client_data, XtPointer call_data)
{
	int whichrule = (int)client_data;	/* CUBA_x_MENU*100 + CUBA_DEG_y */
	int set = XmSET;

/* we call it not as a callback procedure when initializing, to set the defaults */
	if (call_data)
		set = ((XmToggleButtonCallbackStruct *)call_data)->set;

	if (set != XmSET)
		return;

	switch (whichrule/100) {
	case CUBA_RCV_MENU:
		switch (whichrule%100) {
		case CUBA_DEG_3:
			SetCubatureRules(&CRT3, &CRQ3, (CUBARULE *)NULL, (CUBARULE *)NULL);
			break;
		case CUBA_DEG_5:
			SetCubatureRules(&CRT5, &CRQ5, (CUBARULE *)NULL, (CUBARULE *)NULL);
			break;
		case CUBA_DEG_7:
			SetCubatureRules(&CRT7, &CRQ7, (CUBARULE *)NULL, (CUBARULE *)NULL);
			break;
/* there are not yet any product rules for triangles ... so use it for quadrilaterals only */
		case CUBA_DEG_3P:
			SetCubatureRules(&CRT7, &CRQ3PG, (CUBARULE *)NULL, (CUBARULE *)NULL);
			break;
		case CUBA_DEG_5P:
			SetCubatureRules(&CRT7, &CRQ5PG, (CUBARULE *)NULL, (CUBARULE *)NULL);
			break;
		case CUBA_DEG_7P:
			SetCubatureRules(&CRT7, &CRQ7PG, (CUBARULE *)NULL, (CUBARULE *)NULL);
			break;
		default:
			Fatal(3, "FFCubaRulesCallback", "Got an impossible rule code %d.\n", whichrule);
		}
		break;
	case CUBA_EMT_MENU:
		switch (whichrule%100) {
		case CUBA_DEG_3:
			SetCubatureRules((CUBARULE *)NULL, (CUBARULE *)NULL, &CRT3, &CRQ3);
			break;
		case CUBA_DEG_5:
			SetCubatureRules((CUBARULE *)NULL, (CUBARULE *)NULL, &CRT5, &CRQ5);
			break;
		case CUBA_DEG_7:
			SetCubatureRules((CUBARULE *)NULL, (CUBARULE *)NULL, &CRT7, &CRQ7);
			break;
/* there are not yet any product rules for triangles ... so use it for quadrilaterals only */
		case CUBA_DEG_3P:
			SetCubatureRules((CUBARULE *)NULL, (CUBARULE *)NULL, &CRT7, &CRQ3PG);
			break;
		case CUBA_DEG_5P:
			SetCubatureRules((CUBARULE *)NULL, (CUBARULE *)NULL, &CRT7, &CRQ5PG);
			break;
		case CUBA_DEG_7P:
			SetCubatureRules((CUBARULE *)NULL, (CUBARULE *)NULL, &CRT7, &CRQ7PG);
			break;
		default:
			Fatal(3, "FFCubaRulesCallback", "Got an impossible rule code %d.\n", whichrule);
		}
		break;
	default:
		Fatal(3, "FFCubaRuleCallback", "Got an impossible rule code %d.\n", whichrule);
	}
}

static void CreateFFCubatureRulesMenu(Widget FFCubaRulesMenu, int whichmenu)
{
	Widget FFCubaDeg3Button, FFCubaDeg3PButton, 
	       FFCubaDeg5Button, FFCubaDeg5PButton,
	       FFCubaDeg7Button, FFCubaDeg7PButton, 
	       the_button=(Widget)NULL;

	/* default degree to be used - defined in defaults.h */
	int default_degree = (whichmenu == CUBA_RCV_MENU ? DEFAULT_CR_RCV_DEG : DEFAULT_CR_SRC_DEG);

	XtVaSetValues(FFCubaRulesMenu,
		      XmNradioBehavior, True,
		      NULL);

/* degree 3 - 4 nodes */
	FFCubaDeg3Button = XtVaCreateManagedWidget("FFCubaDeg3Button",
						   xmToggleButtonWidgetClass,
						   FFCubaRulesMenu,
						   NULL);
	XtAddCallback(FFCubaDeg3Button, XmNvalueChangedCallback, FFCubaRulesCallback, (XtPointer)(whichmenu*100 + CUBA_DEG_3));
	
/* degree 5 - 7 nodes */
	FFCubaDeg5Button = XtVaCreateManagedWidget("FFCubaDeg5Button",
						   xmToggleButtonWidgetClass,
						   FFCubaRulesMenu,
						   NULL);
	XtAddCallback(FFCubaDeg5Button, XmNvalueChangedCallback, FFCubaRulesCallback, (XtPointer)(whichmenu*100 + CUBA_DEG_5));

/* degree 7 - 12 nodes */
	FFCubaDeg7Button = XtVaCreateManagedWidget("FFCubaDeg7Button",
						   xmToggleButtonWidgetClass,
						   FFCubaRulesMenu,
						   NULL);
	XtAddCallback(FFCubaDeg7Button, XmNvalueChangedCallback, FFCubaRulesCallback, (XtPointer)(whichmenu*100 + CUBA_DEG_7));
	
/* degree 3 - 4 nodes product rule (quads only) */
	FFCubaDeg3PButton = XtVaCreateManagedWidget("FFCubaDeg3PButton",
						   xmToggleButtonWidgetClass,
						   FFCubaRulesMenu,
						   NULL);
	XtAddCallback(FFCubaDeg3PButton, XmNvalueChangedCallback, FFCubaRulesCallback, (XtPointer)(whichmenu*100 + CUBA_DEG_3P));
	
/* degree 5 - 9 nodes product rule (quads only) */
	FFCubaDeg5PButton = XtVaCreateManagedWidget("FFCubaDeg5PButton",
						   xmToggleButtonWidgetClass,
						   FFCubaRulesMenu,
						   NULL);
	XtAddCallback(FFCubaDeg5PButton, XmNvalueChangedCallback, FFCubaRulesCallback, (XtPointer)(whichmenu*100 + CUBA_DEG_5P));
	
/* degree 7 - 16 nodes product rule (quads only) */
	FFCubaDeg7PButton = XtVaCreateManagedWidget("FFCubaDeg7PButton",
						   xmToggleButtonWidgetClass,
						   FFCubaRulesMenu,
						   XmNset, default_degree == CUBA_DEG_7P ? TRUE : FALSE,
						   NULL);
	XtAddCallback(FFCubaDeg7PButton, XmNvalueChangedCallback, FFCubaRulesCallback, (XtPointer)(whichmenu*100 + CUBA_DEG_7P));	

	switch (default_degree) {
	case CUBA_DEG_3 : the_button = FFCubaDeg3Button; break;
	case CUBA_DEG_5 : the_button = FFCubaDeg5Button; break;
	case CUBA_DEG_7 : the_button = FFCubaDeg7Button; break;
	case CUBA_DEG_3P : the_button = FFCubaDeg3PButton; break;
	case CUBA_DEG_5P : the_button = FFCubaDeg5PButton; break;
	case CUBA_DEG_7P : the_button = FFCubaDeg7PButton; break;
	default:
		Fatal(3, "CreateFFCubaRUlesMenu", "Invalid default degree %d rule over %s patches - check defaults.h\n", 
		      default_degree, whichmenu == CUBA_RCV_MENU ? "receiving" : "source");
	}

	XtVaSetValues(the_button,
		      XmNset, TRUE,
		      NULL);

/* ... and make sure to the default rule is really set */
	FFCubaRulesCallback((Widget)the_button, (XtPointer)(whichmenu*100 + default_degree), (XtPointer)NULL);
}

static void CreateFFCubatureMenu(Widget FFCubatureMenu)
{
	Widget FFCubaRcvButton, FFCubaRcvMenu, FFCubaEmtButton, FFCubaEmtMenu;

/* ------------------- Emitting (source) patch button ---------------------- */
	FFCubaEmtButton = XtVaCreateManagedWidget("FFCubaEmtButton",
						  xmCascadeButtonWidgetClass,
						  FFCubatureMenu,
						  NULL);

	FFCubaEmtMenu = XmCreatePulldownMenu(FFCubatureMenu, "FFCubaEmtMenu", visargs, nrvisargs);
	CreateFFCubatureRulesMenu(FFCubaEmtMenu, CUBA_EMT_MENU);

	XtVaSetValues(FFCubaEmtButton,
		      XmNsubMenuId, FFCubaEmtMenu,
		      NULL);	

/* ------------------- Receiving patch button ---------------------- */
	FFCubaRcvButton = XtVaCreateManagedWidget("FFCubaRcvButton",
						  xmCascadeButtonWidgetClass,
						  FFCubatureMenu,
						  NULL);

	FFCubaRcvMenu = XmCreatePulldownMenu(FFCubatureMenu, "FFCubaRcvMenu", visargs, nrvisargs);
	CreateFFCubatureRulesMenu(FFCubaRcvMenu, CUBA_RCV_MENU);

	XtVaSetValues(FFCubaRcvButton,
		      XmNsubMenuId, FFCubaRcvMenu,
		      NULL);	
}

/* + + + + + + + + + + + + shaftculling subsubmenu + + + + + + + + + + */
static void ShaftcullModeChangedCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	int set = ((XmToggleButtonCallbackStruct *)call_data)->set;
	if (set == XmSET)
		SetShaftcullMode((int)client_data);
}

static void CreateShaftcullMenu(Widget shaftcullMenu)
{
	Widget shaftcullAlwaysButton, shaftcullRefineButton, shaftcullNeverButton;

	XtVaSetValues(shaftcullMenu,
		      XmNradioBehavior, True,
		      NULL);

/* ------------------------- Never button ------------------------------ */
	shaftcullNeverButton = XtVaCreateManagedWidget("shaftcullNeverButton",
						       xmToggleButtonWidgetClass,
						       shaftcullMenu,
						       XmNset, DEFAULT_SHAFTCULL_MODE == SHAFTCULL_NEVER,
						       NULL);
	XtAddCallback(shaftcullNeverButton, XmNvalueChangedCallback, ShaftcullModeChangedCallback, (XtPointer)SHAFTCULL_NEVER);

/* ------------------------- Refine button ------------------------------ */
	shaftcullRefineButton = XtVaCreateManagedWidget("shaftcullRefineButton",
						       xmToggleButtonWidgetClass,
						       shaftcullMenu,
						       XmNset, DEFAULT_SHAFTCULL_MODE == SHAFTCULL_REFINE,
						       NULL);
	XtAddCallback(shaftcullRefineButton, XmNvalueChangedCallback, ShaftcullModeChangedCallback, (XtPointer)SHAFTCULL_REFINE);

/* ------------------------- Always button ------------------------------ */
	shaftcullAlwaysButton = XtVaCreateManagedWidget("shaftcullAlwaysButton",
						       xmToggleButtonWidgetClass,
						       shaftcullMenu,
						       XmNset, DEFAULT_SHAFTCULL_MODE == SHAFTCULL_ALWAYS,
						       NULL);
	XtAddCallback(shaftcullAlwaysButton, XmNvalueChangedCallback, ShaftcullModeChangedCallback, (XtPointer)SHAFTCULL_ALWAYS);
}

static void CreateFormfactorsMenu(Widget formfactorsMenu)
{
	Widget FFCubatureButton, FFCubatureMenu;
	Widget shaftcullButton, shaftcullMenu;

/* ----------------------- Formfactor cubature button ---------------- */
	FFCubatureButton = XtVaCreateManagedWidget("FFCubatureButton",
						   xmCascadeButtonWidgetClass,
						   formfactorsMenu,
						   NULL);

	FFCubatureMenu = XmCreatePulldownMenu(formfactorsMenu, "FFCubatureMenu", visargs, nrvisargs);
	CreateFFCubatureMenu(FFCubatureMenu);

	XtVaSetValues(FFCubatureButton,
		      XmNsubMenuId, FFCubatureMenu,
		      NULL);

/* ----------------------- Shaftculling button ------------------------ */
	shaftcullButton = XtVaCreateManagedWidget("shaftcullButton",
						  xmCascadeButtonWidgetClass,
						  formfactorsMenu,
						  NULL);

	shaftcullMenu = XmCreatePulldownMenu(formfactorsMenu, "shaftcullMenu", visargs, nrvisargs);
	CreateShaftcullMenu(shaftcullMenu);

	XtVaSetValues(shaftcullButton,
		      XmNsubMenuId, shaftcullMenu,
		      NULL);
}

/* ++++++++++++++++++++++++ tolerances panel ++++++++++++++++++ */
typedef struct TOLERANCES {
	float Aeps, Feps, Beps, Ieps;
} TOLERANCES;

static void TolerancesOKCallback(Widget w, XtPointer client_data, XtPointer calldata)
{
	Widget tolerancesBox = XtParent(w);
	TOLERANCES *tolerances = (TOLERANCES *)client_data;

	SetTolerances(tolerances->Aeps,
		      tolerances->Feps,
		      tolerances->Beps,
		      tolerances->Ieps);

	XtUnmanageChild(tolerancesBox);
}

static void TolerancesCancelCallback(Widget w, XtPointer client_data, XtPointer calldata)
{
	Widget tolerancesBox = XtParent(w);
	XtUnmanageChild(tolerancesBox);
}

static void CreateTolerancesPanel(Widget tolerancesBox)
{
	Widget tolerancesForm, tolerancesOKButton, tolerancesCancelButton;

	static TOLERANCES tolerances = 
	       {DEFAULT_AEPS, DEFAULT_FEPS, DEFAULT_BEPS, DEFAULT_IEPS};

	XFORMFIELD tolerancesFormFields[] = {
	       {XFORMFLOAT, (char *)&tolerances.Aeps, "Min. patch area"},
	       {XFORMFLOAT, (char *)&tolerances.Feps, "Formfactor tolerance"},
	       {XFORMFLOAT, (char *)&tolerances.Beps, "Min. shootable power"},
	       {XFORMFLOAT, (char *)&tolerances.Ieps, "Min. shootable importance"},
	       {0	  , NULL		    , NULL}};

	tolerancesForm = XFormCreate(tolerancesBox, tolerancesFormFields);

	tolerancesOKButton = XtVaCreateManagedWidget("tolerancesOKButton",
						     xmPushButtonWidgetClass,
						     tolerancesBox,
						     NULL);

	tolerancesCancelButton = XtVaCreateManagedWidget("tolerancesCancelButton",
							 xmPushButtonWidgetClass,
							 tolerancesBox,
							 NULL);

	XtAddCallback(tolerancesOKButton, XmNactivateCallback, TolerancesOKCallback, (XtPointer)&tolerances);

	XtAddCallback(tolerancesCancelButton, XmNactivateCallback, TolerancesCancelCallback, (XtPointer)NULL);
}

static void ShowTolerancesBox(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget tolerancesBox = (Widget)client_data;

	XtManageChild(tolerancesBox);
}

/* ++++++++++++++++++++++++ other algorithm options ++++++++++++++++++ */
static void SetHRefinementCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	SetHierarchicalRefinement((((XmToggleButtonCallbackStruct *)call_data)->set == XmSET));
}

static void SetUseImportanceCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	SetImportanceDriven((((XmToggleButtonCallbackStruct *)call_data)->set == XmSET));
}

static void CreateAlgorithmMenu(Widget menuBar)
{
	Widget algorithmButton, algorithmMenu,
	       methodButton, methodMenu, 
	       formfactorsButton, formfactorsMenu,
	       tolerancesButton, tolerancesBox, 
	       HRefinementButton,
	       importanceButton;

	algorithmButton = XtVaCreateManagedWidget("algorithmButton",
						  xmCascadeButtonWidgetClass,
						  menuBar,
						  NULL);

	algorithmMenu = XmCreatePulldownMenu(menuBar, "algorithmMenu", visargs, nrvisargs);

	XtVaSetValues(algorithmButton,
		      XmNsubMenuId, algorithmMenu,
		      NULL);

/* ----------------------- Solution Method button --------------------- */
	methodButton = XtVaCreateManagedWidget("methodButton",
					       xmCascadeButtonWidgetClass,
					       algorithmMenu,
					       NULL);

	methodMenu = XmCreatePulldownMenu(algorithmMenu, "methodMenu", visargs, nrvisargs);

	CreateSolutionMethodMenu(methodMenu);

	XtVaSetValues(methodButton,
		      XmNsubMenuId, methodMenu,
		      NULL);

/* ----------------------- Formfactors button ------------------------ */
	formfactorsButton = XtVaCreateManagedWidget("formfactorsButton",
						    xmCascadeButtonWidgetClass,
						    algorithmMenu,
						    NULL);

	formfactorsMenu = XmCreatePulldownMenu(algorithmMenu, "formfactorsMenu", visargs, nrvisargs);

	CreateFormfactorsMenu(formfactorsMenu);

	XtVaSetValues(formfactorsButton,
		      XmNsubMenuId, formfactorsMenu,
		      NULL);

/* ----------------------- Tolerances button ----------------------------- */
	tolerancesButton = XtVaCreateManagedWidget("tolerancesButton",
						   xmPushButtonWidgetClass,
						   algorithmMenu,
						   NULL);

	tolerancesBox = XmCreateTemplateDialog(tolerancesButton, "tolerancesDialog", visargs, nrvisargs);

	CreateTolerancesPanel(tolerancesBox);

	XtAddCallback(tolerancesButton, XmNactivateCallback, ShowTolerancesBox, (XtPointer)tolerancesBox);

/* ---------------------- H-refinement button --------------------------- */
	HRefinementButton = XtVaCreateManagedWidget("HRefinementButton",
						    xmToggleButtonWidgetClass,
						    algorithmMenu,
						    XmNset, DEFAULT_HIERARCHICAL_REFINEMENT,
						    NULL);
	XtAddCallback(HRefinementButton, XmNvalueChangedCallback, SetHRefinementCallback, (XtPointer)NULL);

/* ----------------------- Use Importance button ------------------------ */
	importanceButton = XtVaCreateManagedWidget("importanceButton",
						   xmToggleButtonWidgetClass,
						   algorithmMenu,
						   XmNset, DEFAULT_IMPORTANCE_DRIVEN,
						   NULL);
	XtAddCallback(importanceButton, XmNvalueChangedCallback, SetUseImportanceCallback, (XtPointer)NULL);
}

/*************************** Camera menu ******************************************/
static void EditCameraOKCallback(Widget w, XtPointer client_data, XtPointer calldata)
{
	CAMERA *cam = (CAMERA *)client_data;

	CameraSet(&Camera, &cam->eyep, &cam->lookp, &cam->updir, 
		  cam->fov, Camera.hres, Camera.vres, &Camera.background);
	/* if (!working) */ RenderScene();
}

static void EditCameraCancelCallback(Widget w, XtPointer client_data, XtPointer calldata)
{
	Widget editCameraShell = XtParent(w);
	CAMERA *cam = (CAMERA *)client_data;

	XtDestroyWidget(editCameraShell);
	CameraDestroy(cam);
}

static void ShowEditCameraBox(Widget w, XtPointer client_data, XtPointer call_data)
{
	Widget editCameraShell, editCameraBox, editCameraForm;
	CAMERA *cam;

	editCameraShell = XtAppCreateShell("Camera",
					   APP_CLASS_NAME,
					   topLevelShellWidgetClass,
					   XtDisplay(w),
					   visargs,
					   nrvisargs);

	editCameraBox = XmCreateMessageBox(editCameraShell, "editCameraBox", NULL, 0);
	XtVaSetValues(editCameraBox,
		      XmNdialogType, XmDIALOG_TEMPLATE,
		      NULL);

	cam = CameraCreate();	
	*cam = Camera;
{
	XFORMFIELD editCameraFormFields[] = {
	{XFORMFLOAT, (char *)&cam->eyep.x, "Eyepoint X"},
	{XFORMFLOAT, (char *)&cam->eyep.y, "Eyepoint Y"},
	{XFORMFLOAT, (char *)&cam->eyep.z, "Eyepoint Z"},
	{XFORMFLOAT, (char *)&cam->lookp.x, "Look at X"},
	{XFORMFLOAT, (char *)&cam->lookp.y, "Look at Y"},
	{XFORMFLOAT, (char *)&cam->lookp.z, "Look at Z"},
	{XFORMFLOAT, (char *)&cam->updir.x, "Up direction X"},
	{XFORMFLOAT, (char *)&cam->updir.y, "Up direction Y"},
	{XFORMFLOAT, (char *)&cam->updir.z, "Up direction Z"},
	{XFORMFLOAT, (char *)&cam->fov,     "Field of view"},
	{0	   , NULL		  , NULL}
	};
	editCameraForm = XFormCreate(editCameraBox, editCameraFormFields);
}

	XtAddCallback(editCameraBox, XmNokCallback, EditCameraOKCallback, (XtPointer)cam);
	XtAddCallback(editCameraBox, XmNcancelCallback, EditCameraCancelCallback, (XtPointer)cam);

	XtManageChild(editCameraBox);
        XtRealizeWidget(editCameraShell);
}

static void SaveCameraCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	CameraPush(&Camera);
	/* if (!working) */ RenderScene();
}

static void RestoreCameraCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	CameraPop(&Camera);
	/* if (!working) */ RenderScene();
}

static void SetAlternateCameraCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	CameraSetAlternate(&Camera);
}

static void ToggleAlternateCameraCallback(Widget w, XtPointer client_data, XtPointer call_Data)
{
	CameraAlternate(&Camera);
	/* if (!working) */ RenderScene();
}

static void CreateCameraMenu(Widget menuBar)
{
	Widget cameraButton, cameraMenu,
	       editCameraButton, 
	       saveCameraButton, restoreCameraButton,
	       setAlternateCameraButton, toggleAlternateCameraButton;

	cameraButton = XtVaCreateManagedWidget("cameraButton",
					       xmCascadeButtonWidgetClass,
					       menuBar,
					       NULL);

	cameraMenu = XmCreatePulldownMenu(menuBar, "cameraMenu", visargs, nrvisargs);

	XtVaSetValues(cameraButton,
		      XmNsubMenuId, cameraMenu,
		      NULL);

/* --------------- Edit button ------------------- */
	editCameraButton = XtVaCreateManagedWidget("editCameraButton",
						   xmPushButtonWidgetClass,
						   cameraMenu,
						   NULL);

	XtAddCallback(editCameraButton, XmNactivateCallback, ShowEditCameraBox, (XtPointer)NULL);

/* --------------- Save button ------------------- */
	saveCameraButton = XtVaCreateManagedWidget("saveCameraButton",
						   xmPushButtonWidgetClass,
						   cameraMenu,
						   NULL);
	XtAddCallback(saveCameraButton, XmNactivateCallback, SaveCameraCallback, (XtPointer)NULL);

/* --------------- Restore button ------------------- */
	restoreCameraButton = XtVaCreateManagedWidget("restoreCameraButton",
						      xmPushButtonWidgetClass,
						      cameraMenu,
						      NULL);
	XtAddCallback(restoreCameraButton, XmNactivateCallback, RestoreCameraCallback, (XtPointer)NULL);

/* --------------- Set Alternate button ------------------- */
	setAlternateCameraButton = XtVaCreateManagedWidget("setAlternateCameraButton",
						      xmPushButtonWidgetClass,
						      cameraMenu,
						      NULL);
	XtAddCallback(setAlternateCameraButton, XmNactivateCallback, SetAlternateCameraCallback, (XtPointer)NULL);

/* --------------- Toggle Alternate button ------------------- */
	toggleAlternateCameraButton = XtVaCreateManagedWidget("toggleAlternateCameraButton",
						      xmPushButtonWidgetClass,
						      cameraMenu,
						      NULL);
	XtAddCallback(toggleAlternateCameraButton, XmNactivateCallback, ToggleAlternateCameraCallback, (XtPointer)NULL);
}

/*************************** Options menu ******************************************/

/* - - - - - - - - - - - - - Movie Options submenu - - - - - - - - - - - - - */
static void MovieOptionsChangedCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	int set = ((XmToggleButtonCallbackStruct *)call_data)->set;
	
	if (set == XmSET)
		movieOptions |= (unsigned)client_data;
	else if (set == XmUNSET)
		movieOptions &= ~(unsigned)client_data;
}

static void SetMovieModeCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	moviemode = (((XmToggleButtonCallbackStruct *)call_data)->set == XmSET);
}

static void CreateMovieOptionsMenu(Widget movieOptionsMenu)
{
	Widget saveRadianceButton, saveUnshotRadianceButton,
	       saveImportanceButton, saveUnshotImportanceButton,
	       saveAltRadianceButton, saveAltUnshotRadianceButton,
	       saveAltImportanceButton, saveAltUnshotImportanceButton,
	       movieModeButton;

	movieOptions = DEFAULT_MOVIE_OPTIONS;

	XtVaSetValues(movieOptionsMenu,
		      XmNradioBehavior, False,
		      NULL);

/* ------------------- Make Movie button ---------------- */
	movieModeButton = XtVaCreateManagedWidget("movieModeButton",
						  xmToggleButtonWidgetClass,
						  movieOptionsMenu,
						  XmNset, DEFAULT_MOVIE_MODE,
						  NULL);
	XtAddCallback(movieModeButton, XmNvalueChangedCallback, SetMovieModeCallback, (XtPointer)NULL);

	saveRadianceButton = XtVaCreateManagedWidget("saveRadianceButton",
						     xmToggleButtonWidgetClass,
						     movieOptionsMenu,
						     XmNset, (DEFAULT_MOVIE_OPTIONS & MOVIE_SAVE_RADIANCE) ? True : False,
						     NULL);
	XtAddCallback(saveRadianceButton, XmNvalueChangedCallback, MovieOptionsChangedCallback, (XtPointer)MOVIE_SAVE_RADIANCE);

	saveUnshotRadianceButton = XtVaCreateManagedWidget("saveUnshotRadianceButton",
						     xmToggleButtonWidgetClass,
						     movieOptionsMenu,
						     XmNset, (DEFAULT_MOVIE_OPTIONS & MOVIE_SAVE_UNSHOT_RADIANCE) ? True : False,
						     NULL);
	XtAddCallback(saveUnshotRadianceButton, XmNvalueChangedCallback, MovieOptionsChangedCallback, (XtPointer)MOVIE_SAVE_UNSHOT_RADIANCE);

	saveImportanceButton = XtVaCreateManagedWidget("saveImportanceButton",
						     xmToggleButtonWidgetClass,
						     movieOptionsMenu,
						     XmNset, (DEFAULT_MOVIE_OPTIONS & MOVIE_SAVE_IMPORTANCE) ? True : False,
						     NULL);
	XtAddCallback(saveImportanceButton, XmNvalueChangedCallback, MovieOptionsChangedCallback, (XtPointer)MOVIE_SAVE_IMPORTANCE);

	saveUnshotImportanceButton = XtVaCreateManagedWidget("saveUnshotImportanceButton",
						     xmToggleButtonWidgetClass,
						     movieOptionsMenu,
						     XmNset, (DEFAULT_MOVIE_OPTIONS & MOVIE_SAVE_UNSHOT_IMPORTANCE) ? True : False,
						     NULL);
	XtAddCallback(saveUnshotImportanceButton, XmNvalueChangedCallback, MovieOptionsChangedCallback, (XtPointer)MOVIE_SAVE_UNSHOT_IMPORTANCE);

	saveAltRadianceButton = XtVaCreateManagedWidget("saveAltRadianceButton",
						     xmToggleButtonWidgetClass,
						     movieOptionsMenu,
						     XmNset, (DEFAULT_MOVIE_OPTIONS & MOVIE_SAVE_ALT_RADIANCE) ? True : False,
						     NULL);
	XtAddCallback(saveAltRadianceButton, XmNvalueChangedCallback, MovieOptionsChangedCallback, (XtPointer)MOVIE_SAVE_ALT_RADIANCE);

	saveAltUnshotRadianceButton = XtVaCreateManagedWidget("saveAltUnshotRadianceButton",
						     xmToggleButtonWidgetClass,
						     movieOptionsMenu,
						     XmNset, (DEFAULT_MOVIE_OPTIONS & MOVIE_SAVE_ALT_UNSHOT_RADIANCE) ? True : False,
						     NULL);
	XtAddCallback(saveAltUnshotRadianceButton, XmNvalueChangedCallback, MovieOptionsChangedCallback, (XtPointer)MOVIE_SAVE_ALT_UNSHOT_RADIANCE);

	saveAltImportanceButton = XtVaCreateManagedWidget("saveAltImportanceButton",
						     xmToggleButtonWidgetClass,
						     movieOptionsMenu,
						     XmNset, (DEFAULT_MOVIE_OPTIONS & MOVIE_SAVE_ALT_IMPORTANCE) ? True : False,
						     NULL);
	XtAddCallback(saveAltImportanceButton, XmNvalueChangedCallback, MovieOptionsChangedCallback, (XtPointer)MOVIE_SAVE_ALT_IMPORTANCE);

	saveAltUnshotImportanceButton = XtVaCreateManagedWidget("saveAltUnshotImportanceButton",
						     xmToggleButtonWidgetClass,
						     movieOptionsMenu,
						     XmNset, (DEFAULT_MOVIE_OPTIONS & MOVIE_SAVE_ALT_UNSHOT_IMPORTANCE) ? True : False,
						     NULL);
	XtAddCallback(saveAltUnshotImportanceButton, XmNvalueChangedCallback, MovieOptionsChangedCallback, (XtPointer)MOVIE_SAVE_ALT_UNSHOT_IMPORTANCE);
}


static void SetDemoModeCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	SetDemoMode(((XmToggleButtonCallbackStruct *)call_data)->set == XmSET);
}


static void ShowGammaDialog(Widget w, XtPointer client_data, XtPointer call_Data)
{
	Widget gammaDialog = (Widget)client_data;

	XtManageChild(gammaDialog);
}

static void GammaCancelCallback(Widget w, XtPointer client_data, XtPointer call_Data)
{
	Widget gammaDialog = XtParent(w);

	XtUnmanageChild(gammaDialog);
}

static void GammaOKCallback(Widget w, XtPointer client_data, XtPointer call_Data)
{
	Widget gammaDialog = XtParent(w);
	float *gamma = (float *)client_data;

	XtUnmanageChild(gammaDialog);
	RGBSetGamma(*gamma);

	PatchListIterate(Patches, PatchComputeVertexColors);
	EliminateTVertices();
	RenderScene();
}

static void CreateGammaCorrectionDialog(Widget gammaDialog)
{
	Widget gammaForm, gammaOkButton, gammaCancelButton;

	static float gamma = DEFAULT_GAMMA;

	XFORMFIELD gammaFormFields[] = {
	        {XFORMFLOAT, (char *)&gamma 	 , "Gamma:"},
		{0	   , NULL		 , NULL}};


	gammaForm = XFormCreate(gammaDialog, gammaFormFields);

	gammaOkButton = XtVaCreateManagedWidget("gammaOKButton",
						xmPushButtonWidgetClass,
						gammaDialog,
						NULL);

	gammaCancelButton = XtVaCreateManagedWidget("gammaCancelButton",
						    xmPushButtonWidgetClass,
						    gammaDialog,
						    NULL);

	XtAddCallback(gammaOkButton, XmNactivateCallback, GammaOKCallback, (XtPointer)&gamma);

	XtAddCallback(gammaCancelButton, XmNactivateCallback, GammaCancelCallback, (XtPointer)NULL);
}

/* + + + + + + + + + + + MGF file format options dialog + + + + + + + + + + + + + + + */
static void MgfIgnoreSidednessCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
	MGFSetIgnoreSidedness(((XmToggleButtonCallbackStruct *)call_data)->set == XmSET);
}

static void MgfNrQuartCircDivCancelCallback(Widget w, XtPointer client_data, XtPointer call_Data)
{
	Widget mgfNrQuartCircDivDialog = XtParent(w);

	XtUnmanageChild(mgfNrQuartCircDivDialog);
}

static void MgfNrQuartCircDivOKCallback(Widget w, XtPointer client_data, XtPointer call_Data)
{
	Widget mgfNrQuartCircDivDialog = XtParent(w);
	int nqcdivs = *((int *)client_data);

	XtUnmanageChild(mgfNrQuartCircDivDialog);
	MGFSetNrQuartCircDivs(nqcdivs);
}

static void CreateMgfNrQuartCircDivDialog(Widget mgfNrQuartCircDivDialog)
{
	Widget mgfNrQuartCircDivForm, mgfNrQuartCircDivOkButton, mgfNrQuartCircDivCancelButton;
	static int nqcdivs = DEFAULT_MGF_NQCDIVS;

	XFORMFIELD mgfNrQuartCircDivFormFields[] = {
	        {XFORMINT  , (char *)&nqcdivs 	 , "Quarter Circle Divisions:"},
		{0	   , NULL		 , NULL}};


	mgfNrQuartCircDivForm = XFormCreate(mgfNrQuartCircDivDialog, mgfNrQuartCircDivFormFields);

	mgfNrQuartCircDivOkButton = XtVaCreateManagedWidget("mgfNrQuartCircDivOKButton",
						xmPushButtonWidgetClass,
						mgfNrQuartCircDivDialog,
						NULL);

	mgfNrQuartCircDivCancelButton = XtVaCreateManagedWidget("mgfNrQuartCircDivCancelButton",
						    xmPushButtonWidgetClass,
						    mgfNrQuartCircDivDialog,
						    NULL);

	XtAddCallback(mgfNrQuartCircDivOkButton, XmNactivateCallback, MgfNrQuartCircDivOKCallback, (XtPointer)&nqcdivs);

	XtAddCallback(mgfNrQuartCircDivCancelButton, XmNactivateCallback, MgfNrQuartCircDivCancelCallback, (XtPointer)NULL);	
}

static void ShowMgfNrQuartCircDivDialog(Widget button, XtPointer client_data, XtPointer call_data)
{
	Widget mgfNrQuartCircDivDialog = (Widget)client_data;

	XtManageChild(mgfNrQuartCircDivDialog);
}

static void CreateMgfOptionsMenu(Widget mgfOptionsMenu)
{
	Widget mgfNrQuartCircDivButton, mgfNrQuartCircDivDialog,
	       mgfIgnoreSidednessButton;

/* - - - - - - - - - Ignore Sidedness button - - - - - - - - - - - - - - - - */
	mgfIgnoreSidednessButton = XtVaCreateManagedWidget("mgfIgnoreSidednessButton",
						   xmToggleButtonWidgetClass,
						   mgfOptionsMenu,
						   XmNset, DEFAULT_MGF_IGNORE_SIDEDNESS ? XmSET : XmUNSET,
						   NULL);

	XtAddCallback(mgfIgnoreSidednessButton, XmNvalueChangedCallback, MgfIgnoreSidednessCallback, (XtPointer)NULL);

/* - - - - - - - - - Number of Quarter Circle Divisions button - - - - - - - */
	mgfNrQuartCircDivButton = XtVaCreateManagedWidget("mgfNrQuartCircDivButton",
						   xmPushButtonWidgetClass,
						   mgfOptionsMenu,
						   NULL);

	mgfNrQuartCircDivDialog = XmCreateTemplateDialog(mgfNrQuartCircDivButton, "mgfNrQuartCircDivDialog", visargs, nrvisargs);

	CreateMgfNrQuartCircDivDialog(mgfNrQuartCircDivDialog);

	XtAddCallback(mgfNrQuartCircDivButton, XmNactivateCallback, ShowMgfNrQuartCircDivDialog, (XtPointer)mgfNrQuartCircDivDialog);
}

static void CreateOptionsMenu(Widget menuBar)
{
	Widget optionsButton, optionsMenu, demoButton,
	       outlineColorButton, outlineColorBox, outlineColorForm, 
	       outlineColorOKButton, outlineColorCancelButton,
	       linkColorButton, linkColorBox, linkColorForm, 
	       linkColorOKButton, linkColorCancelButton,
	       movieOptionsButton, movieOptionsMenu,
	       gammaButton, gammaDialog,
	       mgfOptionsButton, mgfOptionsMenu;

	static RGB outlineColor = DEFAULT_OUTLINECOLOR,
	           linkColor = DEFAULT_LINKCOLOR;

	XFORMFIELD outlineColorFormFields[] = {
		{XFORMFLOAT, (char *)&outlineColor.r, "Red"},
		{XFORMFLOAT, (char *)&outlineColor.g, "Green"},
		{XFORMFLOAT, (char *)&outlineColor.b, "Blue"},
		{0	   , NULL		    , NULL}};

	XFORMFIELD linkColorFormFields[] = {
		{XFORMFLOAT, (char *)&linkColor.r, "Red"},
		{XFORMFLOAT, (char *)&linkColor.g, "Green"},
		{XFORMFLOAT, (char *)&linkColor.b, "Blue"},
		{0	   , NULL		 , NULL}};

	optionsButton = XtVaCreateManagedWidget("optionsButton",
						xmCascadeButtonWidgetClass,
						menuBar,
						NULL);

	optionsMenu = XmCreatePulldownMenu(menuBar, "optionsMenu", visargs, nrvisargs);

	XtVaSetValues(optionsButton,
		    XmNsubMenuId, optionsMenu,
		    NULL);

/* ---------------------- MGF file format options ------------------- */
	mgfOptionsButton = XtVaCreateManagedWidget("mgfOptionsButton",
						   xmCascadeButtonWidgetClass,
						   optionsMenu,
						   NULL);

	mgfOptionsMenu = XmCreatePulldownMenu(optionsMenu, "mgfOptionsMenu", visargs, nrvisargs);

	CreateMgfOptionsMenu(mgfOptionsMenu);

	XtVaSetValues(mgfOptionsButton,
		      XmNsubMenuId, mgfOptionsMenu,
		      NULL);

/* ----------------------- Movie mode button ----------------- */
	movieOptionsButton = XtVaCreateManagedWidget("movieOptionsButton",
					      xmCascadeButtonWidgetClass,
					      optionsMenu,
					      NULL);

	movieOptionsMenu = XmCreatePulldownMenu(optionsMenu, "movieOptionsMenu", visargs, nrvisargs);

	CreateMovieOptionsMenu(movieOptionsMenu);

	XtVaSetValues(movieOptionsButton,
		      XmNsubMenuId, movieOptionsMenu,
		      NULL);

/* ----------------------- Gamma Correction button ------------------- */
	gammaButton = XtVaCreateManagedWidget("gammaButton",
					      xmPushButtonWidgetClass,
					      optionsMenu,
					      NULL);

	gammaDialog = XmCreateTemplateDialog(gammaButton, "gammaDialog", visargs, nrvisargs);

	CreateGammaCorrectionDialog(gammaDialog);

	XtAddCallback(gammaButton, XmNactivateCallback, ShowGammaDialog, (XtPointer)gammaDialog);

/* ----------------------- Outline color button -------------------- */
	outlineColorButton = XtVaCreateManagedWidget("outlineColorButton",
						     xmPushButtonWidgetClass,
						     optionsMenu,
						     NULL);

/* dialoog box om kleur van randjes in te vullen */
	outlineColorBox = XmCreateTemplateDialog(outlineColorButton, "outlineColorDialog", visargs, nrvisargs);

	outlineColorForm = XFormCreate(outlineColorBox, outlineColorFormFields);

	outlineColorOKButton = XtVaCreateManagedWidget("outlineColorOKButton",
						       xmPushButtonWidgetClass,
						       outlineColorBox,
						       NULL);

	outlineColorCancelButton = XtVaCreateManagedWidget("outlineColorCancelButton",
						    xmPushButtonWidgetClass,
						    outlineColorBox,
						    NULL);


	XtAddCallback(outlineColorOKButton, XmNactivateCallback, OutlineColorOKCallback, (XtPointer)&outlineColor);

	XtAddCallback(outlineColorCancelButton, XmNactivateCallback, OutlineColorCancelCallback, (XtPointer)NULL);

	XtAddCallback(outlineColorButton, XmNactivateCallback, ShowOutlineColorBox, (XtPointer)outlineColorBox);

/* ----------------------- Link color button -------------------- */
	linkColorButton = XtVaCreateManagedWidget("linkColorButton",
						     xmPushButtonWidgetClass,
						     optionsMenu,
						     NULL);

/* dialoog box om kleur van randjes in te vullen */
	linkColorBox = XmCreateTemplateDialog(linkColorButton, "linkColorDialog", visargs, nrvisargs);

	linkColorForm = XFormCreate(linkColorBox, linkColorFormFields);

	linkColorOKButton = XtVaCreateManagedWidget("linkColorOKButton",
						       xmPushButtonWidgetClass,
						       linkColorBox,
						       NULL);

	linkColorCancelButton = XtVaCreateManagedWidget("linkColorCancelButton",
						    xmPushButtonWidgetClass,
						    linkColorBox,
						    NULL);

	XtAddCallback(linkColorOKButton, XmNactivateCallback, LinkColorOKCallback, (XtPointer)&linkColor);

	XtAddCallback(linkColorCancelButton, XmNactivateCallback, LinkColorCancelCallback, (XtPointer)NULL);

	XtAddCallback(linkColorButton, XmNactivateCallback, ShowLinkColorBox, (XtPointer)linkColorBox);

/* ----------------------- Demo Mode button -------------------- */
	demoButton = XtVaCreateManagedWidget("demoButton",
					     xmToggleButtonWidgetClass,
					     optionsMenu,
					     XmNset, DEFAULT_DEMOMODE,
					     NULL);

	XtAddCallback(demoButton, XmNvalueChangedCallback, SetDemoModeCallback, (XtPointer)NULL);
}

#ifdef DEBUG
/* **************************** debug menu *********************************** */
static void DumpWorld(Widget dumpWorldButton, XtPointer client_data, XtPointer call_data)
{
	GeomListPrint(stderr, World);
}

static void DoDumpPatch(PATCH *P, POINT *hitp)
{
	if (P->parent) 
		fprintf(stderr, "parent patch ID: %d\n", P->parent->id);

	if (PointInPolygon(P, hitp)) PatchPrint(stderr, P);
	PatchListIterate1A(P->subpatches, DoDumpPatch, hitp);
}

static void DumpPatch(Widget dumpPatchButton, XtPointer client_data, XtPointer call_data)
{
	SelectPatchSetCallback(DoDumpPatch);
	CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void DoShowRadianceAtPoint(PATCH *P, POINT *hitp)
{
	float u, v;
	PATCH *lowestlevel;
	COLOR rad;

	PatchUV(P, hitp, &u, &v);
	lowestlevel = LowestLevelPatch(P, &u, &v);
	rad = lowestlevel->basis->GetValueAtPoint(lowestlevel->radiance, u, v);

	fprintf(stderr, "Radiance at point ");
	PointPrint(stderr, *hitp);
	fprintf(stderr, " = ");
	COLORPrint(stderr, rad);
	fprintf(stderr, " W/m^2sr RGB\n");
}

static void ShowRadianceAtPoint(Widget dumpPatchButton, XtPointer client_data, XtPointer call_data)
{
	SelectPatchSetCallback(DoShowRadianceAtPoint);
	CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void DoPlotPatchRadiance(PATCH *P, POINT *hitp)
{
	float u, v, lu, lv, ustep, vstep;
	FILE *fp;
	char *dumpfilename = "rad.plot";
	int n, m; 
	COLOR rad;
	PATCH *lowestlevel;

        if ((fp = fopen(dumpfilename, "w")) == (FILE *)NULL) {
		fprintf(stderr, "Can't open %s:\n", dumpfilename);
		perror(dumpfilename);
		return;
	}

/*	fprintf(stderr, "Enter nr of steps for plotting u and v (two integers):\n");
	scanf("%d %d", &m, &n); */
	m = 40; n = 40;
	ustep = 1./((float)m);
	vstep = 1./((float)n);

	fprintf(fp, "# radiance on patch id %d, %s basis\n", P->id, P->basis->description);
	for (u=0.; u<=1.; u+=ustep) {
		for (v=0.; v<=1.; v+=vstep) {
			if (P->nrvertices == 3 && (u+v) > 1.)
				fprintf(fp, "%g\n", 0.0);
			else {
				lu = u; lv = v;
				lowestlevel = LowestLevelPatch(P, &lu, &lv);
				rad = lowestlevel->basis->GetValueAtPoint(lowestlevel->radiance, lu, lv);
				fprintf(fp, "%g\n", rad.r + rad.g + rad.b);
			}
		}
		fprintf(fp, "\n");
	}
		
	fclose(fp);
}

static void PlotPatchRadiance(Widget dumpPatchButton, XtPointer client_data, XtPointer call_data)
{
	SelectPatchSetCallback(DoPlotPatchRadiance);
	CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void DoMonitorSelect(PATCH *P, POINT *hitp)
{
	MonitorSelectPatch(P);
}

static void MonitorSelect(Widget monitorSelectButton, XtPointer client_data, XtPointer call_data)
{
	SelectPatchSetCallback(DoMonitorSelect);
	CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void DoMonitorUnselect(PATCH *P, POINT *hitp)
{
	MonitorUnselectPatch(P);
}

static void MonitorUnselect(Widget monitorUnselectButton, XtPointer client_data, XtPointer call_data)
{
	SelectPatchSetCallback(DoMonitorUnselect);
	CanvasPushMode(CANVASMODE_SELECT_PATCH);
}

static void CreateDebugMenu(Widget menuBar)
{
	Widget debugMenu, debugButton, dumpWorldButton, 
	       dumpPatchButton, showRadianceAtPointButton, plotPatchRadianceButton,
	       monitorSelectButton, monitorUnselectButton;
	Widget oneIterationButton;

	debugButton = XtVaCreateManagedWidget("debugButton",
					      xmCascadeButtonWidgetClass,
					      menuBar,
					      NULL);

	debugMenu = XmCreatePulldownMenu(menuBar, "debugMenu", visargs, nrvisargs);

	XtVaSetValues(debugButton,
		      XmNsubMenuId, debugMenu,
		      NULL);
	
/* ------------------- One Iteration button ---------------- */
	oneIterationButton = XtVaCreateManagedWidget("oneIterationButton",
						xmPushButtonWidgetClass,
						debugMenu,
						NULL);

	XtAddCallback(oneIterationButton, XmNactivateCallback, OneIterationCallback, (XtPointer)NULL);

/* ------------------- Show Radiance At Point button ---------------- */
	showRadianceAtPointButton = XtVaCreateManagedWidget("showRadianceAtPointButton",
						  xmPushButtonWidgetClass,
						  debugMenu,
						  NULL);

	XtAddCallback(showRadianceAtPointButton, XmNactivateCallback, ShowRadianceAtPoint, (XtPointer)NULL);

/* ------------------- Plot Patch Radiance button ---------------- */
	plotPatchRadianceButton = XtVaCreateManagedWidget("plotPatchRadianceButton",
						  xmPushButtonWidgetClass,
						  debugMenu,
						  NULL);

	XtAddCallback(plotPatchRadianceButton, XmNactivateCallback, PlotPatchRadiance, (XtPointer)NULL);

/* ------------------- Dump World button ---------------- */
	dumpWorldButton = XtVaCreateManagedWidget("dumpWorldButton",
						  xmPushButtonWidgetClass,
						  debugMenu,
						  NULL);

	XtAddCallback(dumpWorldButton, XmNactivateCallback, DumpWorld, (XtPointer)NULL);

/* ------------------- Dump Patch button ---------------- */
	dumpPatchButton = XtVaCreateManagedWidget("dumpPatchButton",
						  xmPushButtonWidgetClass,
						  debugMenu,
						  NULL);

	XtAddCallback(dumpPatchButton, XmNactivateCallback, DumpPatch, (XtPointer)NULL);

/* ------------------- Monitor Select button ---------------- */
	monitorSelectButton = XtVaCreateManagedWidget("monitorSelectButton",
						      xmPushButtonWidgetClass,
						      debugMenu,
						      NULL);

	XtAddCallback(monitorSelectButton, XmNactivateCallback, MonitorSelect, (XtPointer)NULL);

/* ------------------- Monitor Unselect button ---------------- */
	monitorUnselectButton = XtVaCreateManagedWidget("monitorUnselectButton",
						      xmPushButtonWidgetClass,
						      debugMenu,
						      NULL);

	XtAddCallback(monitorUnselectButton, XmNactivateCallback, MonitorUnselect, (XtPointer)NULL);
}
#endif /*DEBUG*/

/*************************** Help menu ******************************************/
/* wat er gebeurt als je op de Help knop in het Help menu drukt: de gepaste dialoog
 * wordt getoond */
static void ShowHelp(Widget help, XtPointer client_data, XtPointer call_data)
{
	Widget helpBox = (Widget)client_data;

	XtManageChild(helpBox);
}

/* creeert het Help menu */
static void CreateHelpMenu(Widget menuBar)
{
	Widget helpButton, helpMenu, help, helpBox, 
	       helpScrollW, helpLabel, temp;

/* creeer een help button */
	helpButton = XtVaCreateManagedWidget("helpButton",
					     xmCascadeButtonWidgetClass,
					     menuBar,
					     NULL);

/* Help button wordt afzonderlijk gepositioneerd */
	XtVaSetValues(menuBar,
		      XmNmenuHelpWidget, helpButton,
		      NULL);

/* help menu: eigenlijk een Shell - en RowColumn widget combo */
	helpMenu = XmCreatePulldownMenu(menuBar, "helpMenu", visargs, nrvisargs);

/* help button in help menu */
	help = XtVaCreateManagedWidget("helpMenu",
				       xmPushButtonWidgetClass,
				       helpMenu,
				       NULL);

/* specifieren welk menu ge-popup-t wordt door de help knop */
	XtVaSetValues(helpButton,
		      XmNsubMenuId, helpMenu,
		      NULL);

/* creeer de message dialog die de help tekst moet tonen */
	helpBox = XmCreateMessageDialog(help, "helpBox", visargs, nrvisargs);

/* CANCEL en HELP knop op het message dialog niet laten zien */
	temp = XmMessageBoxGetChild(helpBox, XmDIALOG_CANCEL_BUTTON);
	XtUnmanageChild(temp);
	temp = XmMessageBoxGetChild(helpBox, XmDIALOG_HELP_BUTTON);
	XtUnmanageChild(temp);

/* een scrolled window, met enkel een verticale scrollbar en een label widget waarin
 * de help tekst getoond wordt */
	helpScrollW = XtVaCreateManagedWidget("helpScrollW",
					      xmScrolledWindowWidgetClass,
					      helpBox,
					      XmNscrollingPolicy, XmAUTOMATIC,
					      XmNscrollBarDisplayPolicy, XmAS_NEEDED,
					      NULL);

/*	helpScrollBar = XtVaCreateManagedWidget("helpScrollBar",
						xmScrollBarWidgetClass,
						helpScrollW,
						NULL);
*/
	helpLabel = XtVaCreateManagedWidget("helpLabel",
					    xmLabelWidgetClass,
					    helpScrollW,
					    XmNalignment, XmALIGNMENT_BEGINNING,
					    NULL);

	XtVaSetValues(helpScrollW,
		    XmNworkWindow, helpLabel,
		    NULL);

/* help knop moet de helptekst laten zien */
	XtAddCallback(help, XmNactivateCallback, ShowHelp, (XtPointer)helpBox);
}

/*************************** menubar ******************************************/
/* creeert de menu's oproepbaar via de knoppenbalk van het MainWIndow */
static void CreateMainMenu(Widget menuBar)
{
	CreateFileMenu(menuBar);
	CreateAlgorithmMenu(menuBar);
	CreateRenderMenu(menuBar);
	CreatePostPassMenu(menuBar);
	CreateCameraMenu(menuBar);
	CreateOptionsMenu(menuBar);
#ifdef DEBUG
	CreateDebugMenu(menuBar);
#endif
	CreateHelpMenu(menuBar); 
}

int main(int argc, char **argv)
{
	Widget mainWindow, menuBar;
	XSetWindowAttributes wattrs;

/* diverse initialisaties */
	Init();

/* en de rest heeft allemaal te maken met de GUI: */
/* top level apllication shell widget creeeren - opties etc... behandelen */
	topLevel = XtVaAppInitialize(&app_context,	/* application context */
				     APP_CLASS_NAME,	/* application class name */
				     NULL, 0,		/* command line option list */
				     &argc, argv,	/* command line args */
				     NULL,		/* for missing appdefaults file */
				     NULL);		/* terminate varargs list */

/* bepaald beste X visual en bijhorend standaard kleurenpalet */
	find_best_visual_and_colormap(XtDisplay(topLevel), 
				      &best_visual,
				      &std_cmap);

/* Set the visual, depth, and colormap for the shell - deze argumenten moeten helaas
 * ook opgegeven worden bij de creatie van iedere popup of dialoog */
	XtSetArg(visargs[0], XtNvisual, best_visual.visual);
	XtSetArg(visargs[1], XtNdepth, best_visual.depth);
	XtSetArg(visargs[2], XtNcolormap, std_cmap.colormap);
	nrvisargs = 3;
	XtSetValues(topLevel, visargs, nrvisargs);

/* create main window */
	mainWindow = XtVaCreateManagedWidget("mainWindow",	/* widget name */
					     xmFormWidgetClass,	/* class */
					     topLevel,		/* parent widget */
					     NULL);		/* terminate varargs list */

/* menubar bovenaan main window */
	menuBar = XmCreateMenuBar(mainWindow,	/* parent widget */
				  "menuBar",	/* widget name */
				  NULL,		/* geen argumenten */
				  0);		/* aantal argumenten = 0 */

/* creeer de menu's op de knoppenbalk */
	CreateMainMenu(menuBar);
	XtVaSetValues(menuBar,
		      XmNtopAttachment, XmATTACH_FORM,
		      XmNleftAttachment, XmATTACH_FORM,
		      XmNrightAttachment, XmATTACH_FORM,
		      NULL);
	XtManageChild(menuBar);

/* creeer window waarin gerenderd zal worden */
	canvas = RenderCreateWindow(mainWindow);
	XtVaSetValues(canvas,
		      XmNtopAttachment, XmATTACH_WIDGET,
		      XmNtopWidget, menuBar,
		      XmNleftAttachment, XmATTACH_FORM,
		      XmNrightAttachment, XmATTACH_FORM,
		      XmNbottomAttachment, XmATTACH_FORM,
		      NULL);

/* realizeer alle widgets - breng op het scherm */
	XtRealizeWidget(topLevel);

/* backing store attribuut van canvas window op NotUseful instellen */
	wattrs.backing_store = NotUseful;
	XChangeWindowAttributes(XtDisplay(canvas), XtWindow(canvas), CWBackingStore, &wattrs);

	CanvasInit(canvas);
	InitError(topLevel, visargs, nrvisargs);
	InitMessage(topLevel, visargs, nrvisargs);

/* oneindige lus om gebeurtenissen te onderscheppen/behandelen */
	XtAppMainLoop(app_context);

	return 0;
}


