/*
**    ScEd: A Constraint Based Scene Editor.
**    Copyright (C) 1994-1995  Stephen Chenney (stephen@cs.su.oz.au)
**
**    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.
*/

/*
**	add_constraint.c: Functions for adding new constraints - the dialogs etc.
*/

#include <math.h>
#include <sced.h>
#include <edit.h>
#include <add_constraint.h>
#include <select_point.h>
#include <ConstraintBox.h>
#include <X11/Xaw/AsciiText.h>
#include <ConstraintPt.h>
#include <X11/Xaw/Toggle.h>


static int	Add_Plane_Constraint_Callback(ConstraintSpecPtr);
static int	Add_Line_Constraint_Callback(ConstraintSpecPtr);
static int	Add_Point_Constraint_Callback(ConstraintSpecPtr);
static int	Add_Sphere_Constraint_Callback(ConstraintSpecPtr);
static int	Add_Circle_Constraint_Callback(ConstraintSpecPtr);
static int	Add_RatioPt_Constraint_Callback(ConstraintSpecPtr);
static int	Add_Axis_Constraint_Callback(ConstraintSpecPtr);

static int	Edit_Select_Point_Callback(ConstraintSpecPtr);

static int	feature_counts[NUM_CONSTRAINT_TYPES] = { 0 };

static AddPointDialogPtr	add_dialog_box;

static Vector			points[MAX_NUM_SPECS];
static ConstraintSpec	specs[MAX_NUM_SPECS];


static char*
Edit_Prepare_Set_Extra_Label(AddPointDialogPtr box, int index)
{
	EditInfoPtr	info = Edit_Get_Info();

	if ( box == &new_sphere_dialog || box == &new_circle_dialog )
	{
		if ( info->current_feature == origin_feature )
			return "";
		else
			return "Origin Pt";
	}
	else if ( info->current_feature == origin_feature ||
			  info->current_feature == major_feature ||
			  info->current_feature == minor_feature )
		return "Origin Pt";
	else if ( info->current_feature == scale_feature )
		return "Scaling Pt";
	else if ( info->current_feature == radius_feature )
		return "Radius Pt";

	return "";
}


static void
Edit_Reset_Add_Dialog(AddPointDialogPtr box)
{
	XawTextBlock	text_block;
	int				old_length;
	Boolean			have_extra, title_menu;
	Arg				args[5];
	int				i, n;

	/* Set done and selected. */
	XtSetSensitive(box->done, FALSE);
	XtVaSetValues(box->select, XtNstate, FALSE, NULL);

	/* Clear all toggles and points. */
	for ( i = 0 ; i < box->points.num_widgets ; i++ )
	{
		XtVaGetValues(box->points.point_widgets[i],
					  XtNextraRequired, &have_extra,
					  XtNtitleIsMenu, &title_menu, NULL);
		n = 0;
		XtSetArg(args[n], XtNlabel, "?? ?? ??");	n++;
		if ( i == 0 )
		{
			XtSetArg(args[n], XtNtoggleState, TRUE);	n++;
		}
		if ( have_extra )
		{
			XtSetArg(args[n], XtNextraState, FALSE);	n++;
			XtSetArg(args[n], XtNextraLabel,
					 Edit_Prepare_Set_Extra_Label(box, i));	n++;
		}
		if ( title_menu )
		{
			XtSetArg(args[n], XtNtitleLabel, "Point");	n++;
		}
		XtSetValues(box->points.point_widgets[i], args, n);
	}

	if ( box == &new_ratiopt_dialog )
	{
		/* Set ratio. */
		old_length = strlen(box->ratio_string);
		text_block.firstPos = 0;
		text_block.format = FMT8BIT;
		text_block.length = strlen("1:1");
		text_block.ptr = "1:1";
		XawTextReplace(box->ratio, 0, old_length + 1, &text_block);
	}

	/* Resize. */
	Add_Set_Widths(box);
}

static void
Edit_Set_Add_Dialog(EditInfoPtr info, AddPointDialogPtr box,
					ConstraintPtr feature)
{
	/* Set all the toggles and labels. */
	XawTextBlock	text_block;
	int				old_length;
	char			new_label[48];
	int				i;
	Arg				args[5];
	int				n;
	Boolean			have_extra, title_menu;

	for ( i = 0 ; i < 6 ; i++ )
	{
		if ( feature->c_spec_flags & ( 1 << i ) )
		{
			update_spec_function[feature->c_specs[i].spec_type](
				feature->c_specs + i,
				info->obj->o_world_verts + (info->obj->o_num_vertices - 1),
				info->obj->o_features, &(info->axes.z), points + i);
			specs[i] = feature->c_specs[i];

			XtVaGetValues(box->points.point_widgets[i],
						  XtNextraRequired, &have_extra,
						  XtNtitleIsMenu, &title_menu, NULL);
			n = 0;
			sprintf(new_label, "%1.3g %1.3g %1.3g",
					points[i].x, points[i].y, points[i].z);
			XtSetArg(args[n], XtNlabel, new_label);	n++;
			if ( i == 0 )
			{
				XtSetArg(args[n], XtNtoggleState, TRUE);	n++;
			}
			if ( have_extra )
			{
				if ( specs[i].spec_type == feature_spec )
				{
					XtSetArg(args[n], XtNextraState, TRUE);		n++;
				}
				else
				{
					XtSetArg(args[n], XtNextraState, FALSE);	n++;
				}
				XtSetArg(args[n], XtNextraLabel,
						 Edit_Prepare_Set_Extra_Label(box, i));	n++;
			}
			if ( title_menu )
			{
				if ( i == 1 )
				{
					if ( feature->c_spec_flags & TwoisPt )
					{
						XtSetArg(args[n], XtNtitleLabel, "Point");	n++;
					}
					else
					{
						XtSetArg(args[n], XtNtitleLabel, "Normal");	n++;
					}
				}
				else if ( i == 2 )
				{
					if ( feature->c_spec_flags & ThreeisPt )
					{
						XtSetArg(args[n], XtNtitleLabel, "Point");	n++;
					}
					else
					{
						XtSetArg(args[n], XtNtitleLabel, "Normal");	n++;
					}
				}
			}
			XtSetValues(box->points.point_widgets[i], args, n);
		}
		else if ( i < box->points.num_widgets )
			/* Must be non-essentials. Not many options. */
			XtVaSetValues(box->points.point_widgets[i], XtNlabel, "?? ?? ??",
						  NULL);
	}

	if ( box == &new_ratiopt_dialog )
	{
		/* Set ratio. */
		sprintf(new_label, "%g:%g", feature->c_radius, feature->c_ratio);
		old_length = strlen(box->ratio_string);
		text_block.firstPos = 0;
		text_block.format = FMT8BIT;
		text_block.length = strlen(new_label);
		text_block.ptr = new_label;
		XawTextReplace(box->ratio, 0, old_length + 1, &text_block);
	}

	/* Resize. */
	Add_Set_Widths(box);
}


