/* Routines to create various types of gadgets, menus, etc
   Could do with a few comments and some documentation ...
   This code is placed in the public domain.
   David Gay, 1989.
*/

#include <proto/exec.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <intuition/intuition.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/diskfont.h>
#include "user/gadgets.h"
#include <string.h>

#define XDOUBLEMARGIN   4                   /* Margin for "double" border reque
sters */
#define YDOUBLEMARGIN   4
#define GADGTEXTX       4                   /* How far away (X) text is from Op
tion/Radio gadgets */
#define GADGTEXTY       4                   /* idem, but when text above gadget
 */
#define TEXTBORDERX     2                   /* How far out text gadget rectangl
e is */
#define TEXTBORDERY     4
#define MENUMARGIN      20                  /* Spacing between menus */
#define TEXTMARGIN      10                  /* Space around text items */
#define TEXTGAP         2                   /* Space above & below text items *
/
#define RULEHEIGHT      2                   /* Height of rules */
#define RULEGAP         6                   /* Space above & below rules */
#define RULEMARGIN      10                  /* idem, but left & right */
#define MINITEMWIDTH    (RULEMARGIN + 10)   /* Min. menu item width */
#define SUBX            30                  /* Sub item position */

boolean CheckFont(struct Memory *);

struct Memory
{
    struct MinList Fonts;
    struct MinList Lists;
    struct Remember *Mem;
};

struct FontNode
{
    struct MinNode fn_Node;
    struct TextFont *font;
};

struct ITextNode
{
    struct MinNode it_node;
    struct IntuiText it;
};

typedef struct Gadget GArray[1];

struct ListInfo
{
    struct MinNode li_node;
    char *buf;
    long len;
    struct List *list;
    struct MinList ilist;
    struct IntuiText *blank;
    long nb, current, pos;
    struct Gadget *arrow1, *arrow2, *strg, *sld;
    GArray *glist;
    long visible, exists, dispchars;
    ULONG clicksecs, clickmics;
    long clickpos;
};

static struct TextAttr topaz8 = { "topaz.font", 8 };
static BYTE fore = 1, back = 0, mode = JAM1, depth = 2;
static struct TextAttr *ta = &topaz8;
static boolean fontopen;
static struct TextFont *font;


static UWORD chip down_data[] = {
        0xF83E,0xF83E,0xF83E,0xC006,0xE00E,0xF01E,0xF83E,0xFC7E,
        0xFEFE,0xFFFE,0x07C0,0x07C0,0x07C0,0x3FF8,0x1FF0,0x0FE0,
        0x07C0,0x0380,0x0100,0x0000
};

static struct Image down_img = {
        0, 1,
        15, 10,
        2,
        down_data,
        0x0003, 0x0000,
        NULL
};

static UWORD chip up_data[] = {
        0xFEFE,0xFC7E,0xF83E,0xF01E,0xE00E,0xC006,0xF83E,0xF83E,
        0xF83E,0xFFFE,0x0100,0x0380,0x07C0,0x0FE0,0x1FF0,0x3FF8,
        0x07C0,0x07C0,0x07C0,0x0000
};

static struct Image up_img = {
        0, 1,
        15, 10,
        2,
        up_data,
        0x0003, 0x0000,
        NULL
};

/* Returns number of nodes, or -1 for failure */
static long build_ilist(struct ListInfo *li)
{
    int cnt = 0;
    struct Node *scan;

    NewList((struct List *)&li->ilist);

    for (scan = li->list->lh_Head; scan->ln_Succ; scan = scan->ln_Succ, cnt++)
    {
        struct ITextNode *in = AllocMem(sizeof(struct ITextNode) + li->dispchar
s + 1, MEMF_CLEAR);
        char *str;
        struct IntuiText *it;
        int j;

        if (!in) return -1;
        it = (struct IntuiText *)&in->it;
        str = (char *)(in + 1);

        str[li->dispchars] = '\0';

        strncpy(str, scan->ln_Name, li->dispchars);
        for (j = strlen(str); j < li->dispchars; j++) str[j] = ' ';

        it->FrontPen = fore; it->BackPen = back;
        it->DrawMode = JAM2;
        it->ITextFont = ta;
        it->IText = str;

        AddTail((struct List *)&li->ilist, (struct Node *)in);
    }
    li->exists = li->visible > cnt ? cnt : li->visible;
    return cnt;
}

static void free_ilist(struct ListInfo *li)
{
    struct MinNode *scan, *next;

    for (scan = li->ilist.mlh_Head; next = scan->mln_Succ; scan = next)
        FreeMem(scan, sizeof(struct ITextNode) + li->dispchars + 1);
}

static void recalc_glist(struct ListInfo *li)
{
    int i;
    struct ITextNode *scan;

    for (i = 0, scan = (struct ITextNode *)li->ilist.mlh_Head; i < li->pos; i++
, scan = (struct ITextNode *)scan->it_node.mln_Succ)
        ;
    for (i = 0; i < li->exists; i++, scan = (struct ITextNode *)scan->it_node.m
ln_Succ)
        (*li->glist)[i].GadgetText = &scan->it;
    for (; i < li->visible; i++)
        (*li->glist)[i].GadgetText = li->blank;

    if (li->current >= li->pos && li->current < li->pos + li->exists)
        (*li->glist)[li->current - li->pos].Flags |= SELECTED;
}

struct Memory *NewMemory()
{
    struct Memory *mem;
    struct Remember *key = NULL;

