[comp.sys.amiga] HackBench - a Workbench clone

kinner@wsucshp.UUCP (Bill Kinnersley ) (07/08/87)

 
In article <1025@mit-amt.MEDIA.MIT.EDU> ralph@mit-atrp.UUCP
(Amiga-Man) wrote:

> I'll either fix it or write a new Workbench....Big project, but
> big win.  Anybody doing this already (please ?...so I don't
> have to ?).

Well, OK, it sounds like a good idea so I did it.

Actually, the real motivation was curiosity.  A while  ago,
mwm@eris.BERKELEY.EDU (Mike (His microwave has menus) Meyer)
wrote:

> ...the Workbench is just another application program

Really?  But, but...

In <Intuition Manual p.3-6> =RJ= (R J Mical) wrote:

> Intuition treats the Workbench application as a special case,
> communicating with it in extraordinary ways.  For example,
                           ^^^^^^^^^^^^^        ^^^^^^^^^^^
> ...you can call OpenWorkBench() and CloseWorkBench() even
> though the Workbench tool may have open windows in the screen

Maybe I'm overly suspicious, but when I read that I couldn't
help but wonder..is this the only "extraordinary" treatment
Workbench gets?   Does Workbench know about hooks that we don't? 
Does it receive special messages?  What's the *real* purpose of
that window flag WBENCHWINDOW?

I couldn't think of any reason that Workbench would need special
treatment, and finally the only way to settle it was to write a
Workbench clone.

I haven't tried to get fancy, or even worried much about being
complete.  What I've spent time on is trying to write a clean
core, on which further ideas may now be tried.  Suggestions and
bug reports welcome.

------------8<------------8<-------------8<--------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by kinner at wsucshp on Tue Jul  7 20:03:11 1987
#
# This archive contains:
#	README		hb.h		hbicon.c	hbobj.c		
#	hbgad.c		
#

echo x - README
cat >README <<'@EOF'
	HackBench requires V1.2, and should be compiled using Aztec
3.40a with 16-bit integers:

	cc hbicon
	cc hbobj
	cc hbgad
	ln -o hb hbicon.o hbobj.o hbgad.o -lc

	You'll undoubtedly get a few warnings--my include files are
customized, e.g. Window titles are char *'s instead of UBYTE *'s,
and my functions.h file has been corrected.

	The purpose of HackBench is to provide the source for a
Workbench-like program; it is not intended to be an actual
Workbench replacement.  Some features are not supported, such as
Info, Snapshot, Format, and DiskCopy.  Use of HackBench takes
more memory since Workbench is in ROM.

	HackBench manages WBObjects.  WBObjects are represented
simultaneously:
   1) On the disk as a .info file
   2) On the screen as an icon
   3) Internally as a WBObject data structure.
HackBench coordinates things so that icon manipulations are
transmitted to the disk.

HackBench uses the UserData field of the Intuition Window to
store a pointer back to the WBObject, and sets the WBENCHWINDOW
bit in the Window's Flag field as a means of identification.

	All the WBObjects existing at any time are linked together
on a Master List.  Order of WBObjects in the Master List
determines which icon is drawn first, and therefore which one
appears in front.  Each Disk or Drawer has a Children List,
imitating the structure of the filing system.  Objects currently
selected (and highlighted) appear on the Select List.

	The Master List must constantly reflect what is on the
screen.  When a Window is opened, each .info file found is used
to build a WBObject, which is added to the Children List.  Unlike
Workbench, we save the icons and display them all at once.

	When a window is closed, its Children are deleted, except
for those whose icons are currently open themselves or living on
the backdrop.  If the Drawer is later reopened, these orphans
must be found and relinked with their Parent to avoid
duplication.

	HackBench has several Intuition-like features that are
managed without help from Intuition. Icons are stored in Gadget
structures, but are not actual Intuition Gadgets.  HackBench
handles by itself the display, highlighting and hit detection for
icons.

	An open Drawer simulates an Intuition SuperBitMap Window,
without using a second BitMap.  The icons live in a scrollable
virtual coordinate system, but the extent of this is not fixed. 
As the icons change position, the virtual coordinates
automatically extend or contract as necessary.
@EOF

chmod 666 README

echo x - hb.h
cat >hb.h <<'@EOF'
/****************************************************************
/*								*
/*	HackBench - Part 1 of 4 - hb.h - Definitions		*
/*								*
/*	Copyright (C) 1987 by Bill Kinnersley			*
/*	CS Dept, Washington State Univ, Pullman, WA 99164	*
/*								*
/*	Permission granted to redistribute this program		*
/*	provided the copyright notice remains intact.		*
/*	May not be used as part of any commercial product.	*
/*								*
/****************************************************************/

#include <stdio.h>
#include <exec/memory.h>
#include <exec/alerts.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <intuition/intuitionbase.h>
#include <libraries/dosextens.h>
#include <functions.h>
#include <clib/macros.h>

#define BLACK_FILL  (ITEMTEXT | HIGHCOMP)
#define MN(m,i) ((long)(SHIFTMENU(m)|SHIFTITEM(i)|SHIFTSUB(0x1f)) & 0xffffL)
#define MEMF_CPC    (MEMF_CHIP | MEMF_PUBLIC | MEMF_CLEAR)
#define ALLOC(st)   (struct st *)AllocMem((long)sizeof(struct st), MEMF_CPC)
#define SHIFT	    (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
#define OBJ(nd,of) ((struct MyWBObject *)((long)(nd)-of*sizeof(struct Node)))

struct MyDrawerData {
	struct NewWindow dd_NewWindow;
	LONG dd_CurrentX, dd_CurrentY, dd_MinX, dd_MinY, dd_MaxX, dd_MaxY;
	struct Gadget dd_HorizScroll, dd_VertScroll,
		dd_UpMove, dd_DownMove, dd_LeftMove, dd_RightMove;
	struct Image dd_HorizImage, dd_VertImage;
	struct PropInfo dd_HorizProp, dd_VertProp;
	struct Window *dd_DrawerWin;
	struct List dd_Children;	/* Must be initialized */
	BPTR dd_Lock;
};

struct MyWBObject {
	struct Node wo_MasterNode, wo_Siblings,
		wo_SelectNode, wo_UtilityNode;
	struct MyWBObject *wo_Parent;
	UBYTE wo_Flags;
	char *wo_Name;
	SHORT wo_NameXOffset, wo_NameYOffset;
	struct FreeList wo_FreeList;	/* Must be initialized */
	struct Window *wo_IconWin;
	BPTR wo_Lock;
	struct Gadget wo_Gadget;
	UBYTE wo_Type;
	char *wo_DefaultTool, **wo_ToolTypes;
	LONG wo_CurrentX, wo_CurrentY;
	struct MyDrawerData *wo_DrawerData;
	char *wo_ToolWindow;
	LONG wo_StackSize;
};

/* Gadget ID's */
#define GID_WBOBJECT       0L
#define GID_HORIZSCROLL    1L
#define GID_VERTSCROLL     2L
#define GID_LEFTSCROLL     3L
#define GID_RIGHTSCROLL    4L
#define GID_UPSCROLL       5L
#define GID_DOWNSCROLL     6L
#define GID_NAME           7L

/* Menu Items */
#define OPEN	MN(0,0)
#define CLOZE	MN(0,1)
#define DUP	MN(0,2)
#define RENAME	MN(0,3)
#define INFO	MN(0,4)
#define DISCARD	MN(0,5)

#define EMPTY	MN(1,0)
#define INIT	MN(1,1)

#define CLEANUP	MN(2,0)
#define ERROR	MN(2,1)
#define REDRAW	MN(2,2)
#define SNAP	MN(2,3)
#define VERS	MN(2,4)

#define TRACE	MN(3,0)
#define WBOBJS	MN(3,1)
#define SELOBJS	MN(3,2)
#define UTILOBJS MN(3,3)
#define CHILDREN MN(3,4)
#define QUIT	MN(3,5)

/* Offsets for Object Lists */
#define MAST 0L
#define CHILD 1L
#define SEL 2L
#define UTIL 3L

/* Object Flags */
#define NORM 0
#define HIGH 1
#define RESIZE 2
#define TEMPLOCK 4

/* Window top border */
#define YOFF 10L

@EOF

chmod 666 hb.h

echo x - hbicon.c
cat >hbicon.c <<'@EOF'
/****************************************************************
/*								*
/*	HackBench - Part 2 of 4 - hbicon.c - Icon Routines	*
/*								*
/*	Copyright (C) 1987 by Bill Kinnersley			*
/*	CS Dept, Washington State Univ, Pullman, WA 99164	*
/*								*
/*	Permission granted to redistribute this program		*
/*	provided the copyright notice remains intact.		*
/*	May not be used as part of any commercial product.	*
/*								*
/****************************************************************/

#include "hb.h"

extern struct DosInfo *rnInfo;
extern struct MsgPort *IDCMPPort, WBPort;
extern long IDCMPBit, WBBit;

extern USHORT x_ptr[];
extern struct Gadget
	vs_gad, hs_gad, ren_gad, up_gad, dm_gad, lm_gad, rm_gad;
extern struct PropInfo hs_knob, vs_knob;

extern struct Rectangle rect;
extern char *type[], *title;
extern short wbFlags, toolsRunning;
extern struct List selObjs, wbObjs, utilObjs;
extern int drawobj(), clearobj(), compobj(), openobj(), closeobj();
extern char *rindex();

struct MyWBObject *lastObj, *hitObj, *hitWinObj, *findObj(), *makeWbObj();
struct Window *wbWin, *oldWin, *curWin, *hitWin, *errWin;
struct IntuiMessage saveMsg;
struct Layer_Info *li;

ULONG downSecs, downMicros, upSecs, upMicros;
short oldX, oldY, dblclik, debug=FALSE;
char buf1[61], buf2[61];

struct DeviceList *vol();
BPTR getlock();
long tooSoon();

main() {
	struct Message *msg;
	struct WBStartup *wbmsg;
	long mask;
	BPTR seg;

	openAll();
	curWin = wbWin; /* Current window is the backdrop */
	li = &wbWin->WScreen->LayerInfo;

	findDisks();
	makeMenu();
	OnMenu(wbWin, REDRAW); OnMenu(wbWin, VERS);

	avail();

	while (1) { /* We have two ports to monitor at once */
		mask = Wait(IDCMPBit | WBBit);
		if (mask & WBBit) while (msg = GetMsg(WBPort)) {
			/* Unload the tool that just exited */
			wbmsg = (struct WBStartup *)msg;
			if (debug) printf("Unloading\n");
			if (seg = wbmsg->sm_Segment) UnLoadSeg(seg);
			FreeMem(wbmsg->sm_ArgList,
			wbmsg->sm_NumArgs*(long)sizeof(struct WBArg));
			FreeMem(msg, (long)sizeof(struct WBStartup));
			toolsRunning--;
		}
		if (mask & IDCMPBit) while (msg = GetMsg(IDCMPPort))
			doIDCMP(msg);
	}
}

