/*
 *	$Source: /u1/X/DECToolkit/src/RCS/Forms.c,v $
 *	$Header: Forms.c,v 1.1 86/12/17 09:01:30 swick Exp $
 */

#ifndef lint
static char *rcsid_Forms_c = "$Header: Forms.c,v 1.1 86/12/17 09:01:30 swick Exp $";
#endif	lint

#ifndef lint
static  char    *sccsid = "@(#)Forms.c	1.3          12/11/86";
#endif lint
/*
 *			  COPYRIGHT 1986
 *		   DIGITAL EQUIPMENT CORPORATION
 *		       MAYNARD, MASSACHUSETTS
 *			ALL RIGHTS RESERVED.
 *
 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
 * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 *
 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS,
 * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT
 * SET FORTH ABOVE.
 *
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting documentation,
 * and that the name of Digital Equipment Corporation not be used in advertising
 * or publicity pertaining to distribution of the software without specific, 
 * written prior permission.
 */


/*
 * Forms.c - Interactive Forms Routines
 *
 * This file contains the set of routines for creating, managing,
 * and deleting forms.
 * 
 * Author: Harry Hersh
 * Date: September 23, 1986
 *
 * Modification history:
 *
 *	September 23, 1986: H. Hersh
 *		Original version. Basic code structure defined by
 *		Smokey Wallace.
 *      December 7, 1986: H. Hersh
 *              Restructured to allow variable arguements.
 */

#include <X/Xlib.h>
#include <string.h>
#include "Toolkit.h"


/* Private Definitions  */

/* -----> Static Variables */

static int initialized = FALSE;
static int formContext;   	     /* ID of form tool data  */

static Initialize()
{
   formContext = UniqueEntryType();
   initialized = TRUE;
}


/*==================================================================*/

/* -----> Semi-Public Routines */


static FormData *FormPtrFromWindow(w)
Window w;
{
    FormData *f;
    if (FindEntry(w, formContext, f) != ERRNONE)
       return FALSE;
    return f;
}

/* =================================================================== */

Status BaseFormatter(f)
FormData *f;                /* Forms data structure */
{

/*
 * This routine arranges the various subtools in the form according
 * to the hints contained in the form data structure. This routine
 * maps all the tools, excluding the parent. This routine may be used
 * to rearrange the form as well.
 */

    WindowInfo info,  parentInfo;
    FormItem  *item;
    Status     status;

    int        i,     n,
               x,     y,
               w,     h,
               nextX, nextY,
               lastX, lastY,
               lastH, lastW,
               temp;

    n = f->itemCount;
    nextX = 0;
    nextY = 0;
    lastX = 0;
    lastY = 0;
    lastW = 0;
    lastH = 0;

    status = XQueryWindow(f->parent, &parentInfo);

    /*
     * Determine size and location for each tool in form.
     */

    for (i = 0; i < n; i++) {
        item = &f->items[i];
        status = XQueryWindow(item->sw, &info);    /* Current values */
        switch (item->hints->xOption) {            /* Find X offset */
            case AbsoluteX: 
                x = item->hints->x;
                break;
            case UnchangedX: 
                if(lastX)
                   x = lastX;
                else
                   x = item->hints->x;
                break;
            case RelativeX: 
                x = nextX + item->hints->x;
                break;
            case CenterX:
                x = (parentInfo.width - info.width)/2 + item->hints->x;
                break;
            default: 
                x = nextX;
        }

        switch (item->hints->yOption) {            /* Y offset */
            case AbsoluteY: 
                y = item->hints->y;
                  break;
            case UnchangedY:
                if(lastY)
                   y = lastY;
                else
                   y = item->hints->y;
                break;
            case RelativeY: 
                y = nextY + item->hints->y;
                break;
            case CenterY:
                y = (parentInfo.height - info.height)/2 + item->hints->y;
                break;
            default: 
                y = y;
        }
        nextY = y + info.height /* + (2 * yMargin)  */  ;

        switch (item->hints->hOption) {            /* Height */
            case AbsoluteH: 
                h = item->hints->h;
                break;
            case UnchangedH:
                if(lastH)
                   h = lastH;
                else
                   h = item->hints->h;
                break;
            case CurrentH: 
                h = info.height;
                break;
            case ParentH:
                h = parentInfo.height + item->hints->h;
                y = 0;
                break;
            case ParentBottom:
                h = parentInfo.height + item->hints->h - y;
                break;
            default: 
                h = info.height;
                break;
        }

        switch (item->hints->wOption) {            /* Width */
            case AbsoluteW: 
                w = item->hints->w;
                break;
            case UnchangedW:
                if(lastW)
                   w = lastW;
                else
                   w = item->hints->w;
                break;
            case CurrentW: 
                w = info.width;
                break;
            case ParentW:
                w = parentInfo.width + item->hints->w;
                x = 0;
                break;
            case ParentRight:
                w = parentInfo.width + item->hints->w - x;
                break;
            default: 
                w = info.width;
        }
        
        XConfigureWindow(item->sw, x, y, w, h);
        item->box.x = x;
        item->box.y = y;
        item->box.w = w;
        item->box.h = h;
        lastX = x;
        lastY = y;
        lastW = w;
        lastH = h;
        nextX = x + w;
        temp = y + h /* + (2 * yMargin) */  ;
        if (temp > nextY)
            nextY = temp;
	}
    XMapSubwindows(f->parent);
}

