/*Requests:

sort by CX_PRIORITY
Update the list by monitoring the 2 dirs
*/


/*  WBStartup+Prefs.c */

#define VERSION   "2.8"
#define DATENUM   __AMIGADATE__
#define DATETEXT  "December 12, 1996"
#define COPYRIGHT "Copyright © 1996 John Hughes"

//#define DEBUG 1

/*****************************************************************/

#include <exec/types.h>
#include <exec/memory.h>

#include <clib/dos_protos.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <utility/tagitem.h>    /* TAG_DONE */
#include <dos/exall.h>
#include <clib/alib_protos.h>   /* List */
#include <clib/exec_protos.h>   /* AllocVec() */
#include <workbench/startup.h>
#include <dos/dostags.h>         /* SYS_Asynch */
#include <dos/exall.h>
#include <intuition/gadgetclass.h>
#include <graphics/gfxmacros.h>
#include <graphics/gfxbase.h>
#include <dos/stdio.h>

/* These add the function prototypes and the #pragmas which reduce the code size */
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/gadtools.h>
#include <proto/intuition.h>
#include <proto/icon.h>
#include <proto/graphics.h>
#include <proto/wb.h>
#include <proto/commodities.h>
#include <proto/diskfont.h>
#include <proto/graphics.h>
#include <proto/utility.h>
#include <proto/wb.h>

#include <math.h>

#include "CheckBoxListView.h"
#include "WBStartup+Prefs.h"
#include "GetStringWindow.h"
#include "GetPriorityWindow.h"
#include "ChooseGroupWindow.h"
#include "CopyWindow.h"
#include "DisableWindow.h"

#include "WBStartupPlusPrefs_Cat.h"

//#define MWDEBUG 1
//#include "sc:extras/memlib/memwatch.h"


char const version[]="\0$VER: WBStartup+Prefs "VERSION" "DATENUM"\r\n"COPYRIGHT"\r\njohughes@heartland.bradley.edu";

#define PREFSFILE "ENVARC:WBStartup+"

#define OUTSIDEBORDER 10
#define GADTEXTBORDERSUM 6

ULONG PRIORITYEDGE;  /* The horizontal position in the listview gadget to draw the vertical line */

/*  ln_Type's  */
#define EXECUTE  TRUE
#define STORAGE  FALSE


void GetFilenames(struct List *filenamequeue, APTR memPool, char *directory, UBYTE type, struct WBStartupPrefs *prefs);
struct WBSPNode *AddFileToList(struct List *filenamequeue, APTR memPool, char *pathname, UBYTE type, struct WBStartupPrefs *prefs);
void RunPrograms(struct List *filenamequeue, char *path);
void GetArguments(int argc, char **argv, struct WBStartupPrefs *prefs);
void ProcessEvents(struct List *list, struct List *grouplist, struct WBStartupPrefs *prefs, APTR groupPool);
BOOL RenderGadgets(struct Gadget **glist, void *vi,
struct Window *win, struct Gadget *my_gads[], struct List *list,
struct Gadget *(*CreateGadgetsFunction)(struct Gadget **glistptr, void *vi, struct Window *win, struct Gadget *my_gads[], struct List *list));

void ShowRequester(STRPTR RequesterText);
struct Gadget *createAllGadgets(struct Gadget **glistptr, void *vi, struct Window *win, struct Gadget *my_gads[], struct List *list);
void RestoreTypes(struct List *filenamequeue);
void SaveChanges(struct List *list, struct WBStartupPrefs *prefs);
BOOL ChangePriTooltype(char *filename, BYTE pri);
void ShowWBIconRequester(struct WBSPNode *node, struct WBStartupPrefs *Prefs, struct Screen *scr);
void AlphabetizeList(struct List *list);
ULONG GetScreenHeight(struct Screen *scr);
ULONG BestWindowHeight(struct Screen *scr, struct List *list);
ULONG ListViewHeight(struct Screen *scr, struct List *list);
void SortPriorityList(struct List *list);

void SaveGroups(struct List *list);
void ReadGroups(struct List *list, APTR memPool);
void CreateGroup(struct List *grouplist, struct List *wbsplist, char *groupname, APTR memPool);
void SelectGroup(struct List *wbsplist, struct List *list);
void DeleteGroup(struct GroupNode *node, APTR memPool);

BOOL ShowGroupsRequester(void);
BOOL CopyFile(char *source,char *dest);


struct IntuitionBase *IntuitionBase;
struct Library *GadToolsBase;
struct Library *CxBase, *IconBase;
struct Library *ScreenNotifyBase;
struct Library *WorkbenchBase;
struct Library *DiskfontBase;
struct GfxBase *GfxBase;
struct Library *UtilityBase;

/****************************************************************************************/

struct Hook CBLVHook;

/****************************************************************************************/


main (int argc, char **argv)
{
  struct List filenamequeue;
  struct List grouplist;
  struct WBStartupPrefs prefs;
  struct MsgPort *port;

  struct WBSPNode titlenode =
  {
    {NULL,NULL,TITLE,127,NULL},
    0
  };

  if (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",37))
  {
    if (CxBase = OpenLibrary("commodities.library", 37L))
    {
      if (IconBase = OpenLibrary("icon.library", 36L))
      {
        if (WorkbenchBase = OpenLibrary("workbench.library", 39L))
        {
          if (GadToolsBase=OpenLibrary("gadtools.library",39))
          {
            if (DiskfontBase = OpenLibrary("diskfont.library", 37L))
            {
              if (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 37L))
              {
                if (UtilityBase = OpenLibrary("utility.library", 37L))
                {
                  if (prefs.filenamePool=CreatePool(MEMF_PUBLIC,31,31))  /* Node is 14 bytes, largest filename is 30 */
                  {
                    if (prefs.groupPool=CreatePool(MEMF_PUBLIC,31,31))  /* Node is 14 bytes, largest filename is 30 */
                    {
                      NewList(&grouplist);
                      NewList(&filenamequeue);
                      if (titlenode.wbsp_node.ln_Name=AllocVec(strlen(GetString(STRProgramName))+strlen(GetString(STRPriority))+2,MEMF_PUBLIC))
                        sprintf(titlenode.wbsp_node.ln_Name,"%s,%s",GetString(STRProgramName),GetString(STRPriority));
                      AddHead(&filenamequeue,&titlenode);

                      GetArguments(argc, argv, &prefs);

                      if (prefs.EnabledDirLock=Lock(prefs.ExecutePath,ACCESS_READ))
                      {
                        if (prefs.DisabledDirLock=Lock(prefs.StoragePath,ACCESS_READ))
                        {
                          prefs.NumPrograms=0;
                          GetFilenames(&filenamequeue,prefs.filenamePool,prefs.ExecutePath,EXECUTE,&prefs);
                          GetFilenames(&filenamequeue,prefs.filenamePool,prefs.StoragePath,STORAGE,&prefs);

                          ReadGroups(&grouplist,prefs.groupPool);

                          if (prefs.Alphabetize)
                            AlphabetizeList(&filenamequeue);

                          ProcessEvents(&filenamequeue, &grouplist, &prefs, prefs.groupPool);

                          UnLock(prefs.DisabledDirLock);
                        }
                        else
                          ShowRequester(GetString(STRCouldntLockDir));
                        UnLock(prefs.EnabledDirLock);
                      }
                      else
                        ShowRequester(GetString(STRCouldntLockDir));


                      if (titlenode.wbsp_node.ln_Name)
                        FreeVec(titlenode.wbsp_node.ln_Name);

                      /* If WBStartup+ is running and waiting for us to quit, send it a message */
                      Forbid();
                      if (port=FindPort("WBStartup+"))
                      {
                        struct Message msg;
                        PutMsg(port,&msg);
                      }
                      Permit();


                      DeletePool(prefs.groupPool);
                    }
                    DeletePool(prefs.filenamePool);
                  }
                  CloseLibrary(UtilityBase);
                }
                CloseLibrary((struct Library *)GfxBase);
              }
              CloseLibrary(DiskfontBase);
            }
            CloseLibrary(GadToolsBase);
          }
          CloseLibrary(WorkbenchBase);
        }
        else
          ShowRequester(GetString(STRRequiredDosVer));
        CloseLibrary(IconBase);
      }
      CloseLibrary(CxBase);
    }
    CloseLibrary((struct Library *)IntuitionBase);
  }

  #ifdef DEBUG
    printf("Exiting\n");
  #endif
}

