/*                            TaskTool.c                                  */
/*                  Dynamic Task Priority Manager                         */
/*                       © E.F.Pritchard 1991                             */

#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <graphics/gfxbase.h>
#include <libraries/dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>

#define TOPAZ_EIGHTY 8
#define TOPAZ_SIXTY 9

#ifdef LATTICE
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
int CXBRK(void) { return(0); }
void main(int argc, char *argv[]);
#ifndef NOBACK
    /* This stuff is needed to link with Lattice C's cback.o */
    LONG _stack = 4096;
    char *_procname = "TaskTool";
    LONG _priority = 0;
    LONG _BackGroundIO = 0;
#endif
#endif


/* My Headers */

#include "TaskTool.h"
#include "TaskToolWindow.h"
#include "TaskToolGads.h"


/* Handy Macros */

#define RP TTWindow->RPort


/* Function Declarations (prototypes) */

/*-TaskList.c-*/
extern LONG DisposeTaskList(TaskNode TaskArray[], int ArrayCount);
extern LONG ReadTaskList(TaskNode TaskArray[]);
extern BOOL SetTaskPriSafely(TaskListInfo *);
extern ULONG FindInList(STRPTR, struct Task *, ULONG);

/*-TaskMenuCode.c-*/
extern BOOL HandleMenuPick(struct IntuiMessage *, struct TimeInfo *);

/*-TaskRTimer.c-*/
extern struct MsgPort *OpenTimer(VOID);
extern VOID   CloseTimer(VOID);
extern VOID   SendTimerRequest(struct TimeInfo *);

/*-TaskTool.c internal-*/
BOOL    HandleDGad(struct IntuiMessage *);
BOOL    HandleUGad(struct IntuiMessage *);
VOID    HandleIntuiTick(VOID);
VOID    InitTaskTool(TaskListInfo *, BOOL);
VOID    AdjustGads(struct Window *, struct Gadget * );
VOID    SetUpNameGads(VOID);
VOID    DisplayList( TaskListInfo * );
VOID    SetPriGadsFromData( TaskListInfo * );
VOID    SetNameGadsFromData( TaskListInfo * );
VOID    RefreshTaskTool( TaskListInfo *, BOOL );
VOID    UpDatePriorityStr( TaskListInfo *, struct Gadget * );
VOID    SetPriFromStr( TaskListInfo *, struct Gadget *, struct Gadget *);
VOID    GetPriSlider( TaskListInfo *, struct Gadget *, LONG );
ULONG   GimmeHiGad(ULONG, ULONG);
VOID    UpDateHiGad( TaskListInfo *, BOOL );
VOID    GetNameSlider(TaskListInfo *, struct Gadget *, LONG );
VOID    OpenAll(VOID);
VOID    CleanExit(int);
BOOL    AdjustMenus(struct Menu *, struct TextAttr *);
VOID    AdjustItems(struct RastPort *, struct MenuItem *, struct TextAttr *,
                    USHORT, USHORT, USHORT, USHORT);
VOID    AdjustText(struct IntuiText *, struct TextAttr *);
USHORT  MaxLength(struct Rasport *, struct MenuItem *, USHORT);

/* Globals */

struct GfxBase          *GfxBase        = NULL;
struct IntuitionBase    *IntuitionBase  = NULL;
struct Window           *TTWindow       = NULL;
struct MsgPort          *TimerPort      = NULL;
struct TextFont         *TTfont         = NULL;

jmp_buf env;                        /* See Note in main()... */


/* Task List Info */

TaskListInfo TaskToolInfo ={0};     /* Keeps track of vars nicely */


/* The List... */

TaskNode TaskList[TASKARRAYSIZE];   /* ...The Business */


/* These will hold the displayed text for the Name Gadgets */

UBYTE TaskToolGadText[NAMESDISPLAYED][CHARSDISPLAYED+1];


/* Timer Information... */

TimeInfo TimerInfo = { STOPPED, 0, {5,30,60,300}, {0,0,0,0} };



/*------------------------------------------------------------------------*/

