/*
    This file is part of the FElt finite element analysis package.
    Copyright (C) 1993 Jason I. Gobat and Darren C. Atkinson

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/************************************************************************
 * File:	constraint.c						*
 *									*
 * Description:	This file contains the public and private functions and	*
 *		type definitions for the constraint dialog box.		*
 ************************************************************************/

# include <stdio.h>
# include <stdlib.h>
# include <X11/Intrinsic.h>
# include <X11/StringDefs.h>
# include <X11/Shell.h>
# include <X11/Xaw/AsciiText.h>
# include <X11/Xaw/Command.h>
# include <X11/Xaw/Label.h>
# include <X11/Xaw/List.h>
# include <X11/Xaw/Toggle.h>
# include <X11/Xaw/Viewport.h>
# include "Layout.h"
# include "Constraint.h"
# include "TabGroup.h"
# include "util.h"
# include "fe.h"
# include "objects.h"


struct constraint_dialog {
    Widget         shell;	/* topLevelShell  <specified>	 */
    Widget         layout;	/*	Layout  layout		 */
    Widget         name;	/*	     AsciiText  name	 */
    Widget         trans_x;	/*	     Toggle  transX	 */
    Widget         trans_y;	/*	     Toggle  transY	 */
    Widget         trans_z;	/*	     Toggle  transZ	 */
    Widget         rot_x;	/*	     Toggle  rotX	 */
    Widget         rot_y;	/*	     Toggle  rotY	 */
    Widget         rot_z;	/*	     Toggle  rotZ	 */
    Widget	   dx_tx;	/*	     AsciiText  dxTransX */
    Widget	   dx_ty;	/*	     AsciiText  dxTransY */
    Widget	   dx_tz;	/*	     AsciiText  dxTransZ */
    Widget	   dx_rx;	/*	     AsciiText  dxRotZ	 */
    Widget	   dx_ry;	/*	     AsciiText  dxRotY	 */
    Widget	   dx_rz;	/*	     AsciiText  dxRotZ	 */
    Widget         viewport;	/*	     Viewport  viewport	 */
    Widget         list;	/*		  List  list	 */
    Widget         help;	/*	     MenuButton  help	 */
    Widget         accept;	/*	     Command  accept	 */
    Widget         dismiss;	/*	     Command  dismiss	 */
    Widget         delete;	/*	     Command  delete	 */
    Widget         new;		/*	     Command  new	 */
    Widget         copy;	/*	     Command  copy	 */
    XtCallbackProc callback;
    XtPointer	   closure;
    String        *constraints;
    Constraint     active;
    Boolean        new_copy;
    Tree           tree;
};

static String labels [ ] = {
    "Name:", "X:", "Y:", "Z:", "Translation", "Rotation"
};

static String names [ ] = {
    "nameLabel", "x", "y", "z", "translation", "rotation"
};

static ConstraintDialog dialog;
static Cardinal	   	num_constraints;
static int	   	list_index;


/* Resources */

static Pixel highlight;

static String dummy_list [ ] = {
    NULL
};

static String layout_string =
"vertical { \
     horizontal { \
 	4 \
 	vertical { \
	    4 \
 	    horizontal { \
 		nameLabel \
 		4 \
 		name <+inf -100% *> \
 	    } \
 	    4 \
 	    viewport <+inf * +inf> \
 	    4 \
 	} \
 	4 \
	separator1 <* +inf -100%> \
	4 \
 	vertical { \
 	    4 \
	    ((height name - height translation) / 2) \
            height translation \
	    ((height name - height translation) / 2) \
	    4 \
	    ((height transX - height x) / 2) \
 	    x \
	    ((height transX - height x) / 2) \
	    4 \
	    ((height transY - height y) / 2) \
 	    y \
	    ((height transY - height y) / 2) \
	    4 \
	    ((height transZ - height z) / 2) \
 	    z \
	    ((height transZ - height z) / 2) \
 	    4 \
 	} \
 	4 \
 	vertical { \
 	    4 \
	    ((height name - height translation) / 2) \
	    horizontal { 1 <+inf> translation 1 <+inf> } \
	    ((height name - height translation) / 2) \
	    4 \
	    horizontal { transX 4 dxTransX } \
	    4 \
	    horizontal { transY 4 dxTransY } \
	    4 \
	    horizontal { transZ 4 dxTransZ } \
	    4 \
 	} \
 	4 \
 	vertical { \
 	    4 \
	    ((height name - height rotation) / 2) \
	    horizontal { 1 <+inf> rotation 1 <+inf> } \
	    ((height name - height rotation) / 2) \
	    4 \
	    horizontal { rotX 4 dxRotX } \
	    4 \
	    horizontal { rotY 4 dxRotY } \
	    4 \
	    horizontal { rotZ 4 dxRotZ } \
	    4 \
 	} \
 	4 \
     } \
     separator2 <+inf -100% *> \
     4 \
     horizontal { \
 	4 \
 	help \
 	4 <+inf -100%> \
 	accept \
 	4 <+inf -100%> \
 	dismiss \
 	4 <+inf -100%> \
 	delete \
 	4 <+inf -100%> \
 	new \
 	4 <+inf -100%> \
 	copy \
 	4 \
     } \
     4 \
}";

