/*
 * WBStartup - A (small) WBStartup manager
 * Written by Stephan 'Septh' Schreiber
 * Freeware - use at will
 *
 * There's still a lot of work to do,
 * but I'm already quite happy with this.
 * 
 * (BTW: set your tabs to 4 spaces)
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <exec/types.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/exall.h>
#include <dos/notify.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <graphics/gfxmacros.h>
#include <libraries/gadtools.h>
#include <workbench/workbench.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/utility_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/icon_protos.h>
#include <clib/wb_protos.h>

#include <pragmas/exec_sysbase_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/gadtools_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/icon_pragmas.h>
#include <pragmas/wb_pragmas.h>

#define	DEBUG	1
#include <debug.h>

/*****************************************************************************
 * Macros & constants
 *****************************************************************************/
/* Our default drawers */
#define	ENABLED_DIR		"Sys:WBStartup/WBStartup (Enabled)"
#define	DISABLED_DIR	"Sys:WBStartup/WBStartup (Disabled)"

/* Gadgets IDs */
enum
{	GAD_ENABLED_LV,
	GAD_DISABLED_LV,
	GAD_DISABLE_BUTTON,
	GAD_ENABLE_BUTTON,
	GAD_INFO_BUTTON,
	GAD_START_BUTTON,
	GAD_QUIT_BUTTON,
};

/* Type des tris */
enum
{	SORT_BY_PRIORITY,
	SORT_BY_NAME,
};

/*****************************************************************************
 * Structures & types
 *****************************************************************************/
struct StartupTool
{	struct Node Node;
	UBYTE       Selected;
	UBYTE       StartPri;
	UBYTE       Name[40];
};

#define	STF_SELECTED	(1L<<0)

/*****************************************************************************
 * Global variables
 *****************************************************************************/
const UBYTE *version = "$VER: WBStartup 1.2 (25.11.95)";

extern struct Library   *SysBase;
extern struct Library   *DOSBase;
extern struct WBStartup *_WBenchMsg;

struct Library   *UtilityBase;
struct Library   *IntuitionBase;
struct Library   *GadToolsBase;
struct Library   *GfxBase;
struct Library   *WorkbenchBase;
struct Library   *IconBase;

struct Screen    *scr;
struct DrawInfo  *dri;
APTR              vi;
struct Gadget    *glist;
struct Gadget    *enabled_lv;
struct Gadget    *enabled_str;
struct Gadget    *disabled_lv;
struct Gadget    *disabled_str;
struct Gadget    *disable_button;
struct Gadget    *enable_button;
struct Gadget    *info_button;
struct Gadget    *start_button;
struct Hook       lv_hook;
struct Window    *win;
struct Requester  req;

struct MinList    enabled_list;
LONG              num_enabled;
struct MinList    disabled_list;
LONG              num_disabled;
LONG              sort_type;
LONG              no_icon_position;

struct TextAttr topaz8 = { "topaz.font", 8, 0, 0 };
UBYTE enabled_buf[64];
UBYTE disabled_buf[64];
UBYTE enabled_dir[256];
UBYTE disabled_dir[256];
UBYTE win_title[128];

/*****************************************************************************
 * Protoypes
 *****************************************************************************/
extern void NewList(struct List *list);

/*****************************************************************************
 * Bloque la fenêtre
 *****************************************************************************/
VOID LockWindow(VOID)
{
	InitRequester(&req);
	Request(&req, win);
	SetWindowPointer(win, WA_BusyPointer,  TRUE,
	                      WA_PointerDelay, TRUE,
	                      TAG_DONE);
}

/*****************************************************************************
 * Débloque la fenêtre
 *****************************************************************************/
VOID UnlockWindow(VOID)
{
	EndRequest(&req, win);
	SetWindowPointerA(win, NULL);
}

/*****************************************************************************
 * Show up an EasyRequester
 *****************************************************************************/
LONG ShowReq(UBYTE *text, UBYTE *gadgets, ...)
{
static struct EasyStruct es =
{	sizeof(struct EasyStruct), 0L,
	"WBStartup", NULL, NULL,
};
va_list va;
LONG ret;

	/* Bloque la fenêtre */
	LockWindow();

	/* Affiche le Requester */
	va_start(va, gadgets);

	es.es_TextFormat   = text;
	es.es_GadgetFormat = gadgets;
	ret = EasyRequestArgs(win, &es, NULL, (APTR)va);

	va_end(va);

	/* Débloque la fenêtre */
	UnlockWindow();

	return(ret);
}

/*****************************************************************************
 * Insert a node in a list, in alphabetic order
 *****************************************************************************/
void AddToolNode(struct List *list, struct Node *node)
{
struct Node *n;

	/* Simple security */
	node->ln_Succ = NULL;
	node->ln_Pred = NULL;

	switch(sort_type)
	{	case SORT_BY_PRIORITY:
			Enqueue(list, node);
			break;

		case SORT_BY_NAME:
			/* Go backwards to the preceeding node */
			n = list->lh_TailPred;
			while (n->ln_Pred && (stricmp(n->ln_Name, node->ln_Name) >= 0))
				n = n->ln_Pred;

			/* Insert the new node after its predecessor */
			Insert(list, node, n);
			break;
	}
}

