/*
    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:	callbacks.c						*
 *									*
 * Description:	This file contains the callbacks for the various object	*
 *		dialogs.						*
 ************************************************************************/

# include <stdio.h>
# include <X11/Intrinsic.h>
# include "Constraint.h"
# include "Force.h"
# include "Element.h"
# include "Load.h"
# include "Material.h"
# include "Node.h"
# include "problem.h"
# include "globals.h"
# include "Drawing.h"
# include "fe.h"
# include "vfe.h"


static Boolean	   first_time;
static Constraint  deleted_constraint;
static Distributed deleted_load;
static Force	   deleted_force;
static Material	   deleted_material;


/************************************************************************
 * Function:	CheckOk							*
 *									*
 * Description:	Checks if the user want to proceed with a deletion.	*
 ************************************************************************/

static int CheckOk (object, name)
    String object;
    String name;
{
    String selected;
    char   title [64];


    OutputDialogPrintf (proceed_dialog, "%s %s is still in use.  Proceed?",
			object, name);

    sprintf (title, "Delete %s", object);
    CenterOnWidget (OutputDialogShell (proceed_dialog), toplevel, True);
    WarpToCenter (OutputDialogShell (proceed_dialog));
    selected = OutputDialogSelect (proceed_dialog, title, "okay");

    return selected && !strcmp (selected, "okay");
}


/************************************************************************
 * Function:	CheckConstraintReferences				*
 *									*
 * Description:	Tree iterator to check references to a constraint.	*
 ************************************************************************/

static int CheckConstraintReferences (item)
    Item item;
{
    Node   node;
    String selected;


    node = (Node) item;

    if (node -> constraint == deleted_constraint) {
	error ("Constraint %s is still in use.", deleted_constraint -> name);
	return 1;
    }

    return 0;
}


/************************************************************************
 * Function:	ConstraintDialogChanged					*
 *									*
 * Description:	Callback for when a change occurs in the constraint	*
 *		dialog.							*
 ************************************************************************/

void ConstraintDialogChanged (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    Node		  node;
    NodeDialog		  node_d;
    ConstraintDialogInfo *info;


    info = (ConstraintDialogInfo *) call_data;
    node_d = (NodeDialog) client_data;

    if (info -> deleted == True) {
	deleted_constraint = info -> constraint;
	TreeSetIterator (problem.node_tree, CheckConstraintReferences);
	info -> proceed = TreeIterate (problem.node_tree) ? False : True;
    }

    if (info -> proceed == True) {
	changeflag = True;
	NodeDialogUpdate (node_d, NULL, NULL, problem.constraint_tree);
	node = NodeDialogActive (node_d);
	if (node != NULL && info -> constraint == node -> constraint)
	    NodeDialogDisplay (node_d, node);
    }
}


/************************************************************************
 * Function:	ElementDialogChanged					*
 *									*
 * Description:	Callback for when a change occurs in the element	*
 *		dialog.							*
 ************************************************************************/

void ElementDialogChanged (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    unsigned	       i;
    unsigned	       numnodes;
    Drawn	       drawn;
    ElementDialogInfo *info;


    info = (ElementDialogInfo *) call_data;
    numnodes = info -> element -> definition -> numnodes;

    if (info -> deleted == True) {
	drawn = (Drawn) info -> element -> aux;
	if (drawn -> figure != NULL) {
	    DW_SetAutoRedraw (drawing, False);
	    DW_RemoveFigure  (drawing, drawn -> figure);
	    DW_RemoveFigure  (drawing, drawn -> label);
	    DW_SetAutoRedraw (drawing, True);
	}

	for (i = 1; i <= numnodes; i ++)
	    if (info -> element -> node [i] != NULL)
		((Drawn) (info -> element -> node [i] -> aux)) -> ref_count --;

    } else
	for (i = 1; i <= numnodes; i ++)
	    if (info -> original -> node [i] != info -> element -> node [i]) {
		MoveElement (info -> element, info -> original -> node);
		break;
	    }

    changeflag = True;
}


/************************************************************************
 * Function:	CheckForceReferences					*
 *									*
 * Description:	Tree iterator to check references to a force.		*
 ************************************************************************/

static int CheckForceReferences (item)
    Item item;
{
    Node node;


    node = (Node) item;

    if (node -> force == deleted_force) {
	if (first_time) {
	    if (!CheckOk ("Force", deleted_force -> name))
		return 1;
	    first_time = False;
	}
	node -> force = NULL;
    }

    return 0;
}


/************************************************************************
 * Function:	ForceDialogChanged					*
 *									*
 * Description:	Callback for when a change occurs in the force dialog.	*
 ************************************************************************/