void main(argc, argv)
int argc;
char *argv[];
{
    struct  IntuiMessage *msg = NULL;
    ULONG   class;
    BOOL    exit = FALSE;
    ULONG   Signals,TTWindowSig,TTTimerSig=NULL,TTUserSig;

    VOID OpenAll();
    VOID InitTaskTool();
    VOID RefreshTaskTool();
    BOOL HandleDGad();
    BOOL HandleUGad();
    VOID HandleIntuiTick();
    extern BOOL HandleMenuPick();

    /* Open the resources we need */

    OpenAll();


    /* Initialize Wait() Signals we wish to wait for */

    TTWindowSig=1L << TTWindow->UserPort->mp_SigBit; /* IDCMP port signal */

    if (TimerPort) TTTimerSig=1L << TimerPort->mp_SigBit; /* Timer Port sig */

    TTUserSig=SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_F; /* User signal */


    /* Draw Window Details, Adjust Gadgets to suit borders */

    InitTaskTool( &TaskToolInfo, TRUE );

    /* 'Aha! What is the next bit for?' you ask. It saves the current
     * state of TaskTool's environment so that we can return to this
     * point and this status later. It is used to return from the
     * 'Iconify' operation, since returning normally would cause the
     * Amiga FIREWORKS_DISPLAY mode to be activated- a ReplyMsg would
     * go out when the message has already been deallocated by
     * closing the Window...
     */

    (VOID) setjmp(env);    /* Save the stack & return address */


    /* Read the Task list in, display it and otherwise initialize, man */

    RefreshTaskTool( &TaskToolInfo, TRUE );



    while(!exit) {

        Signals=Wait(TTWindowSig|TTTimerSig|TTUserSig);


        /*- User Signals. Ctrl-C = Quit  Ctrl-F = Refresh -*/
        if (Signals & TTUserSig) {
            if (Signals & SIGBREAKF_CTRL_C) { /* User wants to Quit... */
                exit=TRUE;
                continue;
            }
            else
                RefreshTaskTool(&TaskToolInfo, TRUE); /* Ctrl-F Refresh */
        }


        /*- Timer Timeout Signal -*/
        if (Signals & TTTimerSig)  {
            /* Timer Device Timeout- Refresh and Repost */
            while(GetMsg(TimerPort));
            if (TimerInfo.TimeStatus==RUNNING) {
                RefreshTaskTool(&TaskToolInfo, TRUE);
                SendTimerRequest(&TimerInfo);
            }
        }


        /*- IDCMP Signal (Gadget or Menu Activity etc.) -*/
        if (Signals & TTWindowSig) {
            while (msg= (struct IntuiMessage *)GetMsg(TTWindow->UserPort)) {
                class= msg->Class;
                switch(class) {
                    case    INTUITICKS:
                        HandleIntuiTick();
                        break;
                    case    GADGETDOWN:
                        if (!HandleDGad(msg))
                            /* Handle This, and refresh if required */
                            RefreshTaskTool(&TaskToolInfo, TRUE);
                        break;
                    case    GADGETUP:
                        if (!HandleUGad(msg))
                            /* Handle This, and refresh if required */
                            RefreshTaskTool(&TaskToolInfo, TRUE);
                        break;
                    case    MENUPICK:
                        exit=HandleMenuPick(msg, &TimerInfo);
                        /* Quit option makes exit true */
                        break;
                    case    CLOSEWINDOW:
                        exit=TRUE;
                        break;
                    default:
                        break;
                }

                ReplyMsg((struct Message *)msg);

            } /* End IDCMP Message Processing */
        } /* End TTWindowSig Processing */

    } /* End While */

    CleanExit(0);
}

/*------------------------------------------------------------------------*/
BOOL HandleDGad(msg)
struct IntuiMessage *msg;
/*  On GadgetID:
 *    1-8:  Calculate New HiGad, Change TaskToolInfo data
 *      9:  Ignore
 *  10,11:  Change Position in Name List
 *     12:  Won't happen
 *     13:  Ignore
 *  14,15:  ChangePri
 *     16:  Refresh TaskTool.
 *
 *  Returns FALSE if SetPriSafely() says we need a refresh.
 */
{
    struct Gadget *g;
    USHORT id;

    BOOL RefreshMe = TRUE;

    VOID GetNameSlider();
    VOID GetPriSlider();
    VOID UpDatePriorityStr();
    VOID RefreshTaskTool();

    g  = (struct Gadget *)msg->IAddress;
    id = g->GadgetID;
    switch(id)  {
        case 10: /* Name Arrow Gad Up */
            GetNameSlider(&TaskToolInfo, &NameSliderGad, -1L);
            break;
        case 11: /* Name Arrow Gad Down */
            GetNameSlider(&TaskToolInfo, &NameSliderGad, 1L);
            break;
        case 14:
            GetPriSlider(&TaskToolInfo, &PriSliderGad, -1L);
            UpDatePriorityStr(&TaskToolInfo,&PriStringGad);
            RefreshMe=SetTaskPriSafely( &TaskToolInfo );
            break;
        case 15:
            GetPriSlider(&TaskToolInfo, &PriSliderGad, 1L);
            UpDatePriorityStr(&TaskToolInfo,&PriStringGad);
            RefreshMe=SetTaskPriSafely( &TaskToolInfo );
            break;
        case 16: /* Refresh Gadget */
            RefreshTaskTool(&TaskToolInfo, TRUE);
            break;
        default: /* Anything Else */
            if (id>=1 && id<=NAMESDISPLAYED) {
                if( !((TaskToolInfo.topentry+id-1)>=TaskToolInfo.numentries) )
                    {
                    TaskToolInfo.Task=TaskToolInfo.topentry+id-1;
                    }
                RefreshTaskTool(&TaskToolInfo, FALSE);
            }
            break;
    }
    return(RefreshMe);
}