doIDCMP(msg) struct IntuiMessage *msg; {
	struct MyWBObject *obj;
	struct DeviceList *diDev, *ptr;
	char diskName[31];
	struct Node *node;

/*stc*/	saveMsg = *msg;
	ReplyMsg(msg);
	curWin = oldWin = saveMsg.IDCMPWindow;
	switch (saveMsg.Class) {
	case CLOSEWINDOW :
	    obj = (struct MyWBObject *)oldWin->UserData;
	    closeobj(obj);
	    clearSel();
	    avail();
	    break;
	case MENUPICK: doMenu(saveMsg.Code); break;
	case MOUSEBUTTONS:
	    switch(saveMsg.Code) {
	    case SELECTDOWN: doSelDown(); break;
	    case SELECTUP: doSelUp(); break;
	    }
	    break;
	case GADGETUP: doGadUp(); break;
	case GADGETDOWN: doGadDown(); break;
	case DISKINSERTED:
	/*Delay(10L);*/ /* May be necessary */
	    /* Scan the Device List for a Volume that does not
		appear on the Master List of WBObjects */
	    diDev = (struct DeviceList *) BADDR(rnInfo->di_DevInfo);
	    for (ptr=diDev; ptr; ptr=(struct DeviceList *)
		BADDR(ptr->dl_Next)) if (ptr->dl_Type==DLT_VOLUME) {
		bs2cs(diskName, ptr->dl_Name);
		if (!FindName(&wbObjs, diskName)) break;
	    }
	    if (!ptr) break;
	    instDisk(diskName); /* Make a new disk object */
	    refresh(wbWin);
	    break;
	case DISKREMOVED:
	Delay(10L); /* This does seem to be necessary */
	    /* For each disk object, check to see if it's still
		on the Device List */
	    diDev = (struct DeviceList *) BADDR(rnInfo->di_DevInfo);
	    for (node=wbObjs.lh_Head; node->ln_Succ; node=node->ln_Succ) {
		obj = (struct MyWBObject *)node;
		if (obj->wo_Type!=WBDISK) continue;
		for (ptr=diDev; ptr; ptr=(struct DeviceList *)
		    BADDR(ptr->dl_Next)) if (ptr->dl_Type==DLT_VOLUME) {
		    bs2cs(diskName, ptr->dl_Name);
		    if (strcmp(diskName, obj->wo_Name)==0) break;
		}
		if (ptr) continue;
		clearobj(obj); /* Not found--get rid of it */
		deleteobj(obj);
		break;
	    }
	    refresh(wbWin);
	    break;
	case REFRESHWINDOW:
	    if (debug)
		printf("Window %lx is SIMPLY REFRESHING!\n", curWin);
	    BeginRefresh(curWin);
	    refresh(curWin);
	    EndRefresh(curWin, TRUE);
	    break;
	case NEWSIZE:
	    reclip(curWin);
	    resize(curWin);
	    break;
	}
}

clearSel() { /* Purge the Select List */
	struct Node *node;

	doList(&selObjs, SEL, drawobj, NORM);
	while (node = RemHead(&selObjs)) node->ln_Succ = NULL;
	/* So we can tell later whether a given object
		is still on the List */
	lastObj = NULL;
	OffMenu(wbWin, OPEN); OffMenu(wbWin, CLOZE);
	OffMenu(wbWin, DUP); OffMenu(wbWin, RENAME);
	OffMenu(wbWin, INFO); OffMenu(wbWin, INIT);
	OffMenu(wbWin, SNAP); OffMenu(wbWin, DISCARD);
}

doOpen() {
	struct Node *node;
	struct MyWBObject *obj;

	/* If there's a tool or project on the Select List, run it.
		Otherwise, just open everybody on the List */
	for (node=selObjs.lh_Head; node->ln_Succ; node=node->ln_Succ) {
		obj = OBJ(node,SEL);
		if (obj->wo_Type==WBTOOL || obj->wo_Type==WBPROJECT)
			{run(obj); return;}
	}
	doList(&selObjs, SEL, openobj);
}

/* doSelDown does three things:
	Detects and handles double clicks
	Accumulates the extended selection list
	Toggles menu items
*/
doSelDown() {
	struct MyWBObject *obj;
	struct Node *node;

	if (errWin) {SetWindowTitles(errWin,-1L,title); errWin=NULL;}
	if (obj = findObj(saveMsg.MouseX, saveMsg.MouseY, curWin)) {
		/* Hit an icon */
	    if (dblclik = ((obj==lastObj) && DoubleClick(downSecs,
		downMicros, saveMsg.Seconds, saveMsg.Micros))) {
		/* Double click */
		    if (debug) printf("Double click\n");
		    saveMsg.Seconds -= 20L; /* To prevent triple clicks */
		    doOpen();
		    OffMenu(wbWin, OPEN); OnMenu(wbWin, CLOZE);
		    avail();
	    }

	    else { /* Single click so far */
		if (debug) printf("Single click\n");
		SetPointer(oldWin, x_ptr, 12L, 12L, -6L, -6L);
		if (saveMsg.Qualifier & SHIFT) {
		/* Extended selection rules out some operations */	
		    OffMenu(wbWin, DUP); OffMenu(wbWin, RENAME);
		    OffMenu(wbWin, INFO);
		}
		else {
		    doList(&selObjs, SEL, drawobj, NORM);
		    while (node = RemHead(&selObjs))
			node->ln_Succ = node->ln_Pred = NULL;
		    OnMenu(wbWin, DUP); OnMenu(wbWin, RENAME);
		    OnMenu(wbWin, INFO);
		}
		OnMenu(wbWin, SNAP);
		if (!obj->wo_SelectNode.ln_Succ) /* Not yet selected */
			AddHead(&selObjs, &obj->wo_SelectNode);
		/* Exec gets confused if a node is in the same list twice */
		drawobj(obj, HIGH); /* Highlight it */
		/* Did I hit an open drawer? */
		if (obj->wo_DrawerData
		    && obj->wo_DrawerData->dd_DrawerWin)
			{OnMenu(wbWin, CLOZE); OffMenu(wbWin, OPEN);}
		/* Either a closed drawer or a tool */
		/* Tools can always be opened */
		else {OnMenu(wbWin, OPEN); OffMenu(wbWin, CLOZE);}

		lastObj = obj; oldX = saveMsg.MouseX; oldY = saveMsg.MouseY;
		downSecs = saveMsg.Seconds; downMicros = saveMsg.Micros;
	    }
	}
	else clearSel();		/* Didn't hit an icon */
}

doSelUp() {
    struct Layer *layer;
    struct Window *w;
    short dx, dy;
    BPTR lock, lock2;
    struct MyDrawerData *dd;

    if (dblclik) {dblclik = FALSE; return;}
    ClearPointer(oldWin);
    if (!lastObj) return;
    curWin = oldWin;
    upSecs = saveMsg.Seconds; upMicros = saveMsg.Micros;

/* Selectup msg is sent back to the window where selectdown occurred,
(oldWin), so we have to search the layers for the real window */

    layer = WhichLayer(li, (long)(saveMsg.MouseX + oldWin->LeftEdge),
		(long)(saveMsg.MouseY + oldWin->TopEdge));
    for (hitWin=wbWin->WScreen->FirstWindow; hitWin;
	hitWin=hitWin->NextWindow)
	    if (hitWin->WLayer==layer) break;
    if (!hitWin) return; /* Shouldn't happen */

    if (!(hitWin->Flags & WBENCHWINDOW))
	{error("That's somebody else's window"); return;}
    if (hitWin!=wbWin) if (lastObj->wo_Type==WBDISK) 
	{error("Disks can't be moved into windows"); return;}
    if (lastObj->wo_Type==WBGARBAGE)
	if (hitWin!=lastObj->wo_IconWin)
	{error("A trashcan must stay in its own window"); return;}
    if (lastObj->wo_Type==WBDRAWER)
	if (hitWin==lastObj->wo_DrawerData->dd_DrawerWin)
	/* You tried to move an icon into its own open window, dodo */
	{error("Hey, that's me!"); return;}

    hitWinObj = (struct MyWBObject *)hitWin->UserData;
	/* hitWinObj may be NULL if hitWin is the backdrop */
    dx = saveMsg.MouseX + oldWin->LeftEdge - hitWin->LeftEdge;
    dy = saveMsg.MouseY + oldWin->TopEdge - hitWin->TopEdge;
    hitObj = findObj(dx, dy, hitWin);
    if (debug) printf("SelUp hitObj=%lx hitWin=%lx\n", hitObj, hitWin);

    if ((!hitObj) || (hitObj==lastObj))
	{moveObj(&selObjs, SEL, dx - oldX, dy - oldY); return;}

    /* So from here on we must have hit a new icon */
    if (debug) printf("Hit <%s>, a %s\n",
	hitObj->wo_Name, type[hitObj->wo_Type]);

    switch (hitObj->wo_Type) {
    case WBDISK: if (lastObj->wo_Type==WBDISK)
		{error("DiskCopy not implemented"); return;}
	if (lastObj->wo_Type==WBGARBAGE)
		{error("A trashcan must stay in its own window"); return;}
	if (hitObj==lastObj->wo_Parent) break;
	lock = getlock(lastObj);
	if (vol(lock)==vol(hitObj->wo_Lock)) {
	    /* On same disk--just rename */
	    strcpy(buf1, lastObj->wo_Name); strcpy(buf2, lastObj->wo_Name);
	    if (debug) printf("Renaming %lx <%s> to %lx <:%s>\n",
		lock, lastObj->wo_Name, hitObj->wo_Lock, lastObj->wo_Name);
	    strcat(buf1, ".info"); strcat(buf2, ".info");
	    renBoth(lock, buf1, hitObj->wo_Lock, buf2);
	    break;
	}
	/* Otherwise copy to another disk--not implemented */
	if (debug) printf("I should copy %lx <%s> to %lx <%s:>\n",
		lock, lastObj->wo_Name, hitObj->wo_Lock, hitObj->wo_Name);
	return;

    case WBDRAWER:
    case WBGARBAGE: /* Hit a drawer icon */
	if (hitObj==lastObj->wo_Parent) break;
	if (lastObj->wo_Type==WBDISK) if (hitWin!=wbWin)
	    {error("Can't move a disk into a drawer"); return;}
	if (lastObj->wo_Type==WBGARBAGE)
	    {error("A trashcan must stay in its own window"); return;}

	strcpy(buf1, lastObj->wo_Name); strcpy(buf2, hitObj->wo_Name);
	strcat(buf2, "/"); strcat(buf2, lastObj->wo_Name);
	lock = getlock(lastObj); lock2 = getlock(hitObj);
	if (debug) printf("Renaming %lx <%s> to %lx <%s>\n",
		lock, buf1, lock2, buf2);
	strcat(buf1, ".info"); strcat(buf2, ".info");
	renBoth(lock, buf1, lock2, buf2);
	break;
    default:  /* Just let the icons overlap */
	moveObj(&selObjs, SEL, dx-oldX, dy-oldY);
	return;
    }
    goAway();
    doResize();
}

goAway() { /* Do move from one window to another */
	struct MyDrawerData *dd;
	struct Window *w;

	mark(lastObj->wo_IconWin);
	clearobj(lastObj);
	Remove(&lastObj->wo_Siblings);
	lastObj->wo_Siblings.ln_Succ = NULL;
	dd = hitObj->wo_DrawerData;
	if (w=dd->dd_DrawerWin) { /* Destination window open */
		AddHead(&dd->dd_Children, &lastObj->wo_Siblings);
		lastObj->wo_Parent = hitObj;
		lastObj->wo_IconWin = w;
		mark(w);
		Remove(lastObj); AddTail(&wbObjs, lastObj);
		/* Rearrange, so that refresh() draws it last (on top) */
	}
	else deleteobj(lastObj);
}

moveObj(list, off, x, y)
struct List *list; long off; short x, y; {
    struct Node *node;
    struct MyWBObject *obj, *iconWinObj;
    struct Window *w;
    struct MyDrawerData *dd;
    char *p;
    BPTR lock, lock2;