static Arg color_args [ ] = {
    {XtNborderColor, (XtArgVal) &highlight},
};

static Arg shell_args [ ] = {
    {XtNtitle,    (XtArgVal) NULL},
    {XtNiconName, (XtArgVal) NULL},
};

static Arg layout_args [ ] = {
    {XtNlayout, (XtArgVal) NULL},
};

static Arg viewport_args [ ] = {
    {XtNallowVert,   (XtArgVal) True},
    {XtNforceBars,   (XtArgVal) True},
    {XtNborderWidth, (XtArgVal) 0},
};

static Arg list_args [ ] = {
    {XtNdefaultColumns, (XtArgVal) 1},
    {XtNforceColumns,   (XtArgVal) 1},
    {XtNresize,		(XtArgVal) True},
    {XtNlist,		(XtArgVal) dummy_list},
};

static Arg text_args [ ] = {
    {XtNeditType,    (XtArgVal) XawtextEdit},
    {XtNborderWidth, (XtArgVal) 0},
    {XtNpieceSize,   (XtArgVal) 32},
    {XtNcursorName,  (XtArgVal) "left_ptr"},
};

static Arg label_args [ ] = {
    {XtNlabel,       (XtArgVal) ""},
    {XtNborderWidth, (XtArgVal) 0},
};

static Arg toggle_args [ ] = {
    {XtNlabel,	     (XtArgVal) " "},
};

static Arg core_args [ ] = {
    {XtNwidth,  (XtArgVal) 1},
    {XtNheight, (XtArgVal) 1},
};


/* Translation tables */

static String text_table =
"<Key>Return: ConstraintDialogAction(accept)\n\
 <Key>Escape: ConstraintDialogAction(dismiss)\n\
 Ctrl<Key>d:  ConstraintDialogAction(delete)\n\
 Ctrl<Key>c:  ConstraintDialogAction(copy)\n\
 Ctrl<Key>n:  ConstraintDialogAction(new)\n\
 Ctrl<Key>h:  ConstraintDialogAction(help)\n\
 <Btn1Down>:  SetFocus() select-start()";

static XtTranslations text_translations;


static String command_table =
"<Key>Return:  ConstraintDialogAction(accept)\n\
 <Key>Escape:  ConstraintDialogAction(dismiss)\n\
 Ctrl<Key>d:   ConstraintDialogAction(delete)\n\
 Ctrl<Key>c:   ConstraintDialogAction(copy)\n\
 Ctrl<Key>n:   ConstraintDialogAction(new)\n\
 Ctrl<Key>h:   ConstraintDialogAction(help)\n\
 <Key>space:   AutoRepeat(off) set()\n\
 <KeyUp>space: AutoRepeat(saved) notify() unset()";

static XtTranslations command_translations;


static String toggle_table = 
"<Key>Return: ConstraintDialogAction(accept)\n\
 <Key>Escape: ConstraintDialogAction(dismiss)\n\
 Ctrl<Key>d:  ConstraintDialogAction(delete)\n\
 Ctrl<Key>c:  ConstraintDialogAction(copy)\n\
 Ctrl<Key>n:  ConstraintDialogAction(new)\n\
 Ctrl<Key>h:  ConstraintDialogAction(help)\n\
 <Key>space:  toggle() notify()";

static XtTranslations toggle_translations;


static String viewport_table =
"<Key>Return: ConstraintDialogAction(accept)\n\
 <Key>Escape: ConstraintDialogAction(dismiss)\n\
 Ctrl<Key>d:  ConstraintDialogAction(delete)\n\
 Ctrl<Key>c:  ConstraintDialogAction(copy)\n\
 Ctrl<Key>n:  ConstraintDialogAction(new)\n\
 Ctrl<Key>h:  ConstraintDialogAction(help)\n\
 <Btn1Down>:  SetFocus()";

static XtTranslations viewport_translations;


static String help_table =
"<Key>Return: ConstraintDialogAction(accept)\n\
 <Key>Escape: ConstraintDialogAction(dismiss)\n\
 Ctrl<Key>d:  ConstraintDialogAction(delete)\n\
 Ctrl<Key>c:  ConstraintDialogAction(copy)\n\
 Ctrl<Key>n:  ConstraintDialogAction(new)\n\
 Ctrl<Key>h:  ConstraintDialogAction(help)\n\
 <Key>space:  PostMenu()";

static XtTranslations help_translations;


/* Help message. */