/*------------------------------------------------------------------------*/
BOOL HandleUGad(msg)
struct IntuiMessage *msg;
/*  On GadgetID:
 *      9:  Change Position in Name List
 *  10,11:  Ignore (just for show!)
 *     12:  Refresh PriStringGad (update Buffer), change Pri.
 *     13:  Change Pri, PriStringGad
 *  14,15:  PriArrowGads Relverify Return
 *
 *  Returns FALSE if SetPriSafely thinks we need a refresh.
 */
{
    struct Gadget *g;
    USHORT id;

    BOOL RefreshMe = TRUE;

    VOID GetNameSlider();
    VOID GetPriSlider();
    VOID SetPriFromStr();
    VOID UpDatePriorityStr();

    g  = (struct Gadget *)msg->IAddress;
    id = g->GadgetID;
    switch(id)  {
        case 9: /* Name Slider */
            GetNameSlider(&TaskToolInfo, &NameSliderGad, 0L);
            break;
        case 12: /* PriString Gad */
            SetPriFromStr(&TaskToolInfo,&PriStringGad,&PriSliderGad);
            UpDatePriorityStr(&TaskToolInfo,&PriStringGad);
            RefreshMe = SetTaskPriSafely( &TaskToolInfo );
            break;
        case 13: /* PriSlider */
            GetPriSlider(&TaskToolInfo, &PriSliderGad, 0L);
            UpDatePriorityStr(&TaskToolInfo,&PriStringGad);
            RefreshMe = SetTaskPriSafely( &TaskToolInfo );
            break;
        case 14: /* Pri Arrow Gads Relverify */
        case 15:
            RefreshMe = SetTaskPriSafely(&TaskToolInfo);
            break;
        default: /* Anything Else */
            break;
    }
    return(RefreshMe);
}

/*------------------------------------------------------------------------*/
VOID HandleIntuiTick(VOID)
/*  On receiving INTUITICKS class event, we check to see if an arrow gadget
 *  or propgadget is held down, if it is we take the appropriate action.
 *  in this way we make repeating gadgets, and user friendly updated
 *  scrolling lists etc.
 */

{
    /* -Name Gadgets- */
    if (NameArrowGads[0].Flags&SELECTED)
        GetNameSlider(&TaskToolInfo,&NameSliderGad,1L);
    if (NameArrowGads[1].Flags&SELECTED)
        GetNameSlider(&TaskToolInfo,&NameSliderGad,-1L);
    if (NameSliderGad.Flags&SELECTED)
        GetNameSlider(&TaskToolInfo,&NameSliderGad,0L);

    /* -Priority Gadgets- */
    if (PriArrowGads[0].Flags&SELECTED) {
        GetPriSlider(&TaskToolInfo,&PriSliderGad,1L);
        UpDatePriorityStr(&TaskToolInfo,&PriStringGad);
    }
    if (PriArrowGads[1].Flags&SELECTED) {
        GetPriSlider(&TaskToolInfo,&PriSliderGad,-1L);
        UpDatePriorityStr(&TaskToolInfo,&PriStringGad);
    }
    if (PriSliderGad.Flags&SELECTED) {
        GetPriSlider(&TaskToolInfo,&PriSliderGad,0L);
        UpDatePriorityStr(&TaskToolInfo,&PriStringGad);
    }
}

