/*
 *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
 *    use is allowed under the terms of the DICE-LICENSE FILE,
 *    DICE-LICENSE.TXT.
 */
#include "vmake.h"

Prototype int  init_gadgets(void);
Prototype void free_gadget(struct Gadget *gadget);
Prototype void free_gadlist(struct GADLIST *gadlist);
Prototype struct Gadget *create_gadget(struct GADLIST *gadlist,struct G_OBJECT *object,int ulx, int uly,int width);
Prototype void setup_string_gadget(struct Gadget *gad,int base);
Prototype struct Gadget *setup_cycle_gadget(struct Gadget *gad,struct IntuiText *itext,struct G_VALUE *val);
Prototype void reset_list_object(struct G_LIST *list,int active);
Prototype int  setup_list_object(struct GADLIST *gadlist,struct Gadget *gad);
Prototype struct GADLIST *layout_gadgets(struct G_OBJECT *objlist, int ulx, int uly, int width);
Prototype struct Border *build_border(int width, int height, int mode);
Prototype int  init_gad_sizes(int swidth, int sheight, int resize);
Prototype int  create_borders(void);
Prototype int  recalc_group_sizes(struct G_OBJECT *objlist, int *titsize, int *cycsize);
Prototype int  text_width(char *str);

/***********************************************************************************
 * Procedure: init_gadgets
 * Synopsis:  rc = init_gadgets();
 * Purpose:   Set up all the base system gadgets for the window.  This includes all
 *            The user defined gadgets as well as the three buttons on the bottom
 ***********************************************************************************/
int init_gadgets()
{
   struct Gadget *gad;
   struct IntuiText *itext;
   int ulx, uly;
   int i;
   int buttons;

   global.gadlist = layout_gadgets(global.objects,
                                    global.ri.WindowLeft,
                                    global.ri.WindowTitle,
                                    global.cycsize+global.titsize
                                    );
   if (global.gadlist == NULL) return(1);
   ulx = global.ri.WindowLeft;
   uly = global.height - global.ri.WindowBottom - global.iheight;

   buttons = (global.width - (global.ri.WindowLeft + global.ri.WindowRight)) / BUTTON_WIDTH;
   if (buttons > 5) buttons = 5;

   for(i = 0; i < buttons; i++)
   {
      global.button[i].base.class = CLASS_BUTTON;
      global.button[i].base.state = i; /* Note that these values correspond to */
                                   /* the BUTTON_xxx equates               */

      gad = create_gadget(NULL, (struct G_OBJECT *)(global.button+i),
                                ulx, uly, BUTTON_WIDTH);

      if (gad == NULL) return(1);
      gad->GadgetRender = build_border(BUTTON_WIDTH,
                                       global.iheight, MODE_OUT);

      itext = gad->GadgetText;
      itext->LeftEdge += (BUTTON_WIDTH - IntuiTextLength(itext))/2;

      gad->NextGadget = global.gadlist->gadgets;
      global.gadlist->gadgets = gad;
      global.gadlist->count++;

      gad->GadgetType = BOOLGADGET;

      ulx += (global.width - (global.ri.WindowLeft  +
                               global.ri.WindowRight +
                               (buttons*BUTTON_WIDTH)
                              )
             ) / (buttons-1) + BUTTON_WIDTH;
   }

   return(0);
}
/***********************************************************************************
 * Procedure: free_gadget
 * Synopsis:  free_gadget(gadget)
 * Purpose:   Free up all memory associated with a gadget
 ***********************************************************************************/
void free_gadget(struct Gadget *gadget)
{
   struct G_OBJECT *obj;

   if (gadget)
   {
      obj = (struct G_OBJECT *)(gadget->UserData);

      obj->gadget = NULL;

      if (obj->class == CLASS_STRING)
      {
         /* We need to free the border structure also */
      }
      free_mem(gadget, sizeof(struct Gadget)+sizeof(struct IntuiText));
   }
}

/***********************************************************************************
 * Procedure: free_gadlist
 * Synopsis:  free_gadlist(gadlist)
 * Purpose:   Free up all memory associated with a gadget list created by
 *            layout_gadgets
 ***********************************************************************************/
void free_gadlist(struct GADLIST *gadlist)
{
   struct Gadget *gad, *nextgad;
   struct G_CYCLE *cyc;
   struct G_STRING *str;

   if (!gadlist) /* never allocated or already freed */
      return;

   for(gad = gadlist->gadgets; gad && gadlist->count; gad = nextgad, gadlist->count--)
   {
      nextgad = gad->NextGadget;
      cyc = (struct G_CYCLE *)gad->UserData;

      if ((cyc->base.class & CLASS_MASK) == CLASS_CYCLE)
      {
         /* If there is a string gadget currently in the cycle gadget, we need */
         /* to free it up                                                      */
         if ((str = cyc->curval->string) != NULL)
         {
            free_gadget(str->base.gadget);
         }
      }
      free_gadget(gad);
   }

   free_mem(gadlist, sizeof(struct GADLIST));
}