/* GetFilesnames() is NOT the same function that is in "WBStartup+.c" */
void GetFilenames(struct List *filenamequeue, APTR memPool, char *directory, UBYTE type, struct WBStartupPrefs *prefs)
{
  struct ExAllControl *myexallctrl;  /* The ExAllControl structure */
  struct ExAllData    *eadptr;       /* temp pointer to data structure */
  BPTR mylock;                       /* Lock on the directory */
  int more;                          /* Are there more entries ? */
  struct ExAllData ExAllBuf[20];     /* hold the filenames and types */
  char pathname[500];                /* path and filename of the icon */
  char filename[32];                 /* parsed match string 2*length+2=22 */


  if (mylock=Lock(directory,SHARED_LOCK))
  {
    if (myexallctrl=(struct ExAllControl *)AllocDosObjectTags(DOS_EXALLCONTROL,TAG_END))
    {
      myexallctrl->eac_LastKey = 0;
      myexallctrl->eac_Entries = 0;
      myexallctrl->eac_MatchString = NULL;
      myexallctrl->eac_MatchFunc = NULL;

      do   /* go until more==0 */
      {
        more = ExAll(mylock, ExAllBuf, sizeof(ExAllBuf), ED_TYPE, myexallctrl);
        if ((!more) && (IoErr() != ERROR_NO_MORE_ENTRIES))
        {
            ShowRequester("ExAll failed abnormally");//: %ld.", IoErr());
            break;
        }

        if (myexallctrl->eac_Entries)
        {
          eadptr=ExAllBuf;

          while (eadptr)
          {
            if (eadptr->ed_Type < 0)  /* make sure it is a file, not a directory, see end of dos/dosextens.h */
            {
              strcpy(filename, eadptr->ed_Name);

              /* truncate the filename if it is an icon, ie. remove the ".info" */
              if (!Stricmp(filename+strlen(filename)-5,".info"))
                filename[strlen(filename) - 5] = NULL;  /* take the .info off of the string */
              else
                filename[strlen(filename)] = NULL;  /* take the .info off of the string */

              /* See if the file is already in the list */
              if (!FindName(filenamequeue,filename))
              {
                if (Stricmp(filename,"WBStartup+"))  /* Make sure we don't recursively run ourself */
                {
                  strcpy(pathname,directory);
                  AddPart(pathname, filename, 500);

                  AddFileToList(filenamequeue,memPool,pathname,type,prefs);
                }
              }
            }
            eadptr=eadptr->ed_Next;
          }
        }

      } while (more);

      FreeDosObject(DOS_EXALLCONTROL,myexallctrl);
    }
    else
      ShowRequester("Not enough memory for ExAllControl.");
    UnLock(mylock);
  }
  else
    ShowRequester(GetString(STRCouldntLockDir));
}


struct WBSPNode *AddFileToList(struct List *filenamequeue, APTR memPool, char *pathname, UBYTE type, struct WBStartupPrefs *prefs)
{
  /* pathname should be like:  ram:t/myfile */
  struct WBSPNode *node;             /* hold the filenames and proiority for the List */
  struct DiskObject *diskobj;        /* Pointer to the icon info */
  char *toolstring;                  /* Pointer to a tooltype string */
  char *filenameptr;

  filenameptr=FilePart(pathname);

  if (diskobj=GetDiskObjectNew(pathname))
  {
    if ((diskobj->do_Type==WBPROJECT) || (diskobj->do_Type==WBTOOL))  /* Make sure we got an icon that isn't a drawer icon */
    {
      if (node=(struct WBSPNode *)AllocPooled(memPool,sizeof(struct WBSPNode)))
      {
        if (node->wbsp_node.ln_Name=(char *)AllocPooled(memPool, strlen(filenameptr)+1))
        {
          strcpy(node->wbsp_node.ln_Name, filenameptr);
          if (toolstring=FindToolType(diskobj->do_ToolTypes,"STARTPRI"))
            node->wbsp_node.ln_Pri = node->Original_Priority = atoi((char *)toolstring);
          else
            node->wbsp_node.ln_Pri = node->Original_Priority = 0;
          node->wbsp_node.ln_Type = node->Original_Type = type;  /* EXECUTE or STORAGE */
          Enqueue(filenamequeue,node);
          prefs->NumPrograms++;
        }
      }
    }
    FreeDiskObject(diskobj);
  }
  return(node);
}



void GetArguments(int argc, char **argv, struct WBStartupPrefs *prefs)
{
  UBYTE **ttypes;

  ttypes = ArgArrayInit(argc,argv);
  strcpy(prefs->ExecutePath , ArgString(ttypes,"ENABLEDPATH","SYS:WBStartup/WBStartup (Enabled)"));
  strcpy(prefs->StoragePath , ArgString(ttypes,"DISABLEDPATH","SYS:WBStartup/WBStartup (Disabled)"));
  prefs->Alphabetize = ((FindToolType(ttypes,"ALPHABETIZE")) ? TRUE : FALSE);
  ArgArrayDone();
}