/*------------------------------------------------------------------------*/
VOID InitTaskTool(TTInfo, Adjust)
TaskListInfo *TTInfo;
BOOL Adjust;
/*  Fills window, Draws Borders & Intuitext, calls SetUpNameGads
 *  & AdjustGads, adds gadgets to window, and refreshes all gadgets.
 */
{
    VOID SetUpNameGads();
    VOID AdjustGads();

    /* Fill Window with White Rectangle */

    WindowFill.LeftEdge     = TTWindow->BorderLeft;
    WindowFill.TopEdge      = TTWindow->BorderTop;
    WindowFill.Width        = (TTWindow->Width-(TTWindow->BorderLeft+TTWindow->BorderRight));
    WindowFill.Height       = (TTWindow->Height-(TTWindow->BorderTop+TTWindow->BorderBottom));

    DrawImage(RP, &WindowFill, 0,0 );

    /* Finalize Initialization of Gadgets */

    if (Adjust) {
        SetUpNameGads();
        AdjustGads(TTWindow, &NameGads[0]);
    }

    SetAPen(RP,1);  /* Set Pens (important for String Gadget) */
    SetBPen(RP,2);

    AddGList(TTWindow, &NameGads[0], 0, -1, NULL);

    RefreshGList(&NameGads[0], TTWindow, NULL, -1);

    DrawBorder(RP,&NamesList_Border[0],TTWindow->BorderLeft,TTWindow->BorderTop);

    DrawBorder(RP,&PriSlider_Border[0],TTWindow->BorderLeft,TTWindow->BorderTop);

    DrawBorder(RP,&RefreshGad_Border,TTWindow->BorderLeft,TTWindow->BorderTop);

    PrintIText(RP,&PText,TTWindow->BorderLeft,TTWindow->BorderTop);
}


/*------------------------------------------------------------------------*/
VOID AdjustGads( window, FirstGad )
struct Window *window;
struct Gadget *FirstGad;
/*  Adjusts Gadgets according to Window border. Returns nothing. */
{
    struct Gadget *g;

    g = FirstGad;
    while (g) {
        g->TopEdge  += window->BorderTop;
        g->LeftEdge += window->BorderLeft;
        g = g->NextGadget;
    }
}

/*------------------------------------------------------------------------*/
VOID RefreshTaskTool( TTInfo, ReadList )
TaskListInfo *TTInfo;
BOOL ReadList;
/*  Recalculate Slider and String Gadgets from TaskListInfo data,
 *  reading TaskList first, and updating the info if ReadList is TRUE.
 *  Display Names and HighLight Gadget.
 */
{

    VOID DisplayList();
    VOID SetPriGadsFromData();
    VOID SetNameGadsFromData();

    if (ReadList) {

        if (TTInfo->numentries) {
            DisposeTaskList(TaskList, TTInfo->numentries);
        }

        if ( (TTInfo->numentries=ReadTaskList(TaskList)) >0 )  {
            /* This should ALWAYS happen! */
                TTInfo->Task=0;
                TTInfo->topentry=0;

        }

    }

    /* Setup Sliders/String Gad from data */
    SetPriGadsFromData( TTInfo );   /* Pri Slider and StringGad */
    SetNameGadsFromData( TTInfo );  /* Name Slider */


    if (ReadList)
        /* Display Names- list may have changed */
        DisplayList( TTInfo );
    else
        /* Only change HiGad- just selection changed */
        UpDateHiGad( TTInfo, TRUE);
}

/*------------------------------------------------------------------------*/
VOID SetPriGadsFromData( TTInfo )
TaskListInfo *TTInfo;
/*  Sets Priority Slider and String Gadget from TaskTool Data
 */

{
    VOID UpDatePriorityStr();
    VOID SetPriFromStr();

    UpDatePriorityStr( TTInfo, &PriStringGad ); /* Puts Pri in string Gad */
    SetPriFromStr( TTInfo, &PriStringGad, &PriSliderGad ); /* Sets Slider */
                                                       /* from String Gad */
}

/*------------------------------------------------------------------------*/
VOID UpDatePriorityStr( TTInfo, g )
TaskListInfo *TTInfo;
struct Gadget *g;
/*  Take the priority of the Task from TaskListInfo structure, put in
 *  the String Gadget and Refresh it.
 */
{
    LONG *PriLongInt=&( ((struct StringInfo *)g->SpecialInfo)->LongInt );

    RemoveGadget(TTWindow, g);

    *PriLongInt = (LONG)TaskList[TTInfo->Task].Pri;

    sprintf(PriStringBuff,"%-ld",(*PriLongInt));
    ((struct StringInfo *)g->SpecialInfo)->DispPos=0;

    AddGadget(TTWindow, g, NAMESDISPLAYED+3);
    RefreshGList(g, TTWindow, NULL, 1L);
}

