/****************************************************************
/*								*
/*	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 */
}