/*==================================================================*/

/*
 * -----> Public Routines
 */

Status TMakeForm(pw, arglist)
Window pw;                    /* Parent window for the form */
Targ * arglist;
{

/* This routine builds the framework for an interactive form in the
 * parent window. Optionally, the tools comprising the form, their
 * layout, and a formatter may also be specified. The form is only
 * created, not displayed. The tools windows must be children of
 * the parent. (In version 11, this constraint will be relaxed, as
 * child windows can be reparented.
 */

    FormLayoutHints **hints, *buff;
/*    FormFormatter    BaseFormatter;  */
    FormData        *f;
    WindowInfo       info;
    Window          *wa;
    Status           error;
    int              x, y, h, w;
    int              NumTools, j;


/* -----> Perform initialization */

   if (!initialized)
      Initialize();

/* -----> Set up form data structure */

    f = (FormData *) Tmalloc(sizeof(FormData));
    SaveEntry(pw, formContext, f);

/* -----> Set Defaults */    

    XQueryWindow (pw, &info);
    f->itemCount = NumTools = 0;
    f->parent    = pw;
    f->formatter = BaseFormatter;
    hints = 0;
    x = info.x;
    y = info.y;
    w = info.width;
    h = info.height;

/* -----> Parse Arguments */

   while (arglist -> name) {
      switch (arglist -> name) {
         case T_FORM_HEIGHT:
            h = (int) arglist -> data;
            break;
         case T_FORM_WIDTH:
            w = (int) arglist -> data;
            break;
         case T_FORM_X:
            x = (int) arglist -> data;
            break;
         case T_FORM_Y:
            y = (int) arglist -> data;
            break;
         case T_FORM_TOOLS:
            wa = (int *) arglist -> data;
            while (wa[NumTools] != 0)
               NumTools++;
            break;
         case T_FORM_HINTS:
            hints = (FormLayoutHints (*)[]) arglist -> data;
            break;
         case T_FORM_FORMATTER:
            f->formatter = (FormFormatter) arglist -> data;
            break;
         default:
            break;
      }
   arglist++;
   }

/* -----> Resize the form ? */

   if(x != info.x || y != info.y ||
      w != info.width || h != info.height)
            XConfigureWindow(pw, x, y, w, h);

/* -----> Update data structure, if necessary */

   if(NumTools > f->itemCount) {
      f->items = (FormItem *) Tmalloc(sizeof(FormItem) * NumTools);
      f->itemCount = NumTools;
      for (j = 0; j < NumTools; j++) {
         f->items[j].sw = wa[j];
         if(hints)
            f->items[j].hints = hints[j];
         else {
            XQueryWindow(wa[j], &info);
            buff->x = info.x;
            buff->y = info.y;
            buff->w = info.width;
            buff->h = info.height;
            buff->xOption = AbsoluteX;
            buff->yOption = AbsoluteY;
            buff->wOption = AbsoluteW;
            buff->hOption = AbsoluteH;
            f->items[j].hints = buff;
         }
      }
   }
}

/*==================================================================*/

Status DestroyForm (w)
    Window w;               /* Bounding window for form */