/*------------------------------------------------------------------------*/
VOID SetPriFromStr( TTInfo, Strg, Propg )
TaskListInfo *TTInfo;
struct Gadget *Strg, *Propg;
/* Set the current priority from the value of the LongInt String, so
 * updating the PriSlider.
 */

{
    LONG   totalLines=ABSPRI, visibleLines=1, topLine,
          *PriLongInt=&( ((struct StringInfo *)Strg->SpecialInfo)->LongInt );

    if ( *PriLongInt < MINPRI )  *PriLongInt=(LONG) MINPRI;
    if ( *PriLongInt > MAXPRI )  *PriLongInt=(LONG) MAXPRI;

    topLine = (LONG) *PriLongInt+128; /* Convert to Suitable value */

    NewModifyProp(Propg,TTWindow,NULL,PROPBORDERLESS|FREEHORIZ,((MAXPOT*topLine)/(totalLines)),
        NULL,((MAXBODY*visibleLines)/totalLines),MAXBODY,1L);

    /* Take value of string and put in our data segment */
    TaskList[TTInfo->Task].Pri= (BYTE)*PriLongInt;

}

/*------------------------------------------------------------------------*/
VOID GetPriSlider( TTInfo, g, DeltaX )
TaskListInfo *TTInfo;
struct Gadget *g;
LONG DeltaX;
/* Get the current priority set either by the PriSlider or the Arrows */
{
    USHORT  potv;
    struct  PropInfo *p;
    LONG    topentry = (LONG)TaskList[TTInfo->Task].Pri + 128;
    static  LONG  oldtopentry=-1L;

    p=(struct PropInfo *)g->SpecialInfo;
    if (DeltaX!=0)  {
        topentry += DeltaX;
        if (topentry>0 && topentry<(ABSPRI-1)) {
            potv=((MAXBODY*topentry)/(ABSPRI-1));
        }
        else {
            if (topentry>=(ABSPRI-1)) {
                potv=MAXBODY;
                topentry=(ABSPRI-1);
            }
            if (topentry<=0) {
                potv=0;
                topentry=0;
            }
        }
        if (ABSPRI>1 && topentry!=oldtopentry) {
            NewModifyProp(g, TTWindow, NULL,PROPBORDERLESS|FREEHORIZ,
                potv,NULL,((MAXBODY*1)/ABSPRI-1),MAXBODY,1L);
        }
    }
    else {
        if (ABSPRI>1)
            topentry=(p->HorizPot*(ABSPRI-1))/MAXBODY;
        else
            topentry=0;
    }

    oldtopentry=topentry;   /* Stash away the current topentry- refresh
                             * only when topentry changes for efficiency
                             */

    /* Put new priority into our data segment */

    TaskList[TTInfo->Task].Pri=(BYTE)(topentry-128);

}

/*------------------------------------------------------------------------*/
VOID SetUpNameGads(VOID)
/*  Setup the Name Gadgets-
 *  o Initialize common setttings
 *  o Set up Gadget IDs
 *  o Put NameGads[0-7] in correct y position (Gadget IDs 1-8)
 *  o Set up their GadgetText Pointers to point to our IntuiText
 */
{
    UCOUNT  n;
    WORD    GHeight= (WORD) TTTextAttr.ta_YSize+1;

    for (n=0 ; n<NAMESDISPLAYED ; n++) {
        NameGads[n]=NameGads[0];    /* Initialize common settings */
        TaskToolIText[n]=TaskToolIText[0];
        TaskToolIText[n].IText=TaskToolGadText[n];

        NameGads[n].GadgetID=n+1;   /* Gadget Id */
        NameGads[n].TopEdge=NameGads[n].TopEdge + n*GHeight;
                                    /* Set Gadget y position */

        NameGads[n].GadgetText=&TaskToolIText[n];
                                    /*Point to our IntuiText */
        if (n<(NAMESDISPLAYED-1)) NameGads[n].NextGadget= &NameGads[n+1];
    }
    NameGads[NAMESDISPLAYED-1].NextGadget = &NameSliderGad;
}