/***********************************************************************************
 * Procedure: create_gadget
 * Synopsis:  Gadget = create_gadget(gadlist, object, ulx, uly, width)
 * Purpose:   Create all the appropriate gadgetry and image structures for a list
 *            of gadgets.
 ***********************************************************************************/
struct Gadget *create_gadget(struct GADLIST *gadlist,
                             struct G_OBJECT *object,
                             int ulx, int uly,
                             int width
                            )
{
   struct Gadget *gad;
   struct IntuiText *itext;


   /* Create a gadget structure and an associated intuitext structure to be */
   /* Layed out on the screen                                               */
   gad = (struct Gadget *)get_mem(sizeof(struct Gadget)+sizeof(struct IntuiText));
   if (gad)
   {
      itext = (struct IntuiText *)(gad+1);

      gad->LeftEdge    = ulx;
      gad->TopEdge     = uly;
      gad->Width       = width;
      gad->Height      = global.iheight;
      gad->Flags       = GADGHCOMP;
      gad->Activation  = RELVERIFY;
      gad->GadgetText  = itext;
      gad->UserData    = (APTR)object;
      gad->GadgetID    = object->class; /* set both class and subclass */
      gad->GadgetType   = BOOLGADGET;

      object->gadget      = gad;

      /* Now we fill in the Intuitext structure */
      itext->FrontPen  = 1;
      itext->BackPen   = 0;
      itext->DrawMode  = JAM1;
      itext->LeftEdge  = 0;
      itext->TopEdge   = (global.iheight - global.ri.FontSize) / 2;
      itext->ITextFont = &global.ri.TextAttr;
      itext->IText     = object->title;
      itext->NextText  = NULL;

      if (gadlist)
      {
         gad->NextGadget = gadlist->gadgets;
         gadlist->gadgets = gad;
         gadlist->count++;
      }
   }

   return(gad);
}

/***********************************************************************************
 * Procedure: setup_string_gadget
 * Synopsis:  setup_string_gadget(gad, string, ulx, uly, width, base)
 * Purpose:   Create all the appropriate gadgetry and image structures for a list
 *            of gadgets.
 ***********************************************************************************/
void setup_string_gadget(struct Gadget *gad,
                         int base
                        )
{
   int wraps;
   int i;
   struct Border *border;
   struct StringInfo *si;
   int sluff;

   if (gad == NULL) return;

   wraps = (base == CLASS_STRING) ? 2 : 1;

   gad->LeftEdge += VBAR*wraps;
   gad->TopEdge  += DHBAR;
   gad->Width    -= DVBAR*wraps;
   gad->Height   -= 2*DHBAR;

   sluff = (gad->Height - global.ri.FontSize);
   if (sluff < 0) sluff = 0;
   gad->Height  = global.ri.FontSize;
   gad->TopEdge += sluff / 2;
   if (gad->GadgetText)
   {
      gad->GadgetText->TopEdge -= (DHBAR+sluff);
      gad->GadgetText->LeftEdge -= VBAR*wraps;
   }

   if ((base == CLASS_LIST)   ||
       (base == CLASS_STRING) ||
       (base == CLASS_CYCLE))
   {
      for(i=wraps; i > 0; i--)
      {
         /* We need to put a border around the gadget. */
         /* For some string gadgets, we only need a single border */
         /* We can tell this by the base type */
         border = build_border(gad->Width+4*i, gad->Height + sluff + 2*i,
                               (i == 1) ? MODE_IN : MODE_OUT);
         if (border != NULL)
         {
            border->NextBorder->LeftEdge = border->LeftEdge = -2*i;
            border->NextBorder->TopEdge  = border->TopEdge  = -1*i - (sluff/2);
            border->NextBorder->NextBorder = gad->GadgetRender;
            gad->GadgetRender = border;
         }
      }
   }

   /* Lastly, we need to setup up the SpecialInfo structure for the string gadget */
   /* If for some reason this fails, we will turn it into a button gadget         */
   si = (struct StringInfo *)get_mem(sizeof(struct StringInfo));
   if (si != NULL)
   {
      gad->SpecialInfo = (APTR)si;
      gad->GadgetType = STRGADGET;
      si->Buffer = ((struct G_STRING *)(gad->UserData))->buf;
      si->MaxChars = MAX_STRING;
   }
   else
      gad->GadgetType = BOOLGADGET;
}


/***********************************************************************************
 * Procedure: setup_cycle_gadget
 * Synopsis:  gadget = setup_cycle_gadget(gadget, value)
 * Purpose:   Create any subtending string gadgets and correctly position any
 *            cycle value text
 ***********************************************************************************/