void
Edit_Popup_Add_Dialog(EditInfoPtr info, AddPointDialogPtr box)
{
	SFpositionWidget(box->shell);
	XtPopup(box->shell, XtGrabNone);

	/* Prepare for point selection. */
	select_callback = Edit_Select_Point_Callback;
	select_window = info->window;
	select_highlight = TRUE;
	allow_text_entry = TRUE;
	prompt_offset = TRUE;
	select_center = info->obj->o_world_verts[info->obj->o_num_vertices - 1];
	select_instances = info->all_available;

	info->selecting = TRUE;

	Edit_Sensitize_Buttons(FALSE, SenEditAll);

	/* Draw all the reference points. */
	Edit_Draw_Selection_Points(info);

	Register_Select_Operation(info->window);
}

void
Edit_Add_Constraint_Callback(Widget w, XtPointer cl_data, XtPointer ca)
{
	EditInfoPtr		info = Edit_Get_Info();
	ConstraintSpecType	type = (ConstraintSpecType)cl_data;
	XawTextBlock	text_block;
	int				old_length;
	char			default_name[20];

	XtPopdown(info->obj->o_features[info->current_feature].base->add_shell);

	if ( info->modifying )
		Edit_Cancel_Modify(info);

	info->constr_index = -1;

	switch ( type )
	{
		case plane_feature:
			if ( ! new_plane_dialog.shell )
				Add_Create_Plane_Dialog();
			add_dialog_box = &new_plane_dialog;
			add_dialog_box->flags = ( TwoisPt | ThreeisPt );
			sprintf(default_name, "Plane %d", feature_counts[plane_feature]);
			break;

		case line_feature:
			if ( ! new_line_dialog.shell )
				Add_Create_Line_Dialog();
			add_dialog_box = &new_line_dialog;
			add_dialog_box->flags = TwoisPt;
			sprintf(default_name, "Line %d", feature_counts[line_feature]);
			break;

		case point_feature:
			if ( ! new_point_dialog.shell )
				Add_Create_Point_Dialog();
			add_dialog_box = &new_point_dialog;
			add_dialog_box->flags = 0;
			sprintf(default_name, "Point %d", feature_counts[point_feature]);
			break;

		case sphere_feature:
			if ( ! new_sphere_dialog.shell )
				Add_Create_Sphere_Dialog();
			add_dialog_box = &new_sphere_dialog;
			add_dialog_box->flags = 0;
			sprintf(default_name, "Sphere %d", feature_counts[sphere_feature]);
			break;

		case circle_feature:
			if ( ! new_circle_dialog.shell )
				Add_Create_Circle_Dialog();
			add_dialog_box = &new_circle_dialog;
			add_dialog_box->flags = ( TwoisPt | ThreeisPt );
			sprintf(default_name, "Circle %d", feature_counts[circle_feature]);
			break;

		case ratio_plane_feature:
			if ( ! new_ratiopt_dialog.shell )
				Add_Create_RatioPt_Dialog();
			add_dialog_box = &new_ratiopt_dialog;
			add_dialog_box->flags = RatioPlane;
			sprintf(default_name, "Ratio Pl %d",
					feature_counts[ratio_plane_feature]);
			break;

		case ratio_point_feature:
			if ( ! new_ratiopt_dialog.shell )
				Add_Create_RatioPt_Dialog();
			add_dialog_box = &new_ratiopt_dialog;
			add_dialog_box->flags = RatioPoint;
			sprintf(default_name, "Ratio Pt %d",
					feature_counts[ratio_point_feature]);
			break;

		case axis_feature:
			if ( ! new_axis_dialog.shell )
				Add_Create_Axis_Dialog();
			add_dialog_box = &new_axis_dialog;
			add_dialog_box->flags = Axis;
			sprintf(default_name, "Axis %d", feature_counts[axis_feature]);
			break;

		default:;
	}

	/* Set name. */
	old_length = strlen(add_dialog_box->name_string);
	text_block.firstPos = 0;
	text_block.format = FMT8BIT;
	text_block.length = strlen(default_name);
	text_block.ptr = default_name;
	XawTextReplace(add_dialog_box->name, 0, old_length + 1, &text_block);

	XtSetSensitive(add_dialog_box->remove, FALSE);
	XtSetSensitive(add_dialog_box->select, TRUE);

	Edit_Reset_Add_Dialog(add_dialog_box);

	Edit_Popup_Add_Dialog(info, add_dialog_box);
}

void
Add_Modify_Constraint(EditInfoPtr info, ConstraintPtr feature)
{
	XawTextBlock	text_block;
	int				old_length;

	switch ( feature->c_type )
	{
		case plane_feature:
			if ( feature->c_spec_flags & RatioPlane )
			{
				if ( ! new_ratiopt_dialog.shell )
					Add_Create_RatioPt_Dialog();
				add_dialog_box = &new_ratiopt_dialog;
			}
			else
			{
				if ( ! new_plane_dialog.shell )
					Add_Create_Plane_Dialog();
				add_dialog_box = &new_plane_dialog;
			}
			break;

		case line_feature:
			if ( feature->c_spec_flags & Axis )
			{
				if ( ! new_axis_dialog.shell )
					Add_Create_Axis_Dialog();
				add_dialog_box = &new_axis_dialog;
			}
			else
			{
				if ( ! new_line_dialog.shell )
					Add_Create_Line_Dialog();
				add_dialog_box = &new_line_dialog;
			}
			break;

		case point_feature:
			if ( feature->c_spec_flags & RatioPoint )
			{
				if ( ! new_ratiopt_dialog.shell )
					Add_Create_RatioPt_Dialog();
				add_dialog_box = &new_ratiopt_dialog;
			}
			else
			{
				if ( ! new_point_dialog.shell )
					Add_Create_Point_Dialog();
				add_dialog_box = &new_point_dialog;
			}
			break;

		case sphere_feature:
			if ( ! new_sphere_dialog.shell )
				Add_Create_Sphere_Dialog();
			add_dialog_box = &new_sphere_dialog;
			break;

		case circle_feature:
			if ( ! new_circle_dialog.shell )
				Add_Create_Circle_Dialog();
			add_dialog_box = &new_circle_dialog;
			break;

		default:;
	}

	add_dialog_box->flags = feature->c_spec_flags;

	/* Set name. */
	old_length = strlen(add_dialog_box->name_string);
	text_block.firstPos = 0;
	text_block.format = FMT8BIT;
	text_block.length = strlen(feature->c_label);
	text_block.ptr = feature->c_label;
	XawTextReplace(add_dialog_box->name, 0, old_length + 1, &text_block);

	XtSetSensitive(add_dialog_box->remove, ! feature->c_status);
	XtSetSensitive(add_dialog_box->select, FALSE);

	Edit_Set_Add_Dialog(info, add_dialog_box, feature);

	Edit_Popup_Add_Dialog(info, add_dialog_box);
}

