/*Requests:

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


/*  WBStartup+Prefs.c */

#define VERSION   "3.0"
#define DATENUM   __AMIGADATE__
#define DATETEXT  "January 18, 2002"
#define COPYRIGHT "Original copyright © 1996 John Hughes"
#define AUTHOR    "Pavel Fedin"

//#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/imageclass.h>
#include <dos/stdio.h>
#include <libraries/gadtools.h>
#include <libraries/mui.h>
#include <MUI/NList_mcc.h>
#include <MUI/NListview_mcc.h>

/* These add the function prototypes and the #pragmas which reduce the code size */
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/icon.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 <proto/muimaster.h>

#include <math.h>

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

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

char const version[]="\0$VER: WBStartup+Prefs "VERSION"\0";

#define PREFSFILE "ENVARC:WBStartup+"

#define OUTSIDEBORDER 10
#define GADTEXTBORDERSUM 6

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


void GetFilenames(APTR memPool, char *directory, UBYTE type);
struct WBSPNode *AddFileToList(APTR memPool, char *pathname, UBYTE type);
void GetArguments(int argc, char **argv);
void ProcessEvents(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);
void RestoreTypes(void);
void SaveChanges(void);
BOOL ChangePriTooltype(char *filename, BYTE pri);
void ShowWBIconRequester(struct WBSPNode *node,struct Screen *scr);

void SaveGroups(void);
void ReadGroups(APTR memPool);
void CreateGroup(char *groupname, APTR memPool);
void SelectGroup(struct List *list);
void DeleteGroup(APTR memPool);
BOOL Finditem (char *name);
void __asm __saveds ProcessAppMsg(register __a1 ULONG *argsptr);

BOOL InitGUI(void);
void SortList(void);

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 Library *MUIMasterBase;

Object *appobj,*winobj,*lsobj,*saveobj,*cancelobj,*checkoffobj,*checkonobj;
Object *pwinobj,*sliderobj;
Object *swinobj,*strobj;
Object *gwinobj,*glsobj,*gokobj,*gcancelobj;

struct WBStartupPrefs prefs;

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

struct Hook LVDisplayHook={0,0,LVDisplay,0,0};
struct Hook GLVDisplayHook={0,0,GLVDisplay,0,0};
struct Hook LVCompareHook={0,0,LVCompare,0,0};
struct Hook AppMsgHook={0,0,(HOOKFUNC)ProcessAppMsg,0,0};

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