struct Gadget *setup_cycle_gadget(struct Gadget *gad,
                                  struct IntuiText *itext,
                                  struct G_VALUE *val
                                 )
{
   struct Gadget *strgad;
   int ilen;

   ilen = IntuiTextLength(itext);

   if (val->string)
   {
      int x, width;

      itext->LeftEdge = CYC_ICON_WIDTH + 2;
      /* We need to create a string gadget to get the input text from */

      x = itext->LeftEdge + ilen;
      width = gad->Width - x - 2;

      /* Now, create a string gadget and border to put around the gadget */
      strgad = create_gadget(NULL, (struct G_OBJECT *)val->string,
                                       gad->LeftEdge+x, gad->TopEdge, width);
      if (strgad)
      {
         setup_string_gadget(strgad, CLASS_CYCLE);
      }
   }
   else
   {
      strgad = NULL;
      itext->LeftEdge  = (CYC_ICON_WIDTH + gad->Width - ilen) / 2;
   }
   return(strgad);
}
/***********************************************************************************
 * Procedure: reset_list_object
 * Synopsis:  void reset_list_object(list,active);
 * Purpose:   Initialize all the appropriate text pointers for a list gadget
 ***********************************************************************************/
void reset_list_object(struct G_LIST *list,
                       int active
                      )
{
   struct G_ENTRY *ent;
   int i;

   ent = list->top;

   /* Run through all the entries visible on the screen and for each */
   /* one that is displayed, mark the gadget as selectable.          */
   for(i = 0; i < global.listsize; i++)
   {
/*
      list->btngad[i]->Flags  = (list->btngad[i]->Flags & ~GADGHIGHBITS) |
                                GADGHNONE;
*/
      list->btngad[i]->Flags  = (list->btngad[i]->Flags & ~GADGHIGHBITS) |
                                GADGHCOMP;
      list->btngad[i]->Height = global.eheight;
      list->strgad[i]->GadgetRender = NULL;
      ((struct StringInfo *)list->strgad[i]->SpecialInfo)->Buffer
              = global.defbuf;  /* because RefreshGlist might write to it... */
/*      global.defbuf[0] = 0; */    /* want this to work like a null string */

      /* Is there an entry corresponding to this position on the screen? */
      if (ent)
      {
         /* Certainly is an entry, is it where they want a string gadget ? */
         if (i == active)
         {
            /* Yes, shrink the gadget out of existence so that it doesn't  */
            /* appear on the screen.                                       */
/* prevent mouse activation ?
            list->btngad[i]->Height = 1;
*/
            list->strgad[i]->GadgetRender = list->sborder;
         }
/*         else */
         {
            list->btngad[i]->Flags = (list->btngad[i]->Flags & ~GADGHIGHBITS) |
                                     GADGHCOMP;
         }

         ((struct StringInfo *)
          list->strgad[i]->SpecialInfo)->Buffer = ent->buf;
         ent = (struct G_ENTRY *)ent->base.next;
      }
   }
}

/***********************************************************************************
 * Procedure: setup_list_object
 * Synopsis:  ulx = setup_list_object(gadlist, gadget)
 * Purpose:   Create all the appropriate gadgetry and image structures for a list
 *            object
 ***********************************************************************************/