/*****************************************************************************
 * Look for a node, given its ordinal number
 *****************************************************************************/
LONG GetNodeNumber(struct List *list, struct Node *node)
{
register struct Node *n;
LONG number = - 1;

	n = list->lh_Head;
	if (n->ln_Succ)
	{	do
		{	number++;
			if (n == node)
				break;
		} while (n = n->ln_Succ);
	}

	return(number);
}

/*****************************************************************************
 * Search the Nth node of a list
 *****************************************************************************/
struct Node *GetNode(struct List *list, UWORD number)
{
register struct Node *n;

	for (n = list->lh_Head; n->ln_Succ && number ; n = n->ln_Succ, number--)
	{ /* Nothing */ }

	return(number ? NULL : n);
}

/*****************************************************************************
 * Edit a tool's icon
 *****************************************************************************/
VOID EditToolInfo(struct StartupTool *st)
{
struct List *list;
BPTR lock, olddir;
struct NotifyRequest *nr;
LONG signum;
ULONG sig;
UBYTE icon_name[128];
BOOL changed = FALSE;

	/* Bloque la fenêtre */
	LockWindow();

	list = (struct List *)&enabled_list;
	if (FindName(list, st->Node.ln_Name))
		lock = Lock(enabled_dir, SHARED_LOCK);
	else
	{	list = (struct List *)&disabled_list;
		if (FindName(list, st->Node.ln_Name))
			lock = Lock(disabled_dir, SHARED_LOCK);
		else
			lock = NULL;
	}

	if (lock)
	{	olddir = CurrentDir(lock);

		if (nr = AllocVec(sizeof(struct NotifyRequest), MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR))
		{	if ((signum = AllocSignal(-1L)) != -1L)
			{	sprintf(icon_name, "%s.info", st->Node.ln_Name);

				nr->nr_Name                         = icon_name;
				nr->nr_Flags                        = NRF_SEND_SIGNAL;
				nr->nr_stuff.nr_Signal.nr_Task      = FindTask(NULL);
				nr->nr_stuff.nr_Signal.nr_SignalNum = signum;

				if (StartNotify(nr))
				{	WBInfo(lock, st->Node.ln_Name, win->WScreen);

					sig = 1L << signum;
					if (CheckSignal(sig) & sig)
						changed = TRUE;

					EndNotify(nr);
				}
				FreeSignal(signum);
			}
			FreeVec(nr);
		}

		/* Changé ? */
		if (changed)
		{	struct DiskObject *dobj;
			UBYTE *s;
			LONG pri;
			struct Gadget *lv;
			LONG num;

			if (dobj = GetDiskObject(st->Node.ln_Name))
			{	if (s = FindToolType(dobj->do_ToolTypes, "STARTPRI"))
					StrToLong(s, &pri);
				else
					pri = 0;

				if (pri != st->Node.ln_Pri)
				{	lv = ((list == (struct List *)&enabled_list) ? enabled_lv : disabled_lv);

					GT_SetGadgetAttrs(lv, win, NULL, GTLV_Labels, -1L, TAG_DONE);

					Remove((struct Node *)st);
					st->Node.ln_Pri = pri;
					AddToolNode(list, (struct Node *)st);
					num = GetNodeNumber(list, (struct Node *)st);

					GT_SetGadgetAttrs(lv, win, NULL, GTLV_Labels,      list,
					                                 GTLV_Selected,    num,
					                                 GTLV_MakeVisible, num,
					                                 TAG_DONE);
				}
				FreeDiskObject(dobj);
			}
		}

		CurrentDir(olddir);
		UnLock(lock);
	}

	/* Débloque la fenêtre */
	UnlockWindow();
}

/*****************************************************************************
 * Move a file and its icon
 *****************************************************************************/
BOOL MoveTool(struct StartupTool *st, UBYTE *from_dir, UBYTE *to_dir)
{
BPTR from_lock, to_lock;
UBYTE from_name[256], to_name[256], cmd[1024];
struct DiskObject *dobj;
BOOL ok = FALSE;

	if (st)
	{	if (from_lock = Lock(from_dir, SHARED_LOCK))
		{	if (to_lock = Lock(to_dir, SHARED_LOCK))
			{	/* Build source name */
				strcpy(from_name, from_dir);
				AddPart(from_name, st->Node.ln_Name, sizeof(from_name));

				/* Build destination name */
				strcpy(to_name, to_dir);
				AddPart(to_name, st->Node.ln_Name, sizeof(to_name));

				/* Are both file on the save volume? */
				switch (SameLock(from_lock, to_lock))
				{	case LOCK_SAME:
						/* !?! */
						break;

					case LOCK_SAME_VOLUME:
						/* Rename SOURCE as DEST */
						Rename(from_name, to_name);

						/* Also move the icon */
						if (dobj = GetDiskObject(from_name))
						{	DeleteDiskObject(from_name);

							if (no_icon_position)
								dobj->do_CurrentX = dobj->do_CurrentY = NO_ICON_POSITION;

							PutDiskObject(to_name, dobj);

							FreeDiskObject(dobj);

							/* Done */
							ok = TRUE;
						}
						else ShowReq("Can't write icon for\n'%s'\nDos Error %ld", to_name, IoErr());
						break;

					case LOCK_DIFFERENT:
						/* Copy SOURCE to DEST */
						sprintf(cmd, "c:copy <>NIL: FROM %s TO %s CLONE NOREQ", from_name, to_name);
						SystemTagList(cmd, NULL);

						/* Delete the original source file */
						DeleteFile(from_name);

						/* Also move the icon */
						if (dobj = GetDiskObject(from_name))
						{	DeleteDiskObject(from_name);

							if (no_icon_position)
								dobj->do_CurrentX = dobj->do_CurrentY = NO_ICON_POSITION;

							PutDiskObject(to_name, dobj);

							FreeDiskObject(dobj);

							/* Done */
							ok = TRUE;
						}
						else ShowReq("Can't write icon for\n'%s'\nDos Error %ld", to_name, IoErr());
						break;
				}
				UnLock(to_lock);
			}
			UnLock(from_lock);
		}
	}
	return(ok);
}