    mem = (struct Memory *)AllocRemember(&key, sizeof(struct Memory), MEMF_CLEA
R);
    if (mem)
    {
        mem->Mem = key;
        NewList((struct List *)&mem->Fonts);
        NewList((struct List *)&mem->Lists);
    }

    return(mem);
}

void Free(mem)
struct Memory *mem;
{
    struct MinNode *mn;

    if (mem)
    {
        for (mn = mem->Fonts.mlh_Head; mn->mln_Succ; mn = mn->mln_Succ)
            CloseFont(((struct FontNode *)mn)->font);
        for (mn = mem->Lists.mlh_Head; mn->mln_Succ; mn = mn->mln_Succ)
            free_ilist((struct ListInfo *)mn);

        FreeRemember(&mem->Mem, TRUE);
    }
}

void ModSys(f, b, m, font)
long f, b, m;
struct TextAttr *font;
{
    if (f != -1) fore = f;
    if (b != -1) back = b;
    if (m != -1) mode = m;
    if (font)
    {
        ta = font;
        fontopen = FALSE;
    }
}

void SetDepth(d)
long d;
{
   depth = d;
}

struct Requester *InitReq(left, top, width, height, mem)
long left, top, width, height;
struct Memory *mem;
{
    struct Requester *req;

    req = (struct Requester *)AllocRemember(&mem->Mem, sizeof(struct Requester)
, MEMF_CLEAR);
    if (req)
    {
        req->LeftEdge = left; req->TopEdge = top;
        req->Width = width; req->Height = height;
        req->BackFill = back;
    }

    return(req);
}

boolean SetReqBorder(req, type, mem)
struct Requester *req;
LONG type;
struct Memory *mem;
{
    struct Border *b = NULL;

    switch (type)
    {
        case 1:
            if (!AddRectBorder(&b, XDOUBLEMARGIN / 2, YDOUBLEMARGIN / 2, req->W
idth - XDOUBLEMARGIN, req->Height - YDOUBLEMARGIN, mem))
                break;
        case 0:
            if (!AddRectBorder(&b, 0, 0, req->Width, req->Height, mem))
                b = NULL;
            break;
        default:
            b = (struct Border *)type;
            break;
    }

    return((req->ReqBorder = b) != NULL);
}

void SetReqGadgets(req, gl)
struct Requester *req;
struct Gadget *gl;
{
    struct Gadget *g;

    for (g = gl; g; g = g->NextGadget)
        g->GadgetType |= REQGADGET;

    req->ReqGadget = gl;
}

void SetReqText(req, it)
struct Requester *req;
struct IntuiText *it;
{
    req->ReqText = it;
}

struct Gadget *AddBox(gl, id, text, flags, act, x, y, w, h, thick, mem)
struct Gadget **gl;
char *text;
long id, flags, act, x, y, w, h, thick;
struct Memory *mem;
{
    BYTE *data;
    struct Gadget *gadg = NULL;
    int i, tl, tx, ty, len = strlen(text);
    struct RastPort rp, rp2;
    struct BitMap bm, bm2;
    struct Image *im, *im2;

    if (CheckFont(mem) &&
        (gadg = (struct Gadget *)AllocRemember(&mem->Mem, sizeof(struct Gadget)
 + 2 * sizeof(struct Image), MEMF_CLEAR)) &&
        (data = (BYTE *)AllocRemember(&mem->Mem, 2 * depth * RASSIZE(w, h), MEM
F_CLEAR | MEMF_CHIP)))
    {
        im2 = (im = (struct Image *)(gadg + 1)) + 1;

        InitBitMap(&bm, depth, w, h);
        InitBitMap(&bm2, depth, w, h);
        for (i = 0; i < depth; i++)
        {
            bm.Planes[i] = data;
            bm2.Planes[i] = data + RASSIZE(w, h) * depth;
            data += RASSIZE(w, h);
        }
        InitRastPort(&rp);
        rp.BitMap = &bm;
        InitRastPort(&rp2);
        rp2.BitMap = &bm2;
        SetAPen(&rp, fore);
        SetAPen(&rp2, fore);

        im->Width = im2->Width = w;
        im->Height = im2->Height = h;
        im->Depth = im2->Depth = depth;
        im->ImageData = (USHORT *)bm.Planes[0];
        im2->ImageData = (USHORT *)bm2.Planes[0];
        im->PlanePick = im2->PlanePick = 0xff;

        SetRast(&rp, back);
        SetRast(&rp2, back);
        DrawRoundedRect(&rp, 0, 0, w, h);
        FillRoundedRect(&rp2, 0, 0, w, h);
        if (thick)
        {
            DrawRoundedRect(&rp, 1, 1, w - 2, h - 2);
            DrawRoundedRect(&rp, 1, 0, w - 2, h);
        }

        SetFont(&rp, font);
        SetFont(&rp2, font);
        tl = TextLength(&rp, text, len);
        tx = (w - tl) / 2;
        ty = (h - font->tf_YSize + 1) / 2 + font->tf_Baseline;
        SetDrMd(&rp, JAM1);
        Move(&rp, tx, ty); Text(&rp, text, len);
        SetAPen(&rp2, back); SetDrMd(&rp2, JAM1);
        Move(&rp2, tx, ty); Text(&rp2, text, len);

        gadg->LeftEdge = x;
        gadg->TopEdge = y;
        gadg->Width = w;
        gadg->Height = h;
        gadg->GadgetType = BOOLGADGET;

        gadg->GadgetRender = (APTR)im;
        gadg->SelectRender = (APTR)im2;
        gadg->GadgetID = id;
        gadg->Flags = GADGHIMAGE | GADGIMAGE | flags;
        gadg->Activation = act;

        AppendGadget(gl, gadg);
    }