void ForceDialogChanged (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    Boolean	     displayed;
    Node	     node;
    ForceDialogInfo *info;
    NodeDialog	     node_d;


    info = (ForceDialogInfo *) call_data;
    node_d = (NodeDialog) client_data;

    node = NodeDialogActive (node_d);
    displayed = node != NULL && node -> force == info -> force;

    if (info -> deleted == True) {
	first_time = True;
	deleted_force = info -> force;

	TreeSetIterator (problem.node_tree, CheckForceReferences);
	if (TreeIterate (problem.node_tree)) {
	    info -> proceed = False;
	    return;
	}
    }

    changeflag = True;
    NodeDialogUpdate (node_d, NULL, problem.force_tree, NULL);

    if (displayed == True)
	NodeDialogDisplay (node_d, node);
}


/************************************************************************
 * Function:	CheckLoadReferences					*
 *									*
 * Description:	Tree iterator to check references to a load.		*
 ************************************************************************/

static int CheckLoadReferences (item)
    Item item;
{
    Cardinal i;
    Cardinal j;
    Element  element;


    element = (Element) item;

    for (i = 1; i <= element -> numdistributed; i ++)
	if (element -> distributed [i] == deleted_load) {
	    if (first_time) {
		if (!CheckOk ("Load", deleted_load -> name))
		    return 1;
		first_time = False;
	    }
	    for (j = i + 1; j <= element -> numdistributed; j ++)
		element -> distributed [j - 1] = element -> distributed [j];
	    element -> numdistributed --;
	}

    return 0;
}


/************************************************************************
 * Function:	LoadDialogChanged					*
 *									*
 * Description:	Callback for when a change occurs in the load dialog.	*
 ************************************************************************/

void LoadDialogChanged (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    Cardinal	    i;
    Boolean	    displayed;
    Element	    element;
    LoadDialogInfo *info;
    ElementDialog   element_d;


    info = (LoadDialogInfo *) call_data;
    element_d = (ElementDialog) client_data;

    displayed = False;
    if ((element = ElementDialogActive (element_d)) != NULL)
	for (i = 1; i <= element -> numdistributed; i ++)
	    if (element -> distributed [i] == info -> load)
		displayed = True;


    if (info -> deleted == True) {
	first_time = True;
	deleted_load = info -> load;

	TreeSetIterator (problem.element_tree, CheckLoadReferences);
	if (TreeIterate (problem.element_tree)) {
	    info -> proceed = False;
	    return;
	}

    }

    changeflag = True;
    ElementDialogUpdate (element_d, NULL, NULL, problem.distributed_tree, NULL);

    if (displayed == True)
	ElementDialogDisplay (element_d, element);
}


/************************************************************************
 * Function:	CheckMaterialReferences					*
 *									*
 * Description:	Tree iterator to check references to a material.	*
 ************************************************************************/

static int CheckMaterialReferences (item)
    Item item;
{
    Element element;


    element = (Element) item;

    if (element -> material == deleted_material) {
	error ("Material %s is still in use.", deleted_material -> name);
	return 1;
    }

    return 0;
}


/************************************************************************
 * Function:	MaterialDialogChanged					*
 *									*
 * Description:	Callback for when a change occurs in the material	*
 *		dialog.							*
 ************************************************************************/

void MaterialDialogChanged (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    Element		element;
    MaterialDialogInfo *info;
    ElementDialog	element_d;


    info = (MaterialDialogInfo *) call_data;
    element_d = (ElementDialog) client_data;

    if (info -> deleted == True) {
	deleted_material = info -> material;
	TreeSetIterator (problem.element_tree, CheckMaterialReferences);
	info -> proceed = TreeIterate (problem.element_tree) ? False : True;
    }

    if (info -> proceed == True) {
	changeflag = True;
	ElementDialogUpdate(element_d, NULL, problem.material_tree, NULL, NULL);
	element = ElementDialogActive (element_d);
	if (element != NULL && info -> material == element -> material)
	    ElementDialogDisplay (element_d, element);
    }
}


/************************************************************************
 * Function:	NodeDialogChanged					*
 *									*
 * Description:	Callback for when a change occurs in the node dialog.	*
 ************************************************************************/

void NodeDialogChanged (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    Drawn	    drawn;
    NodeDialogInfo *info;
    ElementDialog   element_d;


    info = (NodeDialogInfo *) call_data;
    element_d = (ElementDialog) client_data;

    if (info -> deleted == True) {
	drawn = (Drawn) info -> node -> aux;
	if (drawn -> ref_count) {
	    error ("Node %u is still referenced by %u element%s.",
		   info -> node -> number, drawn -> ref_count,
		   drawn -> ref_count > 1 ? "s" : "");
	    info -> proceed = False;
	    return;
	}

	if (drawn -> figure != NULL) {
	    DW_SetAutoRedraw (drawing, False);
	    DW_RemoveFigure (drawing, drawn -> figure);
	    if (drawn -> label != NULL)
		DW_RemoveFigure (drawing, drawn -> label);
	    DW_SetAutoRedraw (drawing, True);
	}

    } else if (info -> moved == True)
	DoWalkNode (info -> node);

    ElementDialogUpdate (element_d, NULL, NULL, NULL, problem.node_tree);
    changeflag = True;
}