void ProcessEvents(struct List *list, struct List *grouplist, struct WBStartupPrefs *prefs, APTR groupPool)
{
  ULONG signals;
  ULONG winsigflag;
  BOOL LOOP;
  struct IntuiMessage *winmsg;
  struct Window *win;
  struct Screen *scr;
  UBYTE  *WindowTitle="WBStartup+ Prefs";

  struct Menu *MainMenu;
  struct MenuItem *menuitemaddress;
  struct NewMenu MainNewMenu[25]=
  {
    { NM_TITLE, NULL/*"Project"*/,                0, 0, 0, 0, },
    {   NM_ITEM,  NULL/*"Save"*/,               "S", 0, 0, (APTR)SAVE_MENU, },
    {   NM_ITEM,  NM_BARLABEL,                 0, 0, 0, 0, },
    {   NM_ITEM,  NULL/*"About..."*/,              "?", 0, 0, (APTR)ABOUT_MENU, },
    {   NM_ITEM,  NM_BARLABEL,                 0, 0, 0, 0, },
    {   NM_ITEM,  NULL/*"Quit"*/,               "Q", 0, 0, (APTR)QUIT_MENU, },
    { NM_TITLE, NULL/*"Edit"*/,                   0, 0, 0, 0, },
    {   NM_ITEM,  NULL/*"Restore"*/,            "R", 0, 0, (APTR)RESTORE_MENU, },
    {   NM_ITEM,  NULL/*"Icon Window..."*/,     "I", 0, 0, (APTR)PROGRAMINFO_MENU, },
    {   NM_ITEM,  NM_BARLABEL,                 0, 0, 0, 0, },
    {   NM_ITEM,  NULL/*"Create Group..."*/,    "C", 0, 0, (APTR)CREATEGROUP_MENU, },
    {   NM_ITEM,  NULL/*"Modify Group..."*/,    "M", 0, 0, (APTR)MODIFYGROUP_MENU, },
    {   NM_ITEM,  NM_BARLABEL,                 0, 0, 0, 0, },
    {   NM_ITEM,  NULL/*"Delete Group..."*/,    "D", 0, 0, (APTR)DELETEGROUP_MENU, },
    { NM_TITLE, NULL/*"Sort"*/,                   0, 0, 0, 0, },
    {   NM_ITEM,  NULL/*"Alphabetically"*/,     "Z", 0, 0, (APTR)SORTALPHABETICALLY_MENU, },
    {   NM_ITEM,  NULL/*"Priority"*/,           "P", 0, 0, (APTR)SORTPRIORITY_MENU, },
    { NM_TITLE, NULL/*"Select"*/,                 0, 0, 0, 0, },
    {   NM_ITEM,  NULL/*"All"*/,                "A", 0, 0, (APTR)SELECTALL_MENU, },
    {   NM_ITEM,  NULL/*"None"*/,               "N", 0, 0, (APTR)SELECTNONE_MENU, },
    {   NM_ITEM,  NULL/*"Toggle"*/,             "T", 0, 0, (APTR)SELECTTOGGLE_MENU, },
    {   NM_ITEM,  NM_BARLABEL,                 0, 0, 0, 0, },
    {   NM_ITEM,  NULL/*"Group..."*/,           "G", 0, 0, (APTR)SELECTGROUP_MENU, },
	  { NM_END,   NULL,                          0, 0, 0, 0, },
  };
  struct VisualInfo *vi;

  struct Gadget *glist=NULL, *my_gads[TOTALGADGETS];
	struct Node *n;
  struct WBSPNode *node;
  struct GroupNode *groupnode;  /* ptr to a node in the group list */
	long z;
  ULONG height;
  UBYTE oldpriority;
  char groupname[40];

  BOOL GroupsModified=FALSE;

  ULONG appwinsig = 0L;
  struct MsgPort *awport;
  struct AppWindow *appwin;
  struct AppMessage *amsg;
  struct WBArg *argptr;
  LONG argnum;
  char *sourcename,*destname;

  int count=0;

  /* Put strings in Menu */
  MainNewMenu[count++].nm_Label = GetString(STRProjectMenu);
  MainNewMenu[count++].nm_Label = GetString(STRSaveMenu);
  MainNewMenu[count++].nm_Label = NM_BARLABEL;
  MainNewMenu[count++].nm_Label = GetString(STRAboutMenu);
  MainNewMenu[count++].nm_Label = NM_BARLABEL;
  MainNewMenu[count++].nm_Label = GetString(STRQuitMenu);
  MainNewMenu[count++].nm_Label = GetString(STREditMenu);
  MainNewMenu[count++].nm_Label = GetString(STRRestoreMenu);
  MainNewMenu[count++].nm_Label = GetString(STRIconWindowMenu);
  MainNewMenu[count++].nm_Label = NM_BARLABEL;
  MainNewMenu[count++].nm_Label = GetString(STRCreateGroupMenu);
  MainNewMenu[count++].nm_Label = GetString(STRModifyGroupMenu);
  MainNewMenu[count++].nm_Label = NM_BARLABEL;
  MainNewMenu[count++].nm_Label = GetString(STRDeleteGroupMenu);
  MainNewMenu[count++].nm_Label = GetString(STRSortMenu);
  MainNewMenu[count++].nm_Label = GetString(STRAlphabeticallyMenu);
  MainNewMenu[count++].nm_Label = GetString(STRPriorityMenu);
  MainNewMenu[count++].nm_Label = GetString(STRSelectMenu);
  MainNewMenu[count++].nm_Label = GetString(STRAllMenu);
  MainNewMenu[count++].nm_Label = GetString(STRNoneMenu);
  MainNewMenu[count++].nm_Label = GetString(STRToggleMenu);
  MainNewMenu[count++].nm_Label = NM_BARLABEL;
  MainNewMenu[count].nm_Label = GetString(STRGroupMenu);


  if (scr=LockPubScreen(NULL))
  {
    ChecklistFont.ta_Name = scr->Font->ta_Name;
    ChecklistFont.ta_YSize = scr->Font->ta_YSize;

    if (vi=GetVisualInfo(scr,TAG_END))
    {
      height = BestWindowHeight(scr,list);
      if (win = OpenWindowTags(NULL,
            WA_Left,   100 + ((scr->LeftEdge < 0) ? (-scr->LeftEdge) : 0),
            WA_Top,    20 + ((scr->TopEdge < 0) ? (-scr->TopEdge) : 0),
            WA_Width, (TextLength(&scr->RastPort,GetString(STRProgramName),strlen(GetString(STRProgramName)))*2)
              + (TextLength(&scr->RastPort,GetString(STRPriority),strlen(GetString(STRPriority)))*2)
              + (2 * OUTSIDEBORDER)
              + scr->WBorLeft + scr->WBorRight,
            WA_Height, height,
            WA_MinWidth, 264,
            WA_MinHeight, scr->WBorTop + (2 * scr->Font->ta_YSize) + 1 + 160 + scr->WBorBottom,
            WA_MaxWidth, ~0,
            WA_MaxHeight, ~0,
            WA_AutoAdjust, TRUE,
            WA_IDCMP,  IDCMP_RAWKEY | IDCMP_NEWSIZE | IDCMP_GADGETUP | IDCMP_MENUPICK | IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | LISTVIEWIDCMP,
            WA_Flags, WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM | WFLG_NEWLOOKMENUS | WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE,
            WA_Title,  WindowTitle,
            WA_PubScreen, scr,
            TAG_END))
      {
        winsigflag=1L<<win->UserPort->mp_SigBit;
        if (MainMenu = CreateMenus( &MainNewMenu[0], TAG_DONE ))
          if (LayoutMenus( MainMenu, vi, GTMN_NewLookMenus, TRUE, TAG_DONE ))
            SetMenuStrip( win, MainMenu );

        if (RenderGadgets(&glist, vi, win, my_gads, list, createAllGadgets))
        {

          if (awport=CreateMsgPort())
            if (appwin = AddAppWindow(1,0,win,awport,NULL))
              appwinsig = 1L << awport->mp_SigBit;

          LOOP=TRUE;
          while (LOOP)
          {
            signals=Wait(SIGBREAKF_CTRL_C | winsigflag | appwinsig);
      
            if (signals & SIGBREAKF_CTRL_C)
              LOOP=FALSE;
      
            if (signals & winsigflag)
            {
              while (winmsg = (struct IntuiMessage *)GT_GetIMsg((struct MsgPort *)win->UserPort))
              {
                switch(winmsg->Class)
                {
                  case IDCMP_RAWKEY:
                    switch(winmsg->Code)
                    {
                      case 76:    // Up Arrow
                        GT_GetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Selected, &z, TAG_DONE);
                        if (z>1)
                          z--;
                        GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Selected, z, TAG_DONE);
                        break;
                      case 77:    // Down Arrow
                        GT_GetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Selected, &z, TAG_DONE);
                        if (z == (~0))
                          z=1;
                        else if (z < prefs->NumPrograms)
                          z++;
                        GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Selected, z, TAG_DONE);
                        break;
                    }
                    break;
                  case IDCMP_NEWSIZE:
                    RemoveGList(win,glist,-1);
                    FreeGadgets(glist);
                    RefreshWindowFrame(win);
                    RenderGadgets(&glist, vi, win, my_gads, list, createAllGadgets);
                    break;
                  case IDCMP_MENUPICK:
                    while( winmsg->Code != MENUNULL )
                    {
                      menuitemaddress = ItemAddress( MainMenu, winmsg->Code );
                        switch((int)GTMENUITEM_USERDATA(menuitemaddress))
                        {
                          case SAVE_MENU:
                            SetWindowTitles(win,GetString(STRSaving),(UBYTE *) ~0);
                            SaveChanges(list, prefs);
                            LOOP=FALSE;
                            break;
                          case ABOUT_MENU:
                            {
                              char *abouttext;
                              DisableWindow(win);
                              if (abouttext=AllocVec(strlen(GetString(STRAbout))+5+30+25,MEMF_PUBLIC))
                              {
                                sprintf(abouttext,GetString(STRAbout),VERSION,COPYRIGHT,DATETEXT);
                                ShowRequester(abouttext);
                                FreeVec(abouttext);
                              }
                              EnableWindow(win);
                            }
                            break;
                          case QUIT_MENU:
                            LOOP=FALSE;
                            break;
                          case RESTORE_MENU:
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                            RestoreTypes(list);
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);
                            break;
                          case PROGRAMINFO_MENU:
                            GT_GetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Selected, &z, TAG_DONE);
                            if (z != ~0)
                            {
                              for (n = list->lh_Head; z; z--)
                                n = n->ln_Succ;
                              if (n->ln_Type != TITLE)
                              {
                                DisableWindow(win);
                                ShowWBIconRequester((struct WBSPNode *)n, prefs, win->WScreen);
                                EnableWindow(win);
                              }
                            }
                            else
                              ShowRequester("You must first select a program!");
                            break;
                          case SORTALPHABETICALLY_MENU:
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                            AlphabetizeList(list);
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);
                            prefs->Alphabetize = TRUE;
                            break;
                          case SORTPRIORITY_MENU:
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                            SortPriorityList(list);
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);
                            prefs->Alphabetize = FALSE;
                            break;
                          case SELECTALL_MENU:
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                            for (node = (struct WBSPNode *)list->lh_Head; node->wbsp_node.ln_Succ; node = (struct WBSPNode *)node->wbsp_node.ln_Succ)
                              if (node->wbsp_node.ln_Type != TITLE)
                                node->wbsp_node.ln_Type = EXECUTE;
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);
                            break;
                          case SELECTNONE_MENU:
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                            for (node = (struct WBSPNode *)list->lh_Head; node->wbsp_node.ln_Succ; node = (struct WBSPNode *)node->wbsp_node.ln_Succ)
                              if (node->wbsp_node.ln_Type != TITLE)
                                node->wbsp_node.ln_Type = STORAGE;
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);
                            break;
                          case SELECTTOGGLE_MENU:
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                            for (node = (struct WBSPNode *)list->lh_Head; node->wbsp_node.ln_Succ; node = (struct WBSPNode *)node->wbsp_node.ln_Succ)
                              if (node->wbsp_node.ln_Type != TITLE)
                                if (node->wbsp_node.ln_Type == EXECUTE)
                                  node->wbsp_node.ln_Type = STORAGE;
                                else
                                  node->wbsp_node.ln_Type = EXECUTE;
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);
                            break;
                          case CREATEGROUP_MENU:
                            DisableWindow(win);
                            groupname[0]=NULL;  /* Clear the group name string */
                            GetStringWindow(win->LeftEdge+winmsg->MouseX,win->TopEdge+winmsg->MouseY,GetString(STRGroupName),groupname,30);
                            if (groupname[0])
                            {
                              CreateGroup(grouplist, list, groupname, groupPool);
                              GroupsModified=TRUE;
                            }
                            EnableWindow(win);
                            break;
                          case DELETEGROUP_MENU:
                            DisableWindow(win);
                            if (groupnode = (struct GroupNode *)ChooseGroup(win->LeftEdge+winmsg->MouseX, win->TopEdge+winmsg->MouseY, grouplist, "Delete Group"))
                            {
                              DeleteGroup(groupnode, groupPool);
                              GroupsModified=TRUE;
                            }
                            EnableWindow(win);
                            break;
                          case MODIFYGROUP_MENU:
                            DisableWindow(win);
                            if (groupnode = (struct GroupNode *)ChooseGroup(win->LeftEdge+winmsg->MouseX, win->TopEdge+winmsg->MouseY, grouplist, "Modify Group"))
                            {
                              strcpy(groupname,groupnode->group_node.ln_Name);
                              DeleteGroup(groupnode,groupPool);
                              CreateGroup(grouplist, list, groupname, groupPool);
                              GroupsModified=TRUE;
                            }
                            EnableWindow(win);
                            break;
                          case SELECTGROUP_MENU:
                            DisableWindow(win);
                            groupnode = (struct GroupNode *)ChooseGroup(win->LeftEdge+winmsg->MouseX, win->TopEdge+winmsg->MouseY, grouplist, "Select Group");
                            if (groupnode)
                            {
                              GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                              SelectGroup(list, &(groupnode->enabled));
                              GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);
                            }
                            EnableWindow(win);
                            break;
                        }
                      winmsg->Code = menuitemaddress->NextSelect;
                    }
                    break;
                  case IDCMP_GADGETUP:
                    switch(((struct Gadget *)winmsg->IAddress)->GadgetID)
                    {
                      case SAVE_GAD:
                        SetWindowTitles(win,GetString(STRSaving),(UBYTE *) ~0);
                        SaveChanges(list, prefs);
                        SaveGroups(grouplist);
                        GroupsModified=FALSE;
                        LOOP=FALSE;
                        break;
                      case CANCEL_GAD:
                        LOOP=FALSE;
                        break;
                      case LIST_GAD:
                        z = winmsg->Code;
                        for (n = list->lh_Head; z; z--)
                          n = n->ln_Succ;
                        if (n->ln_Type != TITLE)
                        {
                          if(winmsg->MouseX < my_gads[LIST_GAD]->LeftEdge+xoff) /* checkbox was clicked */
                          {
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                            if (n->ln_Type == EXECUTE)
                              n->ln_Type = STORAGE;
                            else if (n->ln_Type == STORAGE)
                              n->ln_Type = EXECUTE;
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);
                          }
                          else if (winmsg->MouseX > my_gads[LIST_GAD]->LeftEdge+PRIORITYEDGE+2) /* priority clicked */
                          {
                            DisableWindow(win);
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                            oldpriority = n->ln_Pri;
                            n->ln_Pri=GetPriority(n->ln_Pri,win->LeftEdge+winmsg->MouseX,win->TopEdge+winmsg->MouseY);
                            if (oldpriority != n->ln_Pri)
                              if (!prefs->Alphabetize)
                              {
                                Remove(n);
                                Enqueue(list,n);
                              }
                            GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);
                            EnableWindow(win);
                          }
                        }
                        break;
                    }
                    break;
                  case IDCMP_CLOSEWINDOW:
                    LOOP=FALSE;
                    break;
                  case IDCMP_REFRESHWINDOW:
                    BeginRefresh(win);
                    EndRefresh(win, TRUE);
                    break;
                }
                GT_ReplyIMsg(winmsg);
              }
            }  /* End of process window events */

            /*****************************/
            /* Process App Window events */
            /*****************************/
            if (signals & appwinsig)
            {
              while (amsg = (struct AppMessage *) GetMsg(awport))
              {
                int flags=WBSP_ENABLED;

                if (SameLock(amsg->am_ArgList->wa_Lock,prefs->EnabledDirLock)==LOCK_SAME_VOLUME)
                  flags |= WBSP_MOVE;
                flags = CopyWindow(win->LeftEdge+amsg->am_MouseX,win->TopEdge+amsg->am_MouseY,flags);

                if (!(flags & WBSP_CANCEL))
                {
                  argptr = amsg->am_ArgList;
                  for (argnum=0; argnum < amsg->am_NumArgs; argnum++)
                  {
                    SetWindowTitles(win,GetString(STRCopyingFiles),(UBYTE *) ~0);

                    if (sourcename=(char *)AllocVec(500,MEMF_PUBLIC))
                    {
                      int destlen;

                      NameFromLock(argptr->wa_Lock,sourcename,500);
                      AddPart(sourcename,argptr->wa_Name,500);

                      if (flags & WBSP_ENABLED)
                        destlen = strlen(prefs->ExecutePath)+strlen(argptr->wa_Name)+2;
                      else
                        destlen = strlen(prefs->StoragePath)+strlen(argptr->wa_Name)+2;

                      if (destname=(char *)AllocVec(destlen,MEMF_PUBLIC))
                      {
                        strcpy(destname,(flags & WBSP_ENABLED) ? prefs->ExecutePath : prefs->StoragePath);
                        AddPart(destname,argptr->wa_Name,500);

                        /* Copy or Move files */
                        if (flags & WBSP_MOVE)
                        {
                          if (SameLock(amsg->am_ArgList->wa_Lock,prefs->EnabledDirLock)==LOCK_SAME_VOLUME)
                          {
                            /* Move files on same device */
                            struct DiskObject *diskobj;

                            Rename(sourcename,destname);
                            if (diskobj=GetDiskObject(sourcename))
                            {
                              diskobj->do_CurrentX = diskobj->do_CurrentY = NO_ICON_POSITION;
                              if (PutDiskObject(destname,diskobj))
                                DeleteDiskObject(sourcename);
                              FreeDiskObject(diskobj);
                            }
                          }
                          else
                            /* Move files on different devices */
                            if (CopyFile(sourcename,destname))
                            {
                              DeleteFile(sourcename);
                              DeleteDiskObject(sourcename);
                            }
                        }
                        else
                          CopyFile(sourcename,destname);

                        /* Add file to list */
                        GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, ~0, TAG_DONE);
                        AddFileToList(list,prefs->filenamePool,destname, (flags & WBSP_ENABLED) ? EXECUTE : STORAGE, prefs);
                        if (prefs->Alphabetize)
                          AlphabetizeList(list);
                        else
                          SortPriorityList(list);
                        GT_SetGadgetAttrs(my_gads[LIST_GAD], win, NULL, GTLV_Labels, list, TAG_DONE);

                        FreeVec(destname);
                      }
                      FreeVec(sourcename);
                      argptr++;
                    }
                  }
                }
                ReplyMsg((struct Message *)amsg);
                SetWindowTitles(win,WindowTitle,(UBYTE *) ~0);
              }
            }  /* End of Process App Window events */

          }   /* End of while (LOOP) */

          /* Remove AppWindow stuff */
          if (appwin)
            RemoveAppWindow(appwin);
          if (awport)
          {
            while (amsg = (struct AppMessage *)GetMsg(awport))
              ReplyMsg((struct Message *)amsg);
            DeleteMsgPort(awport);
          }

          /* Remove Menu and gadget stuff */
          ClearMenuStrip( win );
          FreeMenus( MainMenu );
          RemoveGList(win,glist,-1);


          /* Notify user if groups were modified. */
          if (GroupsModified)
            if(ShowGroupsRequester())
              SaveGroups(grouplist);


        } /* End of createAllGadgets() */
        FreeGadgets(glist);
        /* Free up the checkmark box object that was made during the listview callbacks */
        CheckBoxList_CallBack_CleanUp(); 
        CloseWindow(win);
      }  /* End of if(OpenWindowTags()) */
      FreeVisualInfo(vi);
    }
    UnlockPubScreen(NULL,scr);
  }
}