    return(gadg);
}

struct Gadget *AddRadio(gl, id, text, side, flags, act, mutex, x, y, w, h, mem)
     
struct Gadget **gl;
char *text;
long id, side, flags, act, x, y, w, h, mutex;
struct Memory *mem;
{
    BYTE *data;
    struct Gadget *gadg = NULL;
    struct IntuiText *it = NULL;
    struct RastPort rp, rp2;
    struct BitMap bm, bm2;
    struct Image *im, *im2;
    int tl, bx, i;
    struct AreaInfo areainf;
    struct TmpRas tmpras;
    BYTE areabuf[4 * 5];
    PLANEPTR plane;

    if (AddIntuiText(&it, text, side ? w + GADGTEXTX : 0, 0, mem) &&
        (gadg = (struct Gadget *)AllocRemember(&mem->Mem, sizeof(struct Gadget)
 + 2 * sizeof(struct Image), MEMF_CLEAR)) &&
        (data = (BYTE *)AllocRemember(&mem->Mem, 2 * depth * RASSIZE(w, h), MEM
F_CLEAR | MEMF_CHIP)))
    {
        tl = IntuiTextLength(it);
        it->TopEdge = (h - font->tf_YSize) / 2;

        gadg->LeftEdge = side ? x : x - tl - GADGTEXTX;
        gadg->TopEdge = y;
        gadg->Width = tl + GADGTEXTX + w;
        gadg->Height = h;
        gadg->GadgetType = BOOLGADGET;
        gadg->MutualExclude = mutex;

        bx = side ? 0 : tl + GADGTEXTX;

        im2 = (im = (struct Image *)(gadg + 1)) + 1;

        InitBitMap(&bm, depth, w, h);
        InitBitMap(&bm2, depth, w, h);
        for (i = 0; i < depth; i++)
        {
            bm.Planes[i] = data;
            bm2.Planes[i] = data + RASSIZE(w, h) * depth;
            data += RASSIZE(w, h);
        }
        InitRastPort(&rp);
        rp.BitMap = &bm;
        InitRastPort(&rp2);
        rp2.BitMap = &bm2;
        if (plane = (PLANEPTR)AllocRaster(w, h))
        {
            InitTmpRas(&tmpras, plane, RASSIZE(w, h));
            rp2.TmpRas = &tmpras;
            memset(areabuf, 0, sizeof(areabuf));
            InitArea(&areainf, (short *)areabuf, 4);
            rp2.AreaInfo = &areainf;

            SetAPen(&rp, fore);
            SetAPen(&rp2, fore);

            im->Width = im2->Width = w;
            im->Height = im2->Height = h;
            im->LeftEdge = im2->LeftEdge = bx;
            im->Depth = im2->Depth = depth;
            im->ImageData = (USHORT *)bm.Planes[0];
            im2->ImageData = (USHORT *)bm2.Planes[0];
            im->PlanePick = im2->PlanePick = 0xff;

            SetRast(&rp, back);
            SetRast(&rp2, back);
            DrawEllipse(&rp, w/2, h/2, w/2 - 1, h/2 - 1);
            DrawEllipse(&rp2, w/2, h/2, w/2 - 1, h/2 - 1);
            AreaEllipse(&rp2, w/2, h/2, w/4, h/4);
            AreaEnd(&rp2);
            FreeRaster(plane, w, h);
            rp2.TmpRas = NULL;
            rp2.AreaInfo = NULL;

            gadg->GadgetRender = (APTR)im;
            gadg->SelectRender = (APTR)im2;
            gadg->GadgetText = it;
            gadg->GadgetID = id;
            gadg->Flags = GADGHIMAGE | GADGIMAGE | flags;
            gadg->Activation = act;

            AppendGadget(gl, gadg);
        }
        else
            gadg = NULL;
    }
    return(gadg);
}

struct Gadget *AddOption(gl, id, text, side, flags, act, x, y, w, h, mem)
struct Gadget **gl;
char *text;
long id, side, flags, act, x, y, w, h;
struct Memory *mem;
{
    struct Gadget *gadg = NULL;
    struct IntuiText *it = NULL;
    struct Border *b = NULL, *b2 = NULL;
    int tl, bx, f = fore, ba = back;

    if (AddIntuiText(&it, text, side ? w + GADGTEXTX : 0, 0, mem) &&
        (gadg = (struct Gadget *)AllocRemember(&mem->Mem, sizeof(struct Gadget)
, MEMF_CLEAR)))
    {
        gadg->Height = h;
        tl = IntuiTextLength(it);
        it->TopEdge = (h - font->tf_YSize) / 2;

        gadg->LeftEdge = side ? x : x - tl - GADGTEXTX;
        gadg->TopEdge = y;
        gadg->Width = tl + 4 + w;
        gadg->GadgetType = BOOLGADGET;

        bx = side ? 0 : tl + GADGTEXTX;
        if (AddLineBorder(&b, bx, 0, bx + w - 1, h - 1, mem) &&
            AddLineBorder(&b, bx + w - 1, 0, bx, h - 1, mem) &&
            AddRectBorder(&b, bx, 0, w, h, mem) &&
            (fore = ba, back = f, AddLineBorder(&b2, bx, 0, bx + w - 1, h - 1,
mem)) &&
            AddLineBorder(&b2, bx + w - 1, 0, bx, h - 1, mem) &&
            (fore = f, back = ba, AddRectBorder(&b2, bx, 0, w, h, mem)))
        {
            gadg->GadgetRender = (APTR)b2;
            gadg->SelectRender = (APTR)b;
            gadg->GadgetText = it;
            gadg->GadgetID = id;
            gadg->Flags = GADGHIMAGE | flags;
            gadg->Activation = TOGGLESELECT | act;

            AppendGadget(gl, gadg);
        }
        else
            gadg = NULL;
    }