    mark(hitWin);
    hitWinObj = (struct MyWBObject *)hitWin->UserData;

    if (tooSoon()) return; /* If selup too soon, ignore */
    doList(list, off, clearobj);
    for (node=list->lh_Head; node->ln_Succ; node=node->ln_Succ) {
	obj = OBJ(node,off);
	if (debug)
	    printf("moveObj: %lx (%d %d) by (%d %d) from %lx to %lx\n",
	    obj, obj->wo_Gadget.LeftEdge, obj->wo_Gadget.TopEdge,
	    x, y, obj->wo_IconWin, hitWin);
	iconWinObj = (struct MyWBObject *)obj->wo_IconWin->UserData;
	mark(obj->wo_IconWin);
	if (obj->wo_IconWin!=hitWin) { /* Changing windows */
	    /* Adjust the virtual coordinates */
	    if (obj->wo_IconWin!=wbWin) {
		dd = iconWinObj->wo_DrawerData;
		obj->wo_CurrentX -= dd->dd_CurrentX;
		obj->wo_CurrentY -= dd->dd_CurrentY;
	    }
	    if (hitWin!=wbWin) {
		dd = hitWinObj->wo_DrawerData;
		obj->wo_CurrentX += dd->dd_CurrentX;
		obj->wo_CurrentY += dd->dd_CurrentY;
	    }
	    if (hitWin==wbWin) { /* To the backdrop */
		/* A backdrop icon needs its very own lock,
		in case its parent's drawer is later closed */
		obj->wo_Lock = DupLock(obj->wo_Parent->
		    wo_DrawerData->dd_Lock);
		if (debug) printf("Duplicate lock: <%s> %lx\n",
			obj->wo_Name, obj->wo_Lock);
	    }

	    else if (hitWinObj==obj->wo_Parent) {
		/* Into the open drawer of my parent */
		if (debug) printf("Oh Lawdy, I'se home--home at las'!\n");
		/* I don't need my lock any more */
		if (obj->wo_Lock) { /* Coming from the backdrop */
		    if (debug) printf("Unlocking <%s>: %lx\n",
			obj->wo_Name, obj->wo_Lock);
		    UnLock(obj->wo_Lock);
		    obj->wo_Lock = NULL;
		}
	    }

	    else { /* Into some other open drawer */
		lock = getlock(obj);
		lock2 = hitWinObj->wo_DrawerData->dd_Lock;
		if (vol(lock)==vol(lock2)) {
		    strcpy(buf1, obj->wo_Name); strcpy(buf2, obj->wo_Name);
		    if (debug) printf("Renaming %lx <%s> to %lx <%s>\n",
			lock, buf1, lock2, buf2);
		    strcat(buf1, ".info"); strcat(buf2, ".info");
		    renBoth(lock, buf1, lock2, buf2);
		}
		else {
			bs2cs(buf2, vol(lock2)->dl_Name);
if (debug) printf("I should copy %lx <%s> to %lx <%s/%s> on <%s:>\n",
			lock, obj->wo_Name, lock2,
			hitWinObj->wo_Name, obj->wo_Name, buf2);
		}
		Remove(&obj->wo_Siblings);
		AddHead(&dd->dd_Children, &obj->wo_Siblings);
		obj->wo_Parent = hitWinObj;
	    }
	    obj->wo_IconWin = hitWin;
	}
	obj->wo_CurrentX += x;
	obj->wo_CurrentY += y;
	Remove(obj); AddTail(&wbObjs, obj);
	/* So that refresh() draws it last (on top) */
    }
    doList(list, off, drawobj, HIGH);
    doResize();
}

mark(w) struct Window *w; { /* Mark window for later resizing */
	if (w!=wbWin)
		((struct MyWBObject *)w->UserData)->wo_Flags |= RESIZE;
	else wbFlags |= RESIZE;
}

doResize() {
    struct Node *node;
    struct MyWBObject *obj;
    struct Window *w;

    for (node=wbObjs.lh_Head; node->ln_Succ; node=node->ln_Succ) {
	obj = (struct MyWBObject *)node;
	if (obj->wo_Flags & RESIZE) {
		w = obj->wo_DrawerData->dd_DrawerWin;
		resize(w);
		refresh(w);
		obj->wo_Flags &= ~RESIZE;
	}
    }
    if (wbFlags & RESIZE) {
	/* It might be worth having a fake object whose drawer is
	the backdrop, but all we really need is the Flags field */
	refresh(wbWin);
	wbFlags &= ~RESIZE;
    }
}

doGadDown () {
	USHORT gid;
	struct MyDrawerData *dd;
	short shift;
	long pos;

	gid = ((struct Gadget *)saveMsg.IAddress)->GadgetID;
	dd = ((struct MyWBObject *)oldWin->UserData)->wo_DrawerData;
	shift = saveMsg.Qualifier & SHIFT;
	lastObj = NULL;

	switch (gid) {
	case GID_VERTSCROLL: case GID_HORIZSCROLL: return;
	case GID_LEFTSCROLL: pos = (ULONG)dd->dd_HorizProp.HorizPot;
		dd->dd_CurrentX -= shift ? 1 : 20;
		break;
	case GID_RIGHTSCROLL: pos = (ULONG)dd->dd_HorizProp.HorizPot;
		dd->dd_CurrentX += shift ? 1 : 20;
		break;
	case GID_UPSCROLL: pos = (ULONG)dd->dd_VertProp.VertPot;
		dd->dd_CurrentY -= shift ? 1 : 10;
		break;
	case GID_DOWNSCROLL: pos = (ULONG)dd->dd_VertProp.VertPot;
		dd->dd_CurrentY += shift ? 1 : 10;
		break;
	}
	resize(oldWin);
	SetRast(oldWin->RPort, 0L);
	refresh(oldWin);
}

doGadUp() {
    USHORT gid;
    struct MyDrawerData *dd;
    long pos;

    gid = ((struct Gadget *)saveMsg.IAddress)->GadgetID;
    dd = ((struct MyWBObject *)oldWin->UserData)->wo_DrawerData;

    switch (gid) {
    case GID_HORIZSCROLL: /* I don't understand this either */
	pos = (ULONG)dd->dd_HorizProp.HorizPot;
	dd->dd_CurrentX = dd->dd_MinX + (pos *
	    (MAX(dd->dd_CurrentX, dd->dd_MaxX - (oldWin->Width-14L))
	     - MIN(dd->dd_CurrentX, dd->dd_MinX)) ) / MAXBODY; 
	break;
    case GID_VERTSCROLL:
	pos = (ULONG)dd->dd_VertProp.VertPot;
	dd->dd_CurrentY = dd->dd_MinY + (pos *
	    (MAX(dd->dd_CurrentY,dd->dd_MaxY-(oldWin->Height-YOFF-9L))
	     - MIN(dd->dd_CurrentY, dd->dd_MinY)) ) / MAXBODY;
	break;
    }
    resize(oldWin);
    SetRast(oldWin->RPort, 0L);
    refresh(oldWin);
}

/* Recalculate the size of a window's virtual coord system */
resize(w) struct Window *w; {
	struct Node *node;
	struct MyWBObject *obj;
	struct MyDrawerData *dd;
	long effH, effW, total, left, right, pot, body;

	obj = (struct MyWBObject *)w->UserData;
	dd = obj->wo_DrawerData;
	dd->dd_MinX = dd->dd_MinY = 10000;
	dd->dd_MaxX = dd->dd_MaxY = -10000;

	/* For each icon living in the window.. */
	for (node=dd->dd_Children.lh_Head; node->ln_Succ;
	node=node->ln_Succ) {
	    obj = OBJ(node,CHILD);
	    if (obj->wo_IconWin!=w) continue;

	    /* Each icon contains two pieces..a title and an image */
	    /* Take the MIN of the left edges.. */
	    dd->dd_MinX = MIN(dd->dd_MinX,
		obj->wo_CurrentX + obj->wo_NameXOffset);

	    /* and the MAX of the right edges */
	    right = MAX(obj->wo_Gadget.Width,
		obj->wo_NameXOffset + 8*strlen(obj->wo_Name));
	    dd->dd_MaxX = MAX(dd->dd_MaxX, obj->wo_CurrentX + right);

	    /* Likewise top and bottom edges */
	    dd->dd_MinY = MIN(dd->dd_MinY, obj->wo_CurrentY);
	    dd->dd_MaxY = MAX(dd->dd_MaxY, obj->wo_CurrentY + 
		obj->wo_Gadget.Height + 10L);
	}

	/* Now recalculate the horizontal extent... */
	/* total: Total size of virtual coord system */
	/* left: Size undisplayed to left */
	/* right: Size undisplayed to right */

	effW = w->Width-14L;
	total = MAX(dd->dd_CurrentX + effW, dd->dd_MaxX)
		- MIN(dd->dd_CurrentX, dd->dd_MinX);
	left = MAX(dd->dd_CurrentX - dd->dd_MinX, 0L);
	right = total - left - effW;
	if ((left + right)==0L) pot = 0L;
	else pot = (MAXBODY * left) / (left + right);
	body = (MAXBODY * effW) / total;
	ModifyProp(&dd->dd_HorizScroll, w, NULL, FREEHORIZ | AUTOKNOB,
		pot, MAXBODY, body, MAXBODY);

	/* and the vertical extent */
	effH = w->Height-YOFF-9L;
	total = MAX(dd->dd_CurrentY + effH, dd->dd_MaxY)
		- MIN(dd->dd_CurrentY, dd->dd_MinY);
	left = MAX(dd->dd_CurrentY - dd->dd_MinY, 0L);
	right = total - left - effH;

	if ((left + right)==0L) pot = 0L;
	else pot = (MAXBODY * left) / (left + right);
	body = (MAXBODY * effH) / total;
	ModifyProp(&dd->dd_VertScroll, w, NULL, FREEVERT | AUTOKNOB,
		MAXBODY, pot, MAXBODY, body);
}

/* Must do this each time a window's size is changed */
/* To prevent drawing over the borders */
reclip(w) struct Window *w; {
	struct Region *reg, *oldreg;

	reg = NewRegion();
	rect.MaxX = w->Width - 19;
	rect.MaxY = w->Height - 11;
	OrRectRegion(reg, &rect);
	oldreg = InstallClipRegion(w->WLayer, reg);
	if (oldreg) DisposeRegion(oldreg);
}

BPTR getlock(obj) struct MyWBObject *obj; {
	BPTR lock;
	char name[31];

	/* Either the obj's own Lock, or the Lock of the drawer it's in,
	or manufacture a new one */
	lock = obj->wo_Lock;
	if (!lock) if (obj->wo_Parent)
		lock = obj->wo_Parent->wo_DrawerData->dd_Lock;
	if (!lock) { /* Presumably a disk */
		if (debug) printf("%lx <%s> (a %s) needs a new lock\n",
			obj, obj->wo_Name, type[obj->wo_Type]);
		strcpy(name, obj->wo_Name);
		strcat(name,":");
		lock = obj->wo_Lock = Lock(name, ACCESS_READ);
		/* Maybe user refused to reinsert disk */
		if (!lock) printf("Can't lock <%s>\n", name);
		if (debug) printf("getlock: new lock: %lx\n",lock);
	}
	return lock;
}

struct DeviceList *vol(lock) BPTR lock; {
	struct FileLock *fl;

	/* Return the Volume corresponding to a given Lock */
	if (!lock) {printf("vol: Null lock\n"); return NULL;}
	fl = (struct FileLock *)BADDR(lock);
	return (struct DeviceList *)BADDR(fl->fl_Volume);
}