static String help_message ="\
The constraint form is used to define, edit and delete constraints.  The name \
box displays the name of the current constraint and can be used to name (or \
rename) a constraint.  Use the list to change the current constraint.  The \
buttons to the right define which DOFs are constrained. \
If a DOF is checked, then the corresponding entry box will also \
be used.  If it is blank or 0.0 a fixed condition will be assumed.  \
If it contains \
a number, this DOF will be treated as a displacement BC.  You can also put the \
keyword 'hinge' in these boxes to define a hinged BC. Use \
the 'Accept' button to register your changes.  'Delete' erases the current \
constraint.  'New' empties all fields.  'Copy' empties the name field only.";


/************************************************************************
 * Function:	ToggleButtonSetState					*
 *									*
 * Description:	Sets the state of the specified toggle button.		*
 ************************************************************************/

static void ToggleButtonSetState (w, state)
   Widget	w;
   Boolean 	state;
{
   Arg		arglist [1]; 


   XtSetArg (arglist [0], XtNstate, state);
   XtSetValues (w, arglist, 1);
}


/************************************************************************
 * Function:	ToggleButtonGetState					*
 *									*
 * Description:	Retrieves the state of the specified toggle button.	*
 ************************************************************************/

static Boolean ToggleButtonGetState (w)
   Widget	w;
{
   Arg		arglist [1];
   Boolean	state;


   XtSetArg (arglist [0], XtNstate, &state);
   XtGetValues (w, arglist, 1);

   return state;
}


/************************************************************************
 * Function:	SetSensitive						*
 *									*
 * Description:	Works just like XtSetSensitive but moves the keyboard	*
 *		focus to the specified widget if necessary.		*
 ************************************************************************/

static void SetSensitive (w, value, move_to)
    Widget  w;
    Boolean value;
    Widget  move_to;
{
    if (value == False && HasFocus (w) == True)
	SetFocus (move_to);

    if (value == False)
	SetTextString (w, "");

    XtSetSensitive (w, value);
}


/************************************************************************
 * Function:	Toggle							*
 *									*
 * Description:	Adjusts the sensitivity of the corresponding text	*
 *		entry when a toggle widget is toggled.			*
 ************************************************************************/

static void Toggle (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    Widget text;


    text = (Widget) client_data;

    SetSensitive (text, ToggleButtonGetState (w), w);
}


/************************************************************************
 * Function:	AppendConstraintName					*
 *									*
 * Description:	Appends the constraint name to the array of names.  The	*
 *		index of the active constraint is also set.		*
 ************************************************************************/

static int AppendConstraintName (item)
    Item item;
{
    if (dialog -> active == (Constraint) item)
	list_index = num_constraints;

    dialog -> constraints [num_constraints ++] = ((Constraint) item) -> name;
    return 0;
}


/************************************************************************
 * Function:	Action							*
 *									*
 * Description:	An action procedure which emulates pressing of the	*
 *		specified button.						*
 ************************************************************************/

static void Action (w, event, params, num_params)
    Widget    w;
    XEvent   *event;
    String   *params;
    Cardinal *num_params;
{
    if (XtClass (w) == topLevelShellWidgetClass)
	w = XtNameToWidget (w, "layout.dismiss");
    else
	w = XtNameToWidget (XtParent (w), params [0]);

    if (!strcmp (XtName (w), "help"))
	XtCallActionProc (w, "PostMenu", event, NULL, 0);
    else
	XtCallCallbacks (w, XtNcallback, NULL);
}


/************************************************************************
 * Function:	Change							*
 *									*
 * Description:	Changes the displayed values to either the currently	*
 *		selected constraint if the widget is not null, or the 	*
 *		activeconstraint if the widget is null.  The newly 	*
 *		displayed constraint is made the active constraint and  * 
 *		any new/copy operation is canceled.			*
 ************************************************************************/