    fore = f; back = ba;
    return(gadg);
}

struct Gadget *AddText(struct Gadget **gl, long id, char *text, long above, cha
r *buf, long maxlen, long undo, long flags, long act, long x, long y, long w, lo
ng h, long noborder, struct Memory *mem)
{
    BYTE *data;
    struct StringInfo *si;
    struct Gadget *gadg = NULL;
    struct IntuiText *it = NULL;
    int tl;
    struct Border *b = NULL;
    char *undobuf;

    data = (BYTE *)AllocRemember(&mem->Mem, sizeof(struct Gadget) + sizeof(stru
ct StringInfo) + (undo ? maxlen + 1: 0), MEMF_CLEAR);
    if (data)
    {
        gadg = (struct Gadget *)data;
        si = (struct StringInfo *)(gadg + 1);
        undobuf = undo ? data + sizeof(struct Gadget) + sizeof(struct StringInf
o) : NULL;

        if (AddIntuiText(&it, text, 0, 0, mem))
        {
            tl = IntuiTextLength(it);
            if (above)
            {
                it->TopEdge = -font->tf_YSize - GADGTEXTY;
                it->LeftEdge = 0;
            }
            else
            {
                it->TopEdge = h - font->tf_YSize - 2;
                it->LeftEdge = -tl - GADGTEXTX;
            }

            gadg->LeftEdge = x;
            gadg->TopEdge = y;
            gadg->Width = w;
            gadg->Height = h;
            gadg->GadgetType = STRGADGET;

            if (noborder || AddRectBorder(&b, -TEXTBORDERX + 1, -TEXTBORDERY +
1, w + TEXTBORDERX, h + TEXTBORDERY, mem))
            {
                gadg->GadgetRender = (APTR)b;
                gadg->GadgetText = it;
                gadg->GadgetID = id;
                gadg->Flags = GADGHCOMP | flags;
                gadg->Activation = act;
                gadg->SpecialInfo = (APTR)si;

                si->Buffer = buf;
                si->UndoBuffer = undobuf;
                si->MaxChars = maxlen;

                AppendGadget(gl, gadg);
            }
            else
                gadg = NULL;
        }
        else
            gadg = NULL;
    }

    return(gadg);
}

struct Gadget *AddSlider(struct Gadget **gl, long id, long act, long x, long y,
 long w, long h, long vert, long knobsize, struct Memory *mem)
{
    BYTE *data;
    struct Gadget *gg = NULL;
    struct PropInfo *info;
    struct Image *im;

    data = (BYTE *)AllocRemember(&mem->Mem, sizeof(struct Gadget) + sizeof(stru
ct PropInfo) + sizeof(struct Image), MEMF_CLEAR);
    if (data)
    {
        gg = (struct Gadget *)data;
        info = (struct PropInfo *)(data + sizeof(struct Gadget));
        im = (struct Image *)(data + sizeof(struct Gadget) + sizeof(struct Prop
Info));

        gg->LeftEdge = x;
        gg->TopEdge = y;
        gg->Width = w;
        gg->Height = h;
        gg->Flags = GADGHCOMP | GADGIMAGE |
                    (y < 0 ? GRELBOTTOM : 0) |
                    (x < 0 ? GRELRIGHT : 0) |
                    (w < 0 ? GRELWIDTH : 0) |
                    (h < 0 ? GRELHEIGHT : 0);
        gg->Activation = act;
        gg->GadgetType = PROPGADGET;
        gg->GadgetRender = (APTR)im; /* dummy image, why ? */
        gg->SpecialInfo = (APTR)info;
        gg->GadgetID = id;

        info->Flags = AUTOKNOB |
                      (vert ? FREEVERT : FREEHORIZ);
        if (vert) info->VertBody = knobsize;
        else info->HorizBody = knobsize;

        AppendGadget(gl, gg);
    }
    return(gg);
}