/*****************************************************************************
 * Disable a tool (ie. move it from enabled_dir to disabled_dir)
 *****************************************************************************/
VOID DisableTool(struct StartupTool *st)
{
	/* First move the executable and its icon */
	if (MoveTool(st, enabled_dir, disabled_dir))
	{	GT_SetGadgetAttrs(enabled_lv,  win, NULL, GTLV_Labels, ~0L, TAG_DONE);
		GT_SetGadgetAttrs(disabled_lv, win, NULL, GTLV_Labels, ~0L, TAG_DONE);

		/* Remove the tool from its list */
		Remove((struct Node *)st);
		num_enabled--;

		/* And put it in the other list */
		AddToolNode((struct List *)&disabled_list, (struct Node *)st);
		num_disabled++;

		/* Update the gadgets */
		sprintf(enabled_buf,  "%ld enabled items",  num_enabled);
		sprintf(disabled_buf, "%ld disabled items", num_disabled);

		GT_SetGadgetAttrs(enabled_lv,     win, NULL, GTLV_Labels,      &enabled_list,
		                                             GTLV_Selected,    ~0L,
		                                             TAG_DONE);

		GT_SetGadgetAttrs(disabled_lv,    win, NULL, GTLV_Labels,      &disabled_list,
		                                             GTLV_Selected,    ~0L,
		                                             GTLV_MakeVisible, GetNodeNumber((struct List *)&disabled_list, (struct Node *)st),
		                                             TAG_DONE);

		GT_SetGadgetAttrs(enabled_str,    win, NULL, GTTX_Text,   &enabled_buf[0],  TAG_DONE);
		GT_SetGadgetAttrs(disabled_str,   win, NULL, GTTX_Text,   &disabled_buf[0], TAG_DONE);
		GT_SetGadgetAttrs(enable_button,  win, NULL, GA_Disabled, TRUE, TAG_DONE);
		GT_SetGadgetAttrs(disable_button, win, NULL, GA_Disabled, TRUE, TAG_DONE);
		GT_SetGadgetAttrs(info_button,    win, NULL, GA_Disabled, TRUE, TAG_DONE);
		//GT_SetGadgetAttrs(start_button,   win, NULL, GA_Disabled, TRUE, TAG_DONE);
	}
}

/*****************************************************************************
 * Enable a tool (ie. move it from disabled_dir to enabled_dir)
 *****************************************************************************/
VOID EnableTool(struct StartupTool *st)
{
	if (MoveTool(st, disabled_dir, enabled_dir))
	{	GT_SetGadgetAttrs(enabled_lv,  win, NULL, GTLV_Labels, ~0L, TAG_DONE);
		GT_SetGadgetAttrs(disabled_lv, win, NULL, GTLV_Labels, ~0L, TAG_DONE);

		/* Remove the tool from its list */
		Remove((struct Node *)st);
		num_disabled--;

		/* And put it in the other list */
		AddToolNode((struct List *)&enabled_list, (struct Node *)st);
		num_enabled++;

		/* Update the gadgets */
		sprintf(enabled_buf,  "%ld enabled items",  num_enabled);
		sprintf(disabled_buf, "%ld disabled items", num_disabled);

		GT_SetGadgetAttrs(enabled_lv,     win, NULL, GTLV_Labels,      &enabled_list,
		                                             GTLV_Selected,    ~0L,
		                                             GTLV_MakeVisible, GetNodeNumber((struct List *)&enabled_list, (struct Node *)st),
		                                             TAG_DONE);

		GT_SetGadgetAttrs(disabled_lv,    win, NULL, GTLV_Labels,   &disabled_list,
		                                             GTLV_Selected, ~0L,
		                                             TAG_DONE);

		GT_SetGadgetAttrs(enabled_str,    win, NULL, GTTX_Text,   &enabled_buf[0],  TAG_DONE);
		GT_SetGadgetAttrs(disabled_str,   win, NULL, GTTX_Text,   &disabled_buf[0], TAG_DONE);
		GT_SetGadgetAttrs(enable_button,  win, NULL, GA_Disabled, TRUE, TAG_DONE);
		GT_SetGadgetAttrs(disable_button, win, NULL, GA_Disabled, TRUE, TAG_DONE);
		GT_SetGadgetAttrs(info_button,    win, NULL, GA_Disabled, TRUE, TAG_DONE);
		//GT_SetGadgetAttrs(start_button,   win, NULL, GA_Disabled, TRUE, TAG_DONE);
	}
}