void
Add_Dialog_Remove_Callback(Widget w, XtPointer cl, XtPointer ca)
{
	EditInfoPtr	info = Edit_Get_Info();

	XtPopdown(add_dialog_box->shell);

	Edit_Cleanup_Selection(info, TRUE);

	Edit_Remove_Constraint(info, info->current_feature,info->constr_index,TRUE);
}

static Boolean
Add_Spec_OK(ConstraintSpecPtr spec, EditInfoPtr info, Boolean allow_self)
{
	if ( ! Spec_Is_Dependent(spec->spec_type) )
		return TRUE;

	if ( ! allow_self && spec_object(spec) == info->obj )
	{
		if ( spec->spec_type == vertex_spec )
			return ( (int)(spec->spec_vector.x) <
					 info->current_feature - pt0_feature );
		else
			return FALSE;
	}

	return ! ( spec_object(spec)->o_flags & ObjDepends );
}

static void
Add_Check_Specifiers(ConstraintSpecPtr spec, EditInfoPtr info,
					 Boolean allow_self)
{
	if ( ! Add_Spec_OK(spec, info, allow_self) )
	{
		if ( info->current_feature == origin_feature )
		{
			spec->spec_vector = Spec_Absolute(spec, select_center);
			spec->spec_type = absolute_spec;
			Popup_Error("Reference specifier changed to Absolute",
						info->window->shell, "Error");
		}
		else
		{
			spec->spec_vector = Spec_Absolute(spec, select_center);
			VSub(spec->spec_vector, select_center, spec->spec_vector);
			spec->spec_type = offset_spec;
			Popup_Error("Reference specifier changed to Offset",
						info->window->shell, "Error");
		}
		spec->spec_data = (void*)NULL;
	}
}


static int
Add_Get_Set_Toggle(AddPointDialogPtr box)
{
	Boolean	state = FALSE;
	int	i;

	for ( i = 0 ; i < box->points.num_widgets && ! state ; i++ )
		XtVaGetValues(box->points.point_widgets[i], XtNtoggleState, &state,
					  NULL);
	return i - 1;
}

static void
Add_Set_Next_Toggle(AddPointDialogPtr box)
{
	int		current = Add_Get_Set_Toggle(box);
	Boolean	state;
	int		i;

	if ( box == &new_point_dialog )
		return;

	i = current + 1;
	if ( i == box->points.num_widgets )
		i = 0;
	while ( i != current )
	{
		XtVaGetValues(box->points.point_widgets[i], XtNtoggleState, &state,
					  NULL);
		if ( ! state )
			break;
		i = ( i == box->points.num_widgets - 1 ? 0 : i + 1 );
	}

	if ( i != current )
		XtVaSetValues(box->points.point_widgets[i], XtNtoggleState, TRUE, NULL);
}


static Boolean
Add_Plane_Done(int flags)
{
	return ( ( flags & HavePt1 ) && ( flags & HavePt2 ) && ( flags & HavePt3 ));
}

static Boolean
Add_Line_Done(int flags)
{
	return ( ( flags & HavePt1 ) && ( flags & HavePt2 ) );
}

static Boolean
Add_Point_Done(int flags)
{
	return ( flags & HavePt1 );
}


static Boolean
Add_Sphere_Done(int flags)
{
	return ( ( flags & HavePt1 ) && ( flags & HavePt2 ) );
}


static Boolean
Add_Circle_Done(int flags)
{
	return ( ( flags & HavePt1 ) && ( flags & HavePt2 ) && ( flags & HavePt3 ));
}

static Boolean
Add_RatioPt_Done(int flags)
{
	return ( ( flags & HavePt1 ) && ( flags & HavePt2 ) );
}

static Boolean
Add_Axis_Done(int flags)
{
	return ( ( flags & HavePt1 ) && ( flags & HavePt2 ) );
}


static Boolean
Add_Test_Done(AddPointDialogPtr box)
{
	if ( box == &new_plane_dialog )
		return Add_Plane_Done(box->flags);
	else if ( box == &new_line_dialog )
		return Add_Line_Done(box->flags);
	else if ( box == &new_point_dialog )
		return Add_Point_Done(box->flags);
	else if ( box == &new_sphere_dialog )
		return Add_Sphere_Done(box->flags);
	else if ( box == &new_circle_dialog )
		return Add_Circle_Done(box->flags);
	else if ( box == &new_ratiopt_dialog )
		return Add_RatioPt_Done(box->flags);
	else if ( box == &new_axis_dialog )
		return Add_Axis_Done(box->flags);

	return FALSE;
}

static Boolean
Add_Test_Point_Validity(ConstraintType con_type, int flags, Vector location,
						int active)
{
	ConstraintData	data;
	Vector			orig_pt = points[active];
	Boolean			result;

	data.c_type = con_type;
	data.c_spec_flags = flags;
	data.c_radius = data.c_ratio = 1.0;
	points[active] = location;

	result = update_con_function[con_type](&data, points, &location);

	points[active] = orig_pt;
	return result;
}

static Boolean
Add_Any_Constraint(ConstraintPtr feature)
{
	EditInfoPtr	info = Edit_Get_Info();
	int			i;

	feature->c_specs = New(ConstraintSpec, feature->c_num_specs);
	for ( i = 0 ; i < feature->c_num_specs ; i++ )
	{
		feature->c_specs[i] = specs[i];
		if ( Spec_Is_Dependent(specs[i].spec_type) )
			Add_Dependency(spec_object(specs + i), info->obj);
	}

	if ( ! update_con_function[feature->c_type](feature, points,
									&(info->features[origin_feature].world)) )
	{
		XBell(XtDisplay(main_window.shell), 0);
		return FALSE;
	}

	return TRUE;
}


static Boolean
Add_Plane_Constraint(ConstraintPtr feature, int flags)
{
	feature->c_type = plane_feature;
	feature->c_spec_flags = flags;
	if ( ( flags & TwoisNorm ) && ( flags & ThreeisNorm ) && (flags & HavePt4) )
		feature->c_num_specs = 4;
	else
		feature->c_num_specs = 3;

	return Add_Any_Constraint(feature);
}


static Boolean
Add_Line_Constraint(ConstraintPtr feature, int flags)
{
	feature->c_type = line_feature;
	feature->c_spec_flags = flags;
	if ( flags & HavePt3 )
		feature->c_num_specs = 3;
	else
		feature->c_num_specs = 2;

	return Add_Any_Constraint(feature);
}