/* BUG: Arrows don't use selected colours */
struct ListInfo *AddList(struct Gadget **gl, long id, char *text, struct List *
list, char *buf, long len, long flags, long act, long x, long y, long w, long h,
 long noborder, struct Memory *mem)
{
    long ok = FALSE;
    struct ListInfo *li;
    int i;
    long nb;
    int a = font->tf_YSize;
    int dispchars = (w - 15 - 3) / font->tf_XSize;
    int nblines = (h - 2) / a;

    if (CheckFont(mem) &&
        (li = (struct ListInfo *)AllocRemember(&mem->Mem, sizeof(struct ListInf
o) + dispchars + 1, MEMF_CLEAR)))
    {
        char *spaces = (char *)(li + 1);

        memset(spaces, ' ', dispchars);
        spaces[dispchars] = '\0';

        AddHead((struct List *)&mem->Lists, (struct Node *)li);
        if (nblines >= 5 && a * (nblines - 2) - 26 > 20 && w > 15 + 3 + 32)
        {
            w = font->tf_XSize * dispchars + 15 + 3;
            h = a * nblines + 2;

            li->buf = buf;
            li->len = len;
            li->list = list;
            li->pos = 0;
            li->current = -1;
            li->visible = nblines - 2;
            li->dispchars = dispchars;
            li->clickpos = -1;

            if ((li->nb = nb = build_ilist(li)) != -1 &&
                AddIntuiText(&li->blank, spaces, 0, 0, mem))
            {
                struct Gadget *arrow1, *arrow2, *sld;
                struct Border *b = NULL;
                long knobsize;

                if (nb == 0)
                    knobsize = 0xffff;
                else
                {
                    knobsize = 0xffff * li->exists;
                    knobsize /= nb;
                }

                if ((sld = AddSlider(gl, id, RELVERIFY, x + w - 16, y + 13, 15,
 h - 2 * a - 26, TRUE, knobsize, mem)) &&
                    (arrow1 = (struct Gadget *)AllocRemember(&mem->Mem, 3 * siz
eof(struct Gadget), MEMF_CLEAR)) &&
                    AddRectBorder(&b, 0, 0, w, h - 2 * a, mem) &&
                    AddRectBorder(&b, w - 17, 12, 17, h - 2 * a - 24, mem) &&
                    AddLineBorder(&b, w - 17, 0, w - 17, h - 2 * a - 1, mem))
                {
                    struct Gadget *strg, *cadre;
                    int tx;

                    sld->UserData = (APTR)li;
                    li->sld = sld;
                    arrow2 = arrow1 + 1;
                    arrow1->TopEdge = y + 1;
                    arrow1->LeftEdge = x + w - 16;
                    arrow1->Width = 15;
                    arrow1->Height = 11;
                    arrow1->Flags = GADGHCOMP | GADGIMAGE;
                    arrow1->Activation = GADGIMMEDIATE | RELVERIFY;
                    arrow1->GadgetType = BOOLGADGET;
                    arrow1->GadgetRender = (APTR)&up_img;
                    arrow1->UserData = (APTR)li;
                    arrow1->GadgetID = id;
                    AppendGadget(gl, arrow1);

                    li->arrow1 = arrow1;
                    arrow2->LeftEdge = x + w - 16;
                    arrow2->TopEdge = y + h - 2 * a - 1 - 11;
                    arrow2->Width = 15;
                    arrow2->Height = 11;
                    arrow2->Flags = GADGHCOMP | GADGIMAGE;
                    arrow2->Activation = GADGIMMEDIATE | RELVERIFY;
                    arrow2->GadgetType = BOOLGADGET;
                    arrow2->GadgetRender = (APTR)&down_img;
                    arrow2->UserData = (APTR)li;
                    arrow2->GadgetID = id;
                    AppendGadget(gl, arrow2);
                    li->arrow2 = arrow2;

                    cadre = arrow2 + 1;
                    cadre->LeftEdge = x;
                    cadre->TopEdge = y;
                    cadre->Width = 1;
                    cadre->Height = 1;
                    cadre->Flags = GADGHNONE;
                    cadre->GadgetType = BOOLGADGET;
                    cadre->GadgetRender = (APTR)b;
                    cadre->UserData = (APTR)li;
                    cadre->GadgetID = id;
                    AppendGadget(gl, cadre);

                    tx = font->tf_XSize * strlen(text);
                    if (tx > w - 32) tx = w - 32;
                    if (strg = AddText(gl, id, text, FALSE, buf, len, TRUE, fla
gs, act | GADGIMMEDIATE, x + tx + GADGTEXTX, y + h - a - 2, w - tx - GADGTEXTX,
a + 2, noborder, mem))
                    {
                        GArray *bl;

                        strg->UserData = (APTR)li;
                        li->strg = strg;

                        if (bl = (GArray *)AllocRemember(&mem->Mem, li->visible
 * sizeof(struct Gadget), MEMF_CLEAR))
                        {
                            li->glist = bl;
                            for (i = 0; i < li->visible; i++)
                            {
                                struct Gadget *gg = &(*bl)[i];

                                gg->LeftEdge = x + 1;
                                gg->TopEdge = y + i * a + 1;
                                gg->Width = w - 18;
                                gg->Height = a;
                                gg->Flags = GADGHCOMP;
                                gg->Activation = GADGIMMEDIATE;
                                gg->GadgetType = BOOLGADGET;
                                gg->UserData = (APTR)li;
                                gg->GadgetID = id;
                                AppendGadget(gl, gg);
                            }
                            recalc_glist(li);
                            ok = TRUE;
                        }
                    }
                }
            }
        }
    }
    return ok ? li : NULL;
}

