/*
 * WinControl v1.0
 *
 * written by John Corigliano with SAS/C 6.56
 *
 * Copyright 1996 John Corigliano
 *
 * jcorig@strauss.udel.edu
 *
 * Feel free to enhance this code!  
 */

/* These are for SAS/C */
#define __USE_SYSBASE
long    __oslibversion = 37L;

/* The includes */
#include <exec/types.h>
#include <exec/exec.h>
#include <exec/interrupts.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <devices/input.h>
#include <workbench/workbench.h>
#include <dos/dos.h>

/* With SAS/C, if we include these, the libs are Auto Open/Close */
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/icon.h>
#include <proto/layers.h>
#include <proto/graphics.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* This has all the stuff for StartMenu */
#include "/_StartMenu/messages.h"

#define ALL_MASKS IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT | \
                  IEQUALIFIER_LALT | IEQUALIFIER_RALT | \
                  IEQUALIFIER_CONTROL | IEQUALIFIER_LCOMMAND | \
                  IEQUALIFIER_RCOMMAND | IEQUALIFIER_MIDBUTTON

UBYTE ver[] = {"$VER: WinControl v1.0 "__AMIGADATE__};

extern struct IntuitionBase *IntuitionBase;
struct Library *IntBase;

#define IS_ICON             0x01
#define IGNORE_SET_TITLE    0x02

/* This will be the linked list of our windows and their gadgets */
typedef struct WinControl {
    struct WinControl *wc_Next;
    struct WinControl *wc_Children;
    struct Window     *wc_Win;
    struct Gadget     *wc_Gadget;
    UBYTE              wc_Title[48];
    UBYTE              wc_Flags;
    struct IBox        wc_RealPos;
    UWORD              wc_MinX, wc_MinY;
    struct Task       *wc_Task;
} WIN_CON;

typedef struct PatternInfo {
    struct PatternInfo *pi_Next;
    UBYTE               pi_Name[64];
    UBYTE               pi_Pattern[128];
    struct BitMap      *pi_BitMap,
                       *pi_Mask;
    WORD                pi_FrontPen,
                        pi_BackPen,
                        pi_Offset;
} PATTERN_INFO;

ULONG quals[6];

WIN_CON *whead, *wtail;
PATTERN_INFO *pat_head;
struct Screen *scn;
struct MsgPort *my_port;
volatile WIN_INFO sm_win;
START_MSG smsg;
IMAGE_SCALED ims;
BOOL showchild = FALSE, show_icons = FALSE;
struct SignalSemaphore sema;

typedef ULONG (*FUNCPTR)();
FUNCPTR OldOpenWin;
FUNCPTR OldOpenWinTag;
FUNCPTR OldCloseWin;
FUNCPTR OldSetTitle;
FUNCPTR OldUpfrontLayer;
ULONG MyOpenWin();
ULONG MyOpenWinTag();
ULONG MyCloseWin();
ULONG MySetTitle();
ULONG MyUpfrontLayer();

VOID HandleIDCMP(WIN_CON *, UWORD);
VOID ShutDown(STRPTR, int);
BOOL UnLink(struct Gadget *);
VOID CatchUp(VOID);
VOID FindStartMenu(VOID);
VOID SendStartMsg(START_MSG *, APTR, UBYTE);
VOID __saveds SendStartMsgAlt(APTR, UBYTE);
VOID SetPatches(VOID);
VOID __asm __saveds HandleOpenWin(register __d0 struct Window *);
BOOL __saveds IsMatch(STRPTR, START_MENU_GADGET *);
VOID __asm __saveds HandleCloseWin(register __a0 struct Window *);
VOID __asm __saveds HandleSetTitle(register __a0 struct Window *,
                                   register __a1 STRPTR);
LONG __asm __saveds HandleUpfrontLayer(register __a1 struct Layer *);
VOID DoGadget(WIN_CON *);
VOID DoChildGadget(WIN_CON *);
VOID DoIconify(WIN_CON *);
VOID UnIconify(WIN_CON *);
WIN_CON *FindWC(struct Gadget *);
WIN_CON * FindParent(struct Window *, struct Task *);
BOOL WinExists(struct Window *);
VOID ReadPatterns(VOID);
VOID FreePatterns(VOID);
BOOL ParseStr(STRPTR, STRPTR, STRPTR, STRPTR, STRPTR, STRPTR, STRPTR);
VOID strip(STRPTR);
VOID SendCloseMsg(struct Window *);