int setup_list_object(struct GADLIST *gadlist,
                      struct Gadget *gad
                     )
{
   int ulx, uly, alen, dlen, ilen;
   struct G_LIST *list;
   int i;
   char *savetitle;
   struct Gadget *slider, *dngad, *upgad, *addgad, *delgad;
   struct PropInfo *pi;

#define ARR_HEIGHT  (7+DHBAR)
#define PROP_WIDTH  (14+DVBAR)

   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
   /* A list object consists of 8 separate gadgets arranged as below*/
   /* 1: Boolean gadgets that overlay string gadgets.  When the     */
   /* 2:   string gadget is to be active, the height of the boolean */
   /* 3:   gadget is set to 1 exposing the string gadget.           */
   /* 4: A slider gadget to allow positioning in the list           */
   /* 5: An up arrow gadget for moving up one item in the list      */
   /* 6: A Down arrow gadget for moving down one item in the list   */
   /* 7: A NEW gadget for creating an entry in front of current item*/
   /* 8: A DEL gadget for removing the current item from the list.  */
   /*                                                               */
   /*                                                               */
   /*  TITLE TEXT                                                   */
   /* ............................................}+---+            */
   /* ::+---------------------------------------+}}|4  |            */
   /* ::|1                                      |}}|   |            */
   /* ::| I T E M 1                             |}}|   |            */
   /* ::+---------------------------------------+}}| _ |+-------+   */
   /* ::+---------------------------------------+}}|[_]||7      |   */
   /* ::|2                                      |}}|   || N E W |   */
   /* ::| I T E M 2                             |}}+---+|       |   */
   /* ::+---------------------------------------+}}|5^ |+-------+   */
   /* ::+---------------------------------------+}}|/ \|+-------+   */
   /* ::|3                                      |}}+---+|8      |   */
   /* ::| I T E M 3                             |}}|\ /|| D E L |   */
   /* ::+---------------------------------------+}}|6V ||       |   */
   /* :}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}+---++-------+   */
   /*                                                               */
   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
   list = (struct G_LIST *)gad->UserData;

   /* We need to figure out how big the NEW and DEL gadgets will be */
   alen = ilen = text_width(global.text[TEXT_NEW]);
   dlen = text_width(global.text[TEXT_DEL]);
   if (dlen > ilen) ilen = dlen;
   ilen += 2*DHBAR;

   gad->GadgetText->TopEdge   = -global.ri.FontSize;
   gad->TopEdge     += global.ri.FontSize;
   gad->Width       -= ilen + PROP_WIDTH;
   gad->Height       = global.listsize*global.eheight + global.listextra;
   gad->Flags        = GADGHNONE;
   gad->GadgetRender = build_border(gad->Width,
                                    gad->Height,
                                    MODE_OUT);

   savetitle = list->base.title;
   list->base.title = "";

   uly = gad->TopEdge;
   ulx = gad->LeftEdge + gad->Width;

   /* Create the slider and two arrow gadgets.  We will steal the  */
   /* first itext structure and throw it on to the main gadget     */
   slider = create_gadget(gadlist, (struct G_OBJECT *)list,
                                  ulx+DVBAR, uly+DHBAR, PROP_WIDTH-2*DVBAR );
   if (slider == NULL) return(0);
   list->slider = slider;
   slider->Height       = global.listextra + global.listsize*global.eheight -
                          2*ARR_HEIGHT - 2*DHBAR;
   slider->GadgetID     = CLASS_PROP;
   slider->GadgetType   = PROPGADGET;
   slider->Activation |= (GACT_IMMEDIATE | GACT_RELVERIFY | GACT_FOLLOWMOUSE);
   pi = (struct PropInfo *)get_mem(sizeof(struct PropInfo)+
                                           sizeof(struct Image));
   if (pi == NULL) return(0);
   slider->GadgetRender = (struct Image *)(pi+1);
   slider->SpecialInfo  = (APTR)pi;
   pi->Flags = AUTOKNOB | FREEVERT | PROPBORDERLESS;
   recalc_prop(list, &pi->VertBody, &pi->VertPot);

   upgad  = create_gadget(gadlist, (struct G_OBJECT *)list,
                                  ulx, uly+slider->Height+2*DHBAR, PROP_WIDTH);
   if (upgad == NULL) return(0);
   upgad->Height       = ARR_HEIGHT;
   upgad->GadgetRender = &global.arrowborder[1];
   upgad->GadgetID     = CLASS_UP;
   upgad->GadgetText   = NULL;

   dngad  = create_gadget(gadlist, (struct G_OBJECT *)list,
                                  ulx, upgad->TopEdge+ARR_HEIGHT, PROP_WIDTH);
   if (dngad == NULL) return(0);
   dngad->Height       = ARR_HEIGHT;
   dngad->GadgetRender = &global.arrowborder[0];
   dngad->GadgetID     = CLASS_DOWN;
   dngad->GadgetText   = NULL;

   /* Create the ADD/DEL Buttons                                              */
   addgad = create_gadget(gadlist, (struct G_OBJECT *)list, ulx+PROP_WIDTH,
                                  uly + global.listextra + global.listsize*global.eheight-2*global.iheight, ilen);
   if (addgad == NULL) return(0);
   addgad->GadgetRender = build_border(ilen, addgad->Height, MODE_OUT);
   addgad->GadgetID     = CLASS_ADD;
   addgad->GadgetText->LeftEdge += (ilen-alen)>>1;
   addgad->GadgetText->IText = global.text[TEXT_NEW];

   delgad = create_gadget(gadlist, (struct G_OBJECT *)list, addgad->LeftEdge,
                                       addgad->TopEdge+addgad->Height, ilen);
   if (delgad == NULL) return(0);
   list->delgad = delgad;
   delgad->GadgetRender = addgad->GadgetRender;
   delgad->GadgetID     = CLASS_DEL;
   if (list->first == NULL)
      delgad->Flags       |= GADGDISABLED;
   delgad->GadgetText->LeftEdge += (ilen-dlen)>>1;
   delgad->GadgetText->IText = global.text[TEXT_DEL];

   for(i = 0; i < global.listsize; i++)
   {
      struct Gadget *tgad;

      /* Create the string and button gadgets.  Because we insert them on the */
      /* List in reverse order and we want the button gadget to appear first, */
      /* we need to create them in reverse order.                             */
      /* This ordering is assumed in the state changing code.                 */
      tgad = list->strgad[i] = create_gadget(gadlist,
                                             (struct G_OBJECT *)list,
                                             gad->LeftEdge+VBAR, uly,
                                             gad->Width-DVBAR);
      if (tgad == NULL) return(0);
      tgad->GadgetText = NULL;
      tgad->Height = global.eheight;
/* is this appropriate??? */
      tgad->GadgetID   = CLASS_LIST+(i<<SUBCLASS_OFF);
      setup_string_gadget(tgad, CLASS_LIST);

      tgad = list->btngad[i] = create_gadget(gadlist,
                                             (struct G_OBJECT *)list,
                                             gad->LeftEdge+VBAR, uly,
                                             gad->Width-DVBAR);
      if (tgad == NULL) return(0);
      tgad->Height     = global.eheight;
      tgad->GadgetText = NULL;
      tgad->GadgetID   = CLASS_LIST+(i<<SUBCLASS_OFF);
      uly += global.eheight;
   }

   list->sborder = list->strgad[0]->GadgetRender;
   list->strgad[0]->GadgetRender = NULL;

   /* Undo the damage we did to the base object */
   list->base.title = savetitle;
   list->base.gadget = gad;

   reset_list_object(list, -1);

   return(uly+global.listextra);
}

