#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>

#include "AlAssocP.h"

/* Private Definitions */


static int DEFAULTVALUE = 4;

#define Offset(field) XtOffset(AlAssocConstraints, alAssoc.field)
static XtResource alAssocConstraintResources[] = {
    {XtNfromVert, XtCWidget, XtRWidget, sizeof(Widget),
       Offset(vert_base), XtRWidget, (caddr_t)NULL},
    {XtNvertDistance, XtCThickness, XtRInt, sizeof(int),
	Offset(vert_dist), XtRInt, (caddr_t)&DEFAULTVALUE}
};
#undef Offset

static void Initialize(), Resize();
static void ConstraintInitialize();
static Boolean SetValues(), ConstraintSetValues();
static XtGeometryResult GeometryManager();
static void ChangeManaged();

AlAssocClassRec alAssocClassRec = {
  { /* core_class fields */
    /* superclass         */    (WidgetClass) &constraintClassRec,
    /* class_name         */    "AlAssoc",
    /* widget_size        */    sizeof(AlAssocRec),
    /* class_initialize   */    NULL,
    /* class_part_init    */    NULL,
    /* class_inited       */    FALSE,
    /* initialize         */    Initialize,
    /* initialize_hook    */    NULL,
    /* realize            */    XtInheritRealize,
    /* actions            */    NULL,
    /* num_actions        */    0,
    /* resources          */    NULL,
    /* num_resources      */    0,
    /* xrm_class          */    NULLQUARK,
    /* compress_motion    */    TRUE,
    /* compress_exposure  */    TRUE,
    /* compress_enterleave*/    TRUE,
    /* visible_interest   */    FALSE,
    /* destroy            */    NULL,
    /* resize             */    Resize,
    /* expose             */    XtInheritExpose,
    /* 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           */    NULL,
    /* query_geometry     */	XtInheritQueryGeometry,	/* %%% fix this! */
  },
  { /* composite_class fields */
    /* geometry_manager   */   GeometryManager,
    /* change_managed     */   ChangeManaged,
    /* insert_child       */   XtInheritInsertChild,
    /* delete_child       */   XtInheritDeleteChild,
    /* move_focus_to_next */   NULL,
    /* move_focus_to_prev */   NULL
  },
  { /* constraint_class fields */
    /* subresourses       */   alAssocConstraintResources,
    /* subresource_count  */   XtNumber(alAssocConstraintResources),
    /* constraint_size    */   sizeof(AlAssocConstraintsRec),
    /* initialize         */   ConstraintInitialize,
    /* destroy            */   NULL,
    /* set_values         */   ConstraintSetValues
  },
  { /* alAssoc_class fields */
    /* empty              */   0
  }
};

WidgetClass alAssocWidgetClass = (WidgetClass)&alAssocClassRec;

/****************************************************************
 *
 * Private Procedures
 *
 ****************************************************************/


/* ARGSUSED */
static void Initialize(request, new)
    Widget request, new;
{
    AlAssocWidget aw = (AlAssocWidget)new;

    if (request->core.width == 0)
      new->core.width = 5;
    if (request->core.height == 0)
      new->core.height = 5;

}
/* ARGSUSED */

static void ConstraintInitialize(request, new)
    Widget request, new;
{
    AlAssocWidget aw = (AlAssocWidget)new;
/* Nothing for now...*/
}


/*  returns the number of vertical pixels used in the AlAssoc widget */

static int CalculatePosition(aw, child)
     AlAssocWidget aw;
     Widget child;
{
   AlAssocConstraintsRec *constraintRec
     = (AlAssocConstraintsRec *) child -> core.constraints;
   Widget base_widget = constraintRec -> alAssoc.vert_base;
   int new_posn;

   if (base_widget == NULL) {
     new_posn = 5+child->core.border_width;
   }
   else {
     new_posn =	CalculatePosition(aw, base_widget) +
       constraintRec -> alAssoc.vert_dist + 
	 child->core.border_width;
   }
   XtConfigureWidget(child, 5, new_posn,
	             aw->core.width-10-child->core.border_width*2,
		     child->core.height,
		     child->core.border_width);
   return new_posn + child->core.height 
     + child->core.border_width;
}

/* Returns overall height of the children, including 5 pixels above
 * the first and 5 pixels below the last. */

static int CalculatePositions(aw)
     AlAssocWidget aw;
{
    WidgetList children = aw->composite.children;
    int num_children = aw->composite.num_children;
    Widget *childP;
    int temp_y, max_y = 0;

    for (childP = children; childP - children < num_children; childP++) {
	if (!XtIsManaged(*childP)) continue;
	if ((temp_y = CalculatePosition(aw, *childP)) > max_y) 
	  max_y = temp_y;
    }
    max_y += 5;    /* bottom margin */
    return max_y;
}

static void Resize(w)
    Widget w;
{
    AlAssocWidget aw = (AlAssocWidget)w;
    WidgetList children = aw->composite.children;
    int num_children = aw->composite.num_children;
    Widget *childP;

    for (childP = children; childP - children < num_children; childP++) {
	if (!XtIsManaged(*childP)) continue;
	XtResizeWidget(*childP, 
		       aw->core.width-10-(*childP)->core.border_width*2,
		       (*childP)->core.height,
		       (*childP)->core.border_width);
    }
}


static XtGeometryResult GeometryManager(w, request, reply)
    Widget w;
    XtWidgetGeometry *request;
    XtWidgetGeometry *reply;	/* RETURN */
{
  AlAssocWidget me = (AlAssocWidget)w->core.parent;
  XtWidgetGeometry allowed;
  XtGeometryResult geo_return;
  int new_height;

  if (request->request_mode & ~(CWWidth|CWHeight))
    return XtGeometryNo;	/* We don't handle any others */

  if (request->request_mode & CWWidth) {
    if (request->width + w->core.border_width*2 +10 <= w->core.width) 
      return XtGeometryNo;

    geo_return = XtMakeResizeRequest(me,
				     request->width+10+w->core.border_width*2,
				     me->core.height,
				     &allowed.width,
				     &allowed.height);
    if (geo_return == XtGeometryNo)
      return XtGeometryNo;
    if (geo_return == XtGeometryYes) {
      int new_width = request->width;
      int new_height = w->core.height;

      if (request->request_mode &CWHeight) 
	new_height = request->height;
      XtResizeWidget(w, new_width, new_height, w->core.border_width);
      CalculatePositions(me);
      return XtGeometryDone;
    }
    if (geo_return == XtGeometryAlmost) {
      if (reply) {
	reply->width = allowed.width;
	if (request->request_mode &CWHeight) 
	  reply->height = request->height;
	return XtGeometryAlmost;
      }
      else
	return XtGeometryNo;
    }
  } else {			/* CWHeight */
    XtResizeWidget(w, w->core.width, request->height, w->core.border_width);
    new_height = CalculatePositions(me);
    (void)XtMakeResizeRequest(me,
                                me->core.width,
                                new_height,
				NULL, 
				NULL);
    return XtGeometryDone;
  }
  abort();			/* Should never get here */
}



/* ARGSUSED */
static Boolean SetValues(current, request, new)
    Widget current, request, new;
{
    return( FALSE );
}


/* ARGSUSED */
static Boolean ConstraintSetValues(current, request, new)
    Widget current, request, new;
{
    return( FALSE );
}

static void ChangeManaged(w)
    Widget w;
{
    AlAssocWidget me = (AlAssocWidget)w;
    int new_height;

    new_height = CalculatePositions(me);
    (void)XtMakeResizeRequest(me,
                                me->core.width,
                                new_height,
				NULL, 
				NULL);
}
    