int main(int argc, char **argv)
{
    GADGET_STATS gs;
    START_MSG *rep_smsg;

    ULONG sig, my_sig, x;
    BOOL done = FALSE;
    BPTR lock;
    UBYTE icon_name[128];
    struct DiskObject *d_obj;
    WIN_CON *w;

    InitSemaphore(&sema);

    /* This just saves a little typing */
    IntBase = (struct Library *)IntuitionBase;

    FindStartMenu();

    if (0 == argc) {
        argc = _WBArgc;
        argv = _WBArgv;
    }
    for (x = 1; x < argc; x++) {
        if (0 == strnicmp(argv[x], "ICON=", 5)) {
            if (0 == strnicmp(&(argv[x][5]), "TRUE", 4)) {
                show_icons = TRUE;
                /* Use the program's WB icon for the imagery in the gadgets */
                lock = GetProgramDir();
                NameFromLock(lock, icon_name, 128);
                AddPart(icon_name, "WinControl", 128);
                /* Scale the image */
                if (d_obj = GetDiskObject(icon_name)) {
                    if (d_obj->do_Gadget.GadgetRender) {
                        ims.ims_Image = d_obj->do_Gadget.GadgetRender;
                        SendStartMsg(&smsg, (APTR)(&ims), SCALE_IMAGE);
                        /* No need to check image - if it's NULL we just won't useit */
                    }
                    FreeDiskObject(d_obj);
                }
            }
        }
        else if (0 == strnicmp(argv[x], "CHILDMENU=", 10)) {
            if (0 == strnicmp(&(argv[x][10]), "TRUE", 4)) {
                showchild = TRUE;
            }
        }
    }

    ReadPatterns();

    CatchUp();
    SetPatches();

    /* The event loop */
    my_sig = 1L << my_port->mp_SigBit;
    while (!done) {
        sig = Wait(my_sig | SIGBREAKF_CTRL_C);
        if (sig & my_sig) {
            while (rep_smsg = (START_MSG *)GetMsg(my_port)) {
                /* If it's GADGET_STATS, the user pressed a button */
                if (rep_smsg->sm_Command == GADGET_STATUS) {
                    /* Copy and reply ASAP */
                    memcpy((APTR)(&gs), rep_smsg->sm_Data, sizeof(GADGET_STATS));
                    ReplyMsg((struct Message *)rep_smsg);
                    /* Handle a button press - these are TOGGLE gadgets */
                    w = FindWC(gs.gs_Gadget);
                    if (gs.gs_IMSG->Class == IDCMP_GADGETDOWN)
                        HandleIDCMP(w, gs.gs_IMSG->Qualifier);
                    else if (gs.gs_IMSG->Class == IDCMP_GADGETUP) {
                        if (w->wc_Children) DoChildGadget(w);
                        else DoGadget(w);
                    }
                }
                /* Start menu booted us! It must be shutting down */
                else if (rep_smsg->sm_Command == REM_TASKBAR_GADGET) {
                    RE_GADGET *re = (RE_GADGET *)(rep_smsg->sm_Data);
                    /* Only quit after ALL gadgets have been removed */
                    done = UnLink(re->re_Gadget);
                    ReplyMsg((struct Message *)rep_smsg);
                }
            }
        }
        else if (sig & SIGBREAKF_CTRL_C) {
            done = TRUE;
        }

    }

    ShutDown(NULL, 0);
}

VOID HandleIDCMP(WIN_CON *w, UWORD qual)
{
    WIN_CON *temp;
    
    qual &= ALL_MASKS;

    if (quals[0] == qual)
        DoIconify(w);
    else if (quals[1] == qual) {
        DoIconify(w);
        temp = w->wc_Children;
        while (temp) {
            DoIconify(temp);
            temp = temp->wc_Next;
        }
    }
    else if (quals[2] == qual) {
        if (w->wc_Flags & IS_ICON) UnIconify(w);
        WindowToFront(w->wc_Win);
        ActivateWindow(w->wc_Win);
        SetGadgetAttrs(w->wc_Gadget, sm_win.wi_Window, NULL,
                            GA_Selected, FALSE, TAG_END);
    }
    else if (quals[3] == qual) {
        if (w->wc_Flags & IS_ICON) UnIconify(w);
        WindowToFront(w->wc_Win);
        ActivateWindow(w->wc_Win);
        temp = w->wc_Children;
        while (temp) {
            if (temp->wc_Flags & IS_ICON) UnIconify(temp);
            WindowToFront(temp->wc_Win);
            ActivateWindow(temp->wc_Win);
            temp = temp->wc_Next;
        }
        SetGadgetAttrs(w->wc_Gadget, sm_win.wi_Window, NULL,
                            GA_Selected, FALSE, TAG_END);

    }
    else if (quals[4] == qual) {
        SetGadgetAttrs(w->wc_Gadget, sm_win.wi_Window, NULL, GA_Selected, FALSE, TAG_END);
        SendCloseMsg(w->wc_Win);
    }
    else if (quals[5] == qual) {
        RE_GADGET re;
        re.re_Gadget = w->wc_Gadget;
        SendStartMsg(&smsg, (APTR)(&re), REM_TASKBAR_GADGET);
        UnLink(w->wc_Gadget);
    }
    else {
        if (w->wc_Children) DoChildGadget(w);
        else DoGadget(w);
        SetGadgetAttrs(w->wc_Gadget, sm_win.wi_Window, NULL,
                            GA_Selected, FALSE, TAG_END);

    }
}

/*
 * Clean up routine.
 */