static void Change (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    char		 buffer [32];
    Constraint		 active;
    struct constraint	 dummy;
    ConstraintDialog	 constraintd;
    XawListReturnStruct	*info;


    constraintd = (ConstraintDialog) client_data;


    /* Retrieve the active constraint from the tree if selected. */

    if (w != NULL) {
	info = (XawListReturnStruct *) call_data;
	if (info -> list_index == XAW_LIST_NONE)
	    return;

	dummy.name = info -> string;
	constraintd -> active = (Constraint) TreeSearch (constraintd -> tree, 
                                                         &dummy);
    }

    active = constraintd -> active;
    constraintd -> new_copy = False;


    /* Update the name. */

    SetTextString (constraintd -> name, active -> name);


    /* Update the toggle buttons. */

    ToggleButtonSetState (constraintd -> trans_x, active -> constraint[1] != 0);
    ToggleButtonSetState (constraintd -> trans_y, active -> constraint[2] != 0);
    ToggleButtonSetState (constraintd -> trans_z, active -> constraint[3] != 0);
    ToggleButtonSetState (constraintd -> rot_x, active -> constraint[4] != 0);
    ToggleButtonSetState (constraintd -> rot_y, active -> constraint[5] != 0);
    ToggleButtonSetState (constraintd -> rot_z, active -> constraint[6] != 0);


    /* Update the text entries. */

    if (active -> constraint [Tx] == 1) {
	SetSensitive (constraintd -> dx_tx, True, NULL);
	sprintf (buffer, (active -> dx [Tx] ? "%g" : ""), active -> dx [Tx]);
	SetTextString (constraintd -> dx_tx, buffer);
    } else {
	SetTextString (constraintd -> dx_tx, "");
	SetSensitive (constraintd -> dx_tx, False, constraintd -> trans_x);
    }

    if (active -> constraint [Ty] == 1) {
	SetSensitive (constraintd -> dx_ty, True, NULL);
	sprintf (buffer, (active -> dx [Ty] ? "%g" : ""), active -> dx [Ty]);
	SetTextString (constraintd -> dx_ty, buffer);
    } else {
	SetTextString (constraintd -> dx_ty, "");
	SetSensitive (constraintd -> dx_ty, False, constraintd -> trans_y);
    }

    if (active -> constraint [Tz] == 1) {
	SetSensitive (constraintd -> dx_tz, True, NULL);
	sprintf (buffer, (active -> dx [Tz] ? "%g" : ""), active -> dx [Tz]);
	SetTextString (constraintd -> dx_tz, buffer);
    } else {
	SetTextString (constraintd -> dx_tz, "");
	SetSensitive (constraintd -> dx_tz, False, constraintd -> trans_z);
    }

    if (active -> constraint [Rx] == 1) {
	SetSensitive (constraintd -> dx_rx, True, NULL);
	sprintf (buffer, (active -> dx [Rx] ? "%g" : ""), active -> dx [Rx]);
	SetTextString (constraintd -> dx_rx, buffer);
    } else {
	SetTextString (constraintd -> dx_rx, "");
	SetSensitive (constraintd -> dx_rx, False, constraintd -> rot_x);
    }

    if (active -> constraint [Ry] == 1) {
	SetSensitive (constraintd -> dx_ry, True, NULL);
	sprintf (buffer, (active -> dx [Ry] ? "%g" : ""), active -> dx [Ry]);
	SetTextString (constraintd -> dx_ry, buffer);
    } else if (active -> constraint [Ry] == 'h') {
	SetSensitive (constraintd -> dx_ry, True, NULL);
	SetTextString (constraintd -> dx_ry, "hinge");
    } else {
	SetTextString (constraintd -> dx_ry, "");
	SetSensitive (constraintd -> dx_ry, False, constraintd -> rot_y);
    }

    if (active -> constraint [Rz] == 1) {
	SetSensitive (constraintd -> dx_rz, True, NULL);
	sprintf (buffer, (active -> dx [Rz] ? "%g" : ""), active -> dx [Rz]);
	SetTextString (constraintd -> dx_rz, buffer);
    } else if (active -> constraint [Rz] == 'h') {
	SetSensitive (constraintd -> dx_rz, True, NULL);
	SetTextString (constraintd -> dx_rz, "hinge");
    } else {
	SetTextString (constraintd -> dx_rz, "");
	SetSensitive (constraintd -> dx_rz, False, constraintd -> rot_z);
    }
}


/****************************************************************************
 * Function:	Accept						   	    *
 *									    *
 * Description:	Accepts changes made to the currently displayed constraint. *
 *		If the name is empty or a duplicate name is given then	    *
 *		an error is reported.  Otherwise, a new constraint is	    *
 *		created if a new/copy operation is in effect.  The	    *
 *		constraint is then redisplayed to correct any invalid	    *
 *		entries.						    *
 ****************************************************************************/