long ModifyList(struct Gadget *gg, struct Requester *req, struct Window *win, l
ong up)
{
    struct ListInfo *li = (struct ListInfo *)gg->UserData;
    int change = FALSE, ret = 0;

    switch (gg->GadgetType & ~GADGETTYPE)
    {
        case STRGADGET:
            if (!up && li->current != -1)
            {
                change = TRUE;
                (*li->glist)[li->current - li->pos].Flags &= ~SELECTED;
                li->current = li->clickpos = -1;
            }
            else if (up) ret = 1;
            break;
        case PROPGADGET:
            {
                struct PropInfo *pi = (struct PropInfo *)gg->SpecialInfo;

                if (li->current != -1) (*li->glist)[li->current - li->pos].Flag
s &= ~SELECTED;

                li->pos = (pi->VertPot + 1) * (li->nb - li->exists) >> 16;
                recalc_glist(li);
                change = TRUE;
            }
            break;
        case BOOLGADGET:
            if (gg == li->arrow1 || gg == li->arrow2)
            {
                if (up)
                {
                    long newpos;

                    if (li->current != -1) (*li->glist)[li->current - li->pos].
Flags &= ~SELECTED;
                    newpos = li->pos + (gg == li->arrow1 ? -1 : 1);
                    if (newpos >= 0 && newpos <= li->nb - li->exists)
                    {
                        struct PropInfo *pi = (struct PropInfo *)li->sld->Speci
alInfo;
                        long newpot;

                        li->pos = newpos;
                        newpot = 0xffff * li->pos;
                        newpot /= li->nb - li->exists;
                        NewModifyProp(li->sld, win, req, pi->Flags, 0, newpot,
0, pi->VertBody, 1);
                        recalc_glist(li);
                        change = TRUE;
                    }
                }
            }
            else
            {
                int i, pos = -1;

                for (i = 0; i < li->exists; i++)
                    if (gg == &(*li->glist)[i])
                    {
                        pos = i;
                        break;
                    }
                if (pos != -1)
                {
                    ULONG secs, micros;
                    struct Node *scan;

                    CurrentTime(&secs, &micros);
                    if (li->current != -1) (*li->glist)[li->current - li->pos].
Flags &= ~SELECTED;
                    li->current = li->pos + pos;
                    (*li->glist)[pos].Flags |= SELECTED;
                    change = TRUE;
                    li->buf[li->len - 1] = '\0';
                    for (i = 0, scan = li->list->lh_Head; i < li->current; i++,
 scan = scan->ln_Succ)
                        ;
                    strncpy(li->buf, scan->ln_Name, li->len - 1);
                    RefreshGList(li->strg, win, req, 1);

                    ret = li->clickpos == li->current && DoubleClick(li->clicks
ecs, li->clickmics, secs, micros) ? 2 : 1;
                    li->clickpos = li->current;
                    li->clicksecs = secs;
                    li->clickmics = micros;
                }
            }
    }
    if (change) RefreshGList(&(*li->glist)[0], win, req, li->exists);

    return ret;
}

long ChangeList(struct ListInfo *li, struct List *list, struct Requester *req,
struct Window *win)
{
    struct PropInfo *pi = (struct PropInfo *)li->sld->SpecialInfo;
    long knobsize;

    free_ilist(li);
    li->list = list;
    if ((li->nb = build_ilist(li)) != -1)
    {
        if (li->current != -1) (*li->glist)[li->current - li->pos].Flags &= ~SE
LECTED;

        li->pos = 0;
        li->current = -1;
        li->clickpos = -1;

        if (li->nb == 0)
            knobsize = 0xffff;
        else
        {
            knobsize = 0xffff * li->exists;
            knobsize /= li->nb;
        }
        NewModifyProp(li->sld, win, req, pi->Flags, 0, 0, 0, knobsize, 1);

        recalc_glist(li);
        RefreshGList(&(*li->glist)[0], win, req, li->visible);

        return TRUE;
    }
    return FALSE;
}

struct Gadget *ListStr(struct ListInfo *li)
{
    return li->strg;
}

struct IntuiText *AddIntuiText(it, str, x, y, mem)
struct IntuiText **it;
char *str;
long x, y;
struct Memory *mem;
{
    struct IntuiText *intui = NULL;

    if (CheckFont(mem))
    {
        intui = (struct IntuiText *)AllocRemember(&mem->Mem, sizeof(struct Intu
iText), MEMF_CLEAR);
        if (intui)
        {
            intui->FrontPen = fore; intui->BackPen = back;
            intui->DrawMode = mode;
            intui->LeftEdge = x; intui->TopEdge = y;
            intui->ITextFont = ta;
            intui->IText = str;

            AppendText(it, intui);
        }
    }

    return(intui);
}

struct Border *AddLineBorder(struct Border **border, long x0, long y0, long x1,
 long y1, struct Memory *mem)
{
    BYTE *data;
    struct Border *bb = NULL;
    WORD *vert;

    data = (BYTE *)AllocRemember(&mem->Mem, sizeof(struct Border) + 2 * 2 * siz
eof(WORD), MEMF_CLEAR);
    if (data) {
        bb = (struct Border *)data;
        vert = (WORD *)(data + sizeof(struct Border));

        bb->FrontPen = fore; bb->BackPen = back;
        bb->DrawMode = mode;
        bb->LeftEdge = 0; bb->TopEdge = 0;
        bb->Count = 2;
        bb->XY = vert;

        vert[0] = x0; vert[1] = y0;
        vert[2] = x1; vert[3] = y1;

        AppendBorder(border, bb);
    }

    return(bb);
}

struct Border *AddRectBorder(border, x, y, w, h, mem)
struct Border **border;
long x, y, w, h;
struct Memory *mem;
{
    BYTE *data;
    struct Border *bb = NULL;
    WORD *vert;

    data = (BYTE *)AllocRemember(&mem->Mem, sizeof(struct Border) + 5 * 2 * siz
eof(WORD), MEMF_CLEAR);
    if (data) {
        bb = (struct Border *)data;
        vert = (WORD *)(data + sizeof(struct Border));

        bb->FrontPen = fore; bb->BackPen = back;
        bb->DrawMode = mode;
        bb->LeftEdge = x; bb->TopEdge = y;
        bb->Count = 5;
        bb->XY = vert;

        vert[1 * 2 + 0] = w - 1;
        vert[2 * 2 + 0] = w - 1;
        vert[2 * 2 + 1] = h - 1;
        vert[3 * 2 + 1] = h - 1;

        AppendBorder(border, bb);
    }

    return(bb);
}