VOID ShutDown(STRPTR s, int rc)
{
    FREE_BITMAP fbm;
    WIN_CON *work, *next;
    RE_GADGET reg;

    if (ims.ims_ScaledBM) {
        fbm.fbm_BitMap = ims.ims_ScaledBM;
        fbm.fbm_Mask = ims.ims_Mask;
        SendStartMsg(&smsg, (APTR)(&fbm), FREE_BIT_MAP);
    }

    FreePatterns();

    if (my_port) DeletePort(my_port);

    /* Okay, so it's not the safest way to un-patch */
    if (OldOpenWin) SetFunction(IntBase, -0xCC, OldOpenWin);
    if (OldOpenWinTag) SetFunction(IntBase, -0x25E, OldOpenWinTag);
    if (OldCloseWin) SetFunction(IntBase, -0x48, OldCloseWin);
    if (OldSetTitle) SetFunction(IntBase, -0x114, OldSetTitle);
    if (OldUpfrontLayer) SetFunction(LayersBase, -0x30, OldUpfrontLayer);

    work = whead;
    while (work) {
        reg.re_Gadget = work->wc_Gadget;
        SendStartMsg(&smsg, (APTR)(&reg), REM_TASKBAR_GADGET);
        next = work->wc_Next;
        UnLink(work->wc_Gadget);
        work = next;
    }

    if (s) printf("WinControl: %s\n", s);
    exit(rc);
}

/*
 * Remove a gadget and return TRUE if it was the last one.
 */
BOOL UnLink(struct Gadget *g)
{
    WIN_CON *work, *cl, *temp = NULL;
    
    ObtainSemaphore(&sema);
    work = whead;

    while (work) {
        if (work->wc_Gadget == g) break;
        temp = work;
        work = work->wc_Next;
    }
    
    if (NULL == work) {
        ReleaseSemaphore(&sema);
        return(FALSE);
    }
    
    if (whead == work) {
        if (work->wc_Next) whead = work->wc_Next;
        else whead = wtail = NULL;
    }
    else if (wtail == work) {
        wtail = temp;
        temp->wc_Next = NULL;
    }
    else
        temp->wc_Next = work->wc_Next;

    if (work->wc_Flags & IS_ICON) UnIconify(work);

     cl = work->wc_Children;
     while (cl) {
        temp = cl->wc_Next;
        if (cl->wc_Flags & IS_ICON) UnIconify(cl);
        FreeMem(cl, sizeof(WIN_CON));
        cl = temp;
    }
    FreeMem(work, sizeof(WIN_CON));
    
    ReleaseSemaphore(&sema);
    return((BOOL)((NULL == whead) ? TRUE : FALSE));
}

/*
 * Used at init to catch any windows that may have been open already.
 */
VOID CatchUp(VOID)
{
    struct Window *win;
    BOOL temp = showchild;

    showchild = FALSE;
    win = scn->FirstWindow;
    while (win) {
        HandleOpenWin(win);
        win = win->NextWindow;
    }
    showchild = temp;
}

/*
 * Need to see if StartMenu is actually up and running.
 */
VOID FindStartMenu(VOID)
{
    WORD x = 0;
    struct MsgPort *tport;
    
    /* Make a couple of tries to find StartMenu */
    while (NULL == (tport = FindPort(STARTMENU_PORT_NAME))) {
        if (++x == 10) break;
        Delay(250);
    }
    if (NULL == tport)
        ShutDown("Could not find StartMenu.", 10);

    /* This port is for StartMenu to send us messages and for us to
       send StartMenu mesages */
    if (NULL == (my_port = CreatePort(0,0)))
        ShutDown("Could not create port.", 10);

    /* These are constant */
    smsg.sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
    smsg.sm_Msg.mn_Length = START_MSG_SIZE;
    smsg.sm_Msg.mn_ReplyPort = my_port;

    /* Want to know what screen StartMenu is running on */
    SendStartMsg(&smsg, (APTR)(&sm_win), GET_WINDOW_INFO);
    if (NULL == (scn = sm_win.wi_Window->WScreen))
        ShutDown("Could not get screen.", 5);
}

/*
 * Easy way to send a message.
 */
VOID SendStartMsg(START_MSG *msg, APTR data, UBYTE command)
{
    struct MsgPort *sm_port;
    
    /* Make sure StartMenu hasn't gone anywhere */
    if (sm_port = FindPort(STARTMENU_PORT_NAME)) {
        msg->sm_Command = command;
        msg->sm_Data    = data;
        PutMsg(sm_port, (struct Message *)msg);
        WaitPort(my_port);           /* StartMenu replies to ALL messages!! */
        GetMsg(my_port);
    }
}

/*
 * This version is used by the patch code. Need to create a new port
 * each time since we never know who's process we'll be running on.
 */
VOID __saveds SendStartMsgAlt(APTR data, UBYTE command)
{
    START_MSG msg;
    struct MsgPort *port_a, *port_b;

    if (port_a = FindPort(STARTMENU_PORT_NAME)) {
        if (port_b = CreatePort(0,50)) {
            msg.sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
            msg.sm_Msg.mn_Length = START_MSG_SIZE;
            msg.sm_Msg.mn_ReplyPort = port_b;
            msg.sm_Command = command;
            msg.sm_Data    = data;
            PutMsg(port_a, (struct Message *)(&msg));
            WaitPort(port_b);
            GetMsg(port_b);
            DeletePort(port_b);
        }
    }
}