static void Accept (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    String		 value;
    struct constraint    dummy;
    Constraint	 	 found;
    Constraint	 	 active;
    Boolean	 	 duplicate;
    ConstraintDialog	 constraintd;
    ConstraintDialogInfo info;


    constraintd = (ConstraintDialog) client_data;


    /* Retrieve the name of the constraint. */

    dummy.name = GetTextString (constraintd -> name);
    found = (Constraint) TreeSearch (constraintd -> tree, &dummy);
    duplicate = found && (found != constraintd -> active || 
                                   constraintd -> new_copy);


    /* Check for a duplicate name. */

    if (!dummy.name [0] || duplicate) {
	XBell (XtDisplay (constraintd -> name), 0);
	SetFocus (constraintd -> name);
	if (!constraintd -> new_copy)
	    SetTextString (constraintd -> name, constraintd -> active -> name);
	else
	    SetTextString (constraintd -> name, "");

    } else {
        

	/* Create a new constraint or new name as needed. */
       
	if (constraintd -> new_copy)
	    constraintd -> active = CreateConstraint (XtNewString (dummy.name));
	else if (strcmp (constraintd -> active -> name, dummy.name)) {
	    XtFree (constraintd -> active -> name);
	    constraintd -> active -> name = XtNewString (dummy.name);
	}
        
	active = constraintd -> active;
  
	active -> constraint[1] = ToggleButtonGetState(constraintd -> trans_x);
	active -> constraint[2] = ToggleButtonGetState(constraintd -> trans_y);
	active -> constraint[3] = ToggleButtonGetState(constraintd -> trans_z);
	active -> constraint[4] = ToggleButtonGetState(constraintd -> rot_x);
	active -> constraint[5] = ToggleButtonGetState(constraintd -> rot_y);
	active -> constraint[6] = ToggleButtonGetState(constraintd -> rot_z);


	if (active -> constraint [Tx])
	    active -> dx [Tx] = atof (GetTextString (constraintd -> dx_tx));

	if (active -> constraint [Ty])
	    active -> dx [Ty] = atof (GetTextString (constraintd -> dx_ty));

	if (active -> constraint [Tz])
	    active -> dx [Tz] = atof (GetTextString (constraintd -> dx_tz));

	if (active -> constraint [Rx]) {
	    value = GetTextString (constraintd -> dx_rx);
	    if (!strcmp (value, "h") || !strcmp (value, "hinge"))
		active -> constraint [Rx] = 'h';
	    else
		active -> dx [Rx] = atof (value);
	}

	if (active -> constraint [Ry]) {
	    value = GetTextString (constraintd -> dx_ry);
	    if (!strcmp (value, "h") || !strcmp (value, "hinge"))
		active -> constraint [Ry] = 'h';
	    else
		active -> dx [Ry] = atof (value);
	}

	if (active -> constraint [Rz]) {
	    value = GetTextString (constraintd -> dx_rz);
	    if (!strcmp (value, "h") || !strcmp (value, "hinge"))
		active -> constraint [Rz] = 'h';
	    else
		active -> dx [Rz] = atof (value);
	}


	if (constraintd -> new_copy)
	    TreeInsert (constraintd -> tree, constraintd -> active);

	if (constraintd -> callback != NULL) {
	    w = constraintd -> shell;
	    info.dialog     = constraintd;
	    info.constraint = constraintd -> active;
	    info.deleted    = False;
	    info.proceed    = True;
	    constraintd -> callback (w, constraintd -> closure, &info);
	}

	ConstraintDialogUpdate (constraintd, constraintd -> tree);
    }
}


/************************************************************************
 * Function:	Dismiss							*
 *									*
 * Description:	Pops down the dialog box.				*
 ************************************************************************/

static void Dismiss (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    ConstraintDialog constraintd;


    constraintd = (ConstraintDialog) client_data;
    XtPopdown (constraintd -> shell);
}


/************************************************************************
 * Function:	Delete							*
 *									*
 * Description:	Deletes the active constraint if a new/copy operation   *
 *		is not in effect.  The dialog is then updated.	 	*	
 ************************************************************************/

static void Delete (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    ConstraintDialog     constraintd;
    ConstraintDialogInfo info;


    constraintd = (ConstraintDialog) client_data;

    if (!constraintd -> new_copy) {
	if (constraintd -> callback != NULL) {
	    w = constraintd -> shell;
	    info.dialog     = constraintd;
	    info.constraint = constraintd -> active;
	    info.deleted    = True;
	    info.proceed    = True;
	    constraintd -> callback (w, constraintd -> closure, &info);
	    if (info.proceed == False)
		return;
	}

	TreeDelete (constraintd -> tree, constraintd -> active);
	DestroyConstraint (constraintd -> active);
	constraintd -> active = NULL;
    }

    ConstraintDialogUpdate (constraintd, constraintd -> tree);
}


/************************************************************************
 * Function:	Copy							*
 *									*
 * Description:	Clears the name entry only and sets the flag indicating	*
 *		that a new/copy operation is in effect.			*
 ************************************************************************/

static void Copy (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    ConstraintDialog constraintd;


    constraintd = (ConstraintDialog) client_data;

    constraintd -> new_copy = True;
    SetFocus (constraintd -> name);
    XawListUnhighlight (constraintd -> list);
    SetTextString (constraintd -> name, "");
}


/************************************************************************
 * Function:	New							*
 *									*
 * Description:	Clears all entries in the constraint dialog; sets the	*
 *		flag indicating that a new/copy operation is in effect.	*
 ************************************************************************/