/***********************************************************************************
 * Procedure: layout_gadgets
 * Synopsis:  gadlist = layout_gadgets(objlist, ulx, yly, width);
 * Purpose:   Create all the appropriate gadgetry and image structures for a list
 *            of gadgets.
 ***********************************************************************************/
struct GADLIST *layout_gadgets(struct G_OBJECT *objlist,
                               int ulx, int uly,
                               int width
                              )
{
   struct Gadget *gad;
   struct IntuiText *itext, *itext1;
   int ilen;
   struct G_OBJECT *obj;
   struct GADLIST *gadlist;

   if (create_borders()) return(NULL);

   gadlist = (struct GADLIST *)get_mem(sizeof(struct GADLIST));
   if (gadlist == NULL) return(NULL);

   /* First we need to lay out the gadgets on the left side of the window */
   for(obj = objlist; obj != NULL; obj = obj->next)
   {
      /* Make sure that the gadget will fit on the screen.  We don't even pretend */
      /* To handle the case of a list gadget going over                           */
      if (uly > (global.height - global.ri.WindowBottom - 2*global.iheight))
         return(gadlist);

      /* Create a gadget structure and an associated intuitext structure to be */
      /* Layed out on the screen                                               */
      gad = create_gadget(gadlist, obj, ulx, uly, width);

      if (gad == NULL) return(gadlist);

      itext = (struct IntuiText *)(gad+1);

      uly += gad->Height;

      ilen = IntuiTextLength(itext);
      switch(obj->class)
      {
         case CLASS_STRING:
            gad->LeftEdge   += global.titsize;
            gad->Width       = global.cycsize;
            itext->LeftEdge  = -ilen;
            setup_string_gadget(gad, CLASS_STRING);
            break;

         case CLASS_CYCLE:
            gad->GadgetRender = global.cycborder;
            gad->GadgetType = BOOLGADGET;
            gad->LeftEdge   += global.titsize;
            gad->Width       = global.cycsize;
            itext->LeftEdge  = -ilen;
            itext1 = (struct IntuiText *)get_mem(sizeof(struct IntuiText));
            if (itext1 == NULL) return(gadlist);
            itext1->FrontPen  = 1;
            itext1->BackPen   = 0;
            itext1->DrawMode  = JAM1;
            itext1->LeftEdge  = 0;
            itext1->TopEdge   = 3;
            itext1->ITextFont = &global.ri.TextAttr;
            itext1->IText     = ((struct G_CYCLE *)obj)->curval->title;
            itext1->NextText  = NULL;
            itext1->LeftEdge  = (CYC_ICON_WIDTH+gad->Width-IntuiTextLength(itext1))/2;

            itext->NextText   = itext1;
            itext1->IText     = ((struct G_CYCLE *)obj)->curval->title;
            break;

         case CLASS_CHECK:
            gad->GadgetType   = BOOLGADGET;
            gad->Width        = CHECK_WIDTH;
            gad->Height       = CHECK_HEIGHT;
            gad->Flags        = GADGHIMAGE;
            if (obj->state & STATE_MASK)   gad->Flags |= SELECTED;
            gad->Activation  |= TOGGLESELECT;
            gad->GadgetRender = global.checkborder[0];
            gad->SelectRender = global.checkborder[1];
            itext->LeftEdge  += CHECK_WIDTH + 4;
            break;

         case CLASS_LIST:
            uly = setup_list_object(gadlist, gad);
            if (uly <= 0) return(gadlist);
            break;

         default:
            gad->GadgetType = BOOLGADGET;
            break;
      }
   }
   return(gadlist);
}

/***********************************************************************************
 * Procedure: build_border
 * Synopsis:  Border = build_border(width, height, mode)
 * Purpose:   Build an return a set of drawing vectors that represent a button of
 *            width and height.  Mode can be either MODE_OUT or MODE_IN
 ***********************************************************************************/