{

/*
 * Delete windows, dispatch entries and memory allocations 
 * for the form. Destruction is done recursively: form destruction sets
 * sub-tool deletion in motion. Once destroyed, a form should
 *  never be referenced again.
 *
 */

    FormData    *f;
    Status       error;
    int          i;

    error = FindEntry(w, formContext, &f);
    if (error != ERRNONE) 
        return error;
    /*
     * For each subtool, delete it
     */

/*  This is waiting until we agree upon the delete mechanism.

    free(f);
    XDestroySubwindows(f->parent);
    XDestroyWindow(f->parent);
    DeleteEntry(w, formContext);
    DeleteEntry(w, proc??)

*/
}


/* ================================================================= */

/*
 * Adds a tool to a form. Size and location attributes may also be
 * specified at this time. If not, size and location are those of the
 * tool's current bounding window.
 *
 * NOTE: After adding a tool to a form, run DisplayForm to update the 
 * layout of the form.
 */

Window TAddToForm(pw, tw, arglist)
Window pw;			/* Window frame */
Window tw;			/* Window of added tool */
Targ * arglist;
{
    FormData   *f;
    FormItem   *item;
    FormLayoutHints *hints;
    WindowInfo info;
    int        position;
    int        status;
    int        x, y, w, h;

    f = FormPtrFromWindow(pw);
    if(!f)
        return ERRNOTFOUND;

    f->itemCount++;
    f->items = (FormItem *)
	Trealloc(f->items, f->itemCount * sizeof(FormItem));

/* -----> Set Defaults */

   position   = f -> itemCount - 1;
   item = &(f->items[position]);
   item->sw = tw;
   XQueryWindow(w, &info);
   hints->x = info.x;
   hints->y = info.y;
   hints->w = info.width;
   hints->h = info.height;
   hints->xOption = AbsoluteX;
   hints->yOption = AbsoluteY;
   hints->wOption = AbsoluteW;
   hints->hOption = AbsoluteH;

/* -----> Parse Arguments */

   while (arglist -> name) {
      switch (arglist -> name) {
         case T_FORMITEM_X:
            hints->x = (int) arglist -> data;
            break;
         case T_FORMITEM_Y:
            hints->y = (int) arglist -> data;
            break;
         case T_FORMITEM_HEIGHT:
            hints->h = (int) arglist -> data;
            break;
         case T_FORMITEM_WIDTH:
            hints->h = (int) arglist -> data;
            break;
         case T_FORMITEM_W_HINT:
            hints->wOption = (enum WLayoutHints) arglist -> data;
            break;
         case T_FORMITEM_H_HINT:
            hints->hOption = (enum HLayoutHints) arglist -> data;
            break;
         case T_FORMITEM_X_HINT:
            hints->xOption = (enum XLayoutHints) arglist -> data;
            break;
         case T_FORMITEM_Y_HINT:
            hints->yOption = (enum YLayoutHints) arglist -> data;
            break;
         default:
            break;
      }
   arglist++;
   }
   item->hints = hints;
}


/* ================================================================= */

/*
 * Removes a tool from the form. If the layout of the form changes as a
 * result, run DisplayForm to update the screen image.
 */

Status TDeleteFromForm(w, subwindow)
  Window w, subwindow;
{
    FormData *f;
    int     i, j;

    f = FormPtrFromWindow(w);
    if(!f)
        return ERRNOTFOUND;

    f->itemCount--;
    f->items = (FormItem *)
	Trealloc(f->items, f->itemCount * sizeof(FormItem));

    j = FALSE;
    for (i = 0; i < f->itemCount; i++) {
	if (f->items[i].sw == subwindow) {
	    j = TRUE;
	}
	if (j && i < f->itemCount - 1)
	    f->items[i] = f->items[i + 1];
    }
    if (!j)
	return;
    f->itemCount--;
    if (f->itemCount > 0) {
	f->items = (FormItem *)
	    Trealloc(f->items, f->itemCount * sizeof(FormItem));
    }
}

/*==================================================================*/

Status DisplayForm (w)
  Window w;               /* Bounding window for form */

/*
 *
 * Maps all the subwindows to the display.
 * 
 */

{
    FormData *f;
    Status    status;

    status = FindEntry(w, formContext, &f);
    if (status == ERRNONE)
	status = f->formatter(f);
}

/*==================================================================*/

FormLayoutHints *MakeFormLayoutHints (
  x, y, w, h, xOption, yOption, wOption, hOption) 

int  x, y,                   /* Offset      */
     w, h;                   /* Size        */
enum XLayoutHints xOption;
enum YLayoutHints yOption;   /* Offset hint */
enum WLayoutHints wOption;
enum HLayoutHints hOption;   /* Size hint   */