static Boolean
Add_Point_Constraint(ConstraintPtr feature, int flags)
{
	feature->c_type = point_feature;
	feature->c_spec_flags = flags;
	feature->c_num_specs = 1;

	return Add_Any_Constraint(feature);
}


static Boolean
Add_Sphere_Constraint(ConstraintPtr feature, int flags)
{
	feature->c_type = sphere_feature;
	feature->c_spec_flags = flags;
	if ( flags & HavePt3 )
		feature->c_num_specs = 3;
	else
		feature->c_num_specs = 2;

	return Add_Any_Constraint(feature);
}

static Boolean
Add_Circle_Constraint(ConstraintPtr feature, int flags)
{
	feature->c_type = circle_feature;
	feature->c_spec_flags = flags;
	if ( ( flags & TwoisNorm ) && ( flags & ThreeisNorm ) && (flags & HavePt4) )
		feature->c_num_specs = 4;
	else
		feature->c_num_specs = 3;
	if ( flags & HavePt5 )
		feature->c_num_specs++;
	if ( flags & HavePt6 )
		feature->c_num_specs++;

	return Add_Any_Constraint(feature);
}

static Boolean
Add_Ratio_Point_Constraint(ConstraintPtr feature, int flags)
{
	feature->c_type = point_feature;
	feature->c_spec_flags = flags;
	feature->c_num_specs = 2;

	return Add_Any_Constraint(feature);
}

static Boolean
Add_Ratio_Plane_Constraint(ConstraintPtr feature, int flags)
{
	feature->c_type = plane_feature;
	feature->c_spec_flags = flags;
	feature->c_num_specs = 2;

	return Add_Any_Constraint(feature);
}


static Boolean
Add_Axis_Constraint(ConstraintPtr feature, int flags)
{
	feature->c_type = line_feature;
	feature->c_spec_flags = flags;
	if ( flags & HavePt3 )
		feature->c_num_specs = 3;
	else
		feature->c_num_specs = 2;

	return Add_Any_Constraint(feature);
}

static void
Add_Set_Point(AddPointDialogPtr box, int pt, ConstraintSpecPtr spec,
			  Boolean ref_allowed)
{
	EditInfoPtr	info = Edit_Get_Info();
	char		new_label[48];

	Add_Check_Specifiers(spec, info, ref_allowed);

	specs[pt] = *spec;
	points[pt] = Spec_Absolute(spec, select_center);

	sprintf(new_label, "%1.3g %1.3g %1.3g",
			points[pt].x, points[pt].y, points[pt].z);
	XtVaSetValues(box->points.point_widgets[pt], XtNlabel, new_label, NULL);

	Add_Set_Next_Toggle(box);
	XtSetSensitive(box->done, Add_Test_Done(box));
	Add_Set_Widths(box);
}


static int
Add_Plane_Constraint_Callback(ConstraintSpecPtr spec)
{
	int		active_pt = Add_Get_Set_Toggle(&new_plane_dialog);
	Boolean	is_origin;
	Boolean	allow_ref = FALSE;
	Vector	location = Spec_Absolute(spec, select_center);

	if ( Add_Plane_Done(new_plane_dialog.flags | ( 1 << active_pt ) ) &&
		 ! Add_Test_Point_Validity(plane_feature,
								   new_plane_dialog.flags | ( 1 << active_pt ),
								   location, active_pt) )
	{
		XBell(XtDisplay(main_window.shell), 0);
		return IGNORE;
	}

	switch ( active_pt )
	{
		case 0:
			if ( new_plane_dialog.flags & HavePt1 )
			{
				XtVaGetValues(new_plane_dialog.points.point_widgets[0],
							  XtNextraState, &is_origin, NULL);
				if ( is_origin )
					XtVaSetValues(new_plane_dialog.points.point_widgets[0],
								  XtNextraState, FALSE, NULL);
				else
					/* Deselect old one. */
					Select_Deselect_Point(specs);
			}
			new_plane_dialog.flags |= HavePt1;
			break;
		case 1:
			if ( new_plane_dialog.flags & HavePt2 )
				Select_Deselect_Point(specs + 1);
			new_plane_dialog.flags |= HavePt2;
			if ( ( new_plane_dialog.flags & HavePt1 ) &&
				 specs[0].spec_type == feature_spec )
				allow_ref = TRUE;
			break;
		case 2:
			if ( new_plane_dialog.flags & HavePt3 )
				Select_Deselect_Point(specs + 2);
			new_plane_dialog.flags |= HavePt3;
			if ( ( new_plane_dialog.flags & HavePt1 ) &&
				 specs[0].spec_type == feature_spec )
				allow_ref = TRUE;
			break;
		case 3:
			if ( new_plane_dialog.flags & HavePt4 )
				Select_Deselect_Point(specs + 3);
			new_plane_dialog.flags |= HavePt4;
			if ( ( new_plane_dialog.flags & HavePt1 ) &&
				 specs[0].spec_type == feature_spec )
				allow_ref = TRUE;
			break;
	}

	Add_Set_Point(&new_plane_dialog, active_pt, spec, allow_ref);

	return SELECT;
}

/*	void
**	Add_Line_Constraint_Callback(int *points)
**	Adds a line as defined by the two points into select_verts.
*/
static int
Add_Line_Constraint_Callback(ConstraintSpecPtr spec)
{
	int		active_pt = Add_Get_Set_Toggle(&new_line_dialog);
	Boolean	is_origin;
	Boolean	allow_ref = FALSE;
	Vector	location = Spec_Absolute(spec, select_center);

	if ( Add_Line_Done(new_line_dialog.flags | ( 1 << active_pt ) ) &&
		 ! Add_Test_Point_Validity(line_feature,
								   new_line_dialog.flags | ( 1 << active_pt ),
								   location, active_pt) )
	{
		XBell(XtDisplay(main_window.shell), 0);
		return IGNORE;
	}

	switch ( active_pt )
	{
		case 0:
			if ( new_line_dialog.flags & HavePt1 )
			{
				XtVaGetValues(new_line_dialog.points.point_widgets[0],
							  XtNextraState, &is_origin, NULL);
				if ( is_origin )
					XtVaSetValues(new_line_dialog.points.point_widgets[0],
								  XtNextraState, FALSE, NULL);
				else
					/* Deselect old one. */
					Select_Deselect_Point(specs);
			}
			new_line_dialog.flags |= HavePt1;
			break;
		case 1:
			if ( new_line_dialog.flags & HavePt2 )
				Select_Deselect_Point(specs + 1);
			new_line_dialog.flags |= HavePt2;
			if ( ( new_line_dialog.flags & HavePt1 ) &&
				 specs[0].spec_type == feature_spec )
				allow_ref = TRUE;
			break;
		case 2:
			if ( new_line_dialog.flags & HavePt3 )
				Select_Deselect_Point(specs + 2);
			new_line_dialog.flags |= HavePt3;
			if ( ( new_line_dialog.flags & HavePt1 ) &&
				 specs[0].spec_type == feature_spec )
				allow_ref = TRUE;
			break;
	}

	Add_Set_Point(&new_line_dialog, active_pt, spec, allow_ref);

	return SELECT;
}


