/*
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 * 
 */

/************************************************************
*	INCLUDE FILES
*************************************************************/
#include <stdio.h>

#include "XmI.h"
#include <Xm/IconBoxP.h>
#include <Xm/ExtP.h>

/************************************************************
*	TYPEDEFS AND DEFINES
*************************************************************/

#define SUPERCLASS ((WidgetClass) &xmManagerClassRec)

/************************************************************
*	MACROS
*************************************************************/
#define GetIconInfo(w) ((IconInfo*) \
           &(((XmIconBoxConstraintsRec*)((char*)((w)->core.constraints)))->icon))


/************************************************************
*	GLOBAL DECLARATIONS
*************************************************************/

/************************************************************
*	STATIC FUNCTION DECLARATIONS
*************************************************************/

static void ClassInitialize();
static void ClassPartInitialize(WidgetClass w_class);
static void Realize(Widget, Mask *, XSetWindowAttributes *);
static void Resize(Widget), ChangeManaged(Widget), InsertChild(Widget);
static void Initialize(Widget, Widget, ArgList, Cardinal *);
static void ConstraintInitialize(Widget, Widget, ArgList, Cardinal *);
static Boolean ConstraintSetValues(Widget, Widget, Widget, ArgList, Cardinal*);

static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal *);

static XtGeometryResult GeometryManager(Widget, XtWidgetGeometry *,
					XtWidgetGeometry *);
static XtGeometryResult QueryGeometry(Widget,
				      XtWidgetGeometry *, XtWidgetGeometry *);

/************************
 * Actions and callbacks.
 ************************/

/*********************
 * Internal Routines.
 *********************/

static void FindNearestCellLocation(Widget, Position *, Position *);
static void GetMinCells(Widget, Cardinal *, Cardinal *);
static void PlaceChildren(Widget, Widget);
static void GetMaxCellSize(Widget, Widget, Dimension *, Dimension *);
static void GetCellFromXY(Widget, Position, Position, Position *, Position *);
static void GetXYFromCell(Widget, IconInfo *, Position *, Position *);
static void CalcCellSizes(Widget, Widget,
			  Boolean, Boolean, Dimension *, Dimension *);

static Boolean SetToEmptyCell(Widget);

/************************************************************
*	STATIC DECLARATIONS
*************************************************************/

static XtResource resources[] =
{
  {
    XmNminimumVerticalCells, XmCDefaultCells, XmRDimension,
    sizeof(Dimension), XtOffsetOf(XmIconBoxRec, box.min_v_cells),
    XmRImmediate, (XtPointer) 2
  },
  {
    XmNminimumHorizontalCells, XmCDefaultCells, XmRHorizontalDimension, 
    sizeof(Dimension), XtOffsetOf(XmIconBoxRec, box.min_h_cells),
    XmRImmediate, (XtPointer) 2
  },

  {
    XmNminimumCellWidth, XmCMinimumCellSize, XmRHorizontalDimension,
    sizeof(Dimension), XtOffsetOf(XmIconBoxRec, box.min_cell_width),
    XmRImmediate, (XtPointer) 20
  },

  {
    XmNminimumCellHeight, XmCMinimumCellSize, XmRDimension,
    sizeof(Dimension), XtOffsetOf(XmIconBoxRec, box.min_cell_height),
    XmRImmediate, (XtPointer) 10
  },

  {
    XmNverticalMargin, XmCMargin, XmRVerticalDimension,
    sizeof(Dimension), XtOffsetOf(XmIconBoxRec, box.v_margin),
    XmRImmediate, (XtPointer) 4
  },
  
  {
    XmNhorizontalMargin, XmCMargin, XmRHorizontalDimension,
    sizeof(Dimension), XtOffsetOf(XmIconBoxRec, box.h_margin),
    XmRImmediate, (XtPointer) 4
  }
};