long __stack=16384;

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

  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 (UtilityBase = OpenLibrary("utility.library", 37L))
                {
		 if (MUIMasterBase = OpenLibrary("muimaster.library",11))
		 {
                  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 */
                    {
		     if (InitGUI())
		     {
//                      NewList(&grouplist);
                      GetArguments(argc, argv);

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

                          ReadGroups(prefs.groupPool);
                          ProcessEvents(prefs.groupPool);

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

                      /* 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();

		      MUI_DisposeObject(appobj);
		      MUI_DisposeObject(checkonobj);
		      MUI_DisposeObject(checkoffobj);
		     }
                     DeletePool(prefs.groupPool);
                    }
                    DeletePool(prefs.filenamePool);
	           }
	           CloseLibrary(MUIMasterBase);
                  }
                  CloseLibrary(UtilityBase);
                }
          CloseLibrary(WorkbenchBase);
        }
        else
          ShowRequester(GetString(STRRequiredDosVer));
        CloseLibrary(IconBase);
      }
      CloseLibrary(CxBase);
    }
    CloseLibrary((struct Library *)IntuitionBase);
  }

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

BOOL InitGUI(void)
{
  ULONG TitleMark;
  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, },
  };

  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 (prefs.Alphabetize)
  	TitleMark=MUIV_NList_TitleMark_Down|1;
  else
  	TitleMark=MUIV_NList_TitleMark_Down|2;

  //Create all needed objects
  appobj=MUI_NewObject("Application.mui",MUIA_Application_Author,AUTHOR,
  					 MUIA_Application_Base,"WBSTARTUP+PREFS",
					 MUIA_Application_Copyright,COPYRIGHT,
					 MUIA_Application_Description,"WBStartup+ preferences editor",
					 MUIA_Application_SingleTask,TRUE,
					 MUIA_Application_Title,"WBStartup+Prefs",
					 MUIA_Application_Version,version[1],
					 MUIA_Application_Window,
	winobj=MUI_NewObject("Window.mui",MUIA_Window_AppWindow,TRUE,
					  MUIA_Window_ID,1,
					  MUIA_Window_Title,"WBStartup+ Prefs",
					  MUIA_Window_RootObject,
		MUI_NewObject("Group.mui",MUIA_Group_Child,
			MUI_NewObject("NListview.mcc",MUIA_CycleChain,TRUE,
						      MUIA_NListview_NList,
				lsobj=MUI_NewObject("NList.mcc",MUIA_NList_Format,"BAR,BAR,",
								MUIA_NList_Title,TRUE,
								MUIA_NList_DisplayHook,&LVDisplayHook,
								MUIA_NList_CompareHook,&LVCompareHook,
								MUIA_NList_TitleMark,TitleMark,
				TAG_DONE),
			TAG_DONE),
					  MUIA_Group_Child,
			MUI_NewObject("Group.mui",MUIA_Group_Horiz,TRUE,
						  MUIA_Group_Child,
				saveobj=MUI_NewObject("Text.mui",MUIA_CycleChain,TRUE,
								 MUIA_Frame,MUIV_Frame_Button,
								 MUIA_Background,MUII_ButtonBack,
							         MUIA_InputMode,MUIV_InputMode_RelVerify,
							         MUIA_Text_Contents,GetString(STRSave),
							         MUIA_Text_PreParse,"\33c",
				TAG_DONE),
						  MUIA_Group_Child,
				cancelobj=MUI_NewObject("Text.mui",MUIA_CycleChain,TRUE,
								   MUIA_Frame,MUIV_Frame_Button,
								   MUIA_Background,MUII_ButtonBack,
								   MUIA_InputMode,MUIV_InputMode_RelVerify,
								   MUIA_Text_Contents,GetString(STRCancel),
								   MUIA_Text_PreParse,"\33c",
				TAG_DONE),
			TAG_DONE),
		TAG_DONE),
					MUIA_Window_Menustrip,
		MUI_MakeObject(MUIO_MenustripNM,&MainNewMenu,0),
	TAG_DONE),
					 MUIA_Application_Window,
	gwinobj=MUI_NewObject("Window.mui",MUIA_Window_RefWindow,winobj,
					   MUIA_Window_LeftEdge,MUIV_Window_LeftEdge_Centered,
					   MUIA_Window_TopEdge,MUIV_Window_TopEdge_Centered,
					   MUIA_Window_ID,2,
					   MUIA_Window_RootObject,
		MUI_NewObject("Group.mui",MUIA_Group_Child,
			MUI_NewObject("NListview.mcc",MUIA_CycleChain,TRUE,
						      MUIA_NListview_NList,
				glsobj=MUI_NewObject("NList.mcc",MUIA_NList_DisplayHook,&GLVDisplayHook,
				
				TAG_DONE),
			TAG_DONE),
					  MUIA_Group_Child,
			MUI_NewObject("Group.mui",MUIA_Group_Horiz,TRUE,
						  MUIA_Group_Child,
				gokobj=MUI_NewObject("Text.mui",MUIA_CycleChain,TRUE,
								MUIA_Frame,MUIV_Frame_Button,
								MUIA_Background,MUII_ButtonBack,
							        MUIA_InputMode,MUIV_InputMode_RelVerify,
							        MUIA_Text_Contents,GetString(STROk),
							        MUIA_Text_PreParse,"\33c",
				TAG_DONE),
						  MUIA_Group_Child,
				gcancelobj=MUI_NewObject("Text.mui",MUIA_CycleChain,TRUE,
								    MUIA_Frame,MUIV_Frame_Button,
								    MUIA_Background,MUII_ButtonBack,
								    MUIA_InputMode,MUIV_InputMode_RelVerify,
								    MUIA_Text_Contents,GetString(STRCancel),
								    MUIA_Text_PreParse,"\33c",
				TAG_DONE),
			TAG_DONE),
		TAG_DONE),
	TAG_DONE),
  TAG_DONE);

  // If everything is OK, set up notifications and exit
  if (appobj)
  {
  	if (checkoffobj=MUI_NewObject("Image.mui",MUIA_Image_Spec,MUII_CheckMark,TAG_DONE))
	{
		if (checkonobj=MUI_NewObject("Image.mui",MUIA_Image_Spec,MUII_CheckMark,
							 MUIA_Image_State,IDS_SELECTED,
							 TAG_DONE))
  			{
				  DoMethod(lsobj,MUIM_NList_UseImage,checkoffobj,1,0);
				  DoMethod(lsobj,MUIM_NList_UseImage,checkonobj,2,0);
				  DoMethod(lsobj,MUIM_Notify,MUIA_NList_TitleClick,1,appobj,2,MUIM_Application_ReturnID,9);
				  DoMethod(lsobj,MUIM_Notify,MUIA_NList_TitleClick,2,appobj,2,MUIM_Application_ReturnID,10);
				  DoMethod(winobj,MUIM_Notify,MUIA_Window_CloseRequest,TRUE,appobj,2,MUIM_Application_ReturnID,MUIV_Application_ReturnID_Quit);
				  DoMethod(cancelobj,MUIM_Notify,MUIA_Pressed,FALSE,appobj,2,MUIM_Application_ReturnID,MUIV_Application_ReturnID_Quit);
				  DoMethod(saveobj,MUIM_Notify,MUIA_Pressed,FALSE,appobj,2,MUIM_Application_ReturnID,3);
				  DoMethod(lsobj,MUIM_Notify,MUIA_NList_EntryClick,MUIV_EveryTime,appobj,2,MUIM_Application_ReturnID,1);
				  DoMethod(appobj,MUIM_Notify,MUIA_Application_MenuAction,MUIV_EveryTime,appobj,2,MUIM_Application_ReturnID,2);
				  DoMethod(winobj,MUIM_Notify,MUIA_AppMessage,MUIV_EveryTime,winobj,3,MUIM_CallHook,&AppMsgHook,MUIV_TriggerValue);
				  DoMethod(gwinobj,MUIM_Notify,MUIA_Window_CloseRequest,TRUE,appobj,2,MUIM_Application_ReturnID,7);
				  DoMethod(gcancelobj,MUIM_Notify,MUIA_Pressed,FALSE,appobj,2,MUIM_Application_ReturnID,7);
				  DoMethod(glsobj,MUIM_Notify,MUIA_NList_DoubleClick,MUIV_EveryTime,appobj,2,MUIM_Application_ReturnID,8);
				  DoMethod(gokobj,MUIM_Notify,MUIA_Pressed,FALSE,appobj,2,MUIM_Application_ReturnID,8);
				  return(TRUE);
			}
		MUI_DisposeObject(checkoffobj);
	}
	MUI_DisposeObject(appobj);
  }
  return(FALSE);
}

/* GetFilesnames() is NOT the same function that is in "WBStartup+.c" */
void GetFilenames(APTR memPool, char *directory, UBYTE type)
{
  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 (!Finditem(filename))
              {
                if (Stricmp(filename,"WBStartup+"))  /* Make sure we don't recursively run ourself */
                {
                  strcpy(pathname,directory);
                  AddPart(pathname, filename, 500);

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

      } while (more);

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

BOOL Finditem (char *name)
{
	LONG entry;
	struct WBSPNode *node;
	BOOL found=FALSE;
	for (entry=0;;entry++)
	{
	      DoMethod(lsobj,MUIM_NList_GetEntry,entry,&node);
	      if (!node)
	        break;
	      if (!(stricmp(node->wbsp_name,name)))
              {
		found=TRUE;
		break;
	      }
        }
        return(found);
}


struct WBSPNode *AddFileToList(APTR memPool, char *pathname, UBYTE type)
{
  /* 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_name=(char *)AllocPooled(memPool, strlen(filenameptr)+1))
        {
          strcpy(node->wbsp_name, filenameptr);
          if (toolstring=FindToolType(diskobj->do_ToolTypes,"STARTPRI"))
            node->wbsp_pri = node->Original_Priority = atoi((char *)toolstring);
          else
            node->wbsp_pri = node->Original_Priority = 0;
          node->wbsp_type = node->Original_Type = type;  /* EXECUTE or STORAGE */
	  DoMethod(lsobj,MUIM_NList_InsertSingle,node,MUIV_NList_Insert_Sorted);
          prefs.NumPrograms++;
        }
      }
    }
    FreeDiskObject(diskobj);
  }
  return(node);
}



void GetArguments(int argc, char **argv)
{
  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(APTR groupPool)
{
  ULONG signals;
  BOOL LOOP;

  struct WBSPNode *node;
  struct GroupNode *groupnode;  /* ptr to a node in the group list */
  char *groupname;
  struct Screen *WScreen;

  BOOL GroupsModified=FALSE;

  ULONG returnid,entry,column,menuitemid,newpri,forcequit;

  //Open the GUI and process user actions
  SetAttrs(winobj,MUIA_Window_Open,TRUE,TAG_DONE);
  signals=0;
  LOOP=TRUE;
  while (LOOP)
  {
	  if (signals)
	  	signals=Wait(signals);
	  returnid=DoMethod(appobj,MUIM_Application_NewInput,&signals);
	  switch (returnid)
	  {
		  case MUIV_Application_ReturnID_Quit:
		  	LOOP=FALSE;
			break;
		  case 1:
		  	GetAttr(MUIA_NList_EntryClick,lsobj,&entry);
		  	GetAttr(MUIA_NList_ClickColumn,lsobj,&column);
			DoMethod(lsobj,MUIM_NList_GetEntry,entry,&node);
			switch (column)
			{
				case 0:
					if (node->wbsp_type)
                              			node->wbsp_type = STORAGE;
                            		else
		                                node->wbsp_type = EXECUTE;
			  		DoMethod(lsobj,MUIM_NList_Redraw,entry);
				break;
				case 2:
					GetPriority(node->wbsp_pri);
				break;
			}
		  break;
		  case 2:
		  	GetAttr(MUIA_Application_MenuAction,appobj,&menuitemid);
			switch(menuitemid)
                        {
                          case SAVE_MENU:
			    SetAttrs(winobj,MUIA_Window_Sleep,TRUE,MUIA_Window_Title,GetString(STRSaving),TAG_DONE);
                            SaveChanges();
                            LOOP=FALSE;
                            break;
                          case ABOUT_MENU:
                            {
                              SetAttrs(winobj,MUIA_Window_Sleep,TRUE,TAG_DONE);
			      MUI_Request(appobj,winobj,0,"WBStartup+Prefs",GetString(STROk),GetString(STRAbout),VERSION,COPYRIGHT,DATETEXT);
                              SetAttrs(winobj,MUIA_Window_Sleep,FALSE,TAG_DONE);
                            }
                            break;
                          case QUIT_MENU:
                            LOOP=FALSE;
                            break;
                          case RESTORE_MENU:
                            RestoreTypes();
                            break;
                          case PROGRAMINFO_MENU:
			    SetAttrs(winobj,MUIA_Window_Sleep,TRUE,TAG_DONE);
			    GetAttr(MUIA_NList_Active,lsobj,(ULONG *)&node);
			    if ((ULONG)node!=MUIV_NList_Active_Off)
                            {
				GetAttr(MUIA_Window_Screen,winobj,(ULONG *)&WScreen);
                                ShowWBIconRequester(node,WScreen);
                            }
                            else
                              ShowRequester("You must first select a program!");
			    SetAttrs(winobj,MUIA_Window_Sleep,FALSE,TAG_DONE);
                            break;
                          case SORTALPHABETICALLY_MENU:
                            prefs.Alphabetize = TRUE;
			    SortList();
//			    DoMethod(lsobj,MUIM_NList_Sort);
                            break;
                          case SORTPRIORITY_MENU:
                            prefs.Alphabetize = FALSE;
			    SortList();
//			    DoMethod(lsobj,MUIM_NList_Sort);
                            break;
                          case SELECTALL_MENU:
                            for (entry=0;;entry++)
			    {
			      DoMethod(lsobj,MUIM_NList_GetEntry,entry,&node);
			      if (!node)
			        break;
                              node->wbsp_type = EXECUTE;
		            }
		    	    DoMethod(lsobj,MUIM_NList_Redraw,MUIV_NList_Redraw_All);
                            break;
                          case SELECTNONE_MENU:
                            for (entry=0;;entry++)
			    {
			      DoMethod(lsobj,MUIM_NList_GetEntry,entry,&node);
			      if (!node)
			        break;
                              node->wbsp_type = STORAGE;
		            }
		    	    DoMethod(lsobj,MUIM_NList_Redraw,MUIV_NList_Redraw_All);
                            break;
                          case SELECTTOGGLE_MENU:
                            for (entry=0;;entry++)
			    {
			      DoMethod(lsobj,MUIM_NList_GetEntry,entry,&node);
			      if (!node)
			        break;
			      if (node->wbsp_type == EXECUTE)
                                node->wbsp_type = STORAGE;
                              else
                                node->wbsp_type = EXECUTE;
		            }
		    	    DoMethod(lsobj,MUIM_NList_Redraw,MUIV_NList_Redraw_All);
                            break;
                          case CREATEGROUP_MENU:
			    GetStringWindow(GetString(STRGroupName),30);
                            break;
                          case DELETEGROUP_MENU:
			    ChooseGroup("Delete Group");
                            break;
                          case MODIFYGROUP_MENU:
			    ChooseGroup("Modify Group");
                            break;
                          case SELECTGROUP_MENU:
			    ChooseGroup("Select Group");
                            break;
                        }
		  break;
		  case 3:
			SetAttrs(winobj,MUIA_Window_Sleep,TRUE,MUIA_Window_Title,GetString(STRSaving),TAG_DONE);
                        SaveChanges();
                        LOOP=FALSE;
		  break;
		  case 4:
		  	GetAttr(MUIA_Slider_Level,sliderobj,&newpri);
			SetAttrs(pwinobj,MUIA_Window_Open,FALSE,TAG_DONE);
			DoMethod(appobj,OM_REMMEMBER,pwinobj);
			MUI_DisposeObject(swinobj);
		  	node->wbsp_pri=(int)newpri;
			DoMethod(lsobj,MUIM_NList_Redraw,entry);
			DoMethod(lsobj,MUIM_NList_Sort);
			SetAttrs(winobj,MUIA_Window_Sleep,FALSE,TAG_DONE);
		  break;
		  case 5:
		  	GetAttr(MUIA_String_Contents,strobj,(ULONG *)&groupname);
                        if (groupname[0])
                        {
                          CreateGroup(groupname, groupPool);
                          GroupsModified=TRUE;
                        }
		  case 6:
		  	SetAttrs(swinobj,MUIA_Window_Open,FALSE,TAG_DONE);
			DoMethod(appobj,OM_REMMEMBER,swinobj);
			MUI_DisposeObject(swinobj);
			SetAttrs(winobj,MUIA_Window_Sleep,FALSE,TAG_DONE);
		  break;
		  case 7:
		  	SetAttrs(gwinobj,MUIA_Window_Open,FALSE,TAG_DONE);
			SetAttrs(winobj,MUIA_Window_Sleep,FALSE,TAG_DONE);
		  break;
		  case 8:
		  	SetAttrs(gwinobj,MUIA_Window_Open,FALSE,TAG_DONE);
			switch (menuitemid)
				{
					case DELETEGROUP_MENU:
					  	DeleteGroup(groupPool);
						GroupsModified=TRUE;
					break;
					case SELECTGROUP_MENU:
						DoMethod(glsobj,MUIM_NList_GetEntry,MUIV_NList_GetEntry_Active,&groupnode);
						SelectGroup(&(groupnode->enabled));
						DoMethod(lsobj,MUIM_NList_Redraw,MUIV_NList_Redraw_All);
					break;
					case MODIFYGROUP_MENU:
						DoMethod(glsobj,MUIM_NList_GetEntry,MUIV_NList_GetEntry_Active,&groupnode);
						strcpy(groupname,groupnode->group_name);
						DeleteGroup(groupPool);
						CreateGroup(groupname,groupPool);
						GroupsModified=TRUE;
					break;
				}
			SetAttrs(winobj,MUIA_Window_Sleep,FALSE,TAG_DONE);
		  break;
		  case 9:
		        prefs.Alphabetize = TRUE;
			SortList();
//			DoMethod(lsobj,MUIM_NList_Sort);
                  break;
                  case 10:
                        prefs.Alphabetize = FALSE;
			SortList();
//			DoMethod(lsobj,MUIM_NList_Sort);
		  break;
	  }
  }
  GetAttr(MUIA_Application_ForceQuit,appobj,&forcequit);
  /* Notify user if groups were modified. */
  if (GroupsModified && !forcequit)
    if(ShowGroupsRequester())
      SaveGroups();
}

void SortList()
{
	DoMethod(lsobj,MUIM_NList_Sort);
	if (prefs.Alphabetize)
		SetAttrs(lsobj,MUIA_NList_TitleMark,MUIV_NList_TitleMark_Down|1);
	else
		SetAttrs(lsobj,MUIA_NList_TitleMark,MUIV_NList_TitleMark_Down|2);
}

/*****************************/
/* Process App Window events */
/*****************************/
void __asm __saveds ProcessAppMsg(register __a1 ULONG *argsptr)
{
  struct AppMessage *amsg;
  struct WBArg *argptr;
  LONG argnum;
  char *sourcename,*destname;
  int flags=WBSP_ENABLED;

  SetAttrs(winobj,MUIA_Window_Sleep,TRUE,TAG_DONE);
  amsg=(struct AppMessage *)*argsptr;
   if (SameLock(amsg->am_ArgList->wa_Lock,prefs.EnabledDirLock)==LOCK_SAME_VOLUME)
    flags |= WBSP_MOVE;
  flags = CopyWindow(flags);
   if (!(flags & WBSP_CANCEL))
  {
    SetAttrs(winobj,MUIA_Window_Title,GetString(STRCopyingFiles),TAG_DONE);
    argptr = amsg->am_ArgList;
    for (argnum=0; argnum < amsg->am_NumArgs; argnum++)
    {
       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 */
          AddFileToList(prefs.filenamePool,destname, (flags & WBSP_ENABLED) ? EXECUTE : STORAGE);
          FreeVec(destname);
        }
        FreeVec(sourcename);
        argptr++;
      }
    }
  }
  SetAttrs(winobj,MUIA_Window_Sleep,FALSE,MUIA_Window_Title,"WBStartup+ Prefs",TAG_DONE);
}

void ShowRequester(STRPTR RequesterText)
{
  MUI_Request(appobj,winobj,0,"WBStartup+Prefs",GetString(STROk),RequesterText,0);
}

void RestoreTypes(void)
{
  struct WBSPNode *node;
  LONG entry;

  for (entry=0;;entry++)
  {
    DoMethod(lsobj,MUIM_NList_GetEntry,entry,&node);
    if (!node)
  	break;	  
    node->wbsp_type = node->Original_Type;
    node->wbsp_pri = node->Original_Priority;
  }
  DoMethod(lsobj,MUIM_NList_Sort);
  DoMethod(lsobj,MUIM_NList_Redraw,MUIV_NList_Redraw_All);
}

void SaveChanges(void)
{
  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
  LONG entry;

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

  for (entry=0;;entry++)
  {
    DoMethod(lsobj,MUIM_NList_GetEntry,entry,&node);
    if (!node)
      	break;
      /* Change STARTPRI tooltypes */
      if (node->wbsp_pri != node->Original_Priority)
      {
        if (node->Original_Type == EXECUTE)
          strcpy(pathname1,prefs.ExecutePath);
        else
          strcpy(pathname1,prefs.StoragePath);
        AddPart(pathname1,node->wbsp_name,1100-1);
        ChangePriTooltype(pathname1,node->wbsp_pri);
      }
  
      /* Move files */
      if (node->wbsp_type != node->Original_Type)
      {
        strcpy(pathname1,prefs.ExecutePath);
        AddPart(pathname1,node->wbsp_name,1100-1);
        sprintf(icon1, "%s.info",pathname1 );
  
        strcpy(pathname2,prefs.StoragePath);
        AddPart(pathname2,node->wbsp_name,1100-1);
        sprintf(icon2, "%s.info",pathname2 );

        if (node->wbsp_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 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_name, scr);
    UnLock(lock);
  }
}

void SaveGroups()
{
  BPTR file;
  struct GroupNode *groupnode;
  struct Node *node;
  LONG entry;

  if (file = Open(PREFSFILE,MODE_NEWFILE))
  {
    for (entry=0;;entry++)
    {
      DoMethod(glsobj,MUIM_NList_GetEntry,entry,&groupnode);
      if (!groupnode)
        break;
/*    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_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(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_name=(char *)AllocPooled(memPool,strlen(buffer+1)+1))
          {
            strcpy(groupnode->group_name,buffer+1);
            NewList(&groupnode->enabled);
//            AddTail(list,groupnode);
	    DoMethod(glsobj,MUIM_NList_InsertSingle,groupnode,MUIV_NList_Insert_Bottom);
          }
          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(char *groupname, APTR memPool)
{
  struct GroupNode *groupnode;
  struct Node *node;
  struct WBSPNode *wbspnode;
  ULONG entry;

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

      for (entry=0;;entry++)
      {
	DoMethod(lsobj,MUIM_NList_GetEntry,entry,&wbspnode);
	if (!wbspnode)
	  break;
        if (wbspnode->wbsp_type == EXECUTE)
        {
          if (node=(struct Node *)AllocPooled(memPool,sizeof(struct Node)))
          {
            if (node->ln_Name=(char *)AllocPooled(memPool,strlen(wbspnode->wbsp_name)+1))
            {
              strcpy(node->ln_Name,wbspnode->wbsp_name);
              AddTail(&groupnode->enabled,node);
            }
            else
              FreePooled(memPool,node,sizeof(struct Node));
          }
        }
    }
    DoMethod(glsobj,MUIM_NList_InsertSingle,groupnode,MUIV_NList_Insert_Bottom);
    }
    else
      FreePooled(memPool,groupnode,sizeof(struct GroupNode));
  }
}

void SelectGroup(struct List *subgrouplist)
{
  struct WBSPNode *node;
  ULONG entry;

  for (entry=0;;entry++)
  {
      DoMethod(lsobj,MUIM_NList_GetEntry,entry,&node);
      if (!node)
        break;
      if (FindName(subgrouplist,node->wbsp_name))
        node->wbsp_type = EXECUTE;
      else
        node->wbsp_type = STORAGE;
  }
}

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

    DoMethod(glsobj,MUIM_NList_GetEntry,MUIV_NList_GetEntry_Active,&node);
    DoMethod(glsobj,MUIM_NList_Remove,MUIV_NList_Remove_Active);
    FreePooled(memPool,node->group_name,strlen(node->group_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)
{
  return((BOOL)MUI_Request(appobj,winobj,0,"WBStartup+Prefs",GetString(STRSaveNoGadget),GetString(STRAskSaveGroups),0));
}


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);
}