/*
 * Is this "System Friendly" ??  :P
 */
VOID SetPatches(VOID)
{
    OldOpenWin = (FUNCPTR)SetFunction(IntBase, -0xCC, MyOpenWin);
    OldOpenWinTag = (FUNCPTR)SetFunction(IntBase, -0x25E, MyOpenWinTag);
    OldCloseWin = (FUNCPTR)SetFunction(IntBase, -0x48, MyCloseWin);
    OldSetTitle = (FUNCPTR)SetFunction(IntBase, -0x114, MySetTitle);
    OldUpfrontLayer = (FUNCPTR)SetFunction(LayersBase, -0x30, MyUpfrontLayer);
}

/*
 * This get called by the OpenWindow() and OpenWindowTags() patch code.
 */
VOID __asm __saveds HandleOpenWin(register __d0 struct Window *win)
{
    WIN_CON *work, *cl;
    START_MENU_GADGET sg = {0};

    if (win->WScreen != scn) return;            // Right screen?
    if (win->Flags & WFLG_BACKDROP) return;     // Ignore these...
    if (win->Flags & WFLG_BORDERLESS) return;   // And these...
    /* WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET = 0xE */
    if (!(win->Flags & 0xE)) return;

    /* Has some weird problem with requesters.  This conditional tries
       to make an educated guess if the window being opened is a
       requester.  If it is, forget about it. */
    if ((0 == win->LeftEdge) && (0 == win->TopEdge) && 
           !(win->Flags & WFLG_SIZEGADGET) && !(win->Flags & WFLG_CLOSEGADGET))
        return;

    /* Don't add the same window twice (damn patches!) */
    ObtainSemaphore(&sema);
    work = whead;
    while (work) {
        if (work->wc_Win == win) {
            ReleaseSemaphore(&sema);
            return;
        }
        if (cl = work->wc_Children) {
            while (cl) {
                if (cl->wc_Win == win) {
                    ReleaseSemaphore(&sema);
                    return;
                }
                cl = cl->wc_Next;
            }
        }
        work = work->wc_Next;
    }

    /* Check for parent */
    if (win->Parent) {
        WIN_CON *parent;
        if (parent = FindParent(win->Parent, FindTask(NULL))) {
            WIN_CON *temp;
            if (cl = AllocMem(sizeof(WIN_CON), MEMF_PUBLIC|MEMF_CLEAR)) {
                if (win->Title && *(win->Title))
                    strncpy(cl->wc_Title, win->Title, 48);
                else
                    strcpy(cl->wc_Title, "<UNKNOWN>");
                cl->wc_Win = win;
                cl->wc_Task = FindTask(NULL);
                cl->wc_Next = NULL;
                if (NULL == parent->wc_Children) {
                    parent->wc_Children = cl;
                    SetGadgetAttrs(parent->wc_Gadget, sm_win.wi_Window, NULL,
                            ITB_Bold, TRUE, TAG_END);
                }                    
                else {
                    temp = parent->wc_Children;
                    while (temp->wc_Next) temp = temp->wc_Next;
                    temp->wc_Next = cl;
                }
                ReleaseSemaphore(&sema);
                return;
            }
        }
    }

    if (work = AllocMem(sizeof(WIN_CON), MEMF_PUBLIC|MEMF_CLEAR)) {
        if (!(IsMatch(win->Title, &sg))) {
            if (win->Title && *(win->Title))
                strncpy(work->wc_Title, win->Title, 48);
            else
                strcpy(work->wc_Title, "<UNKNOWN>");
            sg.smg_Title = work->wc_Title;
            sg.smg_BitMap = ims.ims_ScaledBM;
            sg.smg_Mask   = ims.ims_Mask;
            sg.smg_FrontPen = -1;
            sg.smg_BackPen = -1;
        }
        else
            strcpy(work->wc_Title, sg.smg_Title);
        sg.smg_Port  = my_port;
        sg.smg_PrivateBM = TRUE;
        work->wc_Task = FindTask(NULL);
        /* Create the gadget */
        SendStartMsgAlt((APTR)(&sg), ADD_TASKBAR_GADGET);
        if (sg.smg_Gadget) {
            if (wtail) {
                wtail->wc_Next = work;
                wtail = work;
            }
            else
                whead = wtail = work;
            work->wc_Win        = win;
            work->wc_Gadget     = sg.smg_Gadget;
        }
        else
            FreeMem(work, sizeof(WIN_CON));
    }
    ReleaseSemaphore(&sema);
}