BOOL RenderGadgets(struct Gadget **glist, void *vi,
  struct Window *win, struct Gadget *my_gads[], struct List *list,
  struct Gadget *(*CreateGadgetsFunction)(struct Gadget **glistptr, void *vi, struct Window *win, struct Gadget *my_gads[], struct List *list))
{
  /* This function now intializes *glist to NULL, since it must be null when we later call CreateContext() in another function */
  BOOL success=FALSE;
  struct DrawInfo *dri;

  *glist=NULL;  /* This is for security! Do not remove!, otherwise enforcer hits will appear */

  if (dri = GetScreenDrawInfo(win->WScreen))
  {
    /* Fill window with Crosshatch */
    SetAfPt(win->RPort,(UWORD *)&Crosshatch,1);
    SetAPen(win->RPort,dri->dri_Pens[BACKGROUNDPEN]);
    SetBPen(win->RPort,dri->dri_Pens[SHINEPEN]);
    RectFill(win->RPort,win->BorderLeft,win->BorderTop,win->Width-win->BorderRight-1,win->Height-win->BorderBottom-1);
    FreeScreenDrawInfo( win->WScreen, dri );
  }

  if (CreateGadgetsFunction(glist, vi, win, my_gads, list))
  {
    if (dri = GetScreenDrawInfo(win->WScreen))
    {
      /* Fill clear area in window where the listview gadget will be */
      SetAfPt(win->RPort,(UWORD *)&SolidLine,1);
      SetAPen(win->RPort,0);
      RectFill(win->RPort,my_gads[LIST_GAD]->LeftEdge,my_gads[LIST_GAD]->TopEdge,my_gads[LIST_GAD]->LeftEdge+my_gads[LIST_GAD]->Width-1,my_gads[LIST_GAD]->TopEdge+my_gads[LIST_GAD]->Height-1);
      FreeScreenDrawInfo( win->WScreen, dri );
    }

    AddGList(win,*glist,-1,-1,NULL);
    RefreshGList(*glist,win,NULL,-1);
    GT_RefreshWindow(win,NULL);

    success=TRUE;
  }
  return(success);
}