/*	void
**	Add_Line_Constraint_Callback(int *points)
**	Adds the point indexed by *points.
*/
static int
Add_Point_Constraint_Callback(ConstraintSpecPtr spec)
{
	if ( new_point_dialog.flags & HavePt1 )
		Select_Deselect_Point(specs);
		
	new_point_dialog.flags |= HavePt1;

	Add_Set_Point(&new_point_dialog, 0, spec, FALSE);

	return SELECT;
}


/* Add a sphere constraint. First point is the center, second gives radius. */
static int
Add_Sphere_Constraint_Callback(ConstraintSpecPtr spec)
{
	EditInfoPtr	info = Edit_Get_Info();
	int		active_pt = Add_Get_Set_Toggle(&new_sphere_dialog);
	Boolean	is_origin;
	Vector	location = Spec_Absolute(spec, select_center);

	if ( Add_Sphere_Done(new_sphere_dialog.flags | ( 1 << active_pt ) ) &&
		 ! Add_Test_Point_Validity(sphere_feature,
								   new_sphere_dialog.flags | ( 1 << active_pt ),
								   location, active_pt) )
	{
		XBell(XtDisplay(main_window.shell), 0);
		return IGNORE;
	}

	switch ( active_pt )
	{
		case 0:
			new_sphere_dialog.flags |= HavePt1;
			if ( new_sphere_dialog.flags & HavePt1 )
			{
				if ( info->current_feature != origin_feature )
				{
					XtVaGetValues(new_sphere_dialog.points.point_widgets[0],
								  XtNextraState, &is_origin, NULL);
					if ( is_origin )
						XtVaSetValues(new_sphere_dialog.points.point_widgets[0],
									  XtNextraState, FALSE, NULL);
				}
				else
					is_origin = FALSE;

				if ( is_origin )
					XtVaSetValues(new_sphere_dialog.points.point_widgets[0],
								  XtNextraState, FALSE, NULL);
				else
					/* Deselect old one. */
					Select_Deselect_Point(specs);
			}
			break;
		case 1:
			if ( new_sphere_dialog.flags & HavePt2 )
				Select_Deselect_Point(specs + 1);
			new_sphere_dialog.flags |= HavePt2;
			break;
		case 2:
			if ( new_sphere_dialog.flags & HavePt3 )
				Select_Deselect_Point(specs + 2);
			new_sphere_dialog.flags |= HavePt3;
			break;
	}

	Add_Set_Point(&new_sphere_dialog, active_pt, spec, FALSE);

	return SELECT;
}

static int
Add_Circle_Constraint_Callback(ConstraintSpecPtr spec)
{
	EditInfoPtr	info = Edit_Get_Info();
	int		active_pt = Add_Get_Set_Toggle(&new_circle_dialog);
	Boolean	is_origin;
	Vector	location = Spec_Absolute(spec, select_center);

	if ( Add_Circle_Done(new_circle_dialog.flags | ( 1 << active_pt ) ) &&
		 ! Add_Test_Point_Validity(circle_feature,
								   new_circle_dialog.flags | ( 1 << active_pt ),
								   location, active_pt) )
	{
		XBell(XtDisplay(main_window.shell), 0);
		return IGNORE;
	}

	switch ( active_pt )
	{
		case 0:
			if ( new_circle_dialog.flags & HavePt1 )
			{
				if ( info->current_feature != origin_feature )
				{
					XtVaGetValues(new_circle_dialog.points.point_widgets[0],
								  XtNextraState, &is_origin, NULL);
					if ( is_origin )
						XtVaSetValues(new_circle_dialog.points.point_widgets[0],
									  XtNextraState, FALSE, NULL);
				}
				else
					is_origin = FALSE;

				if ( is_origin )
					XtVaSetValues(new_circle_dialog.points.point_widgets[0],
								  XtNextraState, FALSE, NULL);
				else
					/* Deselect old one. */
					Select_Deselect_Point(specs);
			}
			new_circle_dialog.flags |= HavePt1;
			break;
		case 1:
			if ( new_circle_dialog.flags & HavePt2 )
				Select_Deselect_Point(specs + 1);
			new_circle_dialog.flags |= HavePt2;
			break;
		case 2:
			if ( new_circle_dialog.flags & HavePt3 )
				Select_Deselect_Point(specs + 2);
			new_circle_dialog.flags |= HavePt3;
			break;
		case 3:
			if ( new_circle_dialog.flags & HavePt4 )
				Select_Deselect_Point(specs + 3);
			new_circle_dialog.flags |= HavePt4;
			break;
		case 4:
			if ( new_circle_dialog.flags & HavePt5 )
				Select_Deselect_Point(specs + 4);
			new_circle_dialog.flags |= HavePt5;
			break;
		case 5:
			if ( new_circle_dialog.flags & HavePt6 )
				Select_Deselect_Point(specs + 5);
			new_circle_dialog.flags |= HavePt6;
			break;
	}

	Add_Set_Point(&new_circle_dialog, active_pt, spec, FALSE);

	return SELECT;
}


static int
Add_RatioPt_Constraint_Callback(ConstraintSpecPtr spec)
{
	int		active_pt = Add_Get_Set_Toggle(&new_ratiopt_dialog);
	Vector	location = Spec_Absolute(spec, select_center);

	if ( Add_RatioPt_Done(new_ratiopt_dialog.flags | ( 1 << active_pt ) ) &&
		 ! Add_Test_Point_Validity(point_feature,
								  new_ratiopt_dialog.flags | ( 1 << active_pt ),
								  location, active_pt) )
	{
		XBell(XtDisplay(main_window.shell), 0);
		return IGNORE;
	}

	switch ( active_pt )
	{
		case 0:
			if ( new_ratiopt_dialog.flags & HavePt1 )
				Select_Deselect_Point(specs);
			new_ratiopt_dialog.flags |= HavePt1;
			break;
		case 1:
			if ( new_ratiopt_dialog.flags & HavePt2 )
				Select_Deselect_Point(specs + 1);
			new_ratiopt_dialog.flags |= HavePt2;
			break;
	}

	Add_Set_Point(&new_ratiopt_dialog, active_pt, spec, FALSE);

	return SELECT;
}