/* Convert a BSTR to a C string */
bs2cs(buf, bstr) char *buf; BPTR bstr; {
	char *p;

	p = (char *)BADDR(bstr);
	strncpy(buf, p+1, *p);
	buf[*p] = 0;
}

/* C string to BSTR - (buf must be Long Aligned) */
cs2bs(buf, s) char *buf, *s; {
	strcpy(buf+1, s);
	*buf = strlen(s);
}

renpkt(l1, n1, l2, n2) BPTR l1, l2; char *n1, *n2; {
	long arg[4], res;
	struct DeviceList *dl1, *dl2;
	char *buf1, *buf2;
	struct MsgPort *handid;

	if (debug) printf("Renpkt: <%s> to <%s>\n", n1, n2);
	if (!l1 || !l2) {printf("renpkt error: null lock\n"); return;}
	if ((dl1=vol(l1)) != (dl2=vol(l2)))
		{printf("renpkt error: different volumes\n"); return;}
	handid = ((struct FileLock *)BADDR(l1))->fl_Task;
	/* Must be long aligned: */
	buf1 = AllocMem(31L, MEMF_CPC); buf2 = AllocMem(31L, MEMF_CPC);
	cs2bs(buf1, n1); cs2bs(buf2, n2);
	arg[0] = l1; arg[2] = l2;
	arg[1] = ((long)buf1)>>2; arg[3] = ((long)buf2)>>2;
	if (!sendpkt(handid, ACTION_RENAME_OBJECT, arg, 4L))
		printf("Rename failed\n");
	FreeMem(buf1, 31L); FreeMem(buf2, 31L);
}

renBoth(l1, n1, l2, n2) BPTR l1, l2; char *n1, *n2; {
	/* Rename both the file and its .info file */
	renpkt(l1, n1, l2, n2);
	*rindex(n1,'.') = 0; *rindex(n2,'.') = 0;
	renpkt(l1, n1, l2, n2);
}

long tooSoon() {
	long dt;

	dt = upSecs - downSecs;
	if (dt>1) return FALSE;
	dt = upMicros - downMicros + 1000000L*dt;
	return (dt<200000L);
	/* Two-tenths of a sec between selup and seldown
	is too soon to count as a drag operation */
}
@EOF

chmod 666 hbicon.c

echo x - hbobj.c
cat >hbobj.c <<'@EOF'
/****************************************************************
/*								*
/*	HackBench - Part 3 of 4 - hbobj.c - Object Routines 	*
/*								*
/*	Copyright (C) 1987 by Bill Kinnersley			*
/*	CS Dept, Washington State Univ, Pullman, WA 99164	*
/*								*
/*	Permission granted to redistribute this program		*
/*	provided the copyright notice remains intact.		*
/*	May not be used as part of any commercial product.	*
/*								*
/****************************************************************/

#include "hb.h"

extern struct DosLibrary *DosBase;
extern struct DosInfo *rnInfo;
extern struct MsgPort *IDCMPPort, *WBPort;
extern struct Window *wbWin, *curWin;
extern struct DiskObject defDskObj;
extern struct Menu *menuStrip;
extern struct Gadget
	vs_gad, hs_gad, ren_gad, up_gad, dm_gad, lm_gad, rm_gad;
extern struct PropInfo hs_knob, vs_knob;
extern USHORT zz_ptr[];
extern char *title, *rindex();
extern BPTR initialDir, getlock();
extern short debug;

struct Rectangle rect = {4, 11};

struct List wbObjs, selObjs, utilObjs;
short lastHeight=14, toolsRunning = 0, wbFlags = 0;

void *allocList();
int drawobj(), compobj();

char *type[8] = {"", "DISK", "DRAWER", "TOOL", "PROJECT",
	"GARBAGE", "DEVICE", "KICK"};
char *empty = "";

struct IntuiText icon_txt = {1, 0, JAM2, 5, 0, NULL, NULL, NULL};

/* Manufacture a new WBObject with a given name, window, and parent */
struct MyWBObject *makeObj(diskObj, name, w, parent)
struct DiskObject *diskObj; char *name;
struct Window *w; struct MyWBObject *parent; {
	struct MyWBObject *obj;
	char *buf, *p;
	SHORT size, i, s;
	struct Gadget *g;
	struct Image *image;
	USHORT *data;
	struct MyDrawerData *dd;
	struct NewWindow *nwd;

	if (!(obj= ALLOC(MyWBObject)))
		{error("Can't allocate WB Object"); return;}
	NewList(&obj->wo_FreeList.fl_MemList);
	buf = allocList(obj, 31);
	/* Space for 31 chars in case it's later renamed */
	strcpy(buf, name);
	obj->wo_Name = obj->wo_MasterNode.ln_Name = 
	obj->wo_Siblings.ln_Name = obj->wo_SelectNode.ln_Name = buf;
	AddTail(&wbObjs, obj);
	obj->wo_IconWin = w;
	obj->wo_Type = diskObj->do_Type;
	if (diskObj->do_ToolTypes) {
		for (i = 0; p = diskObj->do_ToolTypes[i]; i++) ;
		obj->wo_ToolTypes = allocList(obj, 4*i);
		for (i = 0; p = diskObj->do_ToolTypes[i]; i++) {
			buf = allocList(obj, strlen(p)+1);
			strcpy(buf, p);
			obj->wo_ToolTypes[i] = buf;
		}
	}
	if (diskObj->do_ToolWindow) {
		buf = allocList(obj, strlen(diskObj->do_ToolWindow)+1);
		strcpy(buf, diskObj->do_ToolWindow);
		obj->wo_ToolWindow = buf;
	}
	if (diskObj->do_DefaultTool) {
		buf = allocList(obj, strlen(diskObj->do_DefaultTool)+1);
		strcpy(buf, diskObj->do_DefaultTool);
		obj->wo_DefaultTool = buf;
	}
	obj->wo_StackSize = diskObj->do_StackSize;
	obj->wo_CurrentX = diskObj->do_CurrentX;
	obj->wo_CurrentY = diskObj->do_CurrentY;
	if (diskObj->do_CurrentX==NO_ICON_POSITION) {
		if (debug) printf("<%s>:No Icon Position\n", name);
		obj->wo_CurrentX = 20; obj->wo_CurrentY = 10;
		/* Should do better placement than this */
	}
	g = &diskObj->do_Gadget;
	/* stc means structure copy */
/*stc*/	obj->wo_Gadget = diskObj->do_Gadget;
	image = allocList(obj, sizeof(struct Image));
/*stc*/ *image = *(struct Image *)(diskObj->do_Gadget.GadgetRender);
	obj->wo_Gadget.GadgetRender = (APTR)image;

	size = (image->Width/16) + ((image->Width%16)>0);
	size *= image->Height * image->Depth;
	data = allocList(obj, 2*size);
	for (i=0; i<size; i++) data[i] = (image->ImageData)[i];
	image->ImageData = data;

	image = allocList(obj, sizeof(struct Image));
	if (diskObj->do_Gadget.SelectRender)
/*stc*/		*image = *(struct Image *)(diskObj->do_Gadget.SelectRender);
	else *image = *(struct Image *)(diskObj->do_Gadget.GadgetRender);
	obj->wo_Gadget.SelectRender = (APTR)image;

	size = (image->Width/16) + ((image->Width%16)>0);
	size *= image->Height * image->Depth;
	data = allocList(obj, 2*size);

	if (diskObj->do_Gadget.SelectRender)
		for (i=0; i<size; i++) data[i] = (image->ImageData)[i];
	else
		for (i=0; i<size; i++) data[i] = ~(image->ImageData)[i];
	image->ImageData = data;

	if (diskObj->do_DrawerData) {
		dd = allocList(obj, sizeof(struct MyDrawerData));
		obj->wo_DrawerData = dd;
/* stc */	dd->dd_NewWindow = diskObj->do_DrawerData->dd_NewWindow;
		dd->dd_NewWindow.Title = obj->wo_Name;
		NewList(&dd->dd_Children);
		dd->dd_CurrentX = diskObj->do_DrawerData->dd_CurrentX;
		dd->dd_CurrentY = diskObj->do_DrawerData->dd_CurrentY;
		nwd = &dd->dd_NewWindow;
		nwd->MinWidth = 90;
		nwd->MinHeight = 40;
		nwd->MaxWidth = nwd->MaxHeight = -1;
		dd->dd_MinX = dd->dd_MinY = 0;
		dd->dd_MaxX = nwd->Width - 14;
		dd->dd_MaxY = nwd->Height - 9;
/*stc*/ 	dd->dd_HorizScroll = hs_gad;
/*stc*/		dd->dd_VertScroll = vs_gad;
/*stc*/ 	dd->dd_UpMove = up_gad;
/*stc*/ 	dd->dd_DownMove = dm_gad;
/*stc*/ 	dd->dd_LeftMove = lm_gad;
/*stc*/ 	dd->dd_RightMove = rm_gad;
/*stc*/ 	dd->dd_HorizProp = hs_knob;
/*stc*/ 	dd->dd_VertProp = vs_knob;
		dd->dd_HorizScroll.GadgetRender = (APTR)&dd->dd_HorizImage;
		dd->dd_HorizScroll.SpecialInfo = (APTR)&dd->dd_HorizProp;
		dd->dd_VertScroll.GadgetRender = (APTR)&dd->dd_VertImage;
		dd->dd_VertScroll.SpecialInfo = (APTR)&dd->dd_VertProp;
		dd->dd_HorizScroll.NextGadget = &dd->dd_VertScroll;
		dd->dd_VertScroll.NextGadget = &dd->dd_UpMove;
		dd->dd_UpMove.NextGadget = &dd->dd_DownMove;
		dd->dd_DownMove.NextGadget = &dd->dd_LeftMove;
		dd->dd_LeftMove.NextGadget = &dd->dd_RightMove;
		dd->dd_NewWindow.FirstGadget = &dd->dd_HorizScroll;
	}
	obj->wo_NameXOffset = (g->Width/2)-4*strlen(name);
	obj->wo_NameYOffset = g->Height;
	obj->wo_Parent = parent;
	if (parent && parent->wo_DrawerData) {
		AddHead(&parent->wo_DrawerData->dd_Children,
			&obj->wo_Siblings);
		if (debug) printf("Added %lx <%s> to %lx <%s>\n",
			obj, obj->wo_Name, parent, parent->wo_Name);
	}
	return obj;
}

/* Given a List, use off to find the objects,
then perform fn on each object with parameter p1 */
doList(list, off, fn, p1)
struct List *list; long off; int (*fn)(); {
	struct MyWBObject *obj;
	struct Node *node;

	for (node=list->lh_Head; node->ln_Succ; node=node->ln_Succ) {
		obj = OBJ(node,off);
		(*fn)(obj, p1);
	}
}