void ShowRequester(STRPTR RequesterText)
{
  struct EasyStruct myRequestStruct={
    sizeof (struct EasyStruct ),
    0,
    "WBStartup+Prefs",
    NULL,
    NULL};

  myRequestStruct.es_GadgetFormat=GetString(STROk);
  myRequestStruct.es_TextFormat=RequesterText;
  EasyRequestArgs(NULL,&myRequestStruct,NULL,NULL);
}

struct Gadget *createAllGadgets(struct Gadget **glistptr, void *vi, struct Window *win, struct Gadget *my_gads[], struct List *list)
{
  struct NewGadget ng;
  struct Gadget *gad;
  ULONG  buttonwidth,maxbuttonwidth;
  ULONG  canceltextwidth,savetextwidth;

  gad = CreateContext(glistptr);

  ng.ng_LeftEdge = win->BorderLeft+OUTSIDEBORDER;
  ng.ng_TopEdge = win->BorderTop+OUTSIDEBORDER;
  ng.ng_Width = win->Width-win->BorderRight-win->BorderLeft-(2*OUTSIDEBORDER);
  ng.ng_Height = win->Height-win->BorderTop-OUTSIDEBORDER-8-win->WScreen->Font->ta_YSize - GADTEXTBORDERSUM - 4 - win->BorderBottom;
  ng.ng_GadgetText = NULL;
  ng.ng_TextAttr = win->WScreen->Font;
  ng.ng_VisualInfo = vi;
  ng.ng_GadgetID = LIST_GAD;
  ng.ng_Flags = 0;