static int
Add_Axis_Constraint_Callback(ConstraintSpecPtr spec)
{
	int		active_pt = Add_Get_Set_Toggle(&new_axis_dialog);
	Boolean	is_origin;
	Vector	location = Spec_Absolute(spec, select_center);

	if ( Add_Axis_Done(new_axis_dialog.flags | ( 1 << active_pt ) ) &&
		 ! Add_Test_Point_Validity(line_feature,
								   new_axis_dialog.flags | ( 1 << active_pt ),
								   location, active_pt) )
	{
		XBell(XtDisplay(main_window.shell), 0);
		return IGNORE;
	}

	switch ( active_pt )
	{
		case 0:
			if ( new_axis_dialog.flags & HavePt1 )
			{
				XtVaGetValues(new_axis_dialog.points.point_widgets[0],
							  XtNextraState, &is_origin, NULL);
				if ( is_origin )
					XtVaSetValues(new_axis_dialog.points.point_widgets[0],
								  XtNextraState, FALSE, NULL);
				else
					/* Deselect old one. */
					Select_Deselect_Point(specs);
			}
			new_axis_dialog.flags |= HavePt1;
			break;
		case 1:
			if ( new_axis_dialog.flags & HavePt2 )
				Select_Deselect_Point(specs + 1);
			new_axis_dialog.flags |= HavePt2;
			break;
		case 2:
			if ( new_axis_dialog.flags & HavePt3 )
				Select_Deselect_Point(specs + 2);
			new_axis_dialog.flags |= HavePt3;
			break;
	}

	Add_Set_Point(&new_axis_dialog, active_pt, spec, FALSE);

	return SELECT;
}


static int
Edit_Select_Point_Callback(ConstraintSpecPtr spec)
{
	if ( add_dialog_box == &new_plane_dialog )
		return Add_Plane_Constraint_Callback(spec);
	else if ( add_dialog_box == &new_line_dialog )
		return Add_Line_Constraint_Callback(spec);
	else if ( add_dialog_box == &new_point_dialog )
		return Add_Point_Constraint_Callback(spec);
	else if ( add_dialog_box == &new_sphere_dialog )
		return Add_Sphere_Constraint_Callback(spec);
	else if ( add_dialog_box == &new_circle_dialog )
		return Add_Circle_Constraint_Callback(spec);
	else if ( add_dialog_box == &new_ratiopt_dialog )
		return Add_RatioPt_Constraint_Callback(spec);
	else if ( add_dialog_box == &new_axis_dialog )
		return Add_Axis_Constraint_Callback(spec);

	return IGNORE;
}

void
Add_Extra_Callback(AddPointDialogPtr box, int index, Boolean state)
{
	static Boolean	last_was_err = FALSE;
	EditInfoPtr		info = Edit_Get_Info();
	char			new_label[48];
	Vector			diff;
	ConstraintSpec	last_spec;

	if ( last_was_err )
	{
		last_was_err = FALSE;
		return;
	}

	if ( ! state )
	{
		/* Unset flags etc. */
		box->flags &= ( AllFlags ^ HavePt1 );
		XtVaSetValues(box->points.point_widgets[index],
					  XtNlabel, "?? ?? ??",
					  XtNtoggleState, TRUE, NULL);
		XtSetSensitive(box->done, FALSE);
		Add_Set_Widths(box);
		return;
	}

	/* Remember old one. */
	last_spec = specs[0];

	if ( info->current_feature == origin_feature ||
		 info->current_feature == major_feature ||
		 info->current_feature == minor_feature )
	{
		sprintf(new_label, "%1.3g %1.3g %1.3g",
				info->features[origin_feature].world.x,
				info->features[origin_feature].world.y,
				info->features[origin_feature].world.z);
		points[0] = info->features[origin_feature].world;
		specs[0].spec_type = feature_spec;
		specs[0].spec_data = (void*)origin_feature;
		specs[0].spec_vector = info->features[origin_feature].world;
	}
	else if ( info->current_feature == scale_feature )
	{
		sprintf(new_label, "%1.3g %1.3g %1.3g",
				info->features[scale_feature].world.x,
				info->features[scale_feature].world.y,
				info->features[scale_feature].world.z);
		points[0] = info->features[scale_feature].world;
		specs[0].spec_type = feature_spec;
		specs[0].spec_data = (void*)scale_feature;;
		specs[0].spec_vector = info->features[scale_feature].world;
	}
	else if ( info->current_feature == radius_feature )
	{
		sprintf(new_label, "%1.3g %1.3g %1.3g",
				info->features[radius_feature].world.x,
				info->features[radius_feature].world.y,
				info->features[radius_feature].world.z);
		points[0] = info->features[radius_feature].world;
		specs[0].spec_type = feature_spec;
		specs[0].spec_data = (void*)radius_feature;
		specs[0].spec_vector = info->features[radius_feature].world;
	}

	if ( box == &new_line_dialog && ( new_line_dialog.flags & HavePt2 ) )
	{
		if ( VEqual(points[0], points[1], diff) )
		{
			XBell(XtDisplay(main_window.shell), 0);
			last_was_err = TRUE;
			XtVaSetValues(box->points.point_widgets[index],
						  XtNextraState, FALSE, NULL);
			return;
		}
	}
	else if ( box == &new_plane_dialog &&
			  ( new_plane_dialog.flags & HavePt2 ) &&
			  ( new_plane_dialog.flags & HavePt3 ) &&
			  ! ( new_plane_dialog.flags & HavePt4 ) )
	{
		/* Check co-linearity. */
		if ( Points_Colinear_f(points[0], points[1], points[2]))
		{
			XBell(XtDisplay(main_window.shell), 0);
			last_was_err = TRUE;
			XtVaSetValues(box->points.point_widgets[index],
						  XtNextraState, FALSE, NULL);
			return;
		}
	}

	if ( box->flags & HavePt1 )
		Select_Deselect_Point(&last_spec);

	box->flags |= HavePt1;

	XtVaSetValues(box->points.point_widgets[index], XtNlabel, new_label, NULL);

	Add_Set_Next_Toggle(box);
	XtSetSensitive(box->done, Add_Test_Done(box));
	Add_Set_Widths(box);
}

void
Add_Plane_Plane_Pt_Callback(Widget w, XtPointer cl, XtPointer ca)
{
	if ( new_plane_dialog.menu_hit == 1 )
	{
		new_plane_dialog.flags &= ( AllFlags ^ TwoisNorm );
		new_plane_dialog.flags |= TwoisPt;

		XtVaSetValues(new_plane_dialog.points.point_widgets[1],
					  XtNtitleLabel, "Point", NULL);
	}
	else
	{
		new_plane_dialog.flags &= ( AllFlags ^ ThreeisNorm );
		new_plane_dialog.flags |= ThreeisPt;

		XtVaSetValues(new_plane_dialog.points.point_widgets[2],
					  XtNtitleLabel, "Point", NULL);
	}

	XtSetSensitive(new_plane_dialog.done,
				   Add_Plane_Done(new_plane_dialog.flags));
	Add_Set_Widths(&new_plane_dialog);
}