/*
 *
 * This routine puts a tool's layout specification into an array of
 * hints for MakeForm.
 *
 */

{
    FormLayoutHints * hints;

    hints = (FormLayoutHints *) malloc(sizeof(FormLayoutHints));
    hints->x = x;
    hints->y = y;
    hints->w = w;
    hints->h = h;
    hints->xOption = xOption;
    hints->yOption = yOption;
    hints->wOption = wOption;
    hints->hOption = hOption;
    return hints;
}

/* =================================================================== */

/*
 * This procedure returns the currently set of arguments, and
 * other state information, for a particular tool contained within a form.
 * If any errors are detected during the parsing of the input arguments, an
 * exit will occur.
 */

Status TGetFormAttr (tw, arglist)
Window tw;			/* tool window */
Targ *arglist;
  {
    int i, return_code;
    FormData *f;
    FormItem *item;
    FormLayoutHints *hints;
    Window *parent, **children;
    int *nchildren;

/* -----> First find parent, then the right tool */

    return_code = XQueryTree(tw, parent, nchildren, children);
    if (!return_code)
       return ERRNOTFOUND;

    f = FormPtrFromWindow(parent);
    if (!f)
       return ERRNOTFOUND;

    for (i = 0; i < f->itemCount; i++)
        if(f->items[i].sw == tw)
           item = &(f->items[i]);
    if (!item)
       return ERRNOTFOUND;
    hints = item->hints;
 
/* -----> Parsing input parameters */

   while (arglist -> name) {
      switch (arglist -> name) {
         case T_FORMITEM_X:
            arglist -> data = (caddr_t) hints->x;
            break;
         case T_FORMITEM_Y:
            arglist -> data = (caddr_t) hints->y;
            break;
         case T_FORMITEM_HEIGHT:
            arglist -> data = (caddr_t) hints->h;
            break;
         case T_FORMITEM_WIDTH:
            arglist -> data = (caddr_t) hints->w;
            break;
         case T_FORMITEM_X_HINT:
            arglist -> data = (caddr_t) hints->xOption;
            break;
         case T_FORMITEM_Y_HINT:
            arglist -> data = (caddr_t) hints->yOption;
            break;
         case T_FORMITEM_W_HINT:
            arglist -> data = (caddr_t) hints->wOption;
            break;
         case T_FORMITEM_H_HINT:
            arglist -> data = (caddr_t) hints->hOption;
            break;
         default:
            break;
      }
   arglist++;
   }
}

/* =================================================================== */

/*
 * This procedure sets or changes arguments for a tool in a form.
 * If any errors are detected during the parsing of the input arguments,
 * an exit will occur.
 */

Status TSetFormAttr (tw, arglist)
Window tw;
Targ *arglist;
{
    int return_code;		/* return code from window creation */
    int i;
    FormData *f;
    FormItem *item;
    FormLayoutHints *hints;
    Window *parent, **children;
    int *nchildren;

/* -----> First find parent, then the tool */

    return_code = XQueryTree(tw, parent, nchildren, children);
    if (!return_code)
       return ERRNOTFOUND;

    f = FormPtrFromWindow(parent);
    if (!f)
       return ERRNOTFOUND;

    for (i = 0; i < f->itemCount; i++)
        if(f->items[i].sw == tw)
           item = &(f->items[i]);
    if (!item)
       return ERRNOTFOUND;
 
/* -----> Parsing input parameters */

   while (arglist -> name) {
      switch (arglist -> name) {
         case T_FORMITEM_X:
            hints->x = (int) arglist -> data;
            break;
         case T_FORMITEM_Y:
            hints->y = (int) arglist -> data;
            break;
         case T_FORMITEM_HEIGHT:
            hints->h = (int) arglist -> data;
            break;
         case T_FORMITEM_WIDTH:
            hints->w = (int) arglist -> data;
            break;
         case T_FORMITEM_X_HINT:
            hints->xOption = (enum XLayoutHints) arglist -> data;
            break;
         case T_FORMITEM_Y_HINT:
            hints->yOption = (enum YLayoutHints) arglist -> data;
            break;
         case T_FORMITEM_W_HINT:
            hints->wOption = (enum WLayoutHints) arglist -> data;
            break;
         case T_FORMITEM_H_HINT:
            hints->hOption = (enum HLayoutHints) arglist -> data;
            break;
         default:
            break;
      }
   arglist++;
   }
   item->hints = hints;
}