static XmSyntheticResource get_resources[] =
{
  {
    XmNhorizontalMargin, sizeof(Dimension),
    XtOffsetOf(XmIconBoxRec, box.h_margin),
    XmeFromHorizontalPixels, (XmImportProc) XmeToHorizontalPixels
  },

  {
    XmNverticalMargin, sizeof(Dimension),
    XtOffsetOf(XmIconBoxRec, box.v_margin),
    XmeFromVerticalPixels, (XmImportProc) XmeToVerticalPixels
  },

  {
    XmNminimumCellWidth, sizeof(Dimension),
    XtOffsetOf(XmIconBoxRec, box.min_cell_width),
    XmeFromHorizontalPixels, (XmImportProc) XmeToHorizontalPixels
  },

  {
    XmNminimumCellHeight, sizeof(Dimension),
    XtOffsetOf(XmIconBoxRec, box.min_cell_height),
    XmeFromVerticalPixels, (XmImportProc) XmeToVerticalPixels
  }
};

static short G_any_cell = XmIconBoxAnyCell;

static XtResource constraints[] =
{
  {
    XmNcellX, XmCCellX, XmRShort,
    sizeof(short), XtOffsetOf(XmIconBoxConstraintsRec, icon.cell_x),
    XmRShort, (XtPointer) &G_any_cell
  },

  {
    XmNcellY, XmCCellY, XmRShort,
    sizeof(short), XtOffsetOf(XmIconBoxConstraintsRec, icon.cell_y),
    XmRShort, (XtPointer) &G_any_cell
  }
};
 