void
Add_Plane_Normal_Pt_Callback(Widget w, XtPointer cl, XtPointer ca)
{
	if ( new_plane_dialog.menu_hit == 1 )
	{
		new_plane_dialog.flags &= ( AllFlags ^ TwoisPt );
		new_plane_dialog.flags |= TwoisNorm;

		XtVaSetValues(new_plane_dialog.points.point_widgets[1],
					  XtNtitleLabel, "Normal", NULL);
	}
	else
	{
		new_plane_dialog.flags &= ( AllFlags ^ ThreeisPt );
		new_plane_dialog.flags |= ThreeisNorm;

		XtVaSetValues(new_plane_dialog.points.point_widgets[2],
					  XtNtitleLabel, "Normal", NULL);
	}

	XtSetSensitive(new_plane_dialog.done,
				   Add_Plane_Done(new_plane_dialog.flags));
	Add_Set_Widths(&new_plane_dialog);
}


void
Add_Sphere_Extra_Callback(AddPointDialogPtr box, int index, Boolean state)
{
	static Boolean	last_was_err = FALSE;
	EditInfoPtr	info = Edit_Get_Info();
	char		new_label[48];
	ConstraintSpec	last_spec;

	if ( info->current_feature == origin_feature )
		return;

	if ( last_was_err )
	{
		last_was_err = FALSE;
		return;
	}

	if ( ! state )
	{
		/* Unset flags etc. */
		box->flags &= ( AllFlags ^ HavePt1 );
		XtVaSetValues(box->points.point_widgets[index],
					  XtNlabel, "?? ?? ??",
					  XtNtoggleState, TRUE, NULL);
		XtSetSensitive(box->done, FALSE);
		Add_Set_Widths(box);
		return;
	}

	/* Remember old one. */
	last_spec = specs[0];

	sprintf(new_label, "%1.3g %1.3g %1.3g",
			info->features[origin_feature].world.x,
			info->features[origin_feature].world.y,
			info->features[origin_feature].world.z);
	points[0] = info->features[origin_feature].world;
	specs[0].spec_type = feature_spec;
	specs[0].spec_data = (void*)origin_feature;
	specs[0].spec_vector = info->features[origin_feature].world;

	if ( box == &new_circle_dialog &&
		 ( new_circle_dialog.flags & HavePt2 ) &&
		 ( new_circle_dialog.flags & HavePt3 ) &&
		 ! ( new_circle_dialog.flags & HavePt4 ) )
	{
		/* Check co-linearity. */
		if ( Points_Colinear_f(points[0], points[1], points[2]))
		{
			XBell(XtDisplay(main_window.shell), 0);
			last_was_err = TRUE;
			XtVaSetValues(box->points.point_widgets[index],
						  XtNextraState, FALSE, NULL);
			return;
		}
	}

	XtVaSetValues(box->points.point_widgets[index], XtNlabel, new_label, NULL);

	if ( box->flags & HavePt1 )
		Select_Deselect_Point(&last_spec);

	box->flags |= HavePt1;

	Add_Set_Next_Toggle(box);
	XtSetSensitive(box->done, Add_Test_Done(box));
	Add_Set_Widths(box);
}

void
Add_Circle_Plane_Pt_Callback(Widget w, XtPointer cl, XtPointer ca)
{
	if ( new_circle_dialog.menu_hit == 1 )
	{
		new_circle_dialog.flags &= ( AllFlags ^ TwoisNorm );
		new_circle_dialog.flags |= TwoisPt;

		XtVaSetValues(new_circle_dialog.points.point_widgets[1],
					  XtNtitleLabel, "Point", NULL);
	}
	else
	{
		new_circle_dialog.flags &= ( AllFlags ^ ThreeisNorm );
		new_circle_dialog.flags |= ThreeisPt;

		XtVaSetValues(new_circle_dialog.points.point_widgets[2],
					  XtNtitleLabel, "Point", NULL);
	}

	XtSetSensitive(new_circle_dialog.done,
				   Add_Circle_Done(new_circle_dialog.flags));
	Add_Set_Widths(&new_circle_dialog);
}

void
Add_Circle_Normal_Pt_Callback(Widget w, XtPointer cl, XtPointer ca)
{
	if ( new_circle_dialog.menu_hit == 1 )
	{
		new_circle_dialog.flags &= ( AllFlags ^ TwoisPt );
		new_circle_dialog.flags |= TwoisNorm;

		XtVaSetValues(new_circle_dialog.points.point_widgets[1],
					  XtNtitleLabel, "Normal", NULL);
	}
	else
	{
		new_circle_dialog.flags &= ( AllFlags ^ ThreeisPt );
		new_circle_dialog.flags |= ThreeisNorm;

		XtVaSetValues(new_circle_dialog.points.point_widgets[2],
					  XtNtitleLabel, "Normal", NULL);
	}

	XtSetSensitive(new_circle_dialog.done,
				   Add_Circle_Done(new_circle_dialog.flags));
	Add_Set_Widths(&new_circle_dialog);
}

void
Add_Axis_Extra_Callback(AddPointDialogPtr box, int index, Boolean state)
{
	static Boolean	last_was_err = FALSE;
	EditInfoPtr		info = Edit_Get_Info();
	char			new_label[48];
	ConstraintSpec	last_spec;
	Vector			diff;

	if ( last_was_err )
	{
		last_was_err = FALSE;
		return;
	}

	if ( ! state )
	{
		/* Unset flags etc. */
		box->flags &= ( AllFlags ^ HavePt1 );
		XtVaSetValues(box->points.point_widgets[index],
					  XtNlabel, "?? ?? ??",
					  XtNtoggleState, TRUE, NULL);
		XtSetSensitive(box->done, FALSE);
		Add_Set_Widths(box);
		return;
	}

	/* Remember old one. */
	last_spec = specs[0];

	sprintf(new_label, "%1.3g %1.3g %1.3g",
			info->features[origin_feature].world.x,
			info->features[origin_feature].world.y,
			info->features[origin_feature].world.z);
	points[0] = info->features[origin_feature].world;
	specs[0].spec_type = feature_spec;
	specs[0].spec_data = (void*)origin_feature;
	specs[0].spec_vector = info->features[origin_feature].world;

	if ( ( new_axis_dialog.flags & HavePt2 ) &&
		 ( new_axis_dialog.flags & HavePt3 ) )
	{
		/* Check co-linearity. */
		if ( Points_Colinear_f(points[0], points[1], points[2]) )
		{
			XBell(XtDisplay(main_window.shell), 0);
			last_was_err = TRUE;
			XtVaSetValues(box->points.point_widgets[index],
						  XtNextraState, FALSE, NULL);
			return;
		}
	}
	else if ( new_axis_dialog.flags & HavePt2 )
	{
		if ( VEqual(points[0], points[1], diff) )
		{
			XBell(XtDisplay(main_window.shell), 0);
			return;
		}
	}

	XtVaSetValues(box->points.point_widgets[index], XtNlabel, new_label, NULL);

	if ( box->flags & HavePt1 )
		Select_Deselect_Point(&last_spec);

	box->flags |= HavePt1;

	Add_Set_Next_Toggle(box);
	XtSetSensitive(box->done, Add_Test_Done(box));
	Add_Set_Widths(box);
}