/*****************************************************************************
 * Main event loop
 *****************************************************************************/
VOID MainLoop(VOID)
{
UWORD esel, dsel;
ULONG secs, mics;
struct StartupTool *st;
BOOL fini;
struct IntuiMessage *imsg, im;

	esel = (UWORD)~0;
	dsel = (UWORD)~0;
	secs = 0L;
	mics = 0L;
	st   = NULL;
	fini = FALSE;
	do
	{	WaitPort(win->UserPort);
		while (imsg = GT_GetIMsg(win->UserPort))
		{	im = *imsg;
			GT_ReplyIMsg(imsg);
			imsg = &im;

			switch(imsg->Class)
			{	case IDCMP_CLOSEWINDOW:
					fini = TRUE;
					break;

				case IDCMP_REFRESHWINDOW:
					GT_BeginRefresh(win);
					GT_EndRefresh(win, TRUE);
					break;

				case IDCMP_VANILLAKEY:
					if (imsg->Code == 27)
						fini = TRUE;
					break;

				case IDCMP_GADGETUP:
					switch(((struct Gadget *)imsg->IAddress)->GadgetID)
					{	case GAD_ENABLED_LV:
							if ((esel == imsg->Code) && DoubleClick(secs, mics, imsg->Seconds, imsg->Micros))
							{	/* Edit tooltypes on double-click */
								EditToolInfo(st);
								esel = GetNodeNumber((struct List *)&enabled_list, (struct Node *)st);
							}
							else
							{	esel = imsg->Code;
								dsel = (UWORD)~0;
								secs = imsg->Seconds;
								mics = imsg->Micros;

								st = (struct StartupTool *)GetNode((struct List *)&enabled_list, esel);

								/* Update the gadgets */
								GT_SetGadgetAttrs(disabled_lv,    win, NULL, GTLV_Selected, ~0,    TAG_DONE);
								GT_SetGadgetAttrs(enable_button,  win, NULL, GA_Disabled,   TRUE,  TAG_DONE);
								GT_SetGadgetAttrs(disable_button, win, NULL, GA_Disabled,   FALSE, TAG_DONE);
								GT_SetGadgetAttrs(info_button,    win, NULL, GA_Disabled,   FALSE, TAG_DONE);
								//GT_SetGadgetAttrs(start_button,   win, NULL, GA_Disabled,   TRUE,  TAG_DONE);
							}
							break;

						case GAD_DISABLED_LV:
							if ((dsel == imsg->Code) && DoubleClick(secs, mics, imsg->Seconds, imsg->Micros))
							{	/* Edit tooltypes on double-click -- not implemented */
								EditToolInfo(st);
								dsel = GetNodeNumber((struct List *)&disabled_list, (struct Node *)st);
							}
							else
							{	esel = (UWORD)~0;
								dsel = imsg->Code;
								secs = imsg->Seconds;
								mics = imsg->Micros;

								st = (struct StartupTool *)GetNode((struct List *)&disabled_list, dsel);

								/* Update the gadgets */
								GT_SetGadgetAttrs(enabled_lv,     win, NULL, GTLV_Selected, ~0,    TAG_DONE);
								GT_SetGadgetAttrs(enable_button,  win, NULL, GA_Disabled,   FALSE, TAG_DONE);
								GT_SetGadgetAttrs(disable_button, win, NULL, GA_Disabled,   TRUE,  TAG_DONE);
								GT_SetGadgetAttrs(info_button,    win, NULL, GA_Disabled,   FALSE, TAG_DONE);
								//GT_SetGadgetAttrs(start_button,   win, NULL, GA_Disabled,   FALSE, TAG_DONE);
							}
							break;

						case GAD_DISABLE_BUTTON:
							DisableTool(st);
							st = NULL;
							break;

						case GAD_ENABLE_BUTTON:
							EnableTool(st);
							st = NULL;
							break;

						case GAD_START_BUTTON:
							/* Start the tool -- not implemented */
							break;

						case GAD_INFO_BUTTON:
							/* Edit the tool's icon */
							EditToolInfo(st);

							if (esel != (UWORD)~0)
								esel = GetNodeNumber((struct List *)&enabled_list, (struct Node *)st);
							else
								dsel = GetNodeNumber((struct List *)&disabled_list, (struct Node *)st);
							break;

						case GAD_QUIT_BUTTON:
							fini = TRUE;
							break;
					}
					break;
			}
		}
	} while (!fini);
}

/*****************************************************************************
 * The GTLV_CallBack hook
 *****************************************************************************/