XmIconBoxClassRec xmIconBoxClassRec = {
  { /* core fields */
    /* superclass		*/	SUPERCLASS,
    /* class_name		*/	"XmIconBox",
    /* widget_size		*/	sizeof(XmIconBoxRec),
    /* class_initialize		*/	ClassInitialize,
    /* class_part_initialize	*/	ClassPartInitialize,
    /* class_inited		*/	FALSE,
    /* initialize		*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	Realize,
    /* actions			*/	NULL,
    /* num_actions		*/	0,
    /* resources		*/	(XtResource*)resources,
    /* num_resources		*/	XtNumber(resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	NULL,
    /* resize			*/	Resize,
    /* expose			*/	NULL,
    /* set_values		*/	SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	XtInheritTranslations,
    /* query_geometry		*/	(XtGeometryHandler) QueryGeometry,
    /* display_accelerator	*/	XtInheritDisplayAccelerator,
    /* extension		*/	NULL
  },
   {		/* composite_class fields */
    /* geometry_manager   */      GeometryManager,
    /* change_managed     */      ChangeManaged,
    /* insert_child       */      InsertChild,			
    /* delete_child       */      XtInheritDeleteChild,			
    /* extension          */      NULL,                                     
   },
   {		/* constraint_class fields */
    /* resource list        */         (XtResource*)constraints,
    /* num resources        */         XtNumber(constraints),	
    /* constraint size      */         sizeof(XmIconBoxConstraintsRec),
    /* init proc            */         ConstraintInitialize,
    /* destroy proc         */         NULL,
    /* set values proc      */         ConstraintSetValues,
    /* extension            */         NULL, 
   },
   {		/* manager_class fields */
    /* default translations   */      XtInheritTranslations,	
    /* syn_resources          */      get_resources,
    /* num_syn_resources      */      XtNumber(get_resources),
    /* syn_cont_resources     */      NULL,
    /* num_syn_cont_resources */      0,
    /* parent_process         */      XmInheritParentProcess,
    /* extension	      */      NULL,	
   },
  { /* Icon Box fields */
      NULL                      /* extension          */
  }
};

WidgetClass xmIconBoxWidgetClass = (WidgetClass)&xmIconBoxClassRec;

/************************************************************
*	STATIC CODE
*************************************************************/

/*	Function Name: Initialize
 *	Description:   Called to initialize information specific
 *                     to this widget.
 *	Arguments:     req - what was originally requested.
 *                     set - what will be created (our superclassed have
 *                           already mucked with this)
 *                     args, num_args - The arguments passed to 
 *                                      the creation call.
 *	Returns:       none.
 */


static void
ClassInitialize()
{
  /* do nothing */
}

/*ARGSUSED*/
static void 
Initialize(Widget req, Widget set, ArgList args, Cardinal * num_args)
{
    XmIconBoxWidget ibw = (XmIconBoxWidget) set;

    /*
     * This is needed so that the right thing happens if an icon box is 
     * created w/o any children.
     */

    CalcCellSizes(set, NULL, FALSE, FALSE, 
		  &(XmIconBox_cell_width(ibw)), &(XmIconBox_cell_height(ibw)));
}

/*	Function Name: Realize
 *	Description:   Called to realize this widget.
 *	Arguments:     w - Widget to realize.
 *                     valueMask, attributes - attributes to use when creating
 *                     this widget's window.
 *	Returns:       none.
 *
 * This overrides the Manager's frobbing with various values.
 */

static void 
Realize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
{
    XtCreateWindow (w, InputOutput, CopyFromParent, *valueMask, attributes);
}

/*	Function Name: Resize
 *	Description:   Called when this widget has been resized.
 *	Arguments:     w - the widget to resize. 
 *	Returns:       none.
 */

static void 
Resize(Widget w)
{
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;

    CalcCellSizes(w, NULL, TRUE, FALSE, 
		  &(XmIconBox_cell_width(ibw)), &(XmIconBox_cell_height(ibw)));
    PlaceChildren(w, NULL);
}

/*	Function Name: SetValues
 *	Description:   Called when some widget data needs to be modified on-
 *                     the-fly.
 *	Arguments:     current - the current (old) widget values.
 *                     request - before superclassed have changed things.
 *                     set - what will acutally be the set values. 
 *                     args, num_args - the arguments in the list.
 *	Returns:       none
 */

/*ARGSUSED*/
static Boolean 
SetValues(Widget current, Widget request, Widget set,
	  ArgList args, Cardinal * num_args)
{
    XmIconBoxWidget old_ibw = (XmIconBoxWidget) current;
    XmIconBoxWidget set_ibw = (XmIconBoxWidget) set;

    if ((XmIconBox_min_v_cells(old_ibw) != XmIconBox_min_v_cells(set_ibw))           ||
	(XmIconBox_min_h_cells(old_ibw) != XmIconBox_min_h_cells(set_ibw))           ||
	(XmIconBox_min_cell_width(old_ibw) != XmIconBox_min_cell_width(set_ibw))     ||
	(XmIconBox_min_cell_height(old_ibw) != XmIconBox_min_cell_height(set_ibw))   ||
	(XmIconBox_v_margin(old_ibw) != XmIconBox_v_margin(set_ibw))                 ||
	(XmIconBox_h_margin(old_ibw) != XmIconBox_h_margin(set_ibw)) ) 
    {
	CalcCellSizes(set, NULL, FALSE, FALSE, 
		      &(XmIconBox_cell_width(set_ibw)), &(XmIconBox_cell_height(set_ibw)));
	PlaceChildren(set, NULL);
    }

    return(FALSE);
}

/*	Function Name: QueryGeometry
 *	Description:   Called when my parent wants to know what size
 *                     I would like to be.
 *	Arguments:     w - the widget to check.
 *                     indended - constriants imposed by the parent.
 *                     preferred - what I would like.
 *	Returns:       See Xt Manual.
 */
    
static XtGeometryResult 
QueryGeometry(Widget w,XtWidgetGeometry *intended, XtWidgetGeometry *preferred)
{
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;
    Cardinal min_x, min_y;
    Dimension max_w, max_h;

    GetMinCells(w, &min_x, &min_y);
    GetMaxCellSize(w, NULL, &max_w, &max_h);

    min_x++;
    min_y++;

    preferred->width = XmIconBox_h_margin(ibw) + min_x * (max_w + XmIconBox_h_margin(ibw));
    preferred->height= XmIconBox_v_margin(ibw) + min_y * (max_h + XmIconBox_v_margin(ibw));

    return(_XmHWQuery(w, intended, preferred));
}

/************************************************************
 *
 * Composite and Constraint Information.
 *
 ************************************************************/

/*	Function Name: GeometryManager
 *	Description:   handles requests from children for a size change.
 *	Arguments:     child - the child to change.
 *                     request - the geometry that the child wants.
 *                     return - what we will allow if this is an almost.
 *	Returns:       status.
 */

/*ARGSUSED*/
static XtGeometryResult
GeometryManager(Widget w, XtWidgetGeometry * request, 
		XtWidgetGeometry * result)
{
    Dimension cwidth, cheight;
    XmIconBoxWidget ibw = (XmIconBoxWidget) XtParent(w);
    IconInfo * info = GetIconInfo(w);
    Boolean w_req = (request->request_mode & CWWidth);
    Boolean h_req = (request->request_mode & CWHeight);
    Boolean x_req = (request->request_mode & CWX);
    Boolean y_req = (request->request_mode & CWY);

    if (!(request->request_mode & (CWWidth | CWHeight | CWX | CWY)))
	return(XtGeometryNo);
    
    result->request_mode = 0;

    if (w_req || h_req) {
	CalcCellSizes((Widget) ibw, w, FALSE, TRUE, &cwidth, &cheight);

	if(w_req)
	    ASSIGN_MAX(cwidth, request->width);
	else
	    ASSIGN_MAX(cwidth, w->core.width);
	
	if(h_req)
	    ASSIGN_MAX(cheight, request->height);
	else
	    ASSIGN_MAX(cheight, w->core.height);

	/*
	 * Do not allow an x, y width and height request at the same time.
	 * since it is unclear what this would mean.  I want to place the
	 * widget in the cell the center of it is over.  With a multiple
	 * request like this is it tough to find out which cell to use
	 * since there are two reasonable values for height and width.
	 */

	result->x = w->core.x;
	result->y = w->core.y;
	result->width  = cwidth;
	result->height = cheight;
	result->request_mode |= CWX | CWY | CWHeight | CWWidth;
    }
    else if ( x_req || y_req ) {
	Position x, y;
	short cell_x, cell_y;

	if (x_req) 
	    x = request->x;
	else
	    x = w->core.x;

	if (y_req)
	    y = request->y;
	else
	    y = w->core.y;

	FindNearestCellLocation((Widget) ibw, &x, &y);

	GetCellFromXY((Widget) ibw, x, y, &cell_x, &cell_y);
	if (XmIconBoxIsCellEmpty((Widget) ibw, cell_x, cell_y, w)) {
	    result->x = x;
	    result->y = y;
	    result->request_mode |= CWX | CWY;
	}
	else			/* Cell is full, return NO. */
	    return(XtGeometryNo);
    }

    if (((request->x == result->x) || !x_req) &&
	((request->y == result->y) || !y_req) &&
	((request->width == result->width) || !w_req) &&
	((request->height == result->height) || !h_req))
    {
	if (request->request_mode & 
	    (CWBorderWidth | CWStackMode | CWSibling)) 
	{
	    return(XtGeometryAlmost);
	}

	if (request->request_mode & XtCWQueryOnly) 
	    return(XtGeometryYes);

	if (w_req || h_req) {
	    if (w_req) 
		info->pref_width = w->core.width = request->width;

	    if (h_req)
		info->pref_height = w->core.height = request->height;
	}
	else {
	    /*
	     * NOTE: We are assuming here that the cell height/width
	     * did not change.  This is valid because this code is
	     * only executed if w_req and h_req are false.
	     */

	    GetCellFromXY((Widget) ibw, result->x, result->y, 
			  &(info->cell_x), &(info->cell_y));
	}
	    
	CalcCellSizes((Widget) ibw, NULL, FALSE, FALSE, 
		      &(XmIconBox_cell_width(ibw)), &(XmIconBox_cell_height(ibw)));

	PlaceChildren((Widget) ibw, w);
	return(XtGeometryYes);
    }

    info->pref_width = info->pref_height = 0; /* invalidate cache. */

    if (((request->x == result->x) || !x_req)         ||
	((request->y == result->y) || !y_req)         ||
	((request->width == result->width) || !w_req) ||
	((request->height == result->height) || !h_req))
    {
	return(XtGeometryAlmost);
    }
    else 
	return(XtGeometryNo);
}

/*	Function Name: InsertChild
 *	Description: called when a new child is to be added.
 *	Arguments: w - the new child.
 *	Returns: none.
 *
 * This routine simply makes sure that no gadgets are added.
 */

static void 
InsertChild(Widget w)
{
   if (_XmGadgetWarning(w))
       return;

   {
      XtWidgetProc insert_child;

      _XmProcessLock();
      insert_child = ( (CompositeWidgetClass) SUPERCLASS)->composite_class.insert_child;
      _XmProcessUnlock();

      (*insert_child)(w);
   }
}

/*	Function Name: ChangeManaged
 *	Description:   When a management change has occured...
 *	Arguments:     w - the icon box widget.
 *	Returns:       none.
 */

static void
ChangeManaged(Widget w)
{
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;
    Widget * childp;

    CalcCellSizes(w, NULL, FALSE, TRUE, 
		  &(XmIconBox_cell_width(ibw)), &(XmIconBox_cell_height(ibw)));

    ForAllChildren(ibw, childp) {   
	IconInfo * info = GetIconInfo(*childp);

	if ((info->cell_x != XmIconBoxAnyCell) && 
	    (info->cell_y != XmIconBoxAnyCell) &&
	    !XmIconBoxIsCellEmpty((Widget) ibw, 
				  info->cell_x, info->cell_y, *childp))
	{
	    static String params[1];
	    Cardinal num = 1;
	    char buf[BUFSIZ];
	    
	    params[0] = buf;
	    snprintf(buf, BUFSIZ, "(%d, %d)", info->cell_x, info->cell_y);
	    
	    _XmWarningMsg(w, XmNcellNotEmpty,
		    XmNcellNotEmptyMsg, params, num);
	    /*
	     * tell it to reset this to an empty cell.
	     */
	    
	    info->cell_y = XmIconBoxAnyCell; 
	}

	if ((info->cell_x == XmIconBoxAnyCell) || 
	    (info->cell_y == XmIconBoxAnyCell))
	{
	    Position x = (*childp)->core.x;
	    Position y = (*childp)->core.y;
	    Position cell_x, cell_y;
	    
	    /*
	     * If the cell location is not specified try to find the
	     * cell nearest the X and Y coords specified.
	     */
	    
	    FindNearestCellLocation((Widget) ibw, &x, &y);
	    GetCellFromXY((Widget) ibw, x, y, &cell_x, &cell_y);
	    
	    if (XmIconBoxIsCellEmpty((Widget) ibw, cell_x, cell_y, w))
	    {
		info->cell_x = cell_x;
		info->cell_y = cell_y;
	    }
	    /*
	     * If this cell is full the just find any empty cell.
	     */
	    else if (!SetToEmptyCell(*childp)) {
		XmeWarning(w, XmNnoEmptyCellsMsg);
	    }
	}
    }

    CalcCellSizes(w, NULL, FALSE, FALSE, 
		  &(XmIconBox_cell_width(ibw)), &(XmIconBox_cell_height(ibw)));

    PlaceChildren(w, NULL);

   XmeNavigChangeManaged(w);	/* For Motif navigation */
}

/*
 * ClassPartInitialize sets up the fast subclassing for the widget.
 */
static void 
#ifdef _NO_PROTO
ClassPartInitialize(w_class)
        WidgetClass w_class ;
#else
ClassPartInitialize(WidgetClass w_class)
#endif /* _NO_PROTO */
{
    _XmFastSubclassInit (w_class, XmICONBOX_BIT);
}


/*	Function Name: ConstraintInitialize
 *	Description:   Called when a childs constriaints need initializing.
 *	Arguments:     req - the widget being requested.
 *                     set - what this will become.
 *                     args, num_args - the argument list.
 *	Returns:       none.
 */

/*ARGSUSED*/
static void
ConstraintInitialize(Widget req, Widget set, ArgList args, Cardinal * num_args)
{
    IconInfo * info = GetIconInfo(set);

    info->pref_width = req->core.width;
    info->pref_height = req->core.height;
}

/*	Function Name: ConstraintSetValues
 *	Description:   Called when some constraint data needs to be modified 
 *                     on-the-fly.
 *	Arguments:     current - the current (old) widget values.
 *                     request - before superclassed have changed things.
 *                     set - what will acutally be the new values. 
 *                     args, num_args - the arguments in the list.
 *	Returns:       none
 */

/*ARGSUSED*/
static Boolean 
ConstraintSetValues(Widget current, Widget request, Widget set,
		    ArgList args, Cardinal * num_args)
{
    IconInfo * set_info = GetIconInfo(set);
    IconInfo * old_info = GetIconInfo(current);

    if (set->core.width != set_info->pref_width)
	set_info->pref_width = 0;

    if (set->core.height != set_info->pref_height)
	set_info->pref_height = 0;

    if ((set_info->cell_x != old_info->cell_x) ||
	(set_info->cell_y != old_info->cell_y)) 
    {
	if ( XmIconBoxIsCellEmpty(XtParent(set), 
				  set_info->cell_x, set_info->cell_y, set)) 
	{
	    GetXYFromCell(XtParent(set), 
			  set_info, &(set->core.x), &(set->core.y));
	}
	else {
	    static String params[1];
	    Cardinal num = 1;
	    char buf[BUFSIZ];
	    
	    params[0] = buf;
	    snprintf(buf, BUFSIZ, "(%d, %d)", set_info->cell_x, set_info->cell_y);

	    _XmWarningMsg(set, XmNcellNotEmpty,
		    XmNcellNotEmptyMsg, params, num);

	    set_info->cell_x = old_info->cell_x;
	    set_info->cell_y = old_info->cell_y;
	}			 
    }
    
    return(False);
}

/************************************************************
 *
 * Actions and Callbacks.
 *
 ************************************************************/

/************************************************************
 *
 * Internal routines.
 *
 ************************************************************/

/*	Function Name: GetCellFromXY
 *	Description:   Gets the cell located at this location.
 *	Arguments:     w - the icon box.
 *                     x, y - the coordinates in X space.
 * RETURNED            cell_x, cell_y - the corrdinates in cell space.
 *	Returns:       none.
 */

static void
GetCellFromXY(Widget w, 
	      Position x, Position y, Position * cell_x, Position * cell_y)
{
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;
    
    *cell_x = (int)x / (int)(XmIconBox_cell_width(ibw) + XmIconBox_h_margin(ibw));
    *cell_y = (int)y / (int)(XmIconBox_cell_height(ibw) + XmIconBox_v_margin(ibw));
}

/*	Function Name: Find Nearest Cell Location
 *	Description: Finds the nearest cell
 *	Arguments: ibw - the icon box widget.
 * IN/OUT          x, y - Used as an input for the current location
 *                        is output with the new location.
 *	Returns: none
 */

static void
FindNearestCellLocation(Widget w, Position *x, Position *y)
{
    IconInfo temp;
    Dimension width, height;
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;

    width = XmIconBox_cell_width(ibw);
    height = XmIconBox_cell_height(ibw);

    GetCellFromXY(w, *x + width/2, 
		  *y + height/2, &(temp.cell_x), &(temp.cell_y));
    
    GetXYFromCell(w, &temp, x, y);
}

/*	Function Name: GetXYFromCell
 *	Description:   Gets the X and Y position for this cell.
 *	Arguments:     info - the cell info.
 * RETURNED            x, y - the coordinates in X space.
 *	Returns:       none.
 */

static void
GetXYFromCell(Widget w, IconInfo * info, Position * x, Position * y)
{
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;
    Position x_temp = (info->cell_x < 0) ? 0 : info->cell_x;
    Position y_temp = (info->cell_y < 0) ? 0 : info->cell_y;

    *x = XmIconBox_h_margin(ibw) + 
	 x_temp * (XmIconBox_cell_width(ibw) + XmIconBox_h_margin(ibw));
    
    *y = XmIconBox_v_margin(ibw) + 
	 y_temp * (XmIconBox_cell_height(ibw) + XmIconBox_v_margin(ibw));
}

/*	Function Name: PlaceChildren
 *	Description:   Places all managed children correctly.
 *	Arguments:     w - the icon box widget.
 *                     child - set attributes rather than configure this child.
 *	Returns:       none.
 */

static void 
PlaceChildren(Widget w, Widget child)
{
    Widget * childp;
    Position x, y;
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;

    ForAllChildren(ibw, childp) {
	if (!XtIsManaged(*childp))
	    continue;

	GetXYFromCell(w, GetIconInfo(*childp), &x, &y);

	if (*childp == child) {
	    child->core.x = x;
	    child->core.y = y;
	    child->core.width = XmIconBox_cell_width(ibw);
	    child->core.height = XmIconBox_cell_height(ibw);
	}
	else {
	    _XmConfigureWidget(*childp, x, y, 
			       XmIconBox_cell_width(ibw), XmIconBox_cell_height(ibw),
			       (*childp)->core.border_width);
	}	
    }
}

/*	Function Name: CalcCellSizes
 *	Description:   Calculates the height and width of each cell.
 *	Arguments:     w - the icon box widget.
 *                     ignore - ignore this child when calculating cell sizes.
 *                     noresize - If true then never attempt a resize.
 *                     query_only - only ask, don't change anything.
 *                     cell_width, cell_height - new size of each cell.
 *	Returns:       none.
 */

static void
CalcCellSizes(Widget w, Widget ignore, Boolean noresize, Boolean query_only, 
	      Dimension * cell_width, Dimension * cell_height)
{
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;

    Cardinal min_x, min_y;
    Dimension max_w, max_h, d_width, d_height, width, height;
    
    GetMinCells(w, &min_x, &min_y);
    GetMaxCellSize(w, ignore, &max_w, &max_h);

    min_x++;
    min_y++;

    d_width = XmIconBox_h_margin(ibw) + min_x * (max_w + XmIconBox_h_margin(ibw));
    d_height = XmIconBox_v_margin(ibw) + min_y * (max_h + XmIconBox_v_margin(ibw));

    if (noresize ||
	(_XmRequestNewSize(w, query_only, d_width, d_height, 
			   &width, &height) != XtGeometryYes))
    {
	if (noresize) {
	    width = w->core.width;
	    height = w->core.height;
	}

	/*
	 * We may need to adjust the cell size.
	 */
	
	if (width < d_width)
	    max_w = (width - XmIconBox_h_margin(ibw))/ min_x - XmIconBox_h_margin(ibw); 
	if (height < d_height)
	    max_h = (height - XmIconBox_v_margin(ibw))/ min_y - XmIconBox_v_margin(ibw); 
    }

    *cell_width = max_w;
    *cell_height = max_h;
}

/*	Function Name: GetMinCells
 *	Description:   Returns the minimum number of cells that should
 *                     be displayed in each direction.
 *	Arguments:     w - the Icon Box widget.
 * RETURN              min_x, min_y - minimum number of cells needed
 *                                    in each direction.
 *                     
 *	Returns:       
 */

static void
GetMinCells(Widget w, Cardinal * min_x, Cardinal * min_y)
{
    Widget * childp;
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;

    *min_x = XmIconBox_min_h_cells(ibw) - 1;
    *min_y = XmIconBox_min_v_cells(ibw) - 1;

    ForAllChildren(ibw, childp) {
	Position x, y;
	IconInfo * info;

	if (!XtIsManaged(*childp))
	    continue;

	info = GetIconInfo(*childp);

	x = (info->cell_x < 0) ? 0 : info->cell_x;
	y = (info->cell_y < 0) ? 0 : info->cell_y;

	if (x > *min_x)
	    *min_x = x;

	if (y > *min_y)
	    *min_y = y;
    }
}

/*	Function Name: GetMaxCellSize
 *	Description:   Gets the maximum size of each cell.
 *	Arguments:     w - the Icon Box widget.
 *                     ignore - ignore this child.
 *                     max_w, max_h - the maximum size of each cell.
 *	Returns:       none.
 */

static void
GetMaxCellSize(Widget w, Widget ignore, Dimension * max_w, Dimension * max_h)
{
    Widget * childp;
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;
    XtWidgetGeometry preferred;
    register Dimension temp;

    *max_w = XmIconBox_min_cell_width(ibw);
    *max_h = XmIconBox_min_cell_height(ibw);

    ForAllChildren(ibw, childp) {
	IconInfo * info = GetIconInfo(*childp);

	if (!XtIsManaged(*childp) || (*childp == ignore))
	    continue;

	if ((info->pref_width != 0) && (info->pref_height != 0)) {
	    preferred.width = info->pref_width;
	    preferred.height = info->pref_height;
	    preferred.border_width = (*childp)->core.border_width;
	}
	else {
	    (void) XtQueryGeometry(*childp, NULL, &preferred);
	    info->pref_width = preferred.width;
	    info->pref_height = preferred.height;
	}

	temp = preferred.width + 2 * preferred.border_width;
	ASSIGN_MAX(*max_w, temp);

	temp = preferred.height + 2 * preferred.border_width;
	ASSIGN_MAX(*max_h, temp);
    }
}

/*	Function Name: SetToEmptyCell
 *	Description:   Puts the widget passed into an empty node as
 *                     close to 0, 0 as possible.
 *	Arguments:     child - the child.
 *	Returns:       True if an empty cell was found.
 */

static Boolean
SetToEmptyCell(Widget child)
{
    XmIconBoxWidget ibw = (XmIconBoxWidget) XtParent(child);

    register Position x, y, cur_x, cur_y;
    register unsigned long square, cur_square;
    Cardinal max_x, max_y;

    GetMinCells((Widget) ibw, &max_x, &max_y);

    cur_x = cur_y = XmIconBoxAnyCell;
    cur_square = max_x * max_x + max_y * max_y;

    for (y = 0; y <= max_y; y++) 
	for (x = 0; x <= max_x; x++) {
	    square = x * x + y * y;

	    if (square <= cur_square &&
		XmIconBoxIsCellEmpty(XtParent(child), x, y, NULL)) {
		cur_square = square;
		cur_x = x;
		cur_y = y;
		break;
	    }
	    else if (square >= cur_square)
		continue;
	}

    if (cur_x == XmIconBoxAnyCell) {
	IconInfo * info = GetIconInfo(child);

	info->cell_x = 0;
	info->cell_y = max_y + 1;
    }
    else {
	IconInfo * info = GetIconInfo(child);

	info->cell_x = cur_x;
	info->cell_y = cur_y;
    }

    return(TRUE);
}

/************************************************************
 *
 * Public Functions.
 *
 ************************************************************/

/*	Function Name: XmIconBoxIsCellEmpty
 *	Description:   Returns true if this cell is unused.
 *	Arguments:     w - the icon box.
 *                     x, y - cell to check.
 *                     ignore - ignore this widget when checking.
 *	Returns:       Returns true if this cell is unused.
 */

Boolean
XmIconBoxIsCellEmpty(Widget w, Position x, Position y, Widget ignore)
{
    XmIconBoxWidget ibw = (XmIconBoxWidget) w;
    Widget * childp;

    _XmWidgetToAppContext(w);
    _XmAppLock(app);

    ForAllChildren(ibw, childp) {   
	IconInfo * info;

	if (!XtIsManaged(*childp) || 
	    (*childp == ignore) || (*childp)->core.being_destroyed)
	{
	    continue;
	}

	info = GetIconInfo(*childp);
	if ((x == info->cell_x) && (y == info->cell_y))
	  {
	    _XmAppUnlock(app);
	    return(False);
	  }
    }

    _XmAppUnlock(app);
    return(True);
}

/*	Function Name: XmCreateIconBox
 *	Description: Creation Routine for UIL and ADA.
 *	Arguments: parent - the parent widget.
 *                 name - the name of the widget.
 *                 args, num_args - the number and list of args.
 *	Returns: The created widget.
 */

Widget
XmCreateIconBox(Widget parent, String name,
		ArgList args, Cardinal num_args)
{
    return(XtCreateWidget(name, xmIconBoxWidgetClass, parent, args, num_args));
}