/* Remove an object from the system */
deleteobj(obj) struct MyWBObject *obj; {
    struct MyDrawerData *dd;
    struct MyWBObject *child;
    struct Node *node;
    long off;

    if (dd = obj->wo_DrawerData) {
	if (dd->dd_DrawerWin) /* Shouldn't happen */
	    {printf("Can't delete me--I'm open\n"); return;}
	if (dd->dd_Lock) {
	    if (debug) printf("deleteobj: Unlocking dd_Lock: <%s> %lx\n",
		obj->wo_Name, dd->dd_Lock);
	    UnLock(dd->dd_Lock);
	    dd->dd_Lock = NULL;
	}
	while (node = RemHead(&dd->dd_Children)) {
	    child = OBJ(node, CHILD);
	    if (debug) printf("Found an orphan %lx\n", child);
	    child->wo_Parent = NULL;
	}
    }
    if (debug) printf("Deleting %lx <%s> %lx\n", obj,
	obj->wo_MasterNode.ln_Name, obj->wo_Lock);
    if (obj->wo_Lock) {
	if (debug) printf("deleteobj: Unlocking wo_Lock: <%s> %lx\n",
	    obj->wo_Name, obj->wo_Lock);
	UnLock(obj->wo_Lock);
	obj->wo_Lock = NULL;
    }
    FreeFreeList(&obj->wo_FreeList);
    for (off=0; off<4; off++) {
	node = (struct Node *)((long)obj + off*sizeof(struct Node));
	if (node->ln_Succ) Remove(node);
    }
    FreeMem(obj, (long)sizeof(struct MyWBObject));
}

struct DiskObject *getDisk(name) char *name; {
	char buf[36];

	strcpy(buf, name);
	strcat(buf,":Disk");
	return GetDiskObject(buf);
}

/* Initial installation of all Volumes on the Device List */
findDisks() {
	struct DeviceList *diDev, *ptr;
	char diskName[31];

	diDev = (struct DeviceList *) BADDR(rnInfo->di_DevInfo);
	for (ptr=diDev; ptr; ptr=(struct DeviceList *)BADDR(ptr->dl_Next)){
	    if (ptr->dl_Type==DLT_VOLUME) {
		bs2cs(diskName, ptr->dl_Name);
		if (debug) printf("<%s>\n", diskName);
		instDisk(diskName);
	    }
	}
}

/* Install an object and icon for a disk */
instDisk(diskName) char *diskName; {
	struct DiskObject *dObj;
	struct MyWBObject *obj;

	if (dObj = getDisk(diskName)) {
		if (dObj->do_CurrentX==NO_ICON_POSITION)
			dObj->do_CurrentX = 576;
		if (dObj->do_CurrentY==NO_ICON_POSITION)
			dObj->do_CurrentY = lastHeight;
		obj = makeObj(dObj, diskName, wbWin, NULL);
		FreeDiskObject(dObj);
	}
	else {
		if (debug) printf("Default icon\n");
		dObj = &defDskObj;
		dObj->do_CurrentY = lastHeight;
		obj = makeObj(dObj, diskName, wbWin, NULL);
	}
	drawobj(obj, NORM);
	lastHeight += 22L;
	/* Should try harder to find an empty spot */
}

/* For debugging, print everybody on a List */
dumplist(list, off) struct List *list; long off; {
	struct Node *node;
	struct MyWBObject *child;

	printf("---\n");
	for (node=list->lh_Head; node->ln_Succ; node=node->ln_Succ) {
		child = OBJ(node,off);
		printf("%lx <%s>\n", node, node->ln_Name);
	}
}

/* Close a drawer object */
closeobj(obj) struct MyWBObject *obj; {
    struct MyDrawerData *dd;
    struct Node *node;
    struct MyWBObject *child;
    struct FileLock mylock;
    struct Region *reg;

    if (!(dd = obj->wo_DrawerData)) return;
    if (!dd->dd_DrawerWin) return;
    NewList(&utilObjs);
    for (node=dd->dd_Children.lh_Head; node->ln_Succ;
    node = node->ln_Succ) {
	child = OBJ(node,CHILD);
	if (debug) printf("child: %lx <%s>\n", child, child->wo_Name);
	if (child->wo_IconWin==dd->dd_DrawerWin) { /* In this window */
	    if (child->wo_DrawerData) /* a drawer */ {
		if (child->wo_DrawerData->dd_DrawerWin) {
		    if (debug) printf("Child open..retained\n");
		    child->wo_IconWin = NULL;
		    if (child->wo_SelectNode.ln_Succ) {
			Remove(&child->wo_SelectNode);
			child->wo_SelectNode.ln_Succ = NULL;
		    }
		    continue;
		}
		if (child->wo_DrawerData->dd_Children.lh_Head->ln_Succ) {
		    if (debug) {
			printf("Child has children..retained\n");
			printf("closeobj: grandchildren:");
			dumplist(&child->wo_DrawerData->dd_Children, CHILD);
		    }
		    child->wo_IconWin = NULL;
		    continue;
		}
	    }/* if drawer */
	    AddHead(&utilObjs, &child->wo_UtilityNode);
	}/* if in window */
    }/* for children */
    doList(&utilObjs, UTIL, deleteobj); /* Kill the kids */
    dd->dd_NewWindow.LeftEdge = dd->dd_DrawerWin->LeftEdge;
    dd->dd_NewWindow.TopEdge = dd->dd_DrawerWin->TopEdge;
    dd->dd_NewWindow.Width = dd->dd_DrawerWin->Width;
    dd->dd_NewWindow.Height = dd->dd_DrawerWin->Height;
    reg = InstallClipRegion(dd->dd_DrawerWin->WLayer, NULL);
    DisposeRegion(reg);
    closeWinSafely(dd->dd_DrawerWin);
    dd->dd_DrawerWin = NULL;
    if (dd->dd_Lock) {
	if (debug) printf("closeobj: cd %lx\n", initialDir);
	CurrentDir(initialDir);
	if (debug) printf("closeobj: Unlocking dd_Lock <%s> %lx\n",
	    obj->wo_Name, dd->dd_Lock);
	UnLock(dd->dd_Lock);
	dd->dd_Lock = NULL;
    }
}

/* Remove an icon from the screen */
clearobj(obj) struct MyWBObject *obj; {
    struct Window *w;
    struct MyDrawerData *dd;
    long cx=0, cy=0;

    if ((w = obj->wo_IconWin)!=wbWin) {
	dd = ((struct MyWBObject *)w->UserData)->wo_DrawerData;
	cx = dd->dd_CurrentX;
	cy = dd->dd_CurrentY;
    }
    if (debug) printf("clearobj: %lx <%s> win=%lx\n", obj, obj->wo_Name, w);
    SetAPen(w->RPort, 0L);
    RectFill(w->RPort,
	obj->wo_CurrentX - cx, obj->wo_CurrentY + YOFF - cy,
	obj->wo_CurrentX + obj->wo_Gadget.Width - cx,
	obj->wo_CurrentY + obj->wo_Gadget.Height + YOFF - cy);
    RectFill(w->RPort,
	obj->wo_CurrentX + obj->wo_NameXOffset - cx,
	obj->wo_CurrentY + obj->wo_NameYOffset + YOFF - cy,
	obj->wo_CurrentX + obj->wo_NameXOffset +
		8L*(long)strlen(obj->wo_Name) - cx,
	obj->wo_CurrentY + obj->wo_NameYOffset + YOFF + 10L - cy);
    SetAPen(w->RPort, 1L);
}

/* Toggle an icon, highlighted <-> normal */
compobj(obj) struct MyWBObject *obj; {
	struct Window *w;
	long cx=0, cy=0;
	struct MyDrawerData *dd;

	if (debug) printf("comp: %lx <%s>\n", obj, obj->wo_Name);
	if ((w = obj->wo_IconWin)!=wbWin) {
		dd = ((struct MyWBObject *)w->UserData)->wo_DrawerData;
		cx = dd->dd_CurrentX;
		cy = dd->dd_CurrentY;
	}
	ClipBlit(w->RPort, obj->wo_CurrentX - cx, obj->wo_CurrentY - cy,
		w->RPort, obj->wo_CurrentX - cx, obj->wo_CurrentY - cy,
		(long)obj->wo_Gadget.Width, (long)obj->wo_Gadget.Height,
		0x50L);
	obj->wo_Flags ^= HIGH;	/* For use by refresh() */
}

/* At position (x,y) see if an icon was hit and if so whom */
struct MyWBObject *findObj(x, y, w) short x, y; struct Window *w; {
	struct Node *node;
	struct Gadget *gad;
	struct MyWBObject *obj;
	struct MyDrawerData *dd;
	long cx=0, cy=0;

	if (w!=wbWin) {
		dd = ((struct MyWBObject *)w->UserData)->wo_DrawerData;
		cx = dd->dd_CurrentX;
		cy = dd->dd_CurrentY;
	}
	/* Scan list in reverse order,
	so that last icon drawn is first one found */
	for (node=wbObjs.lh_TailPred; node->ln_Pred; node=node->ln_Pred) {
		obj = (struct MyWBObject *)node;
		if (w != obj->wo_IconWin) continue;
		gad = &obj->wo_Gadget;
		if ((x > (obj->wo_CurrentX-cx)) &&
			(x < (obj->wo_CurrentX + gad->Width - cx)) &&
			(y > (obj->wo_CurrentY + YOFF - cy)) &&
			(y < (obj->wo_CurrentY + gad->Height + YOFF - cy)))
			return obj;
	}
	return NULL;
}

openobj(obj) struct MyWBObject *obj; {
	struct Window *w;
	struct MyDrawerData *dd;
	char name[80];
	struct Region *reg;
	BPTR lock;

	if (debug) printf("openobj:%lx <%s>\n",obj, obj->wo_Name);
	if (!(dd = obj->wo_DrawerData)) {
		printf("What's this %s doing here??\n",type[obj->wo_Type]);
		return;
	}
	if (dd->dd_DrawerWin) return; /* Already open */
	SetPointer(curWin, zz_ptr, 19L, 16L, -7L, -8L);
	dd->dd_NewWindow.Title = obj->wo_Name;
	dd->dd_NewWindow.IDCMPFlags = 0;
	if (!(w = OpenWindow(&dd->dd_NewWindow)))
		{error("Couldn't open the drawer\n"); goto opcln;}
	dd->dd_DrawerWin = w;
	w->UserPort = IDCMPPort;
	ModifyIDCMP(w, CLOSEWINDOW | GADGETUP | GADGETDOWN |
		MENUPICK | NEWSIZE | MOUSEBUTTONS | REFRESHWINDOW);
	SetMenuStrip(w, menuStrip);
	SetWindowTitles(w, -1L, title);
	reclip(w);
	/* UserData holds a pointer back to the object */
	w->UserData = (BYTE *)obj;

	lock = getlock(obj);
	if (debug) printf("openobj: cd %lx\n", lock);
	CurrentDir(lock);
	if (!dd->dd_Lock) {
		strcpy(name, obj->wo_Name);
		if (obj->wo_Type==WBDISK) strcat(name,":");
		dd->dd_Lock = Lock(name, ACCESS_READ);
		if (debug) printf("openobj Locked <%s> %lx\n",
			name, dd->dd_Lock);
		if (!dd->dd_Lock) {
			error("This drawer cannot be opened");
			/* Probably an .info file all by itself */
			reg = InstallClipRegion(w->WLayer, NULL);
			DisposeRegion(reg);
			closeWinSafely(w);
			dd->dd_DrawerWin = NULL;
			goto opcln;
		}
	}
	dir(obj, w);
	resize(w);
opcln:	ClearPointer(curWin);
}