struct Border *build_border(int width,
                            int height,
                            int mode
                           )
{
   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
   /*                                                                             */
   /* Select the colors based on the rendering mode.  For an out box, we will use */
   /* the lighter color on the upper and left sides and the darker color on the   */
   /* other two sides.  In an in box, the choice is reversed.                     */
   /*   (x,y)                                                5                    */
   /*   1****************************************************.2                   */
   /*    **4                                               3..                    */
   /*    **                                                 ..                    */
   /*    **                                                 ..                    */
   /*    **                                                 ..                    */
   /*    **                                                 ..                    */
   /*    **3 5                                             4..                    */
   /*   2*....................................................1                   */
   /*                                                       (x+width, y+height)   */
   /* 0.       0,    0    10.   wd,   ht                                          */
   /* 2.       0,   ht    12.   wd,    0                                          */
   /* 4.       1, ht-1    14. wd-1,    1                                          */
   /* 6.       1,    0    16. wd-1,   ht                                          */
   /* 8.    wd-1,    0    18.    1,   ht                                          */
   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

   struct Border *border, *border1;
   short *vecs;
   int i;

   static short inits[] = { 0, 0,   0, 0,   1,-1,   1, 0,  -1, 0,
                            0, 0,   0, 0,  -1, 1,  -1, 0,   1, 0};

#define VEC_COUNT 5
#define SINGLEUNIT (sizeof(struct Border)+sizeof(inits))

   width--;
   height--;
   border = (struct Border *)get_mem(2*SINGLEUNIT);
   if (border == NULL) return(NULL);

   border1 = border+1;
   vecs = (short *)(border+2);
   border->NextBorder = border1;
   border->DrawMode   = JAM1;
   border->Count      = VEC_COUNT;
   border->XY         = vecs;

   border1->DrawMode  = JAM1;
   border1->Count     = VEC_COUNT;
   border1->XY        = vecs + (2*VEC_COUNT);

   memcpy((char *)vecs, (char *)inits, sizeof(inits));
   for(i = 8; i < 17; i += 2) vecs[i] += width;
   vecs[3]  += height;
   vecs[5]  += height;
   vecs[11] += height;
   vecs[17] += height;
   vecs[19] += height;

   if (mode == MODE_OUT)
   {
      border1->FrontPen = global.ri.Shadow;
      border->FrontPen = global.ri.Highlight;
   }
   else
   {
      border1->FrontPen = global.ri.Highlight;
      border->FrontPen  = global.ri.Shadow;
   }
   return(border);
}

/***********************************************************************************
 * Procedure: init_gad_sizes
 * Synopsis:  rc = init_gad_sizes(swidth, sheight, resize);
 * Purpose:   Calculate and initialize all the imagery structures
 ***********************************************************************************/
int init_gad_sizes(int swidth,
                   int sheight,
                   int resize)
{
   int i;
   int width, height, wextra, wmarg;
   static int savewidth, saveheight;

   wmarg = MARGIN_LEFT + MARGIN_RIGHT;
   if (!resize)          /* not resizing                */
      if (savewidth)     /* and we already have a size  */
         if ((savewidth < swidth) && (saveheight < sheight))
         {               /* and it will fit this screen */
            swidth = savewidth;
            sheight = saveheight;
            resize = 1;  /* trust me...                 */
         }

   wmarg += RESIZE_WIDTH + MARGIN_LEFT;

   global.listsize = 0;
   /* Make two passes attempting to layout the items.  The first time we will use */
   /* Whatever the prevailing font is.  If this doesn't work, we will switch to a */
   /* Simple TOPAZ80 and try again.  Only if that fails will we give up.          */
   for(i = 5; i >= 0; i--)
   {
      global.iheight = global.ri.TextAttr.ta_YSize+DHBAR;
/* debug - why not try believing TextAttr ? */
/*
      if (global.iheight < global.ri.FontSize)
         global.iheight = global.ri.FontSize;
*/
      if (global.iheight < global.ri.FontSize)
         global.ri.FontSize = global.iheight;

      if (i)
      {
         global.iheight += 4; /* Account for decent margins on the rendering */
         global.eheight = global.ri.FontSize + 2*DHBAR;
         global.listextra = 0;
      }
      else
      {
         global.iheight += 2; /* Account for decent margins on the rendering */
         global.eheight = global.ri.FontSize + DHBAR + HBAR;
         global.listextra = 1;
      }

      /* We have the totals for everything, let's figure out how */
      /* big the final thing is going to be.                     */
      height = recalc_group_sizes(global.objects, &global.titsize, &global.cycsize)+
               global.iheight + MARGIN_MID +  /* Gadgets on the bottom */
               global.ri.WindowTitle + global.ri.WindowBottom;

      width  = global.cycsize + global.titsize + wmarg;           

      if ((wextra = (width - swidth)) > 0)
         {
         if (wextra < global.titsize)
            global.titsize -= wextra;
         else
             global.titsize = 0;
         }
 
      if ((i == 5) && (height <= sheight))
      {
         int num;
         num = (sheight - height) / global.eheight;
         if (num < 3)
         {
            height = sheight + 1; /* Force a failure */
         }
         else
         {
            if (!resize) /* first time in go for medium size */
            {
               if (num > 10) num = 10;
            }
            else
               if (num > MAX_LIST) num = MAX_LIST;
            global.listsize = num;
            height += num*global.eheight;
         }
      }

      if (height <= sheight) break;
      /* can we do anything here about insufficient width ? */

      switch(i)
      {
         case 0:
            break;
         case 1:
         case 2:
            /* Set up so we try with Topaz 80 as our default font the second time around */
            global.ri.TextAttr = TOPAZ80;
            break;
         case 5:
            global.listsize = 9;
            break;
         default:
            global.listsize = (global.listsize / 2) + 1;
            if (global.listsize < 3) global.listsize = 3;
            break;
      }
   }

   /* Everything worked out fine, let us set the height of the window */
   global.width = resize ? swidth : width;
   global.height = resize ? sheight : height;
   savewidth = global.width;
   saveheight = global.height;

   /* Also see if we need to adjust the titsize and cycsize values */
   i = global.width - width;
   if (i > 0)
      global.cycsize += i;

   return(0);
}