struct Menu *AddMenu(struct Menu **ml, struct Screen *scr, char *text, long fla
gs, struct Memory *mem)
{
    struct Screen look;
    struct TextFont *scrfont;
    struct Menu *menu = NULL, *prev;

    if (GetScreenData((char *)&look, sizeof(struct Screen), scr == NULL ? WBENC
HSCREEN : CUSTOMSCREEN, scr))
        if (scrfont = OpenDiskFont(look.Font))
        {
            if (menu = (struct Menu *)AllocRemember(&mem->Mem, sizeof(struct Me
nu), MEMF_CLEAR))
            {
                menu->MenuName = text;
                menu->Flags = flags;
                menu->Height = scrfont->tf_YSize + 1;
                menu->Width = scrfont->tf_XSize * strlen(text) + MENUMARGIN / 2
;

                if (*ml == NULL)
                {
                    menu->LeftEdge = 0;
                    *ml = menu;
                }
                else
                {
                    for (prev = *ml; prev->NextMenu; prev = prev->NextMenu) ;
                    prev->NextMenu = menu;
                    menu->LeftEdge = prev->LeftEdge + prev->Width + MENUMARGIN;
     
                }
            }
            CloseFont(scrfont);
        }
    return menu;
}

/* Assumes HIRES screen (for COMMWIDTH) */
struct MenuItem *AddItem(struct Menu *menu, char *text, long flags, long mutex,
 long cmd, long sub, struct Memory *mem)
{
    struct MenuItem *item = NULL, *prev;
    struct IntuiText *it = NULL;
    WORD width;

    if (AddIntuiText(&it, text, TEXTMARGIN / 2, TEXTGAP / 2, mem))
    {
        if (item = (struct MenuItem *)AllocRemember(&mem->Mem, sizeof(struct Me
nuItem), MEMF_CLEAR))
        {
            item->LeftEdge = 0;
            width = IntuiTextLength(it) + TEXTMARGIN;
            item->Flags = flags | ITEMTEXT;
            item->Height = font->tf_YSize + TEXTGAP;
            item->MutualExclude = mutex;
            item->ItemFill = (APTR)it;
            if (cmd)
            {
                item->Flags |= COMMSEQ;
                width += COMMWIDTH + font->tf_XSize;
                item->Command = cmd;
            }
            if (sub)
                width += 2 * font->tf_XSize + TEXTMARGIN / 2;

            if (width < MINITEMWIDTH) width = MINITEMWIDTH;

            if (menu->FirstItem)
            {
                for (prev = menu->FirstItem; prev->NextItem; prev = prev->NextI
tem) ;
                item->TopEdge = prev->TopEdge + prev->Height;
                prev->NextItem = item;
                if (prev->Width > width)
                    item->Width = prev->Width;
                else
                    for (prev = menu->FirstItem; prev; prev = prev->NextItem)
                    {
                        struct IntuiText *msg;

                        if ((prev->Flags & ITEMTEXT) == 0) /* A rule */
                            ((struct Image *)(prev->ItemFill))->Width = width -
 RULEMARGIN;
                        else if ((msg = ((struct IntuiText *)(prev->ItemFill))-
>NextText) != NULL) /* A SubItem header */
                            msg->LeftEdge += width - prev->Width;

                        prev->Width = width;
                    }
            }
            else
            {
                menu->FirstItem = item;
                item->TopEdge = 0;
                item->Width = width;
            }
            if (sub && !AddIntuiText(&it, ".", item->Width - TEXTMARGIN / 2 - f
ont->tf_XSize, TEXTGAP / 2, mem))
                item = NULL;
        }
    }
    return item;
}

/* Assumes HIRES screen (for COMMWIDTH) */
struct MenuItem *AddSub(struct MenuItem *item, char *text, long flags, long mut
ex, long cmd, struct Memory *mem)
{
    struct MenuItem *subitem = NULL, *prev;
    struct IntuiText *it = NULL;
    WORD width;

    if (AddIntuiText(&it, text, TEXTMARGIN / 2, TEXTGAP / 2, mem))
    {
        if (subitem = (struct MenuItem *)AllocRemember(&mem->Mem, sizeof(struct
 MenuItem), MEMF_CLEAR))
        {
            subitem->LeftEdge = SUBX;
            width = IntuiTextLength(it) + TEXTMARGIN;
            subitem->Flags = flags | ITEMTEXT;
            subitem->Height = font->tf_YSize + TEXTGAP;
            subitem->MutualExclude = mutex;
            subitem->ItemFill = (APTR)it;
            if (cmd)
            {
                subitem->Flags |= COMMSEQ;
                width += COMMWIDTH + font->tf_XSize;
                subitem->Command = cmd;
            }
            if (width < MINITEMWIDTH) width = MINITEMWIDTH;

            if (item->SubItem)
            {
                for (prev = item->SubItem; prev->NextItem; prev = prev->NextIte
m) ;
                subitem->TopEdge = prev->TopEdge + prev->Height;
                prev->NextItem = subitem;
                if (prev->Width > width)
                    subitem->Width = prev->Width;
                else
                    for (prev = item->SubItem; prev; prev = prev->NextItem)
                        prev->Width = width;
            }
            else
            {
                item->SubItem = subitem;
                subitem->TopEdge = font->tf_YSize / 2 + 1;
                subitem->Width = width;
            }
        }
    }
    return subitem;
}
/* Inspired by Stuart Ferguson's code */
struct MenuItem *AddRule(struct Menu *menu, struct Memory *mem)
{
    struct MenuItem *item = NULL, *prev;
    struct Image *img;