/* Search a directory for .info files */
dir(obj, w) struct MyWBObject *obj; struct Window *w; {
	FILE *fd;
	struct InfoData *id;
	LONG success;
	struct FileInfoBlock *m;
	BPTR oldlock, newLock;
	char *name, buf[80], buf2[80], *s;
	struct DiskObject *dObj;
	struct MyWBObject *wbObj;
	struct Node *node;
	struct MyDrawerData *dd;

	dd = obj->wo_DrawerData;
	name = obj->wo_Name;
	strcpy(buf, name);
	strcat(buf,":");

	if (!(newLock = dd->dd_Lock)) {printf("No lock\n"); return;}
	if (debug) printf("dir: cd %lx\n", newLock);
	oldlock = CurrentDir(newLock);
	/* If we were using the .info file itself: */
	/*if (fd=fopen(".info","r")) {
	    if (fseek(fd, 16L, 0)!=-1) {
		while (fgets(buf2, 70, fd)) {
		    buf2[strlen(buf2)-1]=0;
		    if (dObj=GetDiskObject(buf2)) {
			obj = makeObj(dObj, buf2, w, obj);
			doList(&selObjs, SEL, drawobj, NORM);
			FreeDiskObject(dob);
		    }
		    else printf("can't get <%s>\n",buf2);
		}
	    }
	    fclose(fd);
	    return;
	}*/
	if (!(id = ALLOC(InfoData)))
		{error("Can't allocate id"); goto dirCln;}
	if (!(success = Info(newLock, id)))
		{error("Can't get info"); goto dirCln;}
	if (!(m = ALLOC(FileInfoBlock)))
		{error("Can't allocate m"); goto dirCln;}
	if (!(success = Examine(newLock, m)))
		{error("Can't examine directory"); goto dirCln;}
	if (m->fib_DirEntryType<=0)
		{error("Not a directory"); goto dirCln;}
	NewList(&utilObjs);
	while (success = ExNext(newLock, m)) {
	    strcpy(buf, m->fib_FileName);
	    if (s = rindex(buf,'.')) if (strcmp(s,".info")==0) {
		*s = 0;
		if ((*buf) && (strcmp(buf,"Disk"))) {
		    if (debug) printf("Looking for <%s>\n", buf);
		    if (node = FindName(&dd->dd_Children, buf)) {
			wbObj = OBJ(node,CHILD);
			if (debug) printf("Found child <%s> %lx\n",
				node->ln_Name, wbObj);
			if (!wbObj->wo_IconWin)
			    wbObj->wo_IconWin = dd->dd_DrawerWin;
			AddTail(&utilObjs, &wbObj->wo_UtilityNode);
		    }
		    else if (dObj = GetDiskObject(buf)) {
			wbObj = makeObj(dObj, buf, w, obj);
			FreeDiskObject(dObj);
			AddTail(&utilObjs, &wbObj->wo_UtilityNode);
		    }
		    else printf("Can't get <%s>\n", m->fib_FileName);
		} /* if buf */
	    } /* if s */
	} /* while */
	doList(&utilObjs, UTIL, drawobj, NORM);
dirCln:	if (m) FreeMem(m, (long)sizeof(struct FileInfoBlock));
	if (id) FreeMem(id, (long)sizeof(struct InfoData));
}

/* Draw an icon, either normal or hightlighted */
drawobj(obj, mode) struct MyWBObject *obj; {
	struct Window *w;
	long cx=0, cy=0;
	struct MyDrawerData *dd;
	APTR im;

	if ((w = obj->wo_IconWin)!=wbWin) {
		dd = ((struct MyWBObject *)w->UserData)->wo_DrawerData;
		cx = dd->dd_CurrentX;
		cy = dd->dd_CurrentY;
	}
	im = (mode & HIGH) ? obj->wo_Gadget.SelectRender :
		obj->wo_Gadget.GadgetRender;
	DrawImage(w->RPort, im, obj->wo_CurrentX - cx,
		obj->wo_CurrentY + YOFF - cy);
	if (mode & HIGH) obj->wo_Flags |= HIGH;	/* For use by refresh() */
	else obj->wo_Flags &= ~HIGH;
	icon_txt.IText = obj->wo_Name;
	icon_txt.LeftEdge = obj->wo_NameXOffset;
	icon_txt.TopEdge = obj->wo_NameYOffset;
	PrintIText(w->RPort, &icon_txt, obj->wo_CurrentX - cx,
		obj->wo_CurrentY + YOFF - cy);
}

/* Run something */
run(obj) struct MyWBObject *obj; {
	struct WBStartup *msg;
	struct Process *proc;
	long seg, stack;
	struct MsgPort *id;
	struct Node *node;
	char *name, i;
	struct MyWBObject *arg;
	BPTR lock;

	if (obj->wo_Type==WBPROJECT) {
		if (!(name = obj->wo_DefaultTool))
		{error("This project has no default tool"); return;}
		if (debug) printf("Default Tool: <%s>\n", name);
	}
	else if (obj->wo_Type==WBTOOL) name = obj->wo_Name;
	if (debug) printf("Running <%s>\n", name);
	if (!obj->wo_Parent) {printf("I've lost my mommy\n"); return;}
	lock = getlock(obj);
	if (debug) printf("run: cd %lx\n", lock);
	CurrentDir(lock);
	if (!(seg = LoadSeg(name)))
		{error("Can't load the tool"); goto runCln;}
	stack = (obj->wo_StackSize) ? obj->wo_StackSize : 5000L;
	if (debug) printf("stack=%ld\n", stack);
	if (!(id = CreateProc(name, 0L, seg, stack)))
		{printf("Can't create a process\n"); goto runCln;}
	proc = (struct Process *)((long)(id) - sizeof(struct Task));
	if (!(msg = ALLOC(WBStartup)))
		{printf("Can't make a msg\n"); goto runCln;}
	msg->sm_Message.mn_ReplyPort = WBPort;
	msg->sm_Process = id;
	msg->sm_Segment = seg;
	msg->sm_NumArgs = 0;
	if (obj->wo_Type==WBPROJECT) msg->sm_NumArgs++;
	for (node=selObjs.lh_Head; node->ln_Succ; node=node->ln_Succ)
		msg->sm_NumArgs++;
	if (debug) printf("%ld args\n",msg->sm_NumArgs);
	if (!(msg->sm_ArgList =
	AllocMem(msg->sm_NumArgs*(long)sizeof(struct WBArg), MEMF_CPC)))
		{printf("Can't allocate arglist\n"); return;}
	i = 0;
	if (obj->wo_Type==WBPROJECT) {
		msg->sm_ArgList[i].wa_Name = name;
		msg->sm_ArgList[i].wa_Lock = Lock(name, ACCESS_READ);
		i++;
	}
	msg->sm_ArgList[i].wa_Name = obj->wo_Name;
	msg->sm_ArgList[i].wa_Lock = lock;
	i++;
	for (node=selObjs.lh_Head; node->ln_Succ; node=node->ln_Succ) {
		arg = OBJ(node,SEL);
		if (arg==obj) continue;
		if (arg->wo_Type==WBTOOL || arg->wo_Type==WBPROJECT)
			msg->sm_ArgList[i].wa_Name = arg->wo_Name;
		else msg->sm_ArgList[i].wa_Name = empty;
		msg->sm_ArgList[i].wa_Lock = getlock(arg);
		if (debug) printf("<%s> %lx\n",
			msg->sm_ArgList[i].wa_Name,
			msg->sm_ArgList[i].wa_Lock);
		/* Wrong for dirs--the lock should be a lock
		on the object itself */
		i++;
	}
	msg->sm_ToolWindow = obj->wo_ToolWindow;
	if (obj->wo_ToolWindow) if (debug)
		printf("ToolWindow:<%s>\n", obj->wo_ToolWindow);
	PutMsg(id, msg);
	toolsRunning++;
	return;
runCln:	if (seg) {
		if (debug) printf("runCln: unloading\n");
		UnLoadSeg(seg);
	}
}

void *allocList(obj, size) struct MyWBObject *obj; {
	void *buf;

	if (!(buf = AllocMem((long)size, MEMF_CPC)))
		closeAll("Can't allocList");
	AddFreeList(&obj->wo_FreeList, buf, (long)size);
	return buf;
}

refresh(w) struct Window *w; {
	struct MyWBObject *obj;
	struct Node *node;

	if (debug) printf("Refresh %lx <%s>\n", w, w->Title);
	for (node=wbObjs.lh_Head; node->ln_Succ; node=node->ln_Succ) {
		obj = (struct MyWBObject *) node;
		if (obj->wo_IconWin==w) drawobj(obj, obj->wo_Flags & HIGH);
	}
}
@EOF

chmod 666 hbobj.c

echo x - hbgad.c
cat >hbgad.c <<'@EOF'
/****************************************************************
/*								*
/*	HackBench - Part 4 of 4 - hbgad.c - Gadgets and Menus	*
/*								*
/*	Copyright (C) 1987 by Bill Kinnersley			*
/*	CS Dept, Washington State Univ, Pullman, WA 99164	*
/*								*
/*	Permission granted to redistribute this program		*
/*	provided the copyright notice remains intact.		*
/*	May not be used as part of any commercial product.	*
/*								*
/****************************************************************/

#include "hb.h"

extern int drawobj(), clearobj(), openobj(), closeobj(), compobj();
extern char *rindex();
extern short debug;
extern struct Window *wbWin, *curWin, *errWin;
extern struct MyWBObject *lastObj;
extern struct List wbObjs, selObjs, utilObjs;

struct MsgPort *DOSPort, *IDCMPPort, *WBPort;
long IDCMPBit, WBBit;
extern void *IconBase;
struct DosLibrary *DosBase;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct LayersBase *LayersBase;
struct DosInfo *rnInfo;

long sendpkt();
struct Remember *RememberKey=NULL;
char *title = "HackBench release 1.0:                                     ";
char lastErr[60];
BPTR initialDir;

BYTE sbuf[61], undoBuf[61];
struct StringInfo ren_si = {
	sbuf, undoBuf, 0, 60, 0, 0, 1, 60, 0, 0, NULL, 0, NULL
};
struct Gadget ren_gad = {
	NULL, 1, 1, 500, 10, GADGHCOMP, STRINGCENTER | RELVERIFY,
	STRGADGET, NULL, NULL, NULL, 0, (APTR)&ren_si, GID_NAME, NULL
};

struct NewWindow ren_nw = {
	60, 100, 504, 11, 3, 1, NULL, ACTIVATE, &ren_gad,
	NULL, NULL, NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN
};

/* Used to generate menu items */
struct IntuiText gen_txt = {0, 1, JAM2, 5, 0, NULL, NULL, NULL};

USHORT dm_id[] = {0xfe7f, 0xfe7f, 0xfe7f, 0xce73, 0xf24f, 0xfc3f};
struct Image dm_img = {0, 0, 14, 6, 1, &dm_id[0], 1, 0, NULL};
struct Gadget dm_gad = {
	NULL, -15, -14, 15, 6, GADGIMAGE | GRELRIGHT | GRELBOTTOM,
	GADGIMMEDIATE | RIGHTBORDER, BOOLGADGET,
	(APTR)&dm_img, NULL, NULL, 0, NULL, GID_DOWNSCROLL, NULL
};

USHORT up_id[] = {0xfc3f, 0xf24f, 0xce73, 0xfe7f, 0xfe7f, 0xfe7f};
struct Image up_img = {0, 0, 14, 6, 1, &up_id[0], 1, 0, NULL};
struct Gadget up_gad = {
	&dm_gad, -15, 10, 15, 6, GADGIMAGE | GRELRIGHT,
	GADGIMMEDIATE | RIGHTBORDER, BOOLGADGET,
	(APTR)&up_img, NULL, NULL, 0, NULL, GID_UPSCROLL, NULL
};

USHORT rm_id[] = {
	0xffff, 0xf9f0, 0xfe70, 0xff90,0x0000, 0xff90, 0xfe70, 0xf9f0};