static void New (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    ConstraintDialog constraintd;


    constraintd = (ConstraintDialog) client_data;

    Copy (NULL, client_data, NULL);
    ToggleButtonSetState (constraintd -> trans_x, False);
    ToggleButtonSetState (constraintd -> trans_y, False);
    ToggleButtonSetState (constraintd -> trans_z, False);
    ToggleButtonSetState (constraintd -> rot_x, False);
    ToggleButtonSetState (constraintd -> rot_y, False);
    ToggleButtonSetState (constraintd -> rot_z, False);

    SetTextString (constraintd -> dx_tx, "");
    SetTextString (constraintd -> dx_ty, "");
    SetTextString (constraintd -> dx_tz, "");
    SetTextString (constraintd -> dx_rx, "");
    SetTextString (constraintd -> dx_ry, "");
    SetTextString (constraintd -> dx_rz, "");

    SetSensitive (constraintd -> dx_tx, False, constraintd -> trans_x);
    SetSensitive (constraintd -> dx_ty, False, constraintd -> trans_y);
    SetSensitive (constraintd -> dx_tz, False, constraintd -> trans_z);
    SetSensitive (constraintd -> dx_rx, False, constraintd -> rot_x);
    SetSensitive (constraintd -> dx_ry, False, constraintd -> rot_y);
    SetSensitive (constraintd -> dx_rz, False, constraintd -> rot_z);
}


/************************************************************************
 * Function:	ConstraintDialogCreate					*
 *									*
 * Description:	Creates a new constraint dialog.  You never want to	*
 *		have more than one of these but the interface is kept	*
 *		consistent with those of the other dialogs.		*
 ************************************************************************/