/*------------------------------------------------------------------------*/
VOID DisplayList( TTInfo )
TaskListInfo *TTInfo;
/*  Put Task Names in Gadget IntuiText Structures and refresh */
{
    ULONG   topentry=TTInfo->topentry, numentries=TTInfo->numentries;
    UCOUNT  i,Task;  /* i is gadget number, Task is index in TaskArray */

    VOID UpDateHiGad();


    /* Always remove gadgets before playing around with them! */
    RemoveGList(TTWindow, &NameGads[0], NAMESDISPLAYED);


    /* Copy at most CHARSDISPLAYED characters of TaskName to IntuiText */
    for ( i=0,Task=topentry; i<NAMESDISPLAYED && Task<=numentries; i++,Task++ )
        strncpy(TaskToolGadText[i], TaskList[Task].Name, (LONG) CHARSDISPLAYED);

    /* Blank off unused entries */
    for ( ;i<NAMESDISPLAYED; i++)
        TaskToolGadText[i][0] = NULL;


    /* Put Gadgets Back and Redisplay */
    AddGList(TTWindow, &NameGads[0], 0, NAMESDISPLAYED, NULL);

    UpDateHiGad(TTInfo, FALSE); /* Update selected gad- don't refresh... */

    /* Refresh all name gads to display new Names (...and HiGad now) */
    RefreshGList(&NameGads[0], TTWindow, NULL, NAMESDISPLAYED);
}

/*------------------------------------------------------------------------*/
VOID SetNameGadsFromData( TTInfo )
TaskListInfo *TTInfo;
/*  Sets Names Slider from TaskTool Data
 */
{
    LONG    totalLines, topLine;

    totalLines = (LONG)(TTInfo->numentries);

    topLine = (LONG)(TTInfo->topentry);

    if (totalLines>NAMESDISPLAYED) {
        NewModifyProp(&NameSliderGad, TTWindow, NULL,
          PROPBORDERLESS|AUTOKNOB|FREEVERT,
          NULL, ((MAXBODY*topLine)/(totalLines-NAMESDISPLAYED)),
          MAXBODY, ((MAXBODY*NAMESDISPLAYED)/totalLines-NAMESDISPLAYED),1L);
    }
    else {
        NewModifyProp(&NameSliderGad, TTWindow, NULL,
          PROPBORDERLESS|AUTOKNOB|FREEVERT,
          NULL,NULL,MAXBODY,MAXBODY,1L);
    }
}

/*------------------------------------------------------------------------*/
ULONG GimmeHiGad( topline, Task )
ULONG topline, Task;
/*  Return the Gadget to highlight in displayed portion of TaskList, 0 if
 *  Gadget off screen.
 */

{
    if (  Task<topline || Task>=(topline+NAMESDISPLAYED) )
        return(0);
    else
        return(1+Task-topline);
}

/*------------------------------------------------------------------------*/
VOID UpDateHiGad( TTInfo, Refresh )
TaskListInfo *TTInfo;
BOOL Refresh;
/*  Highlight correct Name Gadget in list, and only refresh those gadgets
 *  that need refreshing (if Refresh is TRUE). This demonstrates how to
 *  implement Mutual Exclude Gadgets-
 *  Golden Rule:  Remove Gadgets Before Playing with Them!!
 */

{
    ULONG   OldGad = TTInfo->HiGad,
            NewGad = GimmeHiGad(TTInfo->topentry, TTInfo->Task);

    RemoveGList(TTWindow, &NameGads[0], NAMESDISPLAYED);

    if (OldGad)
        NameGads[OldGad-1].Flags &= ~SELECTED;  /* Deselect old Gadget */

    if ( TTInfo->HiGad = NewGad )
        NameGads[NewGad-1].Flags |= SELECTED;     /* Select New */

    AddGList(TTWindow, &NameGads[0], 0, NAMESDISPLAYED, NULL);


    /* Refreshing only those gadgets which have changed saves time (???) */

    if (Refresh) {
        if (OldGad)
            RefreshGList(&NameGads[OldGad-1], TTWindow, NULL, 1L);
        if (NewGad)
            RefreshGList(&NameGads[NewGad-1], TTWindow, NULL, 1L);
    }
}