BOOL __saveds IsMatch(STRPTR s, START_MENU_GADGET *sg)
{
    PATTERN_INFO *work = pat_head;
    STRPTR t;
    
    if ((NULL == s) || (0 == *s)) return(FALSE);

    while (work) {
        if (work->pi_Pattern[0]) {
            t = &(s[work->pi_Offset]);
            if (MatchPatternNoCase(work->pi_Pattern, s)) {
                if (0 == work->pi_Name[0]) {
                    /*strncpy(&(work->pi_Name[1]), t, 63); */
                    sg->smg_Title = t; /* &(work->pi_Name[1]); */
                }
                else sg->smg_Title = work->pi_Name;
                if (show_icons) {
                    if (work->pi_BitMap) {
                        sg->smg_BitMap = work->pi_BitMap;
                        sg->smg_Mask   = work->pi_Mask;
                    }
                    else {
                        sg->smg_BitMap = ims.ims_ScaledBM;
                        sg->smg_Mask   = ims.ims_Mask;
                    }
                }
                else sg->smg_BitMap = sg->smg_Mask = NULL;
                sg->smg_FrontPen = work->pi_FrontPen;
                sg->smg_BackPen = work->pi_BackPen;
                return(TRUE);
            }
        }
        work = work->pi_Next;
    }
    return(FALSE);
}

/*
 * This get called by the CloseWindow() patch code.
 */
VOID __asm __saveds HandleCloseWin(register __a0 struct Window *win)
{
    WIN_CON *work, *prev = NULL, *cl, *clprev;
    RE_GADGET re;
    
    ObtainSemaphore(&sema);
    work = whead;
    while (work) {
        if (work->wc_Win == win) break;
        clprev = NULL;
        cl = work->wc_Children;
        while (cl) {
            if (cl->wc_Win == win) {
                if (NULL == clprev) {
                    work->wc_Children = cl->wc_Next;
                }
                else {
                    clprev->wc_Next = cl->wc_Next;
                }
                FreeMem(cl, sizeof(WIN_CON));
                if (NULL == work->wc_Children)
                    SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL,
                            ITB_Bold, FALSE, TAG_END);
                ReleaseSemaphore(&sema);
                return;
            }
            clprev = cl;
            cl = cl->wc_Next;
        }
        prev = work;
        work = work->wc_Next;
    }
    if (NULL == work) {
        ReleaseSemaphore(&sema);
        return;
    }

    if (cl = work->wc_Children) {
        struct Gadget *g = work->wc_Gadget;
        clprev = work->wc_Next;
        memcpy(work, cl, sizeof(WIN_CON));
        work->wc_Gadget = g;
        work->wc_Next = clprev;
        work->wc_Children = cl->wc_Next;
        if (work->wc_Children)
            SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL,
                        GA_Text, work->wc_Title, TAG_END);
        else
            SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL,
                        GA_Text, work->wc_Title,
                        ITB_Bold, FALSE, TAG_END);
        FreeMem(cl, sizeof(WIN_CON));
        ReleaseSemaphore(&sema);
        return;
    }

    re.re_Gadget = work->wc_Gadget;
    SendStartMsgAlt((APTR)(&re), REM_TASKBAR_GADGET);
    
    if (whead == work) {
        if (work->wc_Next) whead = work->wc_Next;
        else whead = wtail = NULL;
    }
    else if (wtail == work) {
        wtail = prev;
        prev->wc_Next = NULL;
    }
    else
        prev->wc_Next = work->wc_Next;

    FreeMem(work, sizeof(WIN_CON));
    ReleaseSemaphore(&sema);
}

/*
 * This get called by the SetWindowTitles() patch code.
 */
VOID __asm __saveds HandleSetTitle(register __a0 struct Window *win,
                                   register __a1 STRPTR s)
{
    WIN_CON *work, *cl;
    START_MENU_GADGET sg;

    if (-1 == (LONG)s) return;

    work = whead;
    while (work) {
        if (work->wc_Win == win) break;
        cl = work->wc_Children;
        while (cl) {
            if (cl->wc_Win == win) {
                if (s && *s)
                    strncpy(cl->wc_Title, s, 48);
                else
                    strcpy(cl->wc_Title, "<Unknown>");
                return;
            }
            cl = cl->wc_Next;
        }
        work = work->wc_Next;
    }
    if (NULL == work) {
        return;
    }
    
    if (s && *s) {
        if (IsMatch(s, &sg)) {
            ULONG tag1 = TAG_IGNORE, tag2 = TAG_IGNORE;
            strncpy(work->wc_Title, sg.smg_Title, 48);
            if (-1 != sg.smg_FrontPen) tag1 = ITB_FrontPen;
            if (-1 != sg.smg_BackPen) tag2 = ITB_BackPen;
            SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL,
                        ITB_BitMap, sg.smg_BitMap,
                        ITB_Mask, sg.smg_Mask, 
                        tag1, sg.smg_FrontPen,
                        tag2, sg.smg_BackPen,
                        GA_Text, work->wc_Title, TAG_END);
            return;
        }
        else 
            strncpy(work->wc_Title, s, 48);
    }
    else
        strcpy(work->wc_Title, "<Unknown>");

    SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL,
                        GA_Text, work->wc_Title, TAG_END);

}

/*
 * This get called by the UpfrontLayer() patch code.
 */