ConstraintDialog ConstraintDialogCreate (parent, name, title, callback, closure)
    Widget	   parent;
    String	   name;
    String	   title;
    XtCallbackProc callback;
    XtPointer	   closure;
{
    Cardinal		i;
    Arg			args [1];
    Widget		group [20];
    ConstraintDialog	constraintd;
    Dimension		width;
    Position		x;
    static XtAppContext	app_context = NULL;
    static XtActionsRec	actions [ ] = {{"ConstraintDialogAction", Action}};


    /* Perform one time initialization. */

    if (app_context == NULL) {
	app_context = XtWidgetToApplicationContext (parent);
	XtAppAddActions (app_context, actions, XtNumber (actions));
	AddAutoRepeatAction (app_context);

        layout_args [0].value = StringToLayout (parent, layout_string);

	text_translations = XtParseTranslationTable (text_table);
	command_translations = XtParseTranslationTable (command_table);
        toggle_translations = XtParseTranslationTable (toggle_table);
	viewport_translations = XtParseTranslationTable (viewport_table);
	help_translations = XtParseTranslationTable (help_table);
    }


    /* Create the constraint dialog and its widgets. */

    XtSetArg (shell_args [0], XtNtitle, title);
    XtSetArg (shell_args [1], XtNiconName, title);

    constraintd = XtNew (struct constraint_dialog);

    constraintd -> callback = callback;

    constraintd -> closure = closure;

    constraintd -> constraints   = NULL;

    constraintd -> active   = NULL;

    constraintd -> shell  = XtCreatePopupShell (name,
			 topLevelShellWidgetClass, parent,
			 shell_args, XtNumber (shell_args));

    constraintd -> layout   = XtCreateManagedWidget ("layout",
			 layoutWidgetClass, constraintd -> shell,
			 layout_args, XtNumber (layout_args));

    constraintd -> viewport = XtCreateManagedWidget ("viewport",
			 viewportWidgetClass, constraintd -> layout,
			 viewport_args, XtNumber (viewport_args));

    constraintd -> list     = XtCreateManagedWidget ("list",
			 listWidgetClass, constraintd -> viewport,
			 list_args, XtNumber (list_args));

    constraintd -> name     = XtCreateManagedWidget ("name",
			 asciiTextWidgetClass, constraintd -> layout,
			 text_args, XtNumber (text_args));

    constraintd -> trans_x  = XtCreateManagedWidget ("transX",
			 toggleWidgetClass, constraintd -> layout,
			 toggle_args, XtNumber (toggle_args));

    constraintd -> trans_y  = XtCreateManagedWidget ("transY",
			 toggleWidgetClass, constraintd -> layout,
			 toggle_args, XtNumber (toggle_args));

    constraintd -> trans_z  = XtCreateManagedWidget ("transZ",
			 toggleWidgetClass, constraintd -> layout,
			 toggle_args, XtNumber (toggle_args));

    constraintd -> rot_x = XtCreateManagedWidget ("rotX",
			 toggleWidgetClass, constraintd -> layout,
			 toggle_args, XtNumber (toggle_args));

    constraintd -> rot_y = XtCreateManagedWidget ("rotY",
			 toggleWidgetClass, constraintd -> layout,
			 toggle_args, XtNumber (toggle_args));

    constraintd -> rot_z = XtCreateManagedWidget ("rotZ",
			 toggleWidgetClass, constraintd -> layout,
			 toggle_args, XtNumber (toggle_args));

    constraintd -> dx_tx = XtCreateManagedWidget ("dxTransX",
			   asciiTextWidgetClass, constraintd -> layout,
			   text_args, XtNumber (text_args));

    constraintd -> dx_ty = XtCreateManagedWidget ("dxTransY",
			   asciiTextWidgetClass, constraintd -> layout,
			   text_args, XtNumber (text_args));

    constraintd -> dx_tz = XtCreateManagedWidget ("dxTransZ",
			   asciiTextWidgetClass, constraintd -> layout,
			   text_args, XtNumber (text_args));

    constraintd -> dx_rx = XtCreateManagedWidget ("dxRotX",
			   asciiTextWidgetClass, constraintd -> layout,
			   text_args, XtNumber (text_args));

    constraintd -> dx_ry = XtCreateManagedWidget ("dxRotY",
			   asciiTextWidgetClass, constraintd -> layout,
			   text_args, XtNumber (text_args));

    constraintd -> dx_rz = XtCreateManagedWidget ("dxRotZ",
			   asciiTextWidgetClass, constraintd -> layout,
			   text_args, XtNumber (text_args));

    constraintd -> accept   = XtCreateManagedWidget ("accept",
			 commandWidgetClass, constraintd -> layout,
			 NULL, 0);

    constraintd -> dismiss  = XtCreateManagedWidget ("dismiss",
			 commandWidgetClass, constraintd -> layout,
			 NULL, 0);

    constraintd -> delete   = XtCreateManagedWidget ("delete",
			 commandWidgetClass, constraintd -> layout,
			 NULL, 0);

    constraintd -> new      = XtCreateManagedWidget ("new",
			 commandWidgetClass, constraintd -> layout,
			 NULL, 0);

    constraintd -> copy     = XtCreateManagedWidget ("copy",
			 commandWidgetClass, constraintd -> layout,
			 NULL, 0);

    constraintd -> help     = CreateHelpButton (constraintd -> layout, "help");

    for (i = 0; i < XtNumber (labels); i ++) {
	label_args [0].value = (XtArgVal) labels [i];
	XtCreateManagedWidget (names [i], labelWidgetClass,
		constraintd -> layout, label_args, XtNumber (label_args));
    }


    XtCreateManagedWidget ("separator1", coreWidgetClass,
			constraintd -> layout, core_args, XtNumber (core_args));

    XtCreateManagedWidget ("separator2", coreWidgetClass,
			constraintd -> layout, core_args, XtNumber (core_args));


    /* Create a tab group for the constraint dialog. */

    group [0]  = constraintd -> name;
    group [1]  = constraintd -> viewport;
    group [2]  = constraintd -> trans_x;
    group [3]  = constraintd -> dx_tx;
    group [4]  = constraintd -> trans_y;
    group [5]  = constraintd -> dx_ty;
    group [6]  = constraintd -> trans_z;
    group [7]  = constraintd -> dx_tz;
    group [8]  = constraintd -> rot_x;
    group [9]  = constraintd -> dx_rx;
    group [10] = constraintd -> rot_y;
    group [11] = constraintd -> dx_ry;
    group [12] = constraintd -> rot_z;
    group [13] = constraintd -> dx_rz;
    group [14] = constraintd -> help;
    group [15] = constraintd -> accept;
    group [16] = constraintd -> dismiss;
    group [17] = constraintd -> delete;
    group [18] = constraintd -> new;
    group [19] = constraintd -> copy;

    XtGetValues (constraintd -> layout, color_args, XtNumber (color_args));
    CreateTabGroup (constraintd -> shell, group, XtNumber (group), 
                    highlight, True);

    XtRealizeWidget (constraintd -> shell);
    SetFocus (constraintd -> name);

    XtSetArg (args [0], XtNwidth, &width);
    XtGetValues (constraintd -> layout, args, 1);
    XtSetArg (args [0], XtNx, &x);
    XtGetValues (constraintd -> help, args, 1);
    UpdateHelpMessage (constraintd -> help, help_message, width - 2 * x);


    /* Add the translations to each widget. */

    AddDeleteWindowProtocol (constraintd -> shell, "ConstraintDialogAction()");
    ListAddCursorTranslations (constraintd -> viewport);

    XtOverrideTranslations (constraintd -> name,	text_translations);
    XtOverrideTranslations (constraintd -> dx_tx,	text_translations);
    XtOverrideTranslations (constraintd -> dx_ty,	text_translations);
    XtOverrideTranslations (constraintd -> dx_tz,	text_translations);
    XtOverrideTranslations (constraintd -> dx_rx,	text_translations);
    XtOverrideTranslations (constraintd -> dx_ry,	text_translations);
    XtOverrideTranslations (constraintd -> dx_rz,	text_translations);
    XtOverrideTranslations (constraintd -> trans_x,	toggle_translations);
    XtOverrideTranslations (constraintd -> trans_y,	toggle_translations);
    XtOverrideTranslations (constraintd -> trans_z,	toggle_translations);
    XtOverrideTranslations (constraintd -> rot_x,	toggle_translations);
    XtOverrideTranslations (constraintd -> rot_y,	toggle_translations);
    XtOverrideTranslations (constraintd -> rot_z,	toggle_translations);
    XtOverrideTranslations (constraintd -> accept,	command_translations);
    XtOverrideTranslations (constraintd -> dismiss,	command_translations);
    XtOverrideTranslations (constraintd -> delete,	command_translations);
    XtOverrideTranslations (constraintd -> new,	        command_translations);
    XtOverrideTranslations (constraintd -> copy,	command_translations);
    XtOverrideTranslations (constraintd -> viewport,	viewport_translations);
    XtOverrideTranslations (constraintd -> help,	help_translations);


    /* Add the necessary callbacks. */

    XtAddCallback (constraintd -> list,    XtNcallback, 
                   Change,  (XtPointer) constraintd);
    XtAddCallback (constraintd -> accept,  XtNcallback, 
                   Accept,  (XtPointer) constraintd);
    XtAddCallback (constraintd -> dismiss, XtNcallback, 
                   Dismiss, (XtPointer) constraintd);
    XtAddCallback (constraintd -> delete,  XtNcallback, 
                   Delete,  (XtPointer) constraintd);
    XtAddCallback (constraintd -> new,     XtNcallback, 
                   New,     (XtPointer) constraintd);
    XtAddCallback (constraintd -> copy,    XtNcallback, 
                   Copy,    (XtPointer) constraintd);
    XtAddCallback (constraintd -> trans_x, XtNcallback,
		   Toggle,  (XtPointer) constraintd -> dx_tx);
    XtAddCallback (constraintd -> trans_y, XtNcallback,
		   Toggle,  (XtPointer) constraintd -> dx_ty);
    XtAddCallback (constraintd -> trans_z, XtNcallback,
		   Toggle,  (XtPointer) constraintd -> dx_tz);
    XtAddCallback (constraintd -> rot_x, XtNcallback,
		   Toggle,  (XtPointer) constraintd -> dx_rx);
    XtAddCallback (constraintd -> rot_y, XtNcallback,
		   Toggle,  (XtPointer) constraintd -> dx_ry);
    XtAddCallback (constraintd -> rot_z, XtNcallback,
		   Toggle,  (XtPointer) constraintd -> dx_rz);

    return constraintd;
}