/*------------------------------------------------------------------------*/
VOID GetNameSlider( TTInfo, g, DeltaY )
TaskListInfo *TTInfo;
struct Gadget *g;
LONG DeltaY;
/*  Get the current topentry, set HiGad according to Prop or arrow
 *  movement, and Display List.
 */
    {
    USHORT  potv;
    struct  PropInfo *p;
    LONG    topentry    = (LONG)TTInfo->topentry,
            numentries  = (LONG)TTInfo->numentries;

    VOID    DisplayList();


    p=(struct PropInfo *)g->SpecialInfo;
    if(DeltaY!=0)  {
        topentry += DeltaY;
        if(topentry>0 && topentry<(numentries-NAMESDISPLAYED))  {
            potv=((MAXBODY*topentry)/(numentries-NAMESDISPLAYED));
        }
        else {
            if (topentry>=(numentries-NAMESDISPLAYED))  {
                potv=MAXBODY;
                topentry=(numentries-NAMESDISPLAYED);
            }
            if (topentry<=0)  {
                potv=0;
                topentry=0;
            }
        }

        if (numentries>NAMESDISPLAYED){
            NewModifyProp(g, TTWindow, NULL,
              PROPBORDERLESS|AUTOKNOB|FREEVERT, NULL, potv, MAXBODY,
              ((MAXBODY*NAMESDISPLAYED)/numentries-NAMESDISPLAYED),1L);
        }
    }

    else {
        if (numentries>NAMESDISPLAYED)
            topentry=(p->VertPot*(numentries-NAMESDISPLAYED))/MAXBODY;
        else
            topentry=0;
    }

    TTInfo->topentry   = (ULONG)topentry; /* Save new topentry */

    DisplayList( TTInfo );  /* Redisplay List */

}


/*------------------------------------------------------------------------*/
VOID OpenAll(VOID)
{

    if (!(IntuitionBase=(struct IntuitionBase *)
            OpenLibrary("intuition.library",33)))
        CleanExit(ERROR_INVALID_RESIDENT_LIBRARY);

    if (!(GfxBase=(struct GfxBase *)
            OpenLibrary("graphics.library",33)))
        CleanExit(ERROR_INVALID_RESIDENT_LIBRARY);


    /* Open Font Just to be Nice (Usually Topaz80) */

    if (!(TTfont = OpenFont(&TTTextAttr)))
        CleanExit(RETURN_WARN);


    /* Window Adjustments */

    TaskToolWin.TopEdge =
            (GfxBase->NormalDisplayRows - TaskToolWin.Height) / 2;
    TaskToolWin.LeftEdge =
            (GfxBase->NormalDisplayColumns - TaskToolWin.Width) / 2;



    /* Open Window */
    TTWindow = OpenWindow(&TaskToolWin);

    if (TTWindow) {
        /* Put the font into the rasport */
        SetFont(RP, TTfont);

        /* More Adjustments */
        AdjustMenus(&TaskToolMenus[0], TTWindow->WScreen->Font);

        /* Activate Menu */
        SetMenuStrip(TTWindow, &TaskToolMenus[0]);
    }
    else
        CleanExit(RETURN_WARN);

    /* Open Timer Device */
    if (!(TimerPort = OpenTimer()))   /* Couldn't open device... */
        OffMenu(TTWindow,SHIFTMENU(1)|SHIFTITEM(2)); /* Disable Time Opt */


}


/*------------------------------------------------------------------------*/
VOID CleanExit(returnValue)
int returnValue;
{

    if (TimerPort)  CloseTimer();   /* Close Timer Device */

    if (TaskToolInfo.numentries)
        (void) DisposeTaskList( TaskList, TaskToolInfo.numentries );

    if (TTWindow) {
        /* Remove Menu Strip */
        if (TTWindow->MenuStrip)  ClearMenuStrip(TTWindow);

        CloseWindow(TTWindow);
    }


    /* Close Font */

    if (TTfont)  CloseFont(TTfont);


    /* Close Libraries */

    if (GfxBase)  CloseLibrary((struct Library *)GfxBase);

    if (IntuitionBase)  CloseLibrary((struct Library *)IntuitionBase);


    /* GO BYE BYE (Ya Ya Ya Ya!) - Animal, Muppet Babies. Jim Henson Lives! */

    exit(returnValue);

}

/*------------------------------------------------------------------------*/





/*------------------------------------------------------------------------
 *  The following routines adjust an entire menu system to comform to the
 *  specified fonts' width and height.  Allows for Proportional Fonts.
 *  This is necessary for a clean look regardless of what the users
 *  preference in Fonts may be.  Using these routines, you don't need to
 *  specify TopEdge, LeftEdge, Width or Height in the MenuItem structures.
 *
 *  This set of routines does NOT check/correct if the menu runs off the
 *  screen due to large fonts, too many items, lo-res screen.
 *
 *  Taken from the RKM "Libraries and Devices"
 */