void
Add_Dialog_Cancel_Callback(Widget w, XtPointer cl, XtPointer ca)
{
	XtPopdown(add_dialog_box->shell);

	Edit_Cleanup_Selection(Edit_Get_Info(), TRUE);
}


void
Add_Dialog_Reset_Callback(Widget w, XtPointer cl, XtPointer ca)
{
	AddPointDialogPtr	box = (AddPointDialogPtr)cl;
	int	i;

	for ( i = 0 ; i < 6 ; i++ )
		if ( box->flags & ( 1 << i ) )
			Select_Deselect_Point(specs + i);

	if ( box == &new_plane_dialog || box == &new_circle_dialog )
		box->flags = ( TwoisPt | ThreeisPt );
	else if ( box == &new_line_dialog )
		box->flags = TwoisPt;
	else if ( box == &new_ratiopt_dialog )
	{
		if ( box->flags & RatioPoint )
			box->flags = RatioPoint;
		else
			box->flags = RatioPlane;
	}
	else
		box->flags = 0;

	Edit_Reset_Add_Dialog(box);
}

static void
Add_Dialog_Add_Constraint(EditInfoPtr info)
{
	ConstraintData	new_feature;
	Boolean		selected;
	Boolean		add = FALSE;

	/* Get the start status. */
	XtVaGetValues(add_dialog_box->select, XtNstate, &selected, NULL);

	new_feature.c_label = Strdup(add_dialog_box->name_string);
	new_feature.c_status = FALSE;
	new_feature.c_forced = FALSE;
	VNew(0, 0, 0, new_feature.c_vector);
	VNew(0, 0, 0, new_feature.c_point);
	new_feature.c_value = 0.0;
	new_feature.c_radius = 0.0;
	new_feature.c_ratio = 0.0;

	if ( add_dialog_box == &new_plane_dialog )
		add = Add_Plane_Constraint(&new_feature, add_dialog_box->flags);
	else if ( add_dialog_box == &new_line_dialog )
		add = Add_Line_Constraint(&new_feature, add_dialog_box->flags);
	else if ( add_dialog_box == &new_point_dialog )
		add = Add_Point_Constraint(&new_feature, add_dialog_box->flags);
	else if ( add_dialog_box == &new_sphere_dialog )
		add = Add_Sphere_Constraint(&new_feature, add_dialog_box->flags);
	else if ( add_dialog_box == &new_circle_dialog )
		add = Add_Circle_Constraint(&new_feature, add_dialog_box->flags);
	else if ( add_dialog_box == &new_ratiopt_dialog )
	{
		/* Defaults. */
		new_feature.c_radius = 1.0;
		new_feature.c_ratio = 1.0;
		/* Get values from the string. */
		sscanf(add_dialog_box->ratio_string, "%lf:%lf",
			   &(new_feature.c_radius), &(new_feature.c_ratio));
		if ( new_ratiopt_dialog.flags & RatioPlane )
		  add = Add_Ratio_Plane_Constraint(&new_feature, add_dialog_box->flags);
		else
		  add = Add_Ratio_Point_Constraint(&new_feature, add_dialog_box->flags);
	}
	else if ( add_dialog_box == &new_axis_dialog )
		add = Add_Axis_Constraint(&new_feature, add_dialog_box->flags);
	else
		fprintf(stderr, "Add dialog error\n");

	/* Add the constraint. */
	if ( add )
		Add_Object_Constraint(&new_feature, info->current_feature, -1,selected);
}

void
Add_Dialog_Done_Callback(Widget w, XtPointer cl, XtPointer ca)
{
	EditInfoPtr	info = Edit_Get_Info();

	XtPopdown(add_dialog_box->shell);

	Edit_Cleanup_Selection(info, TRUE);

	if ( info->constr_index != -1 )
	{
		double	ratio_1, ratio_2;

		if ( add_dialog_box == &new_ratiopt_dialog )
		{
			/* Defaults. */
			ratio_1 = 1.0;
			ratio_2 = 1.0;
			/* Get values from the string. */
			sscanf(add_dialog_box->ratio_string, "%lf:%lf", &ratio_1, &ratio_2);
		}
		else
		{
			ratio_1 = ratio_2 = 0.0;
		}
		Edit_Modify_Constraint(info, info->constr_index, specs, points,
							   add_dialog_box->flags,
							   add_dialog_box->name_string,
							   ratio_1, ratio_2, TRUE);
	}
	else
		Add_Dialog_Add_Constraint(info);
}




void
Add_Object_Constraint(ConstraintPtr new, int feature, int index,
					  Boolean selected)
{
	EditInfoPtr	info = Edit_Get_Info();
	int			i;

	if ( new->c_type == plane_feature && ( new->c_spec_flags & RatioPlane ) )
		feature_counts[ratio_plane_feature]++;
	else if ( new->c_type == point_feature &&
			  ( new->c_spec_flags & RatioPoint ) )
		feature_counts[ratio_point_feature]++;
	else if ( new->c_type == line_feature && ( new->c_spec_flags & Axis ) )
		feature_counts[axis_feature]++;
	else
		feature_counts[new->c_type]++;

	if ( index == -1 )
	{
		index = info->obj->o_features[feature].num_constraints;
		Edit_Undo_Register_State(edit_add_op, feature, index);
	}

	info->obj->o_features[feature].constraints =
		More(info->obj->o_features[feature].constraints, ConstraintData,
			 info->obj->o_features[feature].num_constraints + 1);

	/* Shuffle things up. */
	for ( i = info->obj->o_features[feature].num_constraints ; i > index ; i-- )
		info->obj->o_features[feature].constraints[i] =
			info->obj->o_features[feature].constraints[i-1];

	info->obj->o_features[feature].constraints[index] = *new;
	info->obj->o_features[feature].num_constraints++;


	if ( selected )
		new->c_status = TRUE;
	ConstraintBoxAddConstraint(feature_box[feature], new->c_label,
							   new->c_status || new->c_forced, index);

	Edit_Match_Widths();

	if ( selected )
	{
		/* Status will be set by Edit_Select_... */
		new->c_status = FALSE;
		Edit_Select_Constraint(info, index, feature, TRUE, FALSE, TRUE);
	}
}