/************************************************************************
 * Function:	ConstraintDialogPopup					*
 *									*
 * Description:	Pops up the specified constraint dialog.			*
 ************************************************************************/

void ConstraintDialogPopup (constraintd)
    ConstraintDialog constraintd;
{
    XtPopup (constraintd -> shell, XtGrabNone);
}


/************************************************************************
 * Function:	ConstraintDialogActive					*
 *									*
 * Description:	Returns the currently active constraint.		*
 ************************************************************************/

Constraint ConstraintDialogActive (constraintd)
    ConstraintDialog constraintd;
{
    return constraintd -> active;
}


/************************************************************************
 * Function:	ConstraintDialogDisplay					*
 *									*
 * Description:	Displays specified constraint.				*	
 ************************************************************************/

void ConstraintDialogDisplay (constraintd, constraint)
    ConstraintDialog constraintd;
    Constraint	     constraint;
{
    constraintd -> active = constraint;
    ConstraintDialogUpdate (constraintd, constraintd -> tree);
}


/************************************************************************
 * Function:	ConstraintDialogUpdate					*
 *									*
 * Description:	Updates the specified constraint dialog with the 	*
 *		specified tree.  If no active constraint exists, the 	*
 *		first constraint is made active.  If no active 		*
 *		constraint still exists then a new operation is 	*
 *		performed.  Otherwise a change operation is performed 	*
 *		to display the active values.				*
 ************************************************************************/

void ConstraintDialogUpdate (constraintd, tree)
    ConstraintDialog constraintd;
    Tree	     tree;
{
    Cardinal nbytes;


    /* Determine a new active constraint if necessary. */

    if (tree == NULL)
	tree = constraintd -> tree;

    if (constraintd -> active == NULL || tree != constraintd -> tree)
	constraintd -> active = (Constraint) TreeMinimum (tree);


    /* Construct the array of constraint names. */

    num_constraints = 0;
    list_index = -1;
    dialog = constraintd;
    constraintd -> tree = tree;
    constraintd -> new_copy = False;

    nbytes = (TreeSize (constraintd -> tree) + 1) * sizeof (String);
    constraintd -> constraints = 
         (String *) XtRealloc ((char *) constraintd -> constraints, nbytes);

    TreeSetIterator (constraintd -> tree, AppendConstraintName);
    TreeIterate (constraintd -> tree);
    constraintd -> constraints [num_constraints] = NULL;


    /* Update the list widget. */

    XawListChange (constraintd -> list, constraintd -> constraints, 0,0,True);

    if (list_index >= 0)
	XawListHighlight (constraintd -> list, list_index);


    /* Update the text entries. */

    if (constraintd -> active == NULL)
	New (NULL, (XtPointer) constraintd, NULL);
    else
	Change (NULL, (XtPointer) constraintd, NULL);
}