BOOL AdjustMenus( firstmenu, attr)
struct Menu *firstmenu;
struct TextAttr *attr;
{
    struct  RastPort    textrp = {0};   /* Tempory RastPort */
    struct  Menu        *menu;
    struct  TextFont    *font;          /* Font to use */
    USHORT              start, width, height, space;
    BOOL                retval = FALSE;

    /* Open the font */

    if ((font = OpenFont(attr)))
        {
        SetFont(&textrp, font); /* Put font into tempory RastPort */

        width = font->tf_XSize; /* Get width of Font */

        /* To prevent crowding of the Amiga Key when using COMMSEQ,
         * don't allow the items to be less than 8 pixels high.
         */
        height = (font->tf_YSize <8) ? 8 : font->tf_YSize;
        height++;

        start = 2;                  /* Set Starting Pixel */

        /* Step thru the menu structure and adjust it */
        menu = firstmenu;
        while(menu)
            {
            menu->LeftEdge = start;
            menu->Width = space =
                TextLength(&textrp, menu->MenuName,
                    (LONG)strlen(menu->MenuName)) + width;
            AdjustItems(&textrp, menu->FirstItem, attr, width, height, 0, 0);
            menu = menu->NextMenu;
            start += (space + (width + 2));
            }
        CloseFont(font);    /* Close the font */

        retval = TRUE;
    }

    return(retval);

}


/* Adjust the MenuItems and SubItems */
VOID AdjustItems( txtrp, fi, atr, wdth, hght, lvl, edge )
struct RastPort *txtrp;
struct MenuItem *fi;
struct TextAttr *atr;
USHORT wdth,hght,lvl,edge;
{
    struct      MenuItem    *item = fi;
    register    USHORT      num;
                USHORT      strip_width, sub_edge;

    if(fi==NULL)    return;
    strip_width = MaxLength(txtrp, item, wdth);
    num = 0;
    while (item) {
        item->TopEdge = (num * hght) - lvl;
        item->LeftEdge = edge;
        item->Width = strip_width;
        item->Height = hght;
        sub_edge = strip_width - wdth;
        AdjustText((struct IntuiText *)item->ItemFill, atr);
        AdjustItems(txtrp, item->SubItem, atr, wdth, hght, 1, sub_edge);
        item = item->NextItem;
        num++;
    }
}

/* Steps thru each item to determine the maximum width of the strip */
USHORT  MaxLength( txtrp, fi, width)
struct RastPort *txtrp;
struct MenuItem *fi;
USHORT width;
{
    USHORT  maxval = 0, textlen;
    struct  MenuItem    *item = fi;
    struct  IntuiText   *itext;

    while(item) {
        if (item->Flags&COMMSEQ) {
            width += (width + COMMWIDTH);
            break;
        }
        item = item->NextItem;
    }
    item = fi;
    while (item) {
        itext = (struct IntuiText *)item->ItemFill;
        textlen = itext->LeftEdge +
                  TextLength(txtrp, itext->IText,
                                (LONG)strlen(itext->IText)) + width;
        /* returns greater of the two */
        maxval = (textlen<maxval)?maxval:textlen;
        item = item->NextItem;
    }
    return(maxval);
}

/* Adjust the MenuItems font Attribute (to that of User Pref) */
VOID AdjustText( text, attr )
struct IntuiText *text;
struct TextAttr *attr;
{
    struct IntuiText *nt;

    nt=text;
    while (nt) {
        nt->ITextFont = attr;
        nt = nt->NextText;

    }
}

/***************************************************************************
 *                                                                         *
 *                    TaskTool - The Dynamic Task Priority Manager         *
 *       ///                                                               *
 *      ///           This is version v0.8. Some features are              *
 *  \\\///            missing as of yet, but hopefully the                 *
 *   \XX/ Amiga       forthcoming TaskTool v1.0 will be fully              *
 *                    endowed, and there may be more besides...            *
 *                                                                         *
 *                    TaskTool v0.8 is Freely Distributable.               *
 *                    See ReadMe! for details.                             *
 *                                                                         *
 *                    Distibution: 10-10-91/2T/1W                          *
 *                    comp.sys.amiga.introductions  Re:Killing Processes   *
 *                                                                         *
 ***************************************************************************/