  InitHook(&CBLVHook,CheckBoxList_CallBack,NULL);
  my_gads[LIST_GAD] = gad = CreateGadget(LISTVIEW_KIND,gad,&ng,
      GTLV_Labels,list,
      GTLV_ShowSelected,0,
      GTLV_CallBack,(ULONG) &CBLVHook,
      LAYOUTA_Spacing,2,
      TAG_DONE);


  /* Determine where to draw the vertical line in the listview gadget */
  PRIORITYEDGE = (my_gads[LIST_GAD]->Width * 15/24);

  /* Determine the optimal width of the Save and cancel gadgets */
  canceltextwidth = TextLength(&win->WScreen->RastPort,GetString(STRCancel),strlen(GetString(STRCancel)));
  savetextwidth   = TextLength(&win->WScreen->RastPort,GetString(STRSave),strlen(GetString(STRSave)));
  maxbuttonwidth = 2 * max(canceltextwidth,savetextwidth);
  buttonwidth = min(maxbuttonwidth , ((win->Width - win->BorderLeft - win->BorderRight - (OUTSIDEBORDER*2))/2)-8);

  ng.ng_TopEdge = win->Height - (win->WScreen->Font->ta_YSize + GADTEXTBORDERSUM + 4 + win->BorderBottom);
  ng.ng_Width = buttonwidth;
  ng.ng_Height = win->WScreen->Font->ta_YSize + GADTEXTBORDERSUM;
  ng.ng_GadgetText = GetString(STRSave);
  ng.ng_GadgetID = SAVE_GAD;

  my_gads[SAVE_GAD] = gad = CreateGadget(BUTTON_KIND,gad,&ng,TAG_DONE);

  ng.ng_LeftEdge = win->Width-win->BorderRight-OUTSIDEBORDER-buttonwidth;
  ng.ng_GadgetText = GetString(STRCancel);
  ng.ng_GadgetID = CANCEL_GAD;

  my_gads[CANCEL_GAD] = gad = CreateGadget(BUTTON_KIND,gad,&ng,TAG_DONE);

  return(gad);
}

void RestoreTypes(struct List *filenamequeue)
{
  struct WBSPNode *node;

  for (node=(struct WBSPNode *)filenamequeue->lh_Head ; node->wbsp_node.ln_Succ ; node = (struct WBSPNode *)node->wbsp_node.ln_Succ)
  {
    if (node->wbsp_node.ln_Type != TITLE)
    {
      node->wbsp_node.ln_Type = node->Original_Type;
      node->wbsp_node.ln_Pri = node->Original_Priority;
    }
  }
}

void SaveChanges(struct List *list, struct WBStartupPrefs *prefs)
{
  struct WBSPNode *node;
  char pathname1[500];
  char icon1[500];
  char pathname2[500];
  char icon2[500];
  BOOL DifferentVolumes = FALSE;  // TRUE when the Enabled and Disabled paths are on different volumes

  if (SameLock(prefs->EnabledDirLock, prefs->DisabledDirLock)==LOCK_DIFFERENT)
    DifferentVolumes = TRUE;

  for (node=(struct WBSPNode *)list->lh_Head ; node->wbsp_node.ln_Succ ; node = (struct WBSPNode *)node->wbsp_node.ln_Succ)
  {
    if (node->wbsp_node.ln_Type != TITLE)
    {
      /* Change STARTPRI tooltypes */
      if (node->wbsp_node.ln_Pri != node->Original_Priority)
      {
        if (node->Original_Type == EXECUTE)
          strcpy(pathname1,prefs->ExecutePath);
        else
          strcpy(pathname1,prefs->StoragePath);
        AddPart(pathname1,node->wbsp_node.ln_Name,1100-1);
        ChangePriTooltype(pathname1,node->wbsp_node.ln_Pri);
      }
  
      /* Move files */
      if (node->wbsp_node.ln_Type != node->Original_Type)
      {
        strcpy(pathname1,prefs->ExecutePath);
        AddPart(pathname1,node->wbsp_node.ln_Name,1100-1);
        sprintf(icon1, "%s.info",pathname1 );
  
        strcpy(pathname2,prefs->StoragePath);
        AddPart(pathname2,node->wbsp_node.ln_Name,1100-1);
        sprintf(icon2, "%s.info",pathname2 );

        if (node->wbsp_node.ln_Type == STORAGE)
        {
          if (DifferentVolumes)
          {
            /* Move files on different volumes */
            if (CopyFile(pathname1,pathname2))
            {
              DeleteFile(pathname1);
              DeleteDiskObject(pathname1);
            }
          }
          else
          {
            /* Move files on same volumes */
            Rename(pathname1,pathname2);
            Rename(icon1,icon2);
          }
        }
        else
        {
          if (DifferentVolumes)
          {
            /* Move files on different volumes */
            if (CopyFile(pathname2,pathname1))
            {
              DeleteFile(pathname2);
              DeleteDiskObject(pathname2);
            }
          }
          else
          {
            /* Move files on same volumes */
            Rename(pathname2,pathname1);
            Rename(icon2,icon1);
          }
        }
      }
    }
  }
}