/***********************************************************************************
 * Procedure: create_borders
 * Synopsis:  create_borders
 * Purpose:   Create the borders for all the gadgets
 ***********************************************************************************/
int create_borders()
{
   struct Border *border;
   /* * * * * * * * * * * * * * * * * * * * * * * * * * */
   /*                                                   */
   /*              11111111                11111111     */
   /*    012345678901234567      012345678901234567     */
   /*    =================#      =================# 0   */
   /*    ||......23......##      ||..18......54..## 1   */
   /*    ||......##......##      ||..##......##..## 2   */
   /*    ||.....####.....##      ||...##....##...## 3   */
   /*    ||....##76##....##      ||....##76##....## 4   */
   /*    ||...##....##...##      ||.....####.....## 5   */
   /*    ||..##......##..##      ||......##......## 6   */
   /*    ||..18......54..##      ||......23......## 7   */
   /*    |#################      |################# 8   */
   /*                                                   */
   /* * * * * * * * * * * * * * * * * * * * * * * * * * */
   static short Up_Vectors[] = { 4,6,  8,2,  9,2, 13,6, 12,6,  9,3,  8,3,  5,6 };
   static short Dn_Vectors[] = { 4,2,  8,6,  9,6, 13,2, 12,2,  9,5,  8,5,  5,2 };

   /* * * * * * * * * * * * * * * * * * */
   /*              11111111112222       */
   /*    012345678901234567890134       */
   /*    ========================== 0   */
   /*    ||.....7.....8..........   1   */
   /*    ||.....#######......#|..   2   */
   /*    ||...5##.....##d....#|..   3   */
   /*    ||....##.....##.....#|..   4   */
   /*    ||....##..a######b..#|..   5   */
   /*    ||....##....####....#|..   6   */
   /*    ||....##....9##c....#|..   7   */
   /*    ||....##............#|..   8   */
   /*    ||...4##6...0##1....#|..   9   */
   /*    ||.....#######......#|..   10  */
   /*    ||.....3.....2..........   11  */
   /*    |########################  12  */
   /*                                   */
   /* * * * * * * * * * * * * * * * * * */
   static short Cycle_Vectors[] = { 13, 9,  14, 9,  13,10,   7,10,   6, 9,
                                     6, 3,   7, 9,   7, 2,  13, 2,  13, 7,
                                    11, 5,  16, 5,  14, 7,  14, 3, };
   static short Cycle_Line1[]   = { 20, 2,  20,11};
   static short Cycle_Line2[]   = { 21, 2,  21,11};

   static short Check_Vectors[] = { 19, 2,  17, 2,  11, 8,   8, 5,   7, 5,
                                    10, 8,   9, 5,  12, 8,  18, 2 };

   /* Now we have calculated sizes for the objects, layout the border structures */

   /* Create a border for the cycle gadgets */
   global.cycborder = build_border(global.cycsize,
                                       global.iheight, MODE_OUT);
   /* We also need to put in the vectors for the cycle picture */
   border = (struct Border *)get_mem(5*sizeof(struct Border));
   if (border == NULL) return(1);

   border[0].NextBorder = border+1;
   border[1].NextBorder = border+2;
   border[2].NextBorder = global.cycborder;
   global.cycborder = border;

   border[0].DrawMode = JAM1;
   border[0].Count  = sizeof(Cycle_Vectors)/(2*sizeof(short));
   border[0].XY = Cycle_Vectors;
   border[0].FrontPen = 1;

   border[1].DrawMode = JAM1;
   border[1].Count  = sizeof(Cycle_Line1)/(2*sizeof(short));
   border[1].XY = Cycle_Line1;
   border[1].FrontPen = global.ri.Shadow;

   border[2].DrawMode = JAM1;
   border[2].Count  = sizeof(Cycle_Line2)/(2*sizeof(short));
   border[2].XY = Cycle_Line2;
   border[2].FrontPen = global.ri.Highlight;

   global.checkborder[0] = border+3;
   border[3].NextBorder = build_border(CHECK_WIDTH, CHECK_HEIGHT, MODE_OUT);
   border[3].DrawMode = JAM1;
   border[3].Count = sizeof(Check_Vectors)/(2*sizeof(short));
   border[3].XY = Check_Vectors;
   border[3].FrontPen = 0;

   global.checkborder[1] = border+4;
   border[4].NextBorder = build_border(CHECK_WIDTH, CHECK_HEIGHT, MODE_OUT);
   border[4].DrawMode = JAM1;
   border[4].Count = sizeof(Check_Vectors)/(2*sizeof(short));
   border[4].XY = Check_Vectors;
   border[4].FrontPen = 1;

   /* Adjust the height of the cycle vectors to the current height */
   /* See the picture above for these values.  Yes, they are hard  */
   /* coded magic values, but equates for them don't make a lot of */
   /* sense in a situation like this.                              */
   {
      int cheight;
      if (global.iheight > 11)
         cheight = global.iheight;
      else
         cheight = 11;
      cheight -= DHBAR;
      Cycle_Vectors[1] = cheight - 2;
      Cycle_Vectors[3] = cheight - 2;
      Cycle_Vectors[5] = cheight - 1;
      Cycle_Vectors[7] = cheight - 1;
      Cycle_Vectors[9] = cheight - 2;
      Cycle_Vectors[13] = cheight - 2;
   }
/*
   for(i = 0; i < 0x0d; i++)
   {
      int extra;

      extra = global.iheight - 13;
      if (i > 8)
      {
         extra >>= 1;
      }
      else if ((i > 6) || (i == 5))
      {
         extra = 0;
      }

      Cycle_Vectors[1+i*2] += extra;
   }
*/

   Cycle_Line2[3] = Cycle_Line1[3] = (global.iheight - 1) - DHBAR;

   {
      int bheight;
      bheight = (global.listsize*global.eheight) - (2*ARR_HEIGHT) + global.listextra;
      border = build_border(PROP_WIDTH, bheight, MODE_OUT);
      if (border == NULL) return(1);

      border->TopEdge -= bheight;
      border->NextBorder->TopEdge -= bheight;
   }

   global.arrowborder[0].NextBorder = build_border(PROP_WIDTH, ARR_HEIGHT, MODE_OUT);
   global.arrowborder[1].NextBorder = border;
   border->NextBorder->NextBorder = global.arrowborder[0].NextBorder;

   global.arrowborder[0].DrawMode = JAM1;
   global.arrowborder[0].Count  = sizeof(Dn_Vectors)/(2*sizeof(short));
   global.arrowborder[0].XY = Dn_Vectors;
   global.arrowborder[0].FrontPen = 1;

   global.arrowborder[1].DrawMode = JAM1;
   global.arrowborder[1].Count  = sizeof(Up_Vectors)/(2*sizeof(short));
   global.arrowborder[1].XY = Up_Vectors;
   global.arrowborder[1].FrontPen = 1;
   return(0);
}