LONG __asm __saveds HandleUpfrontLayer(register __a1 struct Layer *lay)
{
    WIN_CON *work;

    work = whead;
    while (work) {
        if (work->wc_Win->WLayer == lay) break;
        work = work->wc_Next;
    }
    if (NULL == work) {
        return(1);
    }

    /* If the window (layer) is being popped-to-front, we un-iconify it */
    if (work->wc_Flags & IS_ICON) {
        DoGadget(work);
        /* Set the new state of the gadget to un-selected */
        SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL, GA_Selected, FALSE, TAG_END);
        work->wc_Flags &= ~IS_ICON;
        return(0);
    }
    else {
        return(1);
    }
}
    
/*
 * This gets called when a gadget is pressed
 */
VOID DoGadget(WIN_CON *work)
{
    /* Un-Iconify? */
    if (work->wc_Flags & IS_ICON)
        UnIconify(work);
    /* Otherwise, fake a BOOLEAN gadget (TaskBar gadgets are TOGGLESELECT) */
    else {
        Delay(5);
        SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL, GA_Selected, FALSE, TAG_END);
    }

    Forbid();
    if (WinExists(work->wc_Win)) {
        /* Activate the window */
        WindowToFront(work->wc_Win);
        ActivateWindow(work->wc_Win);
    }
    Permit();
}

VOID DoChildGadget(WIN_CON *work)
{
    ULONG max = 0, num_child = 1, win_width, win_height, ypos, x;
    LONG temp;
    UWORD qual = ALL_MASKS;
    struct Screen *screen = sm_win.wi_Window->WScreen;
    struct Window *win;
    struct RastPort *srp = &(sm_win.wi_Window->WScreen->RastPort);
    struct IntuiMessage *imsg;
    struct TextFont *the_font;
    struct Rectangle r;
    WIN_CON *cl = work->wc_Children;
    BOOL done = FALSE, do_icon = FALSE, do_close = FALSE;

    if (!(work->wc_Gadget->Flags & GFLG_SELECTED))
        SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL,
                            GA_Selected, TRUE, TAG_END);

    max = TextLength(srp, work->wc_Title, strlen(work->wc_Title));
    while(cl) {
        temp = TextLength(srp, cl->wc_Title, strlen(cl->wc_Title));
        if (temp > max) max = temp;
        num_child++;
        cl = cl->wc_Next;
    }
    
    win_width = (screen->BarVBorder * 2) + max + 12;
    if (win_width < work->wc_Gadget->Width) win_width = work->wc_Gadget->Width;
    win_height = (num_child * (screen->Font->ta_YSize + 2)) + 
                    (screen->BarHBorder * 2);
    if (win = OpenWindowTags(NULL,
        WA_Left,        sm_win.wi_Window->LeftEdge + work->wc_Gadget->LeftEdge,
        WA_Top,         sm_win.wi_Window->TopEdge - win_height,
        WA_Width,       win_width,
        WA_Height,      win_height,
        WA_IDCMP,       IDCMP_INACTIVEWINDOW | IDCMP_MOUSEBUTTONS,
        WA_Activate,    TRUE,
        WA_RMBTrap,     TRUE,
        TAG_END)) {
        
        the_font = OpenFont(screen->Font);
        SetFont(win->RPort, the_font);

        if (work->wc_Flags & IS_ICON) SetAPen(win->RPort, 2);
        else SetAPen(win->RPort, 1);
        ypos = screen->Font->ta_YSize + 2;
        Move(win->RPort, 6, ypos);
        Text(win->RPort, work->wc_Title, strlen(work->wc_Title));
        cl = work->wc_Children;
        while (cl) {
            ypos += screen->Font->ta_YSize + 2;
            if (cl->wc_Flags & IS_ICON) SetAPen(win->RPort, 2);
            else SetAPen(win->RPort, 1);
            Move(win->RPort, 6, ypos);
            Text(win->RPort, cl->wc_Title, strlen(cl->wc_Title));
            cl = cl->wc_Next;
        }
        
        temp = -1;
        while (!done) {
            Wait(1L << win->UserPort->mp_SigBit);
            while (imsg = (struct IntuiMessage *)GetMsg(win->UserPort)) {
                if ((IDCMP_MOUSEBUTTONS == imsg->Class) && 
                            (SELECTDOWN == imsg->Code)) {
                    qual = imsg->Qualifier;
                    qual &= ALL_MASKS;
                    if (quals[0] == qual) do_icon = TRUE;
                    else if (quals[4] == qual) do_close = TRUE;
                    temp = (ULONG)(((FLOAT)imsg->MouseY/(FLOAT)win->Height) *
                                (FLOAT)num_child);
                    r.MinX = 6;
                    r.MinY = 2 + ((screen->Font->ta_YSize + 2) * temp);
                    r.MaxX = win_width - 6;
                    r.MaxY = r.MinY + (screen->Font->ta_YSize + 2);
                    SetDrMd(win->RPort, COMPLEMENT);
                    for (x = 0; x < 4; x++) {
                        RectFill(win->RPort, r.MinX, r.MinY, r.MaxX, r.MaxY);
                        Delay(2);
                    }
                }
                if (!((IDCMP_MOUSEBUTTONS == imsg->Class) && 
                            (SELECTUP == imsg->Code)))
                    done = TRUE;
                ReplyMsg((struct Message *)imsg);
            }
        }
        CloseWindow(win);
        if (((0 != temp) && (work->wc_Flags & IS_ICON)) || ((0 == temp) && do_icon))
            SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL,
                            GA_Selected, TRUE, TAG_END);
        else
            SetGadgetAttrs(work->wc_Gadget, sm_win.wi_Window, NULL,
                            GA_Selected, FALSE, TAG_END);
        CloseFont(the_font);
    }

    if (-1 != temp) {
        if (0 != temp) {
            cl = work->wc_Children;
            for (x = 1; x < temp; x++) cl = cl->wc_Next;
            work = cl;
        }
        if (work->wc_Flags & IS_ICON) do_icon = FALSE;
        if (!do_icon) {
            if (do_close) SendCloseMsg(work->wc_Win);
            else {
                if (work->wc_Flags & IS_ICON) UnIconify(work);
                Forbid();
                if (WinExists(work->wc_Win)) {
                    WindowToFront(work->wc_Win);
                    ActivateWindow(work->wc_Win);
                }
                Permit();
            }
        }
        else DoIconify(work);
    }
}