BOOL ChangePriTooltype(char *filename, BYTE pri)
{
  struct DiskObject *dobj;
  char **oldtooltypes;
  char **newtooltypes;
  BOOL success=FALSE;
  char pristr[18];
  int numberoftooltypes=1; /* set it to 1 since we need one for the final NULL */
  char **ptr, **newptr;
  BOOL wrotepri=FALSE;

  if (dobj=GetDiskObjectNew(filename))
  {
    /* count tooltypes */
    for (ptr = dobj->do_ToolTypes; *ptr; ptr++)
      numberoftooltypes++;
    if (!FindToolType(dobj->do_ToolTypes,"STARTPRI"))
      numberoftooltypes++;

    /* Allocate new tooltype pointer list */
    if (newtooltypes = AllocVec(numberoftooltypes * sizeof(char *),MEMF_PUBLIC))
    {
      sprintf(pristr,"STARTPRI=%ld",(long)pri);

      /* Copy tooltype pointers */
      newptr=newtooltypes;
      for (ptr = dobj->do_ToolTypes; *ptr; ptr++)
      {
        if (strncmp(*ptr,"STARTPRI",8))
          *newptr = *ptr;  /* copy argument */
        else
        {
          if (!wrotepri)
          {
            *newptr = &pristr[0];
            wrotepri=TRUE;
          }
          else
            newptr--;
        }
        newptr++;
      }
      if (!wrotepri)
        *newptr++ = &pristr[0];
      *newptr = NULL;

      oldtooltypes = dobj->do_ToolTypes;
      dobj->do_ToolTypes = newtooltypes;

      success = PutDiskObject(filename, dobj);

      dobj->do_ToolTypes = oldtooltypes;

      FreeVec(newtooltypes);
    }
    FreeDiskObject(dobj);
  }

  return(success);
}

void ShowWBIconRequester(struct WBSPNode *node, struct WBStartupPrefs *prefs, struct Screen *scr)
{
  BPTR lock;
  char *dirname;

  if (node->Original_Type == EXECUTE)
    dirname = prefs->ExecutePath;
  else
    dirname = prefs->StoragePath;

  if (lock = Lock(dirname,SHARED_LOCK))
  {
    WBInfo(lock, node->wbsp_node.ln_Name, scr);
    UnLock(lock);
  }
}


void AlphabetizeList(struct List *list)
{
  struct List newlist;
  struct WBSPNode *titlenode, *node, *succ, *pred;

  NewList(&newlist);

  titlenode = (struct WBSPNode *)list->lh_Head;
  RemHead(list);
  while (!IsListEmpty(list))
  {
    node = (struct WBSPNode *)list->lh_Head;
    Remove(node);

    for (succ = (struct WBSPNode *)newlist.lh_Head; succ->wbsp_node.ln_Succ; succ = (struct WBSPNode *)succ->wbsp_node.ln_Succ)
      if( Stricmp(node->wbsp_node.ln_Name, succ->wbsp_node.ln_Name) <0 )
        break;

    if (succ->wbsp_node.ln_Pred == (struct Node *)&newlist)
      pred = NULL;
    else
      pred = (struct WBSPNode *)succ->wbsp_node.ln_Pred;
    Insert(&newlist,node,pred);
  }
  AddHead(&newlist,titlenode);  /* Put title node back in list */
  /* Copy the list structure */
  newlist.lh_TailPred->ln_Succ = (struct Node *)&(list->lh_Tail);
  list->lh_Head = newlist.lh_Head;
  list->lh_Tail = newlist.lh_Tail;
  list->lh_TailPred = newlist.lh_TailPred;
}

ULONG GetScreenHeight(struct Screen *scr)
{
  ULONG height;
  struct Rectangle rect;

  if (QueryOverscan(GetVPModeID(&(scr->ViewPort)), &rect, OSCAN_TEXT))
    height = rect.MaxY - rect.MinY + 1;
  else
    height = scr->WBorTop + (2 * scr->Font->ta_YSize) + 1 + 167 + scr->WBorBottom;
  height = min(height,scr->Height);

  height -= (20 + 20);  /* We want space above and below the window */

  return(height);
}


ULONG BestWindowHeight(struct Screen *scr, struct List *list)
{
  ULONG fontheight, windowheight, screenheight;

  fontheight = scr->Font->ta_YSize;

  /* To calculate the window's bottom border size, I use the same size as the top border. This is because urouhack makes them equal sizes for some odd reason. */
  windowheight = (scr->WBorTop + fontheight + 1) + OUTSIDEBORDER + ListViewHeight(scr,list) + 8 + (fontheight + GADTEXTBORDERSUM) + 4 + (scr->WBorTop + fontheight + 1);
  screenheight = GetScreenHeight(scr);
  windowheight = min(windowheight, screenheight);

  return(windowheight);
}


ULONG ListViewHeight(struct Screen *scr, struct List *list)
{
  struct WBSPNode *node;
  ULONG fontheight;
  ULONG listheight=0;

  fontheight = scr->Font->ta_YSize;

  /* Determine the height of the listview gadget with including all nodes in the list */
  for (node = (struct WBSPNode *)list->lh_Head; node->wbsp_node.ln_Succ; node=(struct WBSPNode *)node->wbsp_node.ln_Succ)
    listheight += fontheight + 2;  /* The 2 is the space between cells */
  listheight += 4; /* top and bottom borders of gadget */

  return(listheight);
}


void SortPriorityList(struct List *list)
{
  struct List newlist;
  struct WBSPNode *titlenode, *node;

  NewList(&newlist);

  titlenode = (struct WBSPNode *)list->lh_Head;
  RemHead(list);
  while (!IsListEmpty(list))
  {
    node = (struct WBSPNode *)list->lh_Head;
    Remove(node);
    Enqueue(&newlist,node);
  }
  AddHead(&newlist,titlenode);  /* Put title node back in list */
  /* Copy the list structure */
  newlist.lh_TailPred->ln_Succ = (struct Node *)&(list->lh_Tail);
  newlist.lh_Head->ln_Pred = (struct Node *)list;
  list->lh_Head = newlist.lh_Head;
  list->lh_Tail = newlist.lh_Tail;
  list->lh_TailPred = newlist.lh_TailPred;
}