struct Image rm_img = {0, 0, 12, 8, 1, &rm_id[0], 1, 0, NULL};
struct Gadget rm_gad = {
	&up_gad, -26, -8, 12, 8, GADGIMAGE | GRELRIGHT | GRELBOTTOM,
	GADGIMMEDIATE | BOTTOMBORDER, BOOLGADGET,
	(APTR)&rm_img, NULL, NULL, 0, NULL, GID_RIGHTSCROLL, NULL
};

USHORT lm_id[] = {
	0xffff, 0xf3f0, 0xcff0, 0x3ff0, 0x00000, 0x3ff0, 0xcff0, 0xf3f0};
struct Image lm_img = {0, 0, 12, 8, 1, &lm_id[0], 1, 0, NULL};
struct Gadget lm_gad = {
	&rm_gad, 2, -8, 12, 8, GADGIMAGE | GRELBOTTOM,
	GADGIMMEDIATE | BOTTOMBORDER,
	BOOLGADGET,
	(APTR)&lm_img, NULL, NULL, 0, NULL, GID_LEFTSCROLL, NULL
};

struct Image knob_img1, knob_img2;

struct PropInfo hs_knob = { FREEHORIZ | AUTOKNOB, 0, 0, MAXBODY, MAXBODY};
struct Gadget hs_gad = {
	&lm_gad, 13, -8, -40, 9, GADGIMAGE | GRELWIDTH | GRELBOTTOM,
	GADGIMMEDIATE | RELVERIFY | BOTTOMBORDER, PROPGADGET,
	(APTR)&knob_img1, NULL, NULL, 0, (APTR)&hs_knob,
	GID_HORIZSCROLL, NULL
};

struct PropInfo vs_knob = {FREEVERT | AUTOKNOB, 0, 0, MAXBODY, MAXBODY};
struct Gadget vs_gad = {
	&hs_gad, -15, 16, 16,-31, GADGIMAGE | GRELHEIGHT | GRELRIGHT,
	GADGIMMEDIATE | RELVERIFY | RIGHTBORDER, PROPGADGET,
	(APTR)&knob_img2, NULL, NULL, 0, (APTR)&vs_knob,
	GID_VERTSCROLL, NULL
};

struct NewWindow nw = {
	0, 0, 640, 200, 0, 1,
	CLOSEWINDOW | GADGETUP | GADGETDOWN | MENUPICK | NEWSIZE |
	    DISKINSERTED | DISKREMOVED | MOUSEBUTTONS | REFRESHWINDOW,
	SMART_REFRESH | REPORTMOUSE | BORDERLESS | ACTIVATE | BACKDROP |
	    WBENCHWINDOW,
	NULL, NULL, "", NULL, NULL, 
	0, 0, 320, 186, WBENCHSCREEN
};

/* Default disk icon */
USHORT disk_icon[] = {
	0x0000, 0x0000, 0x3ffc, 0x3f00, 0x3ffc, 0x3fc0, 0x3ffc, 0x3ff0,
	0x3ffc, 0x3ffc, 0x3fff, 0xfffc, 0x3fff, 0xfffc, 0x3fff, 0xfffc,
	0x3fff, 0xfffc, 0x3fff, 0xfffc, 0x3fff, 0xfffc, 0x3fff, 0xfffc,
	0x3fff, 0xfffc, 0x3fff, 0xfffc, 0x3fff, 0xfffc, 0x0000, 0x0000,

	0xffff, 0xffc0, 0xc0ff, 0xfcf0, 0xc0ff, 0xfc3c, 0xc0ff, 0xfc0f,
	0xc0ff, 0xfc03, 0xc0ff, 0xfc03, 0xc000, 0x0003, 0xc000, 0x0003,
	0xc000, 0x0003, 0xc000, 0x0003, 0xc000, 0x0003, 0xc000, 0x0003,
	0xc000, 0x0003, 0xc000, 0x0003, 0xc000, 0x0003, 0xffff, 0xffff 
};
struct Image disk_img = {0, 0, 32, 16, 2, &disk_icon[0], 3, 0, NULL};

USHORT zz_ptr[] = {
	0x0000, 0x0000, 0x0008, 0x0000, 0x001c, 0x0000, 0x0026, 0x0000,
	0x0013, 0x0060, 0x0009, 0x00f0, 0x0006, 0x01f8,	0x0000, 0x03fc,
	0x0000, 0x07f8, 0x0000, 0x0fe0, 0x0070, 0x1ff0,	0x01f8, 0x3ff8,
	0x030c, 0x7ffc, 0x03dc, 0xf3fc, 0x0bbc, 0x4bfc,	0x030c, 0x03fc,
	0x51f8, 0x51f8, 0x00f0, 0x00f0, 0x2000, 0x2000,	0x2000, 0x2000,
	0x0000, 0x0000, 0x0000, 0x0000
};

/*USHORT zz_ptr[] = {
	0x0000, 0x0000, 0x0600, 0x0600, 0x0f40, 0x0f40, 0x3fe0, 0x3fe0,
	0x7fe0, 0x7fe0, 0x61f0, 0x7ff0, 0x7bf8, 0x7ff8, 0xf7f8, 0xfff8,
	0x61fc, 0x7ffc, 0x7f0c, 0x7ffc, 0x3fde, 0x3ffe, 0x7fbc, 0x7ffc,
	0x3f0c, 0x3ffc, 0x1ff8, 0x1ff8, 0x07f0, 0x07f0, 0x01c0, 0x01c0,
	0x0700, 0x0700, 0x0fc0, 0x0fc0, 0x0680, 0x0680, 0x0000, 0x0000,
	0x00c0, 0x00c0, 0x00e0, 0x00e0, 0x0040, 0x0040, 0x0000, 0x0000,
	0x0000, 0x0000
};
*/
USHORT x_ptr[] = {
	0x0000, 0x0000, 0x0600, 0x0000, 0x1f80, 0x0000, 0x3fc0, 0x0000,
	0x6660, 0x0000, 0x6060, 0x0000, 0xf0f0, 0x0000, 0xf0f0, 0x0000,
	0x6060, 0x0000, 0x6660, 0x0000, 0x3fc0, 0x0000, 0x1f80, 0x0000,
	0x0600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};

/* Defaults if no Disk.info file is found */
struct DrawerData defDrDat = {
	{1, 13, 379, 123, -1, -1, NULL,
	WBENCHWINDOW | SIZEBRIGHT | SIZEBBOTTOM | SMART_REFRESH |
	WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE,
	NULL, NULL, "", NULL, NULL, 90, 40, -1, -1, WBENCHSCREEN},
	0, 0
};

struct DiskObject defDskObj = {
	0, 1, {
		NULL, 575, 15, 32, 16, GADGIMAGE | GADGHBOX,
		RELVERIFY | GADGIMMEDIATE, BOOLGADGET,
		(APTR)&disk_img, NULL, NULL, 0, NULL, 17, NULL},
	WBDISK, NULL, NULL, 575, 6, &defDrDat, NULL, 0
};

short menuSet = FALSE;
long menuLoc = 30L;
struct Menu *lastMenu = NULL, *menuStrip = NULL;
char *benchNames[] = {"ParkBench", "Open", "Close", "Duplicate",
			"Rename", "Info", "Discard"};
char *diskNames[] = {"Disk", "Empty Trash", "Initialize"};
char *specNames[] = {"Special", "Cleanup", "Last Error", "Redraw",
			"Snapshot", "Version"};
char *debugNames[] = {"Debug", "Trace", "Wbobjs", "SelObjs",
			"UtilObjs", "Children", "Quit"};

makeMenu() {
	addMenu(benchNames, 6, BLACK_FILL);
	addMenu(diskNames, 2, BLACK_FILL);
	addMenu(specNames, 5, BLACK_FILL);
	addMenu(debugNames, 6, BLACK_FILL | ITEMENABLED);
	menuSet = TRUE;
	SetMenuStrip(wbWin, menuStrip);
}

addMenu (itemNames, numItems, flag)
char *itemNames[]; short numItems; long flag; {
	short i;
	long l, height=0L, width=0L;
	struct Menu *menu;
	struct MenuItem *firstItem, *lastItem, *menuItem;
	struct IntuiText *menuText;

	menu = (struct Menu *) AllocRemember(&RememberKey,
		(long)sizeof(struct Menu), MEMF_CPC);
	menu->Flags = MENUENABLED;
	menu->MenuName = itemNames[0];
	menu->Width = 10*strlen(itemNames[0]);
	menu->LeftEdge = menuLoc;
	menuLoc += (long)menu->Width + 30L;
	if (lastMenu) lastMenu->NextMenu = menu;
	else menuStrip = menu;
	lastMenu = menu;
	for (i=1; i<=numItems; i++)
		width = MAX(width, strlen(itemNames[i]));
	/*{l = strlen(itemNames[i+1]);if  (l>width) width = l;}*/
	width = 8L*width + 8L;
	lastItem = NULL;
	for (i=0; i<numItems; i++) {
		menuItem = (struct MenuItem *)AllocRemember(&RememberKey,
			(long)sizeof(struct MenuItem), MEMF_CPC);
		if (lastItem) lastItem->NextItem = menuItem;
		else menu->FirstItem = menuItem;
		lastItem = menuItem;
		menuItem->TopEdge = 10L * i;
		menuItem->Height = 8L;
		menuItem->Flags = flag;
		menuItem->Width = width;
		menuItem->MutualExclude = (~(1L<<i));
		menuText = (struct IntuiText *)AllocRemember(&RememberKey,
			(long)sizeof(struct IntuiText), MEMF_CPC);
/*stc*/		*menuText = gen_txt;
		menuText->IText = itemNames[i+1];
		menuItem->ItemFill = (APTR)menuText;
		height += 10L;
	}
	menu->Height = height;
}

openAll() {
	struct Process *me;
	struct Screen *sc;
	struct RootNode *dlRoot;

	if (!(DosBase = (struct DosLibrary *) OpenLibrary(DOSNAME,33L)))
		closeAll("Requires V1.2 DOS Library");
	if (!(GfxBase = (struct GfxBase *)
		OpenLibrary("graphics.library", 33L)))
		closeAll("Requires V1.2 Graphics Library");
	if (!(LayersBase = (struct LayersBase *)
		OpenLibrary("layers.library", 33L)))
		closeAll("Requires V1.2 Layers Library");
	if (!(IntuitionBase = (struct IntuitionBase *)
		OpenLibrary("intuition.library", 33L)))
		closeAll("Requires V1.2 Intuition Library");
	if (!(IconBase = OpenLibrary("icon.library", 31L)))
		closeAll("I give up, where's the icon library?\n");

	/* Make sure the backdrop fills the screen */
	for (sc=IntuitionBase->FirstScreen; sc; sc=sc->NextScreen)
	    if (sc->Flags & WBENCHSCREEN) {
		nw.Height = sc->Height; nw.Width = sc->Width;
		break;
	    }
	if (!(wbWin = OpenWindow(&nw)))
		closeAll("Couldn't open the backdrop window\n");
	if (!(WBPort = CreatePort(NULL, NULL)))
		closeAll("Couldn't create the WB port\n");

	WBBit = 1L << WBPort->mp_SigBit;
	IDCMPPort = wbWin->UserPort;
	IDCMPBit = 1L << wbWin->UserPort->mp_SigBit;
	me = (struct Process *)FindTask(NULL);
	initialDir = me->pr_CurrentDir;
	DOSPort = &me->pr_MsgPort;
	dlRoot = (struct RootNode *) DosBase->dl_Root;
	rnInfo = (struct DosInfo *) BADDR(dlRoot->rn_Info);

	NewList(&wbObjs);
	NewList(&selObjs);
}