/*
 * "Iconify" a window - resize it and move it behind StartMenu's window
 */
VOID DoIconify(WIN_CON *work)
{
    /* These will be the new size */
    ULONG ww = 100, hh = sm_win.wi_Window->Height;
 
    Forbid();
    if (WinExists(work->wc_Win)) {
        /* Save the old values */
        work->wc_RealPos.Left   = work->wc_Win->LeftEdge;
        work->wc_RealPos.Top    = work->wc_Win->TopEdge;
        work->wc_RealPos.Width  = work->wc_Win->Width;
        work->wc_RealPos.Height = work->wc_Win->Height;

        /* Make sure it's completely hidden */
        if (sm_win.wi_Window->Width < ww) ww = sm_win.wi_Window->Width;

        /* Arghhh!  Some very strange bug causes WB to crash if I iconify a
         * WB Window that is empty (no icons) and the size = 26!  I have
         * no idea why, so this quick seems to work.... :(  */
        if (hh == 26) hh = 25;

        /* Store and set the new min sizes */
        work->wc_MinX = work->wc_Win->MinWidth;
        work->wc_MinY = work->wc_Win->MinHeight;
        work->wc_Win->MinWidth = ww;
        work->wc_Win->MinHeight = hh;

        /* Make sure it goes behind StartMenu */
        WindowToFront(sm_win.wi_Window);

        /* Resize it */
        ChangeWindowBox(work->wc_Win, sm_win.wi_Window->LeftEdge,
                                      sm_win.wi_Window->TopEdge,
                                      ww, hh);
        work->wc_Flags |= IS_ICON;
    }
    Permit();
}

VOID UnIconify(WIN_CON *work)
{
    if (work->wc_Flags & IS_ICON) {
        Forbid();
        if (WinExists(work->wc_Win)) {
            ChangeWindowBox(work->wc_Win, work->wc_RealPos.Left,
                                          work->wc_RealPos.Top,
                                          work->wc_RealPos.Width,
                                          work->wc_RealPos.Height);
            work->wc_Win->MinWidth = work->wc_MinX;
            work->wc_Win->MinHeight = work->wc_MinY;
            work->wc_Flags &= ~IS_ICON;
        }
        Permit();
    }
}

/*
 * Find the WIN_CON structure associatated with the gadget
 */
WIN_CON *FindWC(struct Gadget *g)
{
    WIN_CON *work = whead;

    ObtainSemaphore(&sema);
    while (work) {
        if (work->wc_Gadget == g) break;
        work = work->wc_Next;
    }
    ReleaseSemaphore(&sema);
    return(work);
}

WIN_CON * FindParent(struct Window *win, struct Task *task)
{
    WIN_CON *work = whead, *child;
    
    if (!showchild) return(NULL);

    ObtainSemaphore(&sema);
    while (work) {
        if (work->wc_Win == win) {
            if (work->wc_Task == task) {
                ReleaseSemaphore(&sema);
                return(work);
            }
            else {
                ReleaseSemaphore(&sema);
                return(NULL);
            }
        }
        child = work->wc_Children;
        while (child) {
            if (child->wc_Win == win) {
                if (child->wc_Task == task) {
                     ReleaseSemaphore(&sema);
                    return(work);;
                }
                else {
                    ReleaseSemaphore(&sema);
                    return(NULL);
                }
            }
            child = child->wc_Next;
        }
        work = work->wc_Next;
    }
    ReleaseSemaphore(&sema);
    return(NULL);
}

BOOL WinExists(struct Window *win)
{
    struct Window *temp = scn->FirstWindow;
    
    while (temp) {
        if (temp == win) return(TRUE);
        temp = temp->NextWindow;
    }
    return(FALSE);
}