void SaveGroups(struct List *list)
{
  BPTR file;
  struct GroupNode *groupnode;
  struct Node *node;

  if (file = Open(PREFSFILE,MODE_NEWFILE))
  {
    for (groupnode=(struct GroupNode *)list->lh_Head ; groupnode->group_node.ln_Succ ; groupnode = (struct GroupNode *)groupnode->group_node.ln_Succ)
    {
      FPutC(file,'@');
      FPuts(file, groupnode->group_node.ln_Name);
      FPutC(file,10);  /* 10 = NEWLINE */
      for (node = groupnode->enabled.lh_Head ; node->ln_Succ ; node = node->ln_Succ)
      {
        FPuts(file, node->ln_Name);
        FPutC(file,10);
      }
    }
    Close(file);
  }
}

void ReadGroups(struct List *list, APTR memPool)
{
  BPTR file;
  struct GroupNode *groupnode;
  struct Node *node;
  char buffer[40];

  NewList(list);
  if (file = Open(PREFSFILE,MODE_OLDFILE))
  {
    while (FGets(file,buffer,40))
    {
      buffer[strlen(buffer)-1]=NULL; /* Remove the NEWLINE character from the end */
      if (buffer[0] == '@')
      {
        if (groupnode=(struct GroupNode *)AllocPooled(memPool,sizeof(struct GroupNode)))
        {
          if (groupnode->group_node.ln_Name=(char *)AllocPooled(memPool,strlen(buffer+1)+1))
          {
            strcpy(groupnode->group_node.ln_Name,buffer+1);
            NewList(&groupnode->enabled);
            AddTail(list,groupnode);
          }
          else
            FreePooled(memPool,groupnode,sizeof(struct GroupNode));
        }
      }
      else  /* Add to group */
      {
        if (node=(struct Node *)AllocPooled(memPool,sizeof(struct Node)))
        {
          if (node->ln_Name=(char *)AllocPooled(memPool,strlen(buffer)+1))
          {
            strcpy(node->ln_Name,buffer);
            AddTail(&groupnode->enabled,node);
          }
          else
            FreePooled(memPool,node,sizeof(struct Node));
        }
      }
    }
    Close(file);
  }
}

void CreateGroup(struct List *grouplist, struct List *wbsplist, char *groupname, APTR memPool)
{
  struct GroupNode *groupnode;
  struct Node *node;
  struct WBSPNode *wbspnode;

  if (groupnode=(struct GroupNode *)AllocPooled(memPool,sizeof(struct GroupNode)))
  {
    if (groupnode->group_node.ln_Name=(char *)AllocPooled(memPool,strlen(groupname)+1))
    {
      strcpy(groupnode->group_node.ln_Name,groupname);
      NewList(&groupnode->enabled);

      for (wbspnode=(struct WBSPNode *)wbsplist->lh_Head ; wbspnode->wbsp_node.ln_Succ ; wbspnode = (struct WBSPNode *)wbspnode->wbsp_node.ln_Succ)
      {
        if (wbspnode->wbsp_node.ln_Type == EXECUTE)
        {
          if (node=(struct Node *)AllocPooled(memPool,sizeof(struct Node)))
          {
            if (node->ln_Name=(char *)AllocPooled(memPool,strlen(wbspnode->wbsp_node.ln_Name)+1))
            {
              strcpy(node->ln_Name,wbspnode->wbsp_node.ln_Name);
              AddTail(&groupnode->enabled,node);
            }
            else
              FreePooled(memPool,node,sizeof(struct Node));
          }
        }
      }
      AddTail(grouplist,groupnode);
    }
    else
      FreePooled(memPool,groupnode,sizeof(struct GroupNode));
  }
}

void SelectGroup(struct List *wbsplist, struct List *subgrouplist)
{
  struct WBSPNode *node;

  for (node = (struct WBSPNode *)wbsplist->lh_Head; node->wbsp_node.ln_Succ; node = (struct WBSPNode *)node->wbsp_node.ln_Succ)
    if (node->wbsp_node.ln_Type != TITLE)
      if (FindName(subgrouplist,node->wbsp_node.ln_Name))
        node->wbsp_node.ln_Type = EXECUTE;
      else
        node->wbsp_node.ln_Type = STORAGE;
}

void DeleteGroup(struct GroupNode *node, APTR memPool)
{
  struct Node *worknode;
  struct Node *nextnode;

  if (node)
  {
    Remove((struct Node *)node);

    FreePooled(memPool,node->group_node.ln_Name,strlen(node->group_node.ln_Name)+1);

    worknode=node->enabled.lh_Head;
    while (nextnode = worknode->ln_Succ)
    {
      FreePooled(memPool,worknode->ln_Name,strlen(worknode->ln_Name)+1);
      FreePooled(memPool,worknode,sizeof(struct Node));
      worknode=nextnode;
    }

    FreePooled(memPool,node,sizeof(struct GroupNode));
  }
}

BOOL ShowGroupsRequester(void)
{
  BOOL save=FALSE;
  struct EasyStruct myRequestStruct={
    sizeof (struct EasyStruct ),
     0,
    "WBStartup+Prefs",
    NULL,
    NULL};

  myRequestStruct.es_GadgetFormat=GetString(STRSaveNoGadget);
  myRequestStruct.es_TextFormat=GetString(STRAskSaveGroups);

  if (EasyRequestArgs(NULL,&myRequestStruct,NULL,NULL))
    save=TRUE;

  return(save);
}


BOOL CopyFile(char *source,char *dest)
{
  /* source and dest should include the path and filename */
  BPTR infile,outfile;
  STRPTR buf=NULL;
  long x;
  struct DiskObject *diskobj;
  BOOL success=TRUE;
  //const long bufsize=512;
  //long numbuf=200;
  const long bufsize=1;
  long numbuf=200*512;

  /* Allocate the buffer used to copy the file */
  while ((buf==NULL) && (numbuf>0))
    if (!(buf=(STRPTR)AllocVec(bufsize*numbuf,MEMF_PUBLIC)))
//      numbuf-=10;
      numbuf/=2;

  /* Copy File */
  if (buf)
  {
    if (infile=Open(source,MODE_OLDFILE))
    {
      if (outfile=Open(dest,MODE_NEWFILE))
      {
        SetVBuf(outfile,NULL,BUF_FULL,bufsize*numbuf);
        do {
          x=FRead(infile,buf,bufsize,numbuf);
          FWrite(outfile,buf,bufsize,x);
        } while(x);

        Close(infile);
      }
      else
        success=FALSE;
      Close(outfile);
    }
    FreeVec(buf);
  }
  else
    success=FALSE;

  /* Copy Icon */
  if (diskobj=GetDiskObject(source))
  {
    diskobj->do_CurrentX = diskobj->do_CurrentY = NO_ICON_POSITION;
    if (!PutDiskObject(dest,diskobj))
      success=FALSE;
    FreeDiskObject(diskobj);
  }

  return(success);
}