ULONG __asm __saveds GTLVCallBack(register __a0 struct Hook *hook,
                                  register __a2 struct StartupTool *st,
                                  register __a1 struct LVDrawMsg *lvdmsg)
{
static UWORD ghost_pattern[] = { 0x4444, 0x1111 };
UBYTE pri[10];
LONG apen, bpen, pl, pw, w, h, dy, len;
struct TextExtent te;

	if (lvdmsg->lvdm_MethodID == LV_DRAW)
	{	switch(lvdmsg->lvdm_State)
		{	case LVR_NORMAL:
			case LVR_NORMALDISABLED:
				apen = lvdmsg->lvdm_DrawInfo->dri_Pens[TEXTPEN];
				bpen = lvdmsg->lvdm_DrawInfo->dri_Pens[BACKGROUNDPEN];
				break;

			case LVR_SELECTED:
			case LVR_SELECTEDDISABLED:
				apen = lvdmsg->lvdm_DrawInfo->dri_Pens[FILLTEXTPEN];
				bpen = lvdmsg->lvdm_DrawInfo->dri_Pens[FILLPEN];
				break;
		}

		/* Efface le fond */
		SetAPen(lvdmsg->lvdm_RastPort, bpen);
		RectFill(lvdmsg->lvdm_RastPort, lvdmsg->lvdm_Bounds.MinX, lvdmsg->lvdm_Bounds.MinY,
		                                lvdmsg->lvdm_Bounds.MaxX, lvdmsg->lvdm_Bounds.MaxY);

		/* Calcule la largeur de la priorité */
		pl = sprintf(pri, "%ld", st->Node.ln_Pri);
		pw = TextLength(lvdmsg->lvdm_RastPort, pri, pl);

		/* Calcule la largeur de la ligne */
		w = (UWORD)((lvdmsg->lvdm_Bounds.MaxX - lvdmsg->lvdm_Bounds.MinX + 1) - pw - 8);

		/* Calcule la hauteur totale de la ligne */
		h = (UWORD)(lvdmsg->lvdm_Bounds.MaxY - lvdmsg->lvdm_Bounds.MinY + 1);

		/* Centre la ligne verticalement */
		dy = (UWORD)(((h - lvdmsg->lvdm_RastPort->Font->tf_YSize) / 2) + lvdmsg->lvdm_RastPort->Font->tf_Baseline);

		/* Trouve le nombre maximum de caractères à dessiner */
		len = TextFit(lvdmsg->lvdm_RastPort, st->Node.ln_Name, strlen(st->Node.ln_Name), &te, NULL, 1L, w, h);

		/* Dessine le texte */
		SetAPen(lvdmsg->lvdm_RastPort, apen);
		SetDrMd(lvdmsg->lvdm_RastPort, JAM1);

		Move(lvdmsg->lvdm_RastPort, lvdmsg->lvdm_Bounds.MinX + 4, lvdmsg->lvdm_Bounds.MinY + dy);
		Text(lvdmsg->lvdm_RastPort, st->Node.ln_Name, len);

		/* Dessine le texte de la priorité */
		Move(lvdmsg->lvdm_RastPort, lvdmsg->lvdm_Bounds.MaxX - pw - 4, lvdmsg->lvdm_Bounds.MinY + dy);
		Text(lvdmsg->lvdm_RastPort, pri, pl);

		/* Le gadget est inhibé? */
		if ((lvdmsg->lvdm_State == LVR_NORMALDISABLED) || (lvdmsg->lvdm_State == LVR_SELECTEDDISABLED))
		{	SetAPen(lvdmsg->lvdm_RastPort, lvdmsg->lvdm_DrawInfo->dri_Pens[BLOCKPEN]);
			SetAfPt(lvdmsg->lvdm_RastPort, ghost_pattern, 1L);
			RectFill(lvdmsg->lvdm_RastPort, lvdmsg->lvdm_Bounds.MinX, lvdmsg->lvdm_Bounds.MinY,
			                                lvdmsg->lvdm_Bounds.MaxX, lvdmsg->lvdm_Bounds.MaxY);
			SetAfPt(lvdmsg->lvdm_RastPort, NULL, 0);
		}

		return(LVCB_OK);
	}

	return(LVCB_UNKNOWN);
}

/*****************************************************************************
 * Create the gadgets - no font sensitivity, use old good Topaz/8...
 *****************************************************************************/