doMenu(menuNum) USHORT menuNum; {
	if (menuNum==MENUNULL) return;
	switch ((long)menuNum) {
	case OPEN: doOpen();
		OffMenu(wbWin, OPEN); OnMenu(wbWin, CLOZE);
		break;
	case CLOZE: doList(&selObjs, SEL, closeobj);
		clearSel();
		break;
	case DUP: error("Not implemented"); break;
	case RENAME: ren(lastObj); break;
	case INFO:
	case DISCARD:

	case EMPTY:
	case INIT:

	case CLEANUP: error("Not implemented"); break;
	case ERROR: error(lastErr); break;
	case REDRAW: break;
	case SNAP: error("Not implemented"); break;
	case VERS: error(" (C) 1987 by Bill Kinnersley"); break;

	case TRACE: debug = ~debug; break;
	case WBOBJS:  dumplist(&wbObjs, MAST); break;
	case SELOBJS: dumplist(&selObjs, SEL); break;
	case UTILOBJS: dumplist(&utilObjs, UTIL); break;
	case CHILDREN: if (!lastObj) error("No object selected");
		else if (lastObj->wo_DrawerData)
		dumplist(&lastObj->wo_DrawerData->dd_Children, CHILD);
		break;
	case QUIT: closeAll(""); break;
	}
}

/* Rename an object */
ren(obj) struct MyWBObject *obj; {
	struct IntuiMessage *msg;
	struct IntuiMessage saveMsg;
	USHORT gid;
	char buf1[80], *buffer;
	struct Window *renWin;
	ULONG saveIDCMP;
	struct MsgPort *handid;
	long arg[1];

	if (!obj) {error("No icon selected"); return;}
	if (!(buffer = AllocMem(50L, MEMF_CPC))) /* Must be aligned */
		{printf("Couldn't allocate buffer"); return;}
	strcpy(sbuf, obj->wo_Name); strcpy(buf1, obj->wo_Name);
	if (!(renWin = OpenWindow(&ren_nw)))
		closeAll("Can't open rename window");
	renWin->UserPort = IDCMPPort;
	saveIDCMP = wbWin->IDCMPFlags;
	ModifyIDCMP(renWin, ACTIVEWINDOW | GADGETUP);
	ActivateGadget(&ren_gad, renWin, NULL);
	curWin = renWin;
	error("Enter the new name");
	for (;;) {
	    WaitPort(IDCMPPort);
	    while (msg = GetMsg(IDCMPPort)) {
/*stc*/		saveMsg = *msg;
		ReplyMsg(msg);
		if (saveMsg.IDCMPWindow!=renWin) {
		    /* If user clicks somewhere else, ignore him */
		    SetPointer(saveMsg.IDCMPWindow, zz_ptr,
			19L, 16L, -7L, -8L);
		    continue;
		}
		gid = ((struct Gadget *)saveMsg.IAddress)->GadgetID;
		switch (saveMsg.Class) {
		case GADGETUP:
		    if (gid!=GID_NAME) break;
		    if (obj->wo_Type==WBDISK) {
			if (strcmp(sbuf, obj->wo_Name)==0) goto rencln;
			cs2bs(buffer, sbuf);
			arg[0] = ((long)buffer)>>2;
			if (!(handid = DeviceProc(obj->wo_Name))) {
			    printf("Couldn't get handler\n");
			    goto rencln;
			}
			if (!sendpkt(handid, ACTION_RENAME_DISK,
			    arg, 1L)) {
			    error("Couldn't rename the disk");
			    /* User probably cancelled a requester */
			    goto rencln;
			}
		    }
		    else {
			if (debug) printf("I should rename <%s> to <%s>\n",
				obj->wo_Name, sbuf);
			strcat(buf1, ".info"); strcat(sbuf, ".info");
			if (debug) printf("and <%s> to <%s>\n", buf1, sbuf);
			if (rename(buf1, sbuf)==-1)
				{error("Can't rename"); goto rencln;}
			*rindex(buf1,'.') = 0; *rindex(sbuf,'.') = 0;
			rename(buf1, sbuf);
			/* Ignore error this time--if the real file does
			not exist, at least the .info has been renamed */
		    }
		    goto renok;
		case GADGETDOWN:
		    if (gid==GID_NAME) ClearPointer(renWin);
		    break;
		default: SetPointer(curWin, zz_ptr,  19L, 16L, -7L, -8L);
		}
	    }
	}
renok:	strcpy(obj->wo_Name, sbuf);
	obj->wo_NameXOffset = (obj->wo_Gadget.Width/2) - 4*strlen(sbuf);
	clearSel();
rencln: renWin->UserPort = NULL;
	CloseWindow(renWin);
	ModifyIDCMP(wbWin, saveIDCMP);
	FreeMem(buffer, 50L);
	ClearPointer(curWin = wbWin);
}

avail() {
	if (errWin) return;
	sprintf(title+25, "%ld free memory", AvailMem(MEMF_PUBLIC));
	SetWindowTitles(curWin, -1L, title);
}

error(s) char *s; {
	strcpy(lastErr, s);
	DisplayBeep(wbWin->WScreen);
	SetWindowTitles(curWin, -1L, s);
	OnMenu(wbWin, ERROR);
	errWin = curWin;
}

closeAll(s) char *s; {
	struct MyWBObject *obj;
	struct MyDrawerData *dd;
	struct Region *reg;
	printf(s);
	if (debug) printf("Closing things\n");
	while (obj = (struct MyWBObject *)RemHead(&wbObjs)) {
		if (debug) printf("Removing %lx <%s>\n", obj, obj->wo_Name);
		if (dd = obj->wo_DrawerData) {
			if (dd->dd_DrawerWin) {
				if (debug) printf("   (it was open)\n");
				reg = InstallClipRegion(dd->dd_DrawerWin->
					WLayer, NULL);
				DisposeRegion(reg);
				closeWinSafely(dd->dd_DrawerWin);
			}
			if (dd->dd_Lock) {
			    if (debug) printf("Unlocking dd_Lock %lx\n",
				dd->dd_Lock);
			    UnLock(dd->dd_Lock);
			}
		}
		if (obj->wo_Lock) {
			if (debug) printf("Unlocking wo_Lock %lx\n",
				obj->wo_Lock);
			UnLock(obj->wo_Lock);
		}
		FreeFreeList(&obj->wo_FreeList);
		FreeMem(obj, (long)sizeof(struct MyWBObject));
	}
	if (menuSet) ClearMenuStrip(wbWin, menuStrip);
	FreeRemember(&RememberKey, TRUE);
	if (wbWin) CloseWindow(wbWin);
	if (WBPort) DeletePort(WBPort);
	if (IconBase) CloseLibrary(IconBase);
	CurrentDir(initialDir);
	exit(0);
}

closeWinSafely(win) struct Window *win; {
/* This and the next courtesy Neil Katin */
	Forbid();
	stripIntuiMessages(win->UserPort, win);
	win->UserPort = NULL;
	ModifyIDCMP(win, 0L);
	win->MenuStrip = NULL;
	Permit();
	CloseWindow(win);
}

stripIntuiMessages(mp, win) struct MsgPort *mp; struct Window *win; {
	struct IntuiMessage *msg, *succ;

	msg = (struct IntuiMessage *)mp->mp_MsgList.lh_Head;
	while (succ = (struct IntuiMessage *)
		msg->ExecMessage.mn_Node.ln_Succ) {
		if (msg->IDCMPWindow==win) {
			Remove(msg);
			ReplyMsg(msg);
		}
		msg = succ;
	}
}

long sendpkt(pid, action, args, nargs)
struct MsgPort *pid; long action, args[], nargs; {
	struct StandardPacket *packet;
	long count, *pargs, res1, error;

	if (!(packet = ALLOC(StandardPacket)))
		{printf("Can't allocate packet\n"); return;}
	packet->sp_Msg.mn_Node.ln_Name = (char *) &(packet->sp_Pkt);
	packet->sp_Pkt.dp_Link         = &(packet->sp_Msg);
	packet->sp_Pkt.dp_Port         = DOSPort;
	packet->sp_Pkt.dp_Type         = action;
	pargs = &(packet->sp_Pkt.dp_Arg1);
	for (count=0; count<nargs; count++) pargs[count] = args[count];
	PutMsg(pid, packet);
	WaitPort(DOSPort);
	GetMsg(DOSPort);
	res1 = packet->sp_Pkt.dp_Res1;
	FreeMem(packet, (long)sizeof(struct StandardPacket));
	if (!res1) printf("DOS error: %ld action=%ld\n",
		packet->sp_Pkt.dp_Res2, packet->sp_Pkt.dp_Type);
	return res1;
}
@EOF

chmod 666 hbgad.c

exit 0
--------8<-------------8<-------------8<-------------------
"Nesting is for the birds"
                --Bill Kinnersley
    USENET: ...!ucbvax!ucdavis!egg-id!ui3!wsucshp!kinner
    INTERNET: kinner%wsu@RELAY.CS.NET
    CSNET: kinner@cs1.wsu.edu
    MAIL: CS Dept, Washington State Univ, Pullman WA 99164-1210
    PHONE: (509)332-4008

ralph@mit-atrp.UUCP (Amiga-Man) (07/14/87)

In article <160011@wsucshp.UUCP> kinner@wsucshp.UUCP (Bill Kinnersley ) writes:
>
>> I'll either fix it or write a new Workbench....Big project, but
>> big win.  Anybody doing this already (please ?...so I don't
>> have to ?).
>
>Well, OK, it sounds like a good idea so I did it.
>
Fantastic ! You've done a great deed by allowing us all to understand how
we can improve the system at least for ourselves. And perhaps CBM might
take a few hints from a good working example of an improved WB to put
in 1.3 (or is that too late now ?). I hope it's CBM's goal to make the
WB the best user interface in the business. It is already a major win
though the ability to multitask with th erest of the system.

I'd just thought I'd throw in a few conclusions I came up with about how
things could be improved:

- Cluster icons into a single file, so drawer opens are immediate. Don't
  indicate how hard it is for developers to manage their icons. What
  really counts is what the most USER-hours are spent doing !
  I know that FACC should help here, but that is a secondary solution.
  The first open will still be slow, and if you are searching for something
  you're out of luck.
  Unfortunately, I suspect this requires a cloned info.library, instead of
  a new WB. However, I suspect that even the present WB would work with a
  new library. Anyone up for that one ? It just needs to be "call" compatible
  with the present one. From looking in the documentation it may not be too
  hard.

- Support a new form of file requestor which lets you pop back to the WB
  and select the icon there, then pop back to the program. Heck, we
  started it from an icon, KEEP IT GOING FROM ICONS !
  Consistancy is *vital* in a good user interface.

- Support iconification of running programs, AND allow passing icon
  shift-selects into iconified programs. I.E. start my editor up again (from
  it's iconification) and open a buffer with THIS(pop to WB and shift-select
  file icon) file. The iconification should use the original icon too, maybe
  slightly modified, and would probably be out on the workbench.

- Allow a field in the "tool types array" of the icons, which let you
  designate the run-time priority of the program you are starting up.

- Support the running of CLI-type scripts from the WB. I know there
  are rough tools for this, but it should be supported naturally.

These ideas come from someone who uses the WB *ALL* the time, unless
I get *forced* to use the CLI (which happens way too much for a professional
icon based user interface). By the way, I consider myself a serious power
user, not a beginner, although a beginner would benefit from these
features too.

Note to CBM folks: please pass these ideas to the people there who think about
such things. Thanks !