/***********************************************************************************
 * Procedure: recalc_group_sizes
 * Synopsis:  recalc_group_sizes(objlist, &titsize, &cycsize)
 * Purpose:   Calculate the base sizes for different types of objects.
 *            We need to calculate the width for CYCLE titles and size for cycles
 *            Everything else has a fixed size to be used anyway.
 ***********************************************************************************/
int recalc_group_sizes(struct G_OBJECT *objlist,
                    int *titsize,
                    int *cycsize)
{
   int wid;
   int height;
   struct G_OBJECT *obj;
   struct G_CYCLE *cyc;
   struct G_VALUE *val;

   *titsize = 0;
   *cycsize = 0;
   height = 0;

   /* First we need to lay out the gadgets on the left side of the window */
   for(obj = objlist; obj != NULL; obj = obj->next)
   {
      switch(obj->class)
      {
         case CLASS_CYCLE:
            /* go through and get all the cycle values */
            cyc = (struct G_CYCLE *)obj;
            set_cyc_state(cyc, cyc->curval);
            for(val = cyc->values; val != NULL; val = val->next)
            {
               wid = text_width(val->title);
               if (val->string)
               {
                  char *p;
                  int num;

                  p = strchr(val->option, '%');
                  num = 0;
                  if (p)
                  {
                     p++;
                     while ((*p >= '0') && (*p <= '9'))
                        num = (num * 10) + *p++ - '0';
                  }
                  if (num <= 0) num = 3;
                     wid += (text_width("0") * (num+1)) + DVBAR;
               }
               if (wid > *cycsize) *cycsize = wid;
            }
            /* Fall through and get the title size */
         case CLASS_STRING:
            wid = text_width(obj->title);
            if (wid > *titsize) *titsize = wid;
            break;
         case CLASS_LIST:
            height += global.eheight * global.listsize;
            break;
         case CLASS_CHECK:
            break;
         default:
            printf("Huh\n");
            break;
      }
      height += global.iheight;
   }
   *titsize += DVBAR;
   *cycsize += CYC_ICON_WIDTH+DVBAR;

   return(height);
}

/***********************************************************************************
 * Procedure: text_width
 * Synopsis:  len = text_width(str)
 * Purpose:   Return the rendered width of a given string
 ***********************************************************************************/
int text_width(char *str
              )
{
   struct IntuiText itext;

   itext.FrontPen  = 1;
   itext.BackPen   = 0;
   itext.DrawMode  = JAM1;
   itext.LeftEdge  = 0;
   itext.TopEdge   = 1;
   itext.ITextFont = &global.ri.TextAttr;
   itext.NextText  = NULL;
   itext.IText     = str;

   return(IntuiTextLength(&itext));
}