BOOL CreateGadgets(VOID)
{
struct NewGadget ng;
struct Gadget *g;
WORD ox, oy;
BOOL ok = FALSE;

	glist = NULL;
	if (g = CreateContext(&glist))
	{	ox = scr->WBorLeft + 5;
		oy = scr->BarHeight + 1 + 20;

		lv_hook.h_Entry    = (ULONG (*)())GTLVCallBack;
		lv_hook.h_SubEntry = NULL;
		lv_hook.h_Data     = NULL;

		/* ListView 'enabled' */
		ng.ng_LeftEdge   = ox;
		ng.ng_TopEdge    = oy;
		ng.ng_Width      = 250;
		ng.ng_Height     = 130;
		ng.ng_GadgetText = "Enabled Tools";
		ng.ng_TextAttr   = &topaz8;
		ng.ng_GadgetID   = GAD_ENABLED_LV;
		ng.ng_Flags      = PLACETEXT_ABOVE|NG_HIGHLABEL;
		ng.ng_VisualInfo = vi;
		ng.ng_UserData   = NULL;
		g = enabled_lv  = CreateGadget(LISTVIEW_KIND, g, &ng, GTLV_ShowSelected, NULL,
		                                                      GTLV_CallBack,    &lv_hook,
		                                                      GT_Underscore,     '_',
		                                                      LAYOUTA_Spacing,   2L,
		                                                      TAG_DONE);

		ng.ng_TopEdge   += 130;
		ng.ng_Height     = 14;
		ng.ng_GadgetText = NULL;
		g = enabled_str = CreateGadget(TEXT_KIND, g, &ng, GTTX_Border, TRUE,
		                                                  TAG_DONE);

		/* ListView 'disabled' */
		ng.ng_LeftEdge  += 360;
		ng.ng_TopEdge    = oy;
		ng.ng_Height     = 130;
		ng.ng_GadgetText = "Disabled Tools";
		ng.ng_GadgetID   = GAD_DISABLED_LV;
		g = disabled_lv = CreateGadget(LISTVIEW_KIND, g, &ng, GTLV_ShowSelected, NULL,
		                                                      GTLV_CallBack,    &lv_hook,
		                                                      GT_Underscore,     '_',
		                                                      LAYOUTA_Spacing,   2L,
		                                                      TAG_DONE);

		ng.ng_TopEdge   += 130;
		ng.ng_Height     = 14;
		ng.ng_GadgetText = NULL;
		g = disabled_str = CreateGadget(TEXT_KIND, g, &ng, GTTX_Border, TRUE,
		                                                   TAG_DONE);

		/* Bouton 'Disable ->' */
		ng.ng_LeftEdge   = ox + 260;
		ng.ng_TopEdge    = oy;
		ng.ng_Width      = 90;
		ng.ng_Height     = 14;
		ng.ng_GadgetText = "_Disable ->";
		ng.ng_GadgetID   = GAD_DISABLE_BUTTON;
		ng.ng_Flags      = PLACETEXT_IN;
		g = disable_button = CreateGadget(BUTTON_KIND, g, &ng, GT_Underscore, '_',
		                                                       GA_Disabled,   TRUE,
		                                                       TAG_DONE);

		/* Bouton '<- Enable' */
		ng.ng_TopEdge   += 20;
		ng.ng_GadgetText = "<- _Enable";
		ng.ng_GadgetID   = GAD_ENABLE_BUTTON;
		g = enable_button = CreateGadget(BUTTON_KIND, g, &ng, GT_Underscore, '_',
		                                                      GA_Disabled,   TRUE,
		                                                      TAG_DONE);

		/* Bouton 'Info...' */
		ng.ng_TopEdge   += 20;
		ng.ng_GadgetText = "_Info...";
		ng.ng_GadgetID   = GAD_INFO_BUTTON;
		g = info_button = CreateGadget(BUTTON_KIND, g, &ng, GT_Underscore, '_',
		                                                    GA_Disabled,   TRUE,
		                                                    TAG_DONE);

		/* Bouton 'Start' */
		ng.ng_TopEdge   += 20;
		ng.ng_GadgetText = "_Start";
		ng.ng_GadgetID   = GAD_START_BUTTON;
		g = start_button = CreateGadget(BUTTON_KIND, g, &ng, GT_Underscore, '_',
		                                                     GA_Disabled,   TRUE,
		                                                     TAG_DONE);

		/* Bouton 'Quit' */
		ng.ng_TopEdge    = oy + 130;
		ng.ng_GadgetText = "_Quit";
		ng.ng_GadgetID   = GAD_QUIT_BUTTON;
		g = CreateGadget(BUTTON_KIND, g, &ng, GT_Underscore, '_',
		                                      TAG_DONE);

		/* Ok ? */
		if (g)
			ok = TRUE;
	}

	/* Retourne TRUE ou FALSE */
	return(ok);
}

/*****************************************************************************
 * Free a list of tools
 *****************************************************************************/
VOID FreeTools(struct List *list)
{
struct StartupTool *st;

	while (st = (struct StartupTool *)RemTail(list))
		FreeVec(st);
}

/*****************************************************************************
 * Scan a directory of tools
 *****************************************************************************/