VOID ReadPatterns(VOID)
{
    FILE *fi;
    IMAGE_SCALED imsc;
    PATTERN_INFO *work, *prev = NULL;
    struct DiskObject *d_obj;
    UBYTE buf[128], name[128], pat[128], icon[128], icon2[128], fp[8], bp[8], os[8], x;
    BPTR lock;

    if (NULL != (fi = fopen("s:patterns.wc", "r"))) {
        for (x = 0; x < 6; x++) {
            fgets(buf, 128, fi);
            quals[x] = atol(buf);
        }
        while (!(feof(fi))) {
            fgets(buf, 128, fi);
            if (!(feof(fi))) {
                if (ParseStr(buf, name, pat, icon, fp, bp, os)) {
                    if (work = AllocMem(sizeof(PATTERN_INFO), MEMF_ANY|MEMF_CLEAR)) {
                        if (name[0]) strncpy(work->pi_Name, name, 64);
                        if (pat[0]) {
                            if (-1 == ParsePatternNoCase(pat, work->pi_Pattern, 128))
                                work->pi_Pattern[0] = 0;
                        }
                        if (icon[0]) {
                            strcpy(icon2, icon);
                            strcat(icon2, ".info");
                            if (lock = Lock(icon2, SHARED_LOCK)) {
                                UnLock(lock);
                                if (d_obj = GetDiskObject(icon)) {
                                    if (d_obj->do_Gadget.GadgetRender) {
                                        imsc.ims_Image = d_obj->do_Gadget.GadgetRender;
                                        imsc.ims_ScaledBM = NULL;
                                        SendStartMsg(&smsg, (APTR)(&imsc), SCALE_IMAGE);
                                        if (imsc.ims_ScaledBM) {
                                            work->pi_BitMap = imsc.ims_ScaledBM;
                                            work->pi_Mask   = imsc.ims_Mask;
                                        }
                                    }
                                    FreeDiskObject(d_obj);
                                }
                            }
                        }
                        work->pi_FrontPen = atoi(fp);
                        work->pi_BackPen  = atoi(bp);
                        work->pi_Offset   = atoi(os);
                        if (0 != work->pi_Offset) work->pi_Offset -= 1;
                        if (NULL == pat_head)
                            pat_head = work;
                        else
                            prev->pi_Next = work;
                        prev = work;
                    }
                }
            }
        }
        fclose(fi);
    }
}

VOID FreePatterns(VOID)
{
    PATTERN_INFO *work = pat_head, *next;
    FREE_BITMAP fbm;

    while (work) {
        next = work->pi_Next;
        if (work->pi_BitMap) {
            fbm.fbm_BitMap = ims.ims_ScaledBM;
            fbm.fbm_Mask = ims.ims_Mask;
            SendStartMsg(&smsg, (APTR)(&fbm), FREE_BIT_MAP);
        }
        FreeMem(work, sizeof(PATTERN_INFO));
        work = next;
    }

}

BOOL ParseStr(STRPTR str, STRPTR name, STRPTR pat, STRPTR icon, STRPTR fp, STRPTR bp, STRPTR os)
{
    if (NULL == str) return(FALSE);

    while (*str && isspace(*str)) str++;

    while (*str && (*str != '|')) *name++ = *str++;
    if (0 == *str) return(FALSE);
    *name = 0;
    strip(name);
    str++;

    while (*str && (*str != '|')) *pat++ = *str++;
    if (0 == *str) return(FALSE);
    *pat = 0;
    strip(pat);
    str++;

    while (*str && (*str != '|')) *icon++ = *str++;
    if (0 == *str) return(FALSE);
    *icon = 0;
    strip(icon);
    str++;

    while (*str && (*str != '|')) *fp++ = *str++;
    if (0 == *str) return(FALSE);
    *fp = 0;
    strip(fp);
    str++;

    while (*str && (*str != '|')) *bp++ = *str++;
    if (0 == *str) return(FALSE);
    *bp = 0;
    strip(bp);
    str++;

    while (*str && (*str != '\n')) *os++ = *str++;
    *os = 0;
    strip(os);

    return(TRUE);
}

VOID strip(STRPTR s)
{
    UWORD x = 0, y, len;

    /* Strip blank spaces */
    while (isspace(s[x])) x++;
    len = strlen(s) + 1;
    for (y = 0; y < len; y++, x++)
        s[y] = s[x];
    len = strlen(s) - 1;
    while (isspace(s[len])) s[len--] = '\0';
}

VOID SendCloseMsg(struct Window *win)
{
    struct IntuiMessage imsg = {0};
    
    imsg.Class = IDCMP_CLOSEWINDOW;
    imsg.ExecMessage.mn_Node.ln_Type = NT_MESSAGE;
    imsg.ExecMessage.mn_Length = sizeof(struct IntuiMessage);
    imsg.ExecMessage.mn_ReplyPort = my_port;

    if (WinExists(win) && (win->IDCMPFlags & IDCMP_CLOSEWINDOW)) {
        imsg.MouseX = win->LeftEdge + 5;
        imsg.MouseY = win->TopEdge + 5;
        imsg.IDCMPWindow = win;
        PutMsg(win->UserPort, (struct Message *)(&imsg));
        WaitPort(my_port);
        GetMsg(my_port);
    }
}