    if ((img = (struct Image *)AllocRemember(&mem->Mem, sizeof(struct Image), M
EMF_CLEAR)) &&
        (item = (struct MenuItem *)AllocRemember(&mem->Mem, sizeof(struct MenuI
tem), MEMF_CLEAR)))
    {
        item->LeftEdge = 0;
        item->Flags = ITEMENABLED | HIGHNONE;
        item->Height = RULEGAP + RULEHEIGHT;
        item->MutualExclude = 0;
        item->ItemFill = (APTR)img;

        if (menu->FirstItem)
        {
            for (prev = menu->FirstItem; prev->NextItem; prev = prev->NextItem)
 ;
            item->TopEdge = prev->TopEdge + prev->Height;
            prev->NextItem = item;
            item->Width = prev->Width;
        }
        else
        {
            menu->FirstItem = item;
            item->TopEdge = 0;
            item->Width = MINITEMWIDTH;
        }
        img->LeftEdge = RULEMARGIN / 2;
        img->TopEdge = RULEGAP / 2;
        img->Width = item->Width - RULEMARGIN;
        img->Height = RULEHEIGHT;
        img->Depth = depth;
        img->ImageData = NULL;
        img->PlanePick = 0;
        img->PlaneOnOff = fore;
    }
    return item;
}

boolean CheckFont(mem)
struct Memory *mem;
{
    struct FontNode *fn;

    if (ta == NULL) fontopen = TRUE;
    if (!fontopen)
    {
        font = (struct TextFont *)OpenDiskFont(ta);
        if (font)
        {
            fn = (struct FontNode *)AllocRemember(&mem->Mem, sizeof(struct Font
Node), MEMF_CLEAR);
            if (fn)
            {
                fn->font = font;
                AddHead((struct List *)&mem->Fonts, (struct Node *)fn);
                fontopen = TRUE;
            }
            else CloseFont(font);
        }
    }

    return(fontopen);
}

void AppendGadget(gl, gg)
struct Gadget **gl, *gg;
{
    struct Gadget *gadg;

    if (*gl == NULL) *gl = gg;
    else
    {
        gadg = *gl;
        while (gadg->NextGadget) gadg = gadg->NextGadget;
        gadg->NextGadget = gg;
    }
}

void AppendBorder(bl, bb)
struct Border **bl, *bb;
{
    struct Border *bord;

    if (*bl == NULL) *bl = bb;
    else
    {
        bord = *bl;
        while (bord->NextBorder) bord = bord->NextBorder;
        bord->NextBorder = bb;
    }
}

void AppendText(itl, txt)
struct IntuiText **itl, *txt;
{
    struct IntuiText *intui;

    if (*itl == NULL) *itl = txt;
    else
    {
        intui = *itl;
        while (intui->NextText) intui = intui->NextText;
        intui->NextText = txt;
    }
}

#define Line(rp, x1, y1, x2, y2) { Move((rp), (x1), (y1)); Draw((rp), (x2), (y2
)); }

void DrawRect(rp, x, y, w, h)
struct RastPort *rp;
long x, y, w, h;
{
    Move(rp, x, y);
    Draw(rp, x + w - 1, y);
    Draw(rp, x + w - 1, y + h - 1);
    Draw(rp, x, y + h - 1);
    Draw(rp, x, y);
}

void DrawRoundedRect(rp, x, y, w, h)
struct RastPort *rp;
long x, y, w, h;
{
    int x2 = x + w - 1, y2 = y + h - 1;

    Line(rp, x + 5, y, x2 - 5, y); Line(rp, x + 5, y2, x2 - 5, y2);
    Line(rp, x, y + 5, x, y2 - 4); Line(rp, x2, y + 5, x2, y2 - 4);
    WritePixel(rp, x + 4, y + 1); WritePixel(rp, x + 3, y + 1); WritePixel(rp,
x + 2, y + 2); WritePixel(rp, x + 1, y + 3); WritePixel(rp, x + 1, y + 4);
    WritePixel(rp, x + 4, y2 - 1); WritePixel(rp, x + 3, y2 - 1); WritePixel(rp
, x + 2, y2 - 2); WritePixel(rp, x + 1, y2 - 3); WritePixel(rp, x + 1, y2 - 4);
    WritePixel(rp, x2 - 4, y + 1); WritePixel(rp, x2 - 3, y + 1); WritePixel(rp
, x2 - 2, y + 2); WritePixel(rp, x2 - 1, y + 3); WritePixel(rp, x2 - 1, y + 4);
    WritePixel(rp, x2 - 4, y2 - 1); WritePixel(rp, x2 - 3, y2 - 1); WritePixel(
rp, x2 - 2, y2 - 2); WritePixel(rp, x2 - 1, y2 - 3); WritePixel(rp, x2 - 1, y2 -
 4);
}

void FillRoundedRect(rp, x, y, w, h)
struct RastPort *rp;
long x, y, w, h;
{
    int x2 = x + w - 1, y2 = y + h - 1;

    RectFill(rp, x, y + 5, x2, y2 - 5);
    Line(rp, x + 1, y + 4, x2 - 1, y + 4); Line(rp, x + 1, y + 3, x2 - 1, y + 3
); Line(rp, x + 2, y + 2, x2 - 2, y + 2); Line(rp, x + 4, y + 1, x2 - 4, y + 1);
     
    Line(rp, x + 1, y2 - 4, x2 - 1, y2 - 4); Line(rp, x + 1, y2 - 3, x2 - 1, y2
 - 3); Line(rp, x + 2, y2 - 2, x2 - 2, y2 - 2); Line(rp, x + 4, y2 - 1, x2 - 4,
y2 - 1);
}