LONG ScanTools(UBYTE *path, struct List *list)
{
BPTR lock, olddir;
UBYTE pat[32];
struct ExAllControl *eac;
struct ExAllData *ead;
BOOL more;
UBYTE eadata[1024], name[128], *s;
struct DiskObject *dobj;
struct StartupTool *st;
LONG pri, num = -1;

	sprintf(win_title, "Scanning '%s'...", path);
	SetWindowTitles(win, win_title, (UBYTE *)-1L);

	if ((lock = Lock(path, SHARED_LOCK)) == NULL)
	{	if (IoErr() == ERROR_OBJECT_NOT_FOUND)
		{	switch(ShowReq("Directory '%s'\ndoes not exist.\nDo you want to create it?", "Yes|No", path))
			{	case 1:	// Yes
					if (lock = CreateDir(path))
					{	/* Add an icon, the same as 'Sys:WBStartup' (if possible) */
						if (dobj = GetDiskObjectNew("Sys:WBStartup"))
						{	if (dobj->do_Type != WBDRAWER)
							{	FreeDiskObject(dobj);
								dobj = GetDefDiskObject(WBDRAWER);
							}

							if (dobj)
							{	dobj->do_CurrentX = dobj->do_CurrentY = NO_ICON_POSITION;

								PutDiskObject(path, dobj);
								FreeDiskObject(dobj);
							}
							else ShowReq("Can't create icon for\n'%s'\n", "Continue", path);
						}
						else ShowReq("Can't create icon for\n'%s'\n", "Continue", path);
					}
					else ShowReq("Can't create directory\n'%s'\nDos Error %ld\n", "Cancel", path, IoErr());
					break;

				case 0:	// No
					break;
			}
		}
		else ShowReq("Can't access directory\n'%s'\nDos Error %ld\n", "Cancel", path, IoErr());
	}

	if (lock)
	{	olddir = CurrentDir(lock);

		/* Parse our pattern */
		ParsePatternNoCase("#?.info", pat, sizeof(pat));

		if (eac = AllocDosObject(DOS_EXALLCONTROL, NULL))
		{	eac->eac_LastKey     = NULL;
			eac->eac_MatchString = pat;
			eac->eac_MatchFunc   = NULL;

			num = 0;

			do
			{	more = ExAll(lock, (struct ExAllData *)eadata, sizeof(eadata), ED_NAME, eac);

				if ((!more) && (IoErr() != ERROR_NO_MORE_ENTRIES))
				{	/* Erreur */
					break;
				}

				if (eac->eac_Entries == 0)
				{	/* Fin normale, mais pas d'entrées */
					continue;
				}

				/* Ajoute la ou les entrées */
				ead = (struct ExAllData *)eadata;
				do
				{	/* Copie le nom, sans le '.info' */
					s = stpcpy(name, ead->ed_Name);	// stpcpy() retourne la FIN de la chaîne
					s[-5] = '\0';

					/* Lit l'icône */
					if (dobj = GetDiskObject(name))
					{	/* Est-ce un OUTIL ou un PROJET ? */
						if ((dobj->do_Type == WBTOOL) || (dobj->do_Type == WBPROJECT))
						{	/* Ajoute un noeud à la liste */
							if (st = AllocVec(sizeof(struct StartupTool), MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR))
							{	st->Node.ln_Name = &st->Name[0];
								strcpy(st->Node.ln_Name, name);

								/* Trouve la priorité */
								if ((s = FindToolType(dobj->do_ToolTypes, "STARTPRI")) && StrToLong(s, &pri))
									st->Node.ln_Pri = pri;

								AddToolNode(list, (struct Node *)st);

								num++;
							}
						}
						FreeDiskObject(dobj);
					}

					/* Passe à l'entrée suivante */
					ead = ead->ed_Next;
				} while (ead);
			} while (more);

			FreeDosObject(DOS_EXALLCONTROL, eac);
		}
		CurrentDir(olddir);
		UnLock(lock);
	}

	/* Return the number of tools read, or -1 on error */
	return(num);
}

/*****************************************************************************
 * Get the arguments
 *****************************************************************************/
#define	TEMPLATE	"ENABLED_DIR/K,DISABLED_DIR/K,SORT/K,NO_ICON_POSITION/S"
enum
{	OPT_ENABLED_DIR,
	OPT_DISABLED_DIR,
	OPT_SORT,
	OPT_NO_ICON_POSITION,
	NUM_OPTS
};

BOOL GetArgs(VOID)
{
BOOL ok = TRUE;

	/* Set the defaults */
	strcpy(enabled_dir,  ENABLED_DIR);
	strcpy(disabled_dir, DISABLED_DIR);
	sort_type = SORT_BY_PRIORITY;
	no_icon_position = FALSE;

	if (_WBenchMsg)
	{	BPTR olddir;
		struct DiskObject *dobj;
		UBYTE *s;

		olddir = CurrentDir(_WBenchMsg->sm_ArgList->wa_Lock);

		/* Read our ToolTypes */
		if (dobj = GetDiskObject(_WBenchMsg->sm_ArgList->wa_Name))
		{	if (s = FindToolType(dobj->do_ToolTypes, "ENABLED_DIR"))
				strcpy(enabled_dir, s);

			if (s = FindToolType(dobj->do_ToolTypes, "DISABLED_DIR"))
				strcpy(disabled_dir, s);

			if (s = FindToolType(dobj->do_ToolTypes, "SORT"))
			{	if (MatchToolValue(s, "PRIORITY"))
					sort_type = SORT_BY_PRIORITY;
				else if (MatchToolValue(s, "NAME"))
					sort_type = SORT_BY_NAME;
			}

			if (s = FindToolType(dobj->do_ToolTypes, "NO_ICON_POSITION"))
				no_icon_position = TRUE;

			FreeDiskObject(dobj);
		}
		CurrentDir(olddir);
	}
	else
	{	ULONG opts[NUM_OPTS];
		struct RDArgs *rda;

		memset(opts, 0, sizeof(opts));
		rda = ReadArgs(TEMPLATE, opts, NULL);

		/* Check for Ctrl-C break */
		if (CheckSignal(SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
		{	if (rda)
			{	FreeArgs(rda);
				rda = NULL;
			}
			SetIoErr(ERROR_BREAK);
			ok = FALSE;		// Tell caller to quit
		}

		if (rda)
		{	if (opts[OPT_ENABLED_DIR])
				strcpy(enabled_dir, (UBYTE *)opts[OPT_ENABLED_DIR]);

			if (opts[OPT_DISABLED_DIR])
				strcpy(disabled_dir, (UBYTE *)opts[OPT_DISABLED_DIR]);

			if (opts[OPT_SORT])
			{	if (stricmp((UBYTE *)opts[OPT_SORT], "PRIORITY"))
					sort_type = SORT_BY_PRIORITY;
				else if (stricmp((UBYTE *)opts[OPT_SORT], "NAME"))
					sort_type = SORT_BY_NAME;
			}

			if (opts[OPT_NO_ICON_POSITION])
				no_icon_position = TRUE;

			FreeArgs(rda);
		}
		else PrintFault(IoErr(), NULL);
	}

	/* Return FALSE if Ctrl-C was hit during ReadArgs() */
	return(ok);
}

/*****************************************************************************
 * Program entry point
 *****************************************************************************/
void main(void)
{
struct IBox zoom;

	/* Open the used libraries */
	if (UtilityBase = OpenLibrary("utility.library", 39L))
	{	if (IntuitionBase = OpenLibrary("intuition.library", 39L))
		{	if (GadToolsBase = OpenLibrary("gadtools.library", 39L))
			{	if (GfxBase = OpenLibrary("graphics.library", 39L))
				{	if (WorkbenchBase = OpenLibrary("workbench.library", 39L))
					{	if (IconBase = OpenLibrary("icon.library", 39L))
						{	/* Read the arguments, stop if Ctrl-C is hit */
							if (GetArgs())
							{	/* Open the window */
								if ((scr = LockPubScreen("FRONTPUBSCREEN")) == NULL)
									scr = LockPubScreen(NULL);
								if (scr)
								{	if (dri = GetScreenDrawInfo(scr))
									{	if (vi = GetVisualInfoA(scr, NULL))
										{	if (CreateGadgets())
											{	/* Setup our alternate window position */
												zoom.Left   = -1;
												zoom.Top    = -1;
												zoom.Width  = 150;
												zoom.Height = scr->BarHeight + 1;

												if (win = OpenWindowTags(NULL, WA_Left,         0,
												                               WA_Top,          scr->BarHeight + 1,
												                               WA_InnerWidth,   620L,
												                               WA_InnerHeight,  169L,
												                               WA_AutoAdjust,   TRUE,
												                               WA_MinWidth,     zoom.Width,
												                               WA_MinHeight,    zoom.Height,
												                               WA_Zoom,        &zoom,
												                               WA_Flags,        WFLG_CLOSEGADGET|WFLG_DRAGBAR|WFLG_DEPTHGADGET|
												                                                WFLG_SIMPLE_REFRESH|
												                                                WFLG_RMBTRAP|WFLG_NEWLOOKMENUS|WFLG_ACTIVATE,
												                               WA_IDCMP,        IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|
												                                                IDCMP_VANILLAKEY|
												                                                LISTVIEWIDCMP|BUTTONIDCMP,
												                               WA_PubScreen,    scr,
												                               WA_Title,        "WBStartup",
												                               WA_ScreenTitle,  "WBStartup - ©1995 by RingarSoft, Inc.",
												                               WA_Gadgets,      glist,
												                               WA_BusyPointer,  TRUE,
												                               WA_PointerDelay, TRUE,
												                               TAG_DONE))
												{	GT_RefreshWindow(win, NULL);

													/* Initialize our lists */
													NewList((struct List *)&enabled_list);
													NewList((struct List *)&disabled_list);

													/* Scan both source and destination drawers */
													if ((num_enabled  = ScanTools(enabled_dir,  (struct List *)&enabled_list)) >= 0)
													{	if ((num_disabled = ScanTools(disabled_dir, (struct List *)&disabled_list)) >= 0)
														{	/* Reset the window's title */
															SetWindowTitles(win, "WBStartup", (UBYTE *)-1L);

															/* Update the gadgets */
															sprintf(enabled_buf,  "%ld enabled tools",  num_enabled);
															sprintf(disabled_buf, "%ld disabled tools", num_disabled);

															GT_SetGadgetAttrs(enabled_lv,   win, NULL, GTLV_Labels, &enabled_list,    TAG_DONE);
															GT_SetGadgetAttrs(enabled_str,  win, NULL, GTTX_Text,   &enabled_buf[0],  TAG_DONE);
															GT_SetGadgetAttrs(disabled_lv,  win, NULL, GTLV_Labels, &disabled_list,   TAG_DONE);
															GT_SetGadgetAttrs(disabled_str, win, NULL, GTTX_Text,   &disabled_buf[0], TAG_DONE);

															ClearPointer(win);

															/* Handle events */
															MainLoop();
														}
													}

													/* Clear the gadgets */
													GT_SetGadgetAttrs(enabled_lv,  win, NULL, GTLV_Labels, ~0L, TAG_DONE);
													GT_SetGadgetAttrs(disabled_lv, win, NULL, GTLV_Labels, ~0L, TAG_DONE);

													/* Free the scanned lists of tools */
													FreeTools((struct List *)&disabled_list);
													FreeTools((struct List *)&enabled_list);

													/* Close the window */
													CloseWindow(win);
												}
												/* Free the gadgets */
												FreeGadgets(glist);
											}
											FreeVisualInfo(vi);
										}
										FreeScreenDrawInfo(scr, dri);
									}
									UnlockPubScreen(NULL, scr);
								}
							}
							/* Close the libraries */
							CloseLibrary(IconBase);
						}
						CloseLibrary(WorkbenchBase);
					}
					CloseLibrary(GfxBase);
				}
				CloseLibrary(GadToolsBase);
			}
			CloseLibrary(IntuitionBase);
		}
		CloseLibrary(UtilityBase);
	}
}
