[mod.mac.sources] BMgr - Blob Manager source

macintosh@felix.UUCP (01/27/87)

#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	BMgr/BlobMgr.h
#	BMgr/BMgrCalcRegion.c
#	BMgr/BMgrClick.c
#	BMgr/BMgrDrag.c
#	BMgr/BMgrDraw.c
#	BMgr/BMgrFlags.c
#	BMgr/BMgrFreeze.c
#	BMgr/BMgrHideShow.c
mkdir BMgr
sed 's/^X//' << 'SHAR_EOF' > BMgr/BlobMgr.h
X/*	Blob Manager global types and constants	*/
X
X# ifndef	_BlobMgr_
X
X# define	_BlobMgr_
X
X# ifndef	_Quickdraw_
X#include <QuickDraw.h>	/* brings in MacTypes.h as well */
X# endif
X
X# ifndef	nil
X# define	nil		0L
X# endif
X
X
X/*	blob flag masks  */
X
Xenum
X{
X	bEnableMask = 1,
X	bFreezeMask = 2,
X	bStatModeMask = 4,
X	bDragModeMask = 8,
X	bDrawModeMask = 12,	/* two bits, derived: bStatModeMask + bDragModeMask */
X	bNeedGlobMask = 16,
X	bScaleMask = 32,
X	bHJustMask = 192,	/* two bits:  64 + 128 */
X	bVJustMask = 768,	/* two bits:  256 + 512 */
X	bPicMask = 1024
X};
X
X
X/*	blob drawing modes	*/
X
Xenum
X{
X	normalDraw = 1,		/* draw blob normally */
X	dimDraw = 2			/* draw dimmed blob */
X};
X
X/*	blob part codes	*/
X
Xenum
X{
X	inStatBlob = 1,			 /* static region */
X	inDragBlob = 2,			 /* drag region */
X	inFullBlob = 3			 /* entire blob */
X};
X
X/*
X	special max glue count - no limit
X*/
X
X# define	infiniteGlue	0x8000
X
X
X/*
X	BlobClick return codes
X*/
X
Xenum
X{
X	/* 0 = nothing done */
X	bcGlue = 1,		/* donor blob glued to receptor */
X	bcUnglue,		/* glob unglued from receptor */
X	bcXfer,			/* glob transferred from one receptor to another */
X	bcDup,			/* glob duplicated from one receptor onto another */
X	bcSwap			/* globs of two receptors swapped */
X};
X
X
X/*
X	Messages passed by BlobClick to advisory functions
X*/
X
Xenum
X{
X	advDClick,		/* click in donor */
X	advRClick,		/* click in receptor */
X	advGlue,		/* glue transaction proposed */
X	advUnglue,		/* unglue transaction proposed */
X	advXfer,		/* transfer transaction proposed */
X	advDup,			/* duplicate transaction proposed */
X	advSwap			/* swap transaction proposed */
X};
X
X
X/*	DragGrayRgn result code for bad drag - Inside Mac is wrong! */
X
X# define	badDragResult	0x80008000L
X
X
X/*
X	BlobRecord structure
X	
X	flags:
X	bit 0: 1 = enabled, 0 = disabled
X	bit 1: 1 = frozen, 0 = thawed (1/0)
X	bit 2: 1 = static region is dimmed, 0 = normal
X	bit 3: 1 = drag region is dimmed, 0 = normal
X	bit 4: 1 = need explicit match, 0 = don't
X
X	fzflags:
X	bit 2: 1 = static region was dimmed before freeze, 0 = was normal
X	bit 3: 1 = drag region was dimmed before freeze, 0 = was normal
X
X*/
X
Xtypedef struct BlobRecord
X{
X	int			flags;			/* flags word */
X	int			fzFlags;		/* freeze flags word */
X	union						/* pic if bPicMask flag word bit set*/
X	{							/* proc if bit clear */
X		PicHandle	bPic;		/* blob picture (and original frame) */
X		ProcPtr		bDrawProc;	/* blob drawing procedure */
X	} bPicProc;
X	RgnHandle	statRgn;		/* current static region */
X	RgnHandle	dragRgn;		/* current drag region */
X	Rect		statRect;		/* original static region frame */
X	Rect		dragRect;		/* original drag region frame */
X	int			glueMax;		/* maximum times can glue to others */
X	int			glueCount;		/* number of times glued to others */
X	struct MatchRecord	**matches;	/* set of valid matches */
X	struct BlobRecord	**glob;	 /* currently glued blob */
X	long		bRefCon;		/* reference value */
X	struct BlobRecord	**nextBlob;	 /* next blob in set */
X} BlobRecord, *BlobPtr, **BlobHandle;
X
X
Xtypedef struct MatchRecord
X{
X	BlobHandle			mBlob;			/* blob that matches */
X	struct MatchRecord	**nextMatch;	/* next match record */
X} MatchRecord, *MatchPtr, **MatchHandle;
X
X
Xtypedef struct BlobSetRecord
X{
X	BlobHandle				firstBlob;		/* first Blob of set */
X	BlobHandle				lastBlob;		/* last Blob of set */
X	struct BlobSetRecord	**nextBlobSet;	/* next blob set */
X} BlobSetRecord, *BlobSetPtr, **BlobSetHandle;
X
X
X/*
X	Pseudo-functions
X*/
X
X# define	FirstBlob(bSet)		((**(bSet)).firstBlob)
X# define	LastBlob(bSet)		((**(bSet)).lastBlob)
X# define	NextBlob(b)			((**(b)).nextBlob)
X# define	SetBRefCon(b,val)	((**(b)).bRefCon = (long) (val))
X# define	GetBRefCon(b)		((long) (**(b)).bRefCon)
X# define	BStatBox(b)			((**((**(b)).statRgn)).rgnBBox)
X# define	BDragBox(b)			((**((**(b)).dragRgn)).rgnBBox)
X# define	BGlob(b)			((**(b)).glob)
X# define	FirstBMatch(b)		((**((**(b)).matches)).mBlob)
X
X
X/*
X	Functions returning non-integral values
X*/
X
XBlobHandle		GetBlobHandle ();
XBlobHandle		NewBlob ();
XBlobSetHandle	NewBlobSet ();
XBoolean 		BlobQuiet ();
XBoolean 		BlobSetQuiet ();
XBoolean 		InBlobMatchSet ();
Xlong			DTrackBlob ();
Xlong			TrackBlob ();
XBoolean			BlobDimmed ();
XBoolean			BlobActive ();
XBoolean			BlobFrozen ();
XBoolean			BlobEnabled ();
XBoolean			CanGlue ();
XBoolean			PicBlob ();
XRgnHandle		BCalcRegion ();
XBoolean			BTrackMouse ();
X
X# endifSHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrCalcRegion.c
X# include	"BlobMgr.h"
X
X
X/*
X	Return copy of the region corresponding to the part code.  The
X	caller is responsible for disposing of the region that is returned.
X*/
X
XRgnHandle BCalcRegion (b, partCode)
XBlobHandle	b;
Xint			partCode;
X{
XRgnHandle	rgn;
X
X	rgn = NewRgn ();
X	if (partCode == inDragBlob)
X		CopyRgn ((**b).dragRgn, rgn);
X	else
X	{
X		CopyRgn ((**b).statRgn, rgn);
X		if (partCode == inFullBlob)
X			UnionRgn ((**b).dragRgn, rgn, rgn);
X	}
X	return (rgn);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrClick.c
X/*
X	BlobClick and support routines. BlobClick is the basic blob
X	transaction handler.  Other routines and variables are used
X	to modify the way BlobClick functions.
X
X
X	unglueZoom determines whether BlobClick will zoom an outline of the
X	receptor drag region back to the donor when a donor is unglued
X	from a receptor by double-clicking.
X
X	badDragZoom determines whether the outline of a dragged donor will
X	be zoomed back where it was dragged from if it's not dragged
X	somewhere it can be glued to.
X
X	The permission booleans control the types of transactions that
X	BlobClick is allowed to perform:
X
X	ungluePerm is true if double-clicking detaches globs from receptors.	
X	xferPerm is true if globs can be transferred between receptors.	
X	swapPerm is true if globs can be swapped between receptors.
X	dupPerm is true if globs can be duplicated onto other receptors.
X	replacePerm is true if transfers, swaps and duplications can cause
X	a receptor's glob to be replaced by another.  Note that if
X	replacePerm is false, transfers and duplications can only be made
X	to empty receptors, and swaps fail altogether.
X
X	Swaps take precedence over duplications, and both take precedence
X	over transfers, if two or more of these flags are on.  All are
X	subject to the value of the replacement flag, as noted above.
X
X	lastWhen and lastWhere are for double-click detection.
X	If lastWhen is set to 0, the next click will not be interpreted
X	as a double-click.  Since that's how it's initialized, BlobClick
X	never mistakes the first click passed to it for a double-click.
X
X	BlobClick only saves the click time and location when the user
X	clicks in a receptor blob drag region and releases the mouse without
X	moving it, since that's the only time a double-click is relevant.
X
X	bcAdvise is the BlobClick filter function.  If it's nil, BlobClick
X	does all the work itself.  If bcFilter is set to the address of
X	some function, that function is consulted in certain circumstances
X	to see whether to continue processing or not.  If the filter returns
X	false, BlobClick terminates early.
X
X	bcResult holds the result of the last call to BlobClick.  db1,
X	db2, rb1 and rb2 are set to indicate which blobs were involved in
X	any transaction that occurs (these are known as the cast (as in
X	cast of thousands) of the transaction).  The result code and the
X	cast may be obtained by the host program with the BClickResult
X	routine, and have meanings as follows:
X
X	Glue: db1 = donor glued, rb1 = receptor glued to, db2 = donor replaced by
X		db1 (nil if none), rb2 nil.
X	Unglue: db1 = donor unglued, rb1 = receptor unglued from, db2, rb2 nil.
X	Transfer: db1 = donor transferred, rb1 = source receptor,
X		rb2 = destination receptor, db2 = donor replaced by db1 (nil if none).
X	Duplication: same as transfer, except db1 = donor duplicated.
X	Swap: db1 = donor originally on rb1, db2 = donor originally on rb2,
X		rb1 = source receptor, rb2 = dest receptor.
X
X	The result code and the cast are undefined if a filter function
X	causes BlobClick to terminate early.
X
X	srcRect and dstRect are used by BlobClick and BadDrag to keep
X	track of how to do zoomback on bad drags.
X*/
X
X# include	"BlobMgr.h"
X
X
Xstatic Boolean	unglueZoom = true;
Xstatic Boolean	badDragZoom = true;
X
Xstatic Boolean	ungluePerm = true;
Xstatic Boolean	xferPerm = true;
Xstatic Boolean	replacePerm = true;
Xstatic Boolean	dupPerm = false;
Xstatic Boolean	swapPerm = true;
X
X
Xstatic long		lastWhen = 0L;
Xstatic Point	lastWhere;
X
Xstatic Boolean	(*bcAdvise)() = nil;
X
Xstatic int			bcResult;
Xstatic BlobHandle	db1;
Xstatic BlobHandle	db2;
Xstatic BlobHandle	rb1;
Xstatic BlobHandle	rb2;
X
Xstatic Rect			srcRect, dstRect;
X
X
X/*
X	Set or get the flags determining zoomback behavior on bad drags
X	and unglue transactions
X*/
X
XSetBCZoomFlags (uGlueZoom, bDragZoom)
XBoolean uGlueZoom, bDragZoom;
X{
X	unglueZoom = uGlueZoom;
X	badDragZoom = bDragZoom;
X}
X
X
XGetBCZoomFlags (uGlueZoom, bDragZoom)
XBoolean *uGlueZoom, *bDragZoom;
X{
X	*uGlueZoom = unglueZoom;
X	*bDragZoom = badDragZoom;
X}
X
X
X/*
X	Set or get the flags determining transaction permissions
X*/
X
XSetBCPermissions (canUnglue, canXfer, canDup, canSwap, canRep)
XBoolean canUnglue, canXfer, canDup, canSwap, canRep;
X{
X	ungluePerm = canUnglue;
X	xferPerm = canXfer;
X	dupPerm = canDup;
X	swapPerm = canSwap;
X	replacePerm = canRep;
X}
X
X
XGetBCPermissions (canUnglue, canXfer, canDup, canSwap, canRep)
XBoolean *canUnglue, *canXfer, *canDup, *canSwap, *canRep;
X{
X	*canUnglue = ungluePerm;
X	*canXfer = xferPerm;
X	*canDup = dupPerm;
X	*canSwap = swapPerm;
X	*canRep = replacePerm;
X}
X
X
X/*
X	Set or get the address of the BlobClick advisory filter routine.
X	Pass nil to SetBCFilter to turn the filter off.
X*/
X
XSetBCAdvisory (p)
XBoolean	(*p)();
X{
X	bcAdvise = p;
X}
X
X
XGetBCAdvisory (p)
XBoolean (**p)();
X{
X	*p = bcAdvise;
X}
X
X
X/*
X	FindReceptor is called with the point at which the mouse was released
X	after a donor or a receptor's glob was dragged.  It returns true if
X	the mouse was released in a receptor that has an undimmed drag region
X	and either (i) has no glob, or (ii) has a glob but replacement
X	permission is on.  Otherwise it returns false.
X*/
X
Xstatic Boolean FindReceptor (thePoint, rSet, r)
XPoint			thePoint;	/* point at which mouse released */
XBlobSetHandle	rSet;		/* receptor set */
XBlobHandle		*r;			/* return receptor hit in this */
X{
X	return (FindBlob (thePoint, rSet, r)
X			&& GetBDrawMode (*r, inDragBlob) == normalDraw
X			&& (BGlob (*r) == nil || replacePerm));
X}
X
X
X/*
X	BlobClick blob transaction handler
X*/
X
XBlobClick (thePt, t, dSet, rSet)
XPoint			thePt;
Xlong			t;
XBlobSetHandle	dSet, rSet;
X{
XBlobHandle			d, r1, r2;
XPoint				thePoint;
Xregister long		dragDelta;
Xregister Boolean	badDrag = true;	/* assume the worst */
X
X	thePoint = thePt;			/* do setup:  make local copy of point */
X	bcResult = 0;				/* and initialize status variables */
X	db1 = db2 = rb1 = rb2 = nil;
X
X/*
X	Check for double-click if unglue permission is on.  If so and both
X	clicks were in drag region of a non-dim receptor that has a glob
X	glued to it, unglue the glob.  (Note that the hit-tests only work
X	if the blob is not dimmed - which is as things should be.)
X*/
X	if (ungluePerm && t - lastWhen <= GetDblTime ())
X	{
X		if (rSet != nil
X			&& FindBlob (thePoint, rSet, &r1) == inDragBlob
X			&& TestBlob (r1, lastWhere) == inDragBlob)
X		{
X			if (bcAdvise == nil || (*bcAdvise) (advUnglue, r1))
X			{
X				db1 = BGlob (r1);
X				rb1 = r1;
X				bcResult = bcUnglue;
X
X				if (unglueZoom)
X					ZUnglueGlob (r1);
X				else
X					UnglueGlob (r1);
X			}
X		}
X		lastWhen = 0L;
X		return;
X	}
X
X	lastWhen = 0L;
X
X/*
X	See if the mouse was clicked in a donor blob.  If so, drag an
X	outline around.  If the dragged outline ends up somewhere it
X	can be glued to, do so, else zoom the outline back if appropriate.
X*/
X
X	if (dSet != nil && FindBlob (thePoint, dSet, &d) == inDragBlob)
X	{
X		if (bcAdvise != nil && (*bcAdvise) (advDClick, d) == false)
X			return;		/* advisory says "quit" */
X
X		dragDelta = DTrackBlob (d, inDragBlob, thePoint);
X		if (dragDelta != badDragResult && dragDelta != 0)
X		{
X			thePoint.h += LoWord (dragDelta);
X			thePoint.v += HiWord (dragDelta);
X			srcRect = dstRect = BDragBox (d);
X			OffsetRect (&dstRect, LoWord (dragDelta), HiWord (dragDelta));
X			if (rSet != nil && FindReceptor (thePoint, rSet, &r1))
X			{
X				if (bcAdvise == nil || (*bcAdvise) (advGlue, r1))
X				{
X					db1 = d;
X					db2 = BGlob (r1);
X					rb1 = r1;
X					bcResult = bcGlue;
X					GlueGlob (d, r1);
X					badDrag = false;
X				}
X			}
X			if (badDrag && badDragZoom)
X				ZoomRect (&dstRect, &srcRect);
X		}
X	}
X
X/*
X	Mouse was not clicked in a donor.  If it was clicked in a receptor,
X	a glob may have been dragged back to the donor, or to another
X	receptor, so get ready to process possible unglue or inter-receptor
X	transaction.  Can quit early if no receptor set was given
X	(rSet = nil) or no inter-receptor or unglue transactions are allowed.
X*/
X	else if (rSet != nil && (ungluePerm || xferPerm || swapPerm || dupPerm))
X	{
X		if (FindBlob (thePoint, rSet, &r1) == inDragBlob && BGlob (r1) != nil)
X		{
X			if (bcAdvise != nil && (*bcAdvise) (advRClick, r1) == false)
X				return;		/* advisory says "quit" */
X
X/*
X	If the mouse is released without being moved, then save the click
X	info, since it might be the first click of a double-click.  There's
X	no need to check any further for a possible transaction since the
X	glob wasn't dragged anywhere.
X*/
X			if ((dragDelta = DTrackBlob (r1, inDragBlob, thePoint)) == 0L)
X			{
X				lastWhen = t;		/* save click info for possible */
X				lastWhere = thePt;	/* double-click next time */
X			}
X			else if (dragDelta != badDragResult)
X			{
X				thePoint.h += LoWord (dragDelta);
X				thePoint.v += HiWord (dragDelta);
X
X/*
X	Was the glob dragged back to it's owner?  If so, unglue it if
X	unglue permission is on.  Can't use TestBlob, 'cause that'll
X	be false if the donor is dimmed.
X*/
X				d = BGlob (r1);
X				if (ungluePerm && BlobActive (d)
X						&& (PtInRgn (thePoint, (**d).dragRgn)
X							|| PtInRgn (thePoint, (**d).statRgn)))
X				{
X					if (bcAdvise == nil || (*bcAdvise) (advUnglue, r1))
X					{
X						db1 = d;
X						rb1 = r1;
X						bcResult = bcUnglue;
X						UnglueGlob (r1);	/* no zooming */
X					}
X					return;
X				}
X
X				srcRect = dstRect = BDragBox (r1);
X				OffsetRect (&dstRect, LoWord (dragDelta), HiWord (dragDelta));
X
X				if (FindReceptor (thePoint, rSet, &r2) && r1 != r2)
X				{
X
X	/*
X		Now know where the glob was dragged, so possibly have some kind
X		of inter-receptor transaction.  If replaces aren't allowed, then
X		don't continue unless the receptor that was dragged to has no glob.
X		Otherwise do whichever of swapping, duplicating or transferring is
X		allowed.  Precedence is in that order if more than one of them is
X		allowed.
X	*/
X					rb1 = r1;			/* set cast now, but they */
X					rb2 = r2;			/* won't be meaningful if */
X					db1 = BGlob (r1);	/* no transaction is performed */
X					db2 = BGlob (r2);
X					if (db2 != nil && swapPerm)
X					{
X						if (bcAdvise == nil || (*bcAdvise) (advSwap, r2))
X						{
X							SwapGlob (r1, r2);
X							bcResult = bcSwap;
X							badDrag = false;
X						}
X					}
X					else if (dupPerm)
X					{
X						if (CanGlue (db1))
X						{
X							if (bcAdvise == nil || (*bcAdvise) (advDup, r2))
X							{
X								DupGlob (r1, r2);	/* duplicate blob */
X								bcResult = bcDup;
X								badDrag = false;
X							}
X						}
X					}
X					else if (xferPerm)
X					{
X						if (bcAdvise == nil || (*bcAdvise) (advXfer, r2))
X						{
X							TransferGlob (r1, r2);	/* transfer blob */
X							bcResult = bcXfer;
X							badDrag = false;
X						}
X					}
X				}
X				if (badDrag && badDragZoom)
X					ZoomRect (&dstRect, &srcRect);
X			}
X		}
X	}
X}
X
X
X/*
X	Result result code of last call to BlobClick.
X*/
X
XBClickResult ()
X{
X	return (bcResult);
X}
X
X
X/*
X	Result cast of characters involved in last call to BlobClick.
X*/
X
XBClickCast (d1, d2, r1, r2)
XBlobHandle	*d1, *d2, *r1, *r2;
X{
X	*d1 = db1;
X	*d2 = db2;
X	*r1 = rb1;
X	*r2 = rb2;
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrDrag.c
X# include	<WindowMgr.h>
X# include	"BlobMgr.h"
X
X
X/* -------------------------------------------------------------------- */
X/*					Blob Tracking and Dragging Routines					*/
X/* -------------------------------------------------------------------- */
X
X
X/*
X	Blob dragging variables.  See discussion of DragGrayRgn in
X	the WIndow Manager section of Inside Mac.  The defaults for the
X	limit and slop rects are inappropriate and really should be set
X	by the application.
X*/
X
XRect	blobLimitRect = { -30000, -30000, 30000, 30000 };
XRect	blobSlopRect = { -30000, -30000, 30000, 30000 };
Xint		blobAxis = noConstraint;
X
X
X/*	Set blob dragging limit and slop rectangles	*/
X
XSetBDragRects (limitRect, slopRect)
XRect	*limitRect, *slopRect;
X{
X	blobLimitRect = *limitRect;
X	blobSlopRect = *slopRect;
X}
X
X
X/*	get current blob dragging limit and slop rectangles	*/
X
XGetBDragRects (limitRect, slopRect)
XRect	*limitRect, *slopRect;
X{
X	*limitRect = blobLimitRect;
X	*slopRect = blobSlopRect;
X}
X
X
X/*	Set blob dragging axis constraints	*/
X
XSetBDragAxis (axis)
Xint	axis;
X{
X	blobAxis = axis;
X}
X
X
X/*	Get current blob dragging axis constraints	*/
X
XGetBDragAxis ()
X{
X	return (blobAxis);
X}
X
X
X/*
X	Track blob by dragging an outline of the indicated part around
X	until the mouse button is released.	Return the difference between
X	the starting and ending x and y coordinates in the low and high
X	order words of the result.
X*/
X
X
Xlong TrackBlob (b, partCode, startPoint, limitRect, slopRect, axis)
XBlobHandle	b;
Xint			partCode;
XPoint		startPoint;
XRect		*limitRect, *slopRect;
Xint			axis;
X{
XRgnHandle	rgn;
XRect		limit, bounds;
Xlong		result;
X
X	rgn = BCalcRegion (b, partCode);
X/*
X	adjust the limit rectangle so the outline of the dragged regiopn
X	always fits completely within the bounds rectangle
X*/
X	limit = *limitRect;
X	bounds = (**rgn).rgnBBox;
X	limit.left += startPoint.h - bounds.left;
X	limit.top += startPoint.v - bounds.top;
X	limit.right -= bounds.right - startPoint.h - 1;
X	limit.bottom -= bounds.bottom - startPoint.v - 1;
X	
X	result = DragGrayRgn (rgn, startPoint, &limit, slopRect, axis, nil);
X	DisposeRgn (rgn);
X	return (result);
X}
X
X
X/*
X	Track a blob, using the default limit and slop rects
X*/
X
Xlong DTrackBlob (b, partCode, startPoint)
XBlobHandle	b;
Xint			partCode;
XPoint		startPoint;
X{
X
X	return (TrackBlob (b, partCode, startPoint,
X								&blobLimitRect, &blobSlopRect, blobAxis));
X}
X
X
X/*
X	Drag a blob by tracking it, and then moving it to the point
X	where the mouse button was released.
X*/
X
XDragBlob (b, startPoint, limitRect, slopRect, axis)
XBlobHandle	b;
XPoint		startPoint;
XRect		*limitRect, *slopRect;
Xint			axis;
X{
Xlong	result;
X
X	result = TrackBlob (b, inFullBlob, startPoint,
X							limitRect, slopRect, axis);
X
X	if ((result != badDragResult) && (result != 0))
X		OffsetBlob (b, inFullBlob, LoWord (result), HiWord (result));
X}
X
X
XDDragBlob (b, startPoint)
XBlobHandle	b;
XPoint		startPoint;
X{
X	DragBlob (b, startPoint, &blobLimitRect, &blobSlopRect, blobAxis);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrDraw.c
X/* --------------------------------------------------------------------- */
X/*					Blob Manager Low-level Drawing Routines				 */
X/* --------------------------------------------------------------------- */
X
X# include	"BlobMgr.h"
X
X
X
X/*
X	Blob dimming parameters.  dimPat and dimMode are the pattern and
X	drawing mode used to dim out blobs.  The initial pattern is gray.
X*/
X
Xstatic Pattern	dimPat = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
Xstatic int		dimMode = patBic;
X
Xstatic RgnHandle	oldClipRgn;
X
X
X/*
X	Set or get the pattern to be used to dim blobs
X*/
X
XSetBDimInfo (p, mode)
XPattern	*p;
Xint		mode;
X{
X	BlockMove (p, &dimPat, sizeof (Pattern));
X	dimMode = mode;
X}
X
XGetBDimInfo (p, mode)
XPattern	*p;
Xint		*mode;
X{
X	BlockMove (&dimPat, p, sizeof (Pattern));
X	*mode = dimMode;
X}
X
X
X/*
X	Set the clipping to the given region of the given blob.  Return
X	a handle to the region, or nil if the region is empty and doesn't
X	need to be drawn.  The handle should not be disposed of by the
X	caller - it's a copy of the handle, not a copy of the region.
X	If the handle returned is not nil, call RestoreDrawClip after doing
X	the drawing to restore the clipping properly.
X*/
X
Xstatic RgnHandle SetDrawClip (b, partCode)
XBlobHandle	b;
Xint			partCode;
X{
Xregister RgnHandle	drawRgn, newClipRgn;
X
X	drawRgn = (partCode == inDragBlob ? (**b).dragRgn : (**b).statRgn);
X	if (EmptyRgn (drawRgn))	/* don't bother if fail here! */
X		return (nil);
X
X	oldClipRgn = NewRgn ();
X	newClipRgn = NewRgn ();
X	GetClip (oldClipRgn);
X	SectRgn (drawRgn, oldClipRgn, newClipRgn);
X	SetClip (newClipRgn);
X	DisposeRgn (newClipRgn);
X	return (drawRgn);
X}
X
X
Xstatic RestoreDrawClip ()
X{
X	SetClip (oldClipRgn);				/* restore original clipping */
X	DisposeRgn (oldClipRgn);
X}
X
X
X/*
X	Dim the given region.
X*/
X
Xstatic DimRgn (rgn)
XRgnHandle	rgn;
X{
XPenState	ps;
X
X	GetPenState (&ps);			/* save current pen state */
X	PenPat (dimPat);
X	PenMode (dimMode);
X	PaintRgn (rgn);
X	SetPenState (&ps);			/* restore pen state */
X}
X
X
X/*
X	Given a picture and a rectangle corresponding to a portion of the picture
X	frame DrawBlobPic draws the picture in the given part of the given blob,
X	accounting for size and location differences.	Any portion of sRect
X	lying outside of the picture's frame will not be drawn.
X	The partCode must be inDragBlob or inFullBlob.
X
X	The drawing is clipped to the region the be drawn
X	(respecting current clipping).
X*/
X
Xstatic DrawBlobPic (b, partCode, p, srcRect)
XBlobHandle	b;
Xint			partCode;
XPicHandle	p;
XRect		*srcRect;
X
X{
Xregister RgnHandle	rgn;
XRect		dstRect, pFrame;
X
X	rgn = SetDrawClip (b, partCode);
X	if (p != nil && rgn != nil)		/* don't bother if fail here! */
X	{
X		pFrame = (**p).picFrame;			/* map frame to correct size */
X		dstRect = (**rgn).rgnBBox;			/* and location in relation */
X		MapRect (&pFrame, srcRect, &dstRect);	/* to region to be drawn */
X		DrawPicture (p, &pFrame);
X		if (GetBDrawMode (b, partCode) == dimDraw)	/* region is dimmed */
X			DimRgn (rgn);
X	}
X	if (rgn != nil)
X		RestoreDrawClip ();				/* restore original clipping */
X}
X
X
X/*
X	Draw a blob using a drawing procedure.  bDst is the blob to be drawn,
X	partCode specifies the region of bDst to draw (either inDragBlob or
X	inStatBlob).  bSrc is the blob whose region is to be drawn in the
X	specified region of bDst, and proc is the drawing procedure to use.
X	bSrc is different than bDst if partCode is inDragBlob and bDst has
X	a glob.
X*/
X
Xstatic CallBDrawProc (bDst, bSrc, proc, partCode)
XBlobHandle	bDst, bSrc;
XProcPtr		proc;
Xint			partCode;
X{
Xregister RgnHandle	rgn;
X
X	rgn = SetDrawClip (bDst, partCode);
X	if (proc != nil && rgn != nil)
X	{
X		(*proc) (bDst, bSrc, partCode);
X		if (GetBDrawMode (bDst, partCode) == dimDraw)	/* region is dimmed */
X			DimRgn (rgn);
X	}
X	if (rgn != nil)
X		RestoreDrawClip ();				/* restore original clipping */
X}
X
X
X/*
X	DrawBlob draws the indicated part of the blob (the entire blob
X	if partCode is inFullBlob).
X
X	The blob must be enabled.  If draw mode is dimDraw then draw
X	it dimmed.
X*/
X
X
XDrawBlob (b, partCode)
XBlobHandle	b;
Xint			partCode;
X
X{
Xregister BlobHandle	b2;
Xregister PicHandle	pic;
XRect		srcFrame;
X
X	if (BlobEnabled (b))
X	{
X		if ((partCode == inStatBlob) || (partCode == inFullBlob))
X		{
X			if (PicBlob (b))
X			{
X				srcFrame = (**b).statRect;
X				DrawBlobPic (b, inStatBlob, (**b).bPicProc.bPic, &srcFrame);
X			}
X			else
X				CallBDrawProc (b, b, (**b).bPicProc.bDrawProc, inStatBlob);
X		}
X
X		if ((partCode == inDragBlob) || (partCode == inFullBlob))
X		{
X		/*
X			Set b2 to the blob that should have it's drag region drawn
X			in b's drag region.  If b has no glob, then it's b.
X		*/
X			if ((b2 = BGlob (b)) == nil)
X				b2 = b;
X			if (PicBlob (b2))
X			{
X				pic = (**b2).bPicProc.bPic;	/* picture to draw */
X				srcFrame = (**b2).dragRect;	/* part of picture to draw */
X				DrawBlobPic (b, inDragBlob, pic, &srcFrame);
X			}
X			else
X				CallBDrawProc (b, b2, (**b2).bPicProc.bDrawProc, inDragBlob);
X		}
X	}
X}
X
X
XDrawBlobSet (bSet)
XBlobSetHandle	bSet;
X{
X	BlobLoopProc2 (&DrawBlob, bSet, inFullBlob);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrFlags.c
X# include	"BlobMgr.h"
X
X
X/* -------------------------------------------------------------------- */
X/*					 Blob Flag Manipulation Routines					*/
X/*																		*/
X/*	These routines operate only on the content of the blob state fields.*/
X/*	They do NOT do any redrawing or change the display in any way.		*/
X/*																		*/
X/* -------------------------------------------------------------------- */
X
X
XSetBlobFlags (b, bitMask)
XBlobHandle	b;
Xint			bitMask;
X{
X	(**b).flags |= bitMask;
X}
X
X
XClearBlobFlags (b, bitMask)
XBlobHandle	b;
Xint			bitMask;
X{
X	(**b).flags &= ~bitMask;			/* what's the operator here? */
X}
X
X
X/*
X	Test the function result to be sure it's equal to the bitMask
X	passed in, if exact match is required.
X*/
X
XTestBlobFlags (b, bitMask)
XBlobHandle	b;
Xint			bitMask;
X{
X	return ((**b).flags & bitMask);
X}
X
X
XEnableBlob (b)
XBlobHandle	b;
X{
X	SetBlobFlags (b, bEnableMask);
X}
X
X
XEnableBlobSet (bSet)
XBlobSetHandle	bSet;
X{
X	BlobLoopProc1 (&EnableBlob, bSet);
X}
X
X
XDisableBlob (b)
XBlobHandle	b;
X{
X	ClearBlobFlags (b, bEnableMask);
X}
X
X
XDisableBlobSet (bSet)
XBlobSetHandle	bSet;
X{
X	BlobLoopProc1 (&DisableBlob, bSet);
X}
X
X
X
XGetBDrawMode (b, partCode)
XBlobHandle	b;
Xint			partCode;
X{
Xint		mask;
X
X	mask = 0;
X	switch (partCode)
X	{
X		case inDragBlob: mask = bDragModeMask; break;
X		case inStatBlob: mask = bStatModeMask; break;
X	}
X	return (TestBlobFlags (b, mask) == mask ? dimDraw : normalDraw);
X}
X
X
XSetBDrawMode (b, partCode, mode)
XBlobHandle	b;
Xint			partCode;
Xint			mode;
X{
Xint		mask;
X
X	switch (partCode)
X	{
X		case inFullBlob: mask = bDrawModeMask; break;
X		case inDragBlob: mask = bDragModeMask; break;
X		case inStatBlob: mask = bStatModeMask; break;
X	}
X	if (mode == dimDraw)
X		SetBlobFlags (b, mask);
X	else
X		ClearBlobFlags (b, mask);
X}
X
X
XGetFzBDrawMode (b, partCode)
XBlobHandle	b;
Xint			partCode;
X{
Xint		mask;
X
X	mask = 0;
X	switch (partCode)
X	{
X		case inDragBlob: mask = bDragModeMask; break;
X		case inStatBlob: mask = bStatModeMask; break;
X	}
X	return (((**b).fzFlags & mask) == mask ? dimDraw : normalDraw);
X}
X
X
X
XBoolean BlobDimmed (b, partCode)
XBlobHandle	b;
Xint			partCode;
X{
Xint		bitMask;
X
X	switch (partCode)
X	{
X		case inFullBlob: bitMask = bDrawModeMask; break;
X		case inStatBlob: bitMask = bStatModeMask; break;
X		case inDragBlob: bitMask = bDragModeMask; break;
X	}
X	return (TestBlobFlags (b, bitMask) == bitMask);
X}
X
X
XBoolean BlobEnabled (b)
XBlobHandle	b;
X{
X	return (TestBlobFlags (b, bEnableMask) == bEnableMask);
X}
X
X
XBoolean BlobFrozen (b)
XBlobHandle	b;
X{
X	return (TestBlobFlags (b, bFreezeMask) == bFreezeMask);
X}
X
X
X/*
X	Active = enabled, but not frozen.
X*/
X
XBoolean BlobActive (b)
XBlobHandle	b;
X{
X	return (TestBlobFlags (b, bEnableMask | bFreezeMask) == bEnableMask);
X}
X
X
X/*
X	Return true if the blob can be glued to another blob.  This is true
X	if the blob is infinitely gluable or the current glue count is less
X	than the blob's maximum glue count.
X*/
X
XBoolean CanGlue (b)
XBlobHandle	b;
X{
Xint		glueMax;
X
X	glueMax = GetBGlueMax (b);
X	return (glueMax == infiniteGlue || glueMax > (**b).glueCount);
X}
X
X
X/*
X	Return true if the blob is a picture blob, false if it's a
X	procedure blob
X*/
X
XBoolean PicBlob (b)
XBlobHandle	b;
X{
X	return (TestBlobFlags (b, bPicMask) == bPicMask);
X}SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrFreeze.c
X# include	"BlobMgr.h"
X
X
XFreezeBlob (b)
XBlobHandle	b;
X{
X	if (!BlobFrozen (b))
X	{
X		SetBlobFlags (b, bFreezeMask);
X		(**b).fzFlags = ((**b).flags & (bDrawModeMask));
X	}
X}
X
X
XFreezeBlobSet (bSet)
XBlobSetHandle	bSet;
X{
X	BlobLoopProc1 (&FreezeBlob, bSet);
X}
X
X
XThawBlob (b)
XBlobHandle	b;
X{
X	if (BlobFrozen (b))
X	{
X		ClearBlobFlags (b, bFreezeMask);	/* unfreeze blob */
X		HiliteBlob (b, inStatBlob, GetFzBDrawMode (b, inStatBlob));
X		HiliteBlob (b, inDragBlob, GetFzBDrawMode (b, inDragBlob));
X	}
X}
X
X
XThawBlobSet (bSet)
XBlobSetHandle	bSet;
X{
X	BlobLoopProc1 (&ThawBlob, bSet);
X}
X
X
X/*
X	Display enabled quiet blobs with drawing mode quietMode, display
X	enabled noisy blobs with drawing mode noisyMode.	Freezes all enabled
X	blobs, so should be balanced with a call to ThawBlobs.
X*/
X
X
XBlobFeedback (bSet, quietMode, noisyMode)
XBlobSetHandle	bSet;
Xint				quietMode, noisyMode;
X{
XBlobHandle	b;
X
X	for (b = FirstBlob (bSet); b != nil; b = NextBlob (b))
X	{
X		FreezeBlob (b);
X		HiliteBlob (b, inFullBlob, (BlobQuiet (b) ? quietMode : noisyMode));
X	}
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrHideShow.c
X# include	"BlobMgr.h"
X
X
X
X/*
X	If blob not already hidden, hide it by filling both the bounds
X	box of each region with current grafport's background pattern,
X	and accumulate the boxes into the update region.
X*/
X
XHideBlob (b)
XBlobHandle	b;
X{
XGrafPtr	thePort;
XRect	r;
X
X	if (BlobEnabled (b))			/* ignore if already disabled */
X	{
X		DisableBlob (b);
X		GetPort (&thePort);
X
X		r = BStatBox (b);
X		FillRect (&r, &thePort->bkPat);
X		InvalRect (&r);
X
X		r = BDragBox (b);
X		FillRect (&r, &thePort->bkPat);
X		InvalRect (&r);
X	}
X}
X
X
XHideBlobSet (bSet)
XBlobSetHandle	bSet;
X{
X	BlobLoopProc1 (&HideBlob, bSet);
X}
X
X
X/*
X	If blob is not already enabled, show it.
X*/
X
XShowBlob (b)
XBlobHandle	b;
X{
X	if (BlobEnabled (b) == false)		/* ignore if already enabled */
X	{
X		EnableBlob (b);
X		DrawBlob (b, inFullBlob);
X	}
X}
X
X
XShowBlobSet (bSet)
XBlobSetHandle	bSet;
X{
X	BlobLoopProc1 (&ShowBlob, bSet);
X}
SHAR_EOF
exit

macintosh@felix.UUCP (01/28/87)

References:

#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	BMgr/BMgrHilite.c
#	BMgr/BMgrHitTest.c
#	BMgr/BMgrIndex.c
#	BMgr/BMgrLoop.c
#	BMgr/BMgrMatch.c
#	BMgr/BMgrMove.c
#	BMgr/BMgrNew.c
#	BMgr/BMgrPict.c
#	BMgr/BMgrRand.c
#	BMgr/BMgrSetProc.c
#	BMgr/BMgrSetRgns.c
#	BMgr/BMgrShuffle.c
#	BMgr/BMgrTrackMouse.c
#	BMgr/BMgrTrans.c
#	BMgr/BMgrZTrans.c
#	BMgr/BMgrZoom.c
mkdir BMgr
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrHilite.c
X# include	"BlobMgr.h"
X
X
X/* -------------------------------------------------------------------- */
X/*					Blob State Change Display Routines					*/
X/* -------------------------------------------------------------------- */
X
X
X
X/*
X	Set the drawing mode for the given part of the blob.
X	Redraw if it changes.
X*/
X
XHiliteBlob (b, partCode, mode)
XBlobHandle	b;
Xint			partCode;
Xint			mode;
X{
X
X	if ((partCode == inDragBlob) || (partCode == inFullBlob))
X	{
X		if (GetBDrawMode (b, inDragBlob) != mode)
X		{
X			SetBDrawMode (b, inDragBlob, mode);
X			DrawBlob (b, inDragBlob);
X		}
X	}
X	if ((partCode == inStatBlob) || (partCode == inFullBlob))
X	{
X		if (GetBDrawMode (b, inStatBlob) != mode)
X		{
X			SetBDrawMode (b, inStatBlob, mode);
X			DrawBlob (b, inStatBlob);
X		}
X	}
X}
X
X
XHiliteBlobSet (bSet, partCode, mode)
XBlobSetHandle	bSet;
Xint				partCode;
Xint				mode;
X
X{
Xregister BlobHandle	b;
X
X	for (b = FirstBlob (bSet); b != nil; b = NextBlob (b))
X	{
X		HiliteBlob (b, partCode, mode);
X	}
X}
X
X
X/*
X	Increment use count of blob and dim drag area if at or above maximum
X	glue count.
X*/
X
XIncBlobGlue (b)
XBlobHandle	b;
X{
X	if ((**b).glueMax != infiniteGlue)
X	{
X		++((**b).glueCount);
X		if ((**b).glueCount >= (**b).glueMax)
X			HiliteBlob (b, inDragBlob, dimDraw);
X	}
X}
X
X
X/*
X	Decrement use count of blob and undim drag area if less than maximum
X	glue count.
X*/
X
XDecBlobGlue (b)
XBlobHandle	b;
X{
X	if ((**b).glueMax != infiniteGlue)
X	{
X		--((**b).glueCount);
X		if ((**b).glueCount < (**b).glueMax)
X			HiliteBlob (b, inDragBlob, normalDraw);
X	}
X}
X
X
XSetBGlueMax (b, max)
XBlobHandle	b;
Xint			max;
X{
X	(**b).glueMax = max;
X
X/* dim or undim appropriately */
X
X	if ((**b).glueCount < max || max == infiniteGlue)
X		HiliteBlob (b, inDragBlob, normalDraw);
X	else
X		HiliteBlob (b, inDragBlob, dimDraw);
X}
X
X
XGetBGlueMax (b)
XBlobHandle	b;
X{
X	return ((**b).glueMax);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrHitTest.c
X# include	"BlobMgr.h"
X
X
X
X
X/* -------------------------------------------------------------------- */
X/*						Blob Hit Testing Routines						*/
X/* -------------------------------------------------------------------- */
X
X
X/*
X	Test whether the blob contains the point, and if so, return a part
X	of the part of the blob that the point is in.	Does not consider
X	dimmed parts of blobs.	Blob must be active (enabled, not frozen).
X	Return zero if no hit.
X*/
X
XTestBlob (b, thePoint)
XBlobHandle	b;
XPoint		thePoint;
X{
Xint		result;
X
X	result = 0;
X	if (BlobActive (b))
X	{
X		if (!BlobDimmed (b, inDragBlob) && PtInRgn (thePoint, (**b).dragRgn))
X				result = inDragBlob;
X		else if (!BlobDimmed (b, inStatBlob)
X			&& PtInRgn (thePoint, (**b).statRgn))
X				result = inStatBlob;
X	}
X	return (result);
X}
X
X
X/*
X	If the point is in an undimmed region of any active blob of the set,
X	return a handle to the blob in b and the part code as the function
X	result.
X*/
X
XFindBlob (thePoint, bSet, bPtr)
XPoint			thePoint;
XBlobSetHandle	bSet;
XBlobHandle		*bPtr;	/* pointer to blobhandle */
X
X{
XBlobHandle	b;
Xint			partCode;
Xint			result;
X
X	result = 0;
X	for (b = FirstBlob (bSet); b != nil; b = NextBlob (b))
X	{
X		partCode = TestBlob (b, thePoint);
X		if (partCode != 0)
X		{
X			result = partCode;
X			*bPtr = b;
X			break;
X		}
X	}
X	return (result);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrIndex.c
X# include	"BlobMgr.h"
X
X
X/*
X	Get the handle of the (i)th blob in the list.  The first
X	blob is numbered 0.
X*/
X
XBlobHandle GetBlobHandle (bSet, i)
XBlobSetHandle	bSet;
Xint				i;
X{
Xregister BlobHandle	b;
X
X	for (b = FirstBlob (bSet); b != nil; b = NextBlob (b))
X	{
X		--i;
X		if (i < 0) break;		/* found it */
X	}
X	return (b);
X}
X
X
X/*
X	Get the index of the given blob in the list, given its handle.
X	The blob is assumed to be in the set.  Returns a number from
X	0 to BlobSetSize (bSet)-1.
X*/
X
XGetBlobIndex (b, bSet)
XBlobHandle		b;
XBlobSetHandle	bSet;
X{
Xregister BlobHandle	b2;
Xregister int			i;
X
X	i = 0;
X	for (b2 = FirstBlob (bSet); b2 != nil; b2 = NextBlob (b2))
X	{
X		if (b == b2) break;		/* found it */
X		++i;
X	}
X	return (i);
X}
X
X
XBlobSetSize (bSet)
XBlobSetHandle	bSet;
X{
Xregister BlobHandle	b;
Xregister int			i;
X
X	for (i = 0, b = FirstBlob (bSet); b != nil; b = NextBlob (b))
X	{
X		++i;
X	}
X	return (i);
X}SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrLoop.c
X# include	"BlobMgr.h"
X
X
X/* -------------------------------------------------------------------- */
X/*							Blob Loop Routines							*/
X/*																		*/
X/*	These routines loop through the given blob set, calling the given	*/
X/*	procedure for every element of the set.								*/
X/* -------------------------------------------------------------------- */
X
X
XBlobLoopProc1 (p, bSet)
XProcPtr			p;
XBlobSetHandle	bSet;
X{
Xregister BlobHandle	b;
X
X	if (bSet != nil)
X	{
X		for (b = FirstBlob (bSet); b != nil; b = NextBlob (b))
X			(*p) (b);
X	}
X}
X
X
XBlobLoopProc2 (p, bSet, partCode)
XProcPtr			p;
XBlobSetHandle	bSet;
Xint				partCode;
X{
Xregister BlobHandle	b;
X
X	if (bSet != nil)
X	{
X		for (b = FirstBlob (bSet); b != nil; b = NextBlob (b))
X			(*p) (b, partCode);
X	}
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrMatch.c
X# include	"BlobMgr.h"
X
X
X/* -------------------------------------------------------------------- */
X/*					 Blob Match Testing Operations						*/
X/* -------------------------------------------------------------------- */
X
X
X/*
X	Return true if b1 is in b2's match set.
X*/
X
XBoolean InBlobMatchSet (b1, b2)
XBlobHandle	b1, b2;
X{
XMatchHandle	m;
XBoolean		result;
X
X	result = false;
X	for (m = (**b2).matches; m != nil; m = (**m).nextMatch)
X	{
X		if ((**m).mBlob == b1)
X		{
X			result = true;	/* found it */
X			break;
X		}
X	}
X	return (result);
X}
X
X
X/*
X	Default quiet test procedure.
X	
X	A blob requiring an explicit match is quiet if it has a glob and
X	the glob is a member of the match set, or, if the match set is
X	empty, it has no glob.	A blob not requiring an
X	explicit match is quiet if it has an explicit match, or if it
X	has a glob but an empty match set, or if it has no glob.
X
X	Otherwise the blob is noisy.
X*/
X
Xstatic Boolean DefaultQuietTest (b)
XBlobHandle	b;
X{
XBlobHandle	g;
XBoolean		needGlob;
XBoolean		result;
X
X	g = (**b).glob;
X	needGlob = (TestBlobFlags (b, bNeedGlobMask) != 0);
X/*
X	if the blob has no glob, it is matched if it's a non-explicit
X	blob.  If it's an explicit blob, it's matched if the match set
X	is empty.
X*/
X	if (g == nil)
X		result = (needGlob ? (**b).matches == nil : true);
X	else				/* has a glob - is it ok? */
X	{
X/*
X	if the blob doesn't need a glob but has one, then it's still
X	quiet if the match set is empty ("any glob will do").
X*/
X		if (!needGlob && ((**b).matches == nil))
X			result = true;
X		else
X			result = InBlobMatchSet (g, b);	/* see if in match set */
X	}
X	return (result);
X}
X
X
Xstatic Boolean	(*bQuietTest) () = DefaultQuietTest;
X
X
XBoolean BlobQuiet (b)
XBlobHandle	b;
X{
X	return ((*bQuietTest) (b));
X}
X
X
X/*
X	Install quiet test function for BlobQuiet.  Pass nil to restore
X	default.
X*/
X
XSetBQuietTest (f)
XBoolean	(*f)();
X{
X	bQuietTest = (f == nil ? DefaultQuietTest : f);
X}
X
X
XBoolean BlobSetQuiet (bSet)
XBlobSetHandle	bSet;
X{
XBlobHandle	b;
XBoolean		result;
X
X	result = true;
X	for (b = FirstBlob (bSet); b != nil; b = NextBlob (b))
X	{
X		if (!BlobQuiet (b))
X		{
X			result = false;
X			break;
X		}
X	}
X	return (result);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrMove.c
X# include	"BlobMgr.h"
X
X
X/* -------------------------------------------------------------------- */
X/*							Blob Movement Operations					*/
X/* -------------------------------------------------------------------- */
X
X
X/*
X	Offset a blob or part of a blob by (hoff, voff).	If the blob is enabled,
X	hide it and redraw at the new location.
X*/
X
XOffsetBlob (b, partCode, hoff, voff)
XBlobHandle	b;
Xint			partCode, hoff, voff;
X{
XBoolean	vis;
X
X	if ((hoff == 0) && (voff == 0)) return;	/* avoid unnecessary redraw */
X
X	vis = false;
X	if (BlobEnabled (b))
X	{
X		vis = true;
X		HideBlob (b);
X	}
X
X	if ((partCode == inDragBlob) || (partCode == inFullBlob))
X		OffsetRgn ((**b).dragRgn, hoff, voff);
X	if ((partCode == inStatBlob) || (partCode == inFullBlob))
X		OffsetRgn ((**b).statRgn, hoff, voff);
X
X	if (vis)
X		ShowBlob (b);
X}
X
X
X/*
X	Move a blob or part of a blob to (h, v).	If the blob is enabled,
X	hide it and redraw at the new location.	If partCode is inDragBlob
X	or inStatBlob, move the appropriate region to the location.	If
X	inFullBlob, move both regions in synchrony so static region is at
X	given location, drag region is at same location relative to the
X	static region as it was before.
X*/
X
XMoveBlob (b, partCode, h, v)
XBlobHandle	b;
Xint			partCode, h, v;
X{
X	if (partCode == inDragBlob)
X	{
X		h -= (**((**b).dragRgn)).rgnBBox.left;	/* convert to offsets */
X		v -= (**((**b).dragRgn)).rgnBBox.top;
X	}
X	else	/* it's inStatBlob or inFullBLob */
X	{
X		h -= (**((**b).statRgn)).rgnBBox.left;
X		v -= (**((**b).statRgn)).rgnBBox.top;
X	}
X	OffsetBlob (b, partCode, h, v);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrNew.c
X# include	"BlobMgr.h"
X
X
X
X# define	New(x)	(x **) NewHandle ((Size) sizeof (x))
X
X/*
X	Handle to list of all existing blob sets - empty initially
X*/
X
Xstatic BlobSetHandle	blobSetMaster = nil;
X
X
X/* ---------------------------------------------------------------------- */
X/*						Object Creation Routines						 */
X/* ---------------------------------------------------------------------- */
X
X
X
XNewBlobMatch (b1, b2)
XBlobHandle b1, b2;
X{
XMatchHandle	m;
X
X	m = New (MatchRecord);
X	if (m != nil)
X	{
X		(**m).mBlob = b1;
X		(**m).nextMatch = (**b2).matches;
X		(**b2).matches = m;
X	}
X}
X
X
X/*	Add a blob to (the END of) a blob set	*/
X
XAddBlob (b, bSet)
XBlobHandle		b;
XBlobSetHandle	bSet;
X{
X
X	if (FirstBlob (bSet) == nil)
X	{
X		FirstBlob (bSet) = b;
X		LastBlob (bSet) = b;
X	}
X	else
X	{
X		NextBlob (LastBlob (bSet)) = b;
X		LastBlob (bSet) = b;
X	}
X	NextBlob (b) = nil;
X}
X
X
X/*
X	Create a new blob, adding it to the given set.	The blob is enabled
X	or not according to the enable parameter.	The blob is given empty
X	static and drag regions and rectangles, nil picture, match set, and
X	glob handles.	The drawing modes are set according to statDraw
X	and dragDraw.	The blob is set to be single-use or not by the
X	glueMax parameter.	mustMatch determines whether the blob must
X	have a glue blob to be matched.	The refCon is installed in the
X	record.
X*/
X
XBlobHandle NewBlob (bSet,	/* set to add blob to */
X					enable,	/* whether blob is enabled or not */
X					glueMVal,	/* maximum glue count */
X					mustMatch,	/* whether must have glob to be matched */
X					refCon)		/* reference constant */
X
XBlobSetHandle	bSet;
XBoolean			enable;
Xint				glueMVal;
XBoolean			mustMatch;
Xlong			refCon;
X{
XBlobHandle	bHand;
XBlobPtr		b;
XRect		r;
X
X	bHand = New (BlobRecord);
X	if (bHand != nil)
X	{
X		AddBlob (bHand, bSet);	/* sets nextBlob field */
X		b = *bHand;
X		b->flags = 0 | bPicMask;	/* clear flags, then set appropriate fields */
X		if (enable)
X			EnableBlob (bHand);
X		if (mustMatch)
X			SetBlobFlags (bHand, bNeedGlobMask);
X		b->bPicProc.bPic = nil;			/* no picture or drawing proc */
X		SetRect (&r, 0, 0, 0, 0);
X		b->statRect = r;				/* empty rects */
X		b->dragRect = r;
X		b->statRgn = NewRgn ();			/* empty regions */
X		b->dragRgn = NewRgn ();
X		b->glueMax = glueMVal;
X		b->glueCount = 0;
X		b->glob = nil;
X		b->matches = nil;
X		b->bRefCon = refCon;
X
X	}
X
X	return (bHand);
X}
X
X
X/*
X	Create a new, empty blob set.	Add it to the master blob set list.
X	Return a handle to it.
X*/
X
XBlobSetHandle NewBlobSet ()
X{
XBlobSetHandle	bSet;
X
X	bSet = New (BlobSetRecord);
X	if (bSet != nil)
X	{
X		FirstBlob (bSet) = nil;				/* initialize to empty set */
X		LastBlob (bSet) = nil;
X		(**bSet).nextBlobSet = blobSetMaster;	/* add to master list */
X		blobSetMaster = bSet;
X	}
X
X	return (bSet);
X}
X
X
X/* ---------------------------------------------------------------------- */
X/*						 Object Disposal Routines						*/
X/* ---------------------------------------------------------------------- */
X
X
XDisposeBlobPic (b)
XBlobHandle	b;
X{
X	if (PicBlob (b) && (**b).bPicProc.bPic != nil)
X	{
X		KillPicture ((**b).bPicProc.bPic);
X		(**b).bPicProc.bPic = nil;
X	}
X}
X
X
XDisposeBlobMatchSet (b)
XBlobHandle	b;
X{
XMatchHandle	m1, m2;
X
X	m1 = (**b).matches;
X	(**b).matches = nil;
X	for (;;)
X	{
X		if (m1 == nil) return;
X		m2 = (**m1).nextMatch;
X		DisposHandle (m1);
X		m1 = m2;
X	}
X}
X
X
XDisposeBlob (b)
XBlobHandle	b;
X{
X	DisposeRgn ((**b).statRgn);
X	DisposeRgn ((**b).dragRgn);
X	DisposeBlobPic (b);
X	DisposeBlobMatchSet (b);
X	DisposHandle (b);
X}
X
X
X/*
X	Remove blob set from the master blob set list, then dispose of
X	everything in the list.	All handles to the set or elements in
X	it become invalid.
X*/
X
XDisposeBlobSet (bSet)
XBlobSetHandle	bSet;
X{
XBlobHandle		b1, b2;
XBlobSetHandle	bSet2;
X
X/*
X	remove set from master list
X*/
X	if (bSet == blobSetMaster)	/* first set in list */
X		blobSetMaster = (**blobSetMaster).nextBlobSet;
X	else
X	{
X		bSet2 = blobSetMaster;
X		for (;;)
X		{
X			if (bSet2 == nil) return;	/* set isn't in master list */
X			if ((**bSet2).nextBlobSet == bSet)	/* found it */
X			{
X				(**bSet2).nextBlobSet = (**bSet).nextBlobSet;
X				break;
X			}
X			bSet2 = (**bSet2).nextBlobSet;		/* try next one */
X		}
X	}
X/*
X	toss elements of set
X*/
X	b1 = (**bSet).firstBlob;
X	for (;;)
X	{
X		if (b1 == nil) break;
X		b2 = (**b1).nextBlob;
X		DisposeBlob (b1);
X		b1 = b2;
X	}
X/*
X	toss set header
X*/
X	DisposHandle (bSet);
X}
X
X
X/*
X	Dispose of all existing sets.	This is the routine to call at
X	the end of the host program to dispose of all Blob Manager
X	structures.
X*/
X
XDisposeBlobSets ()
X{
X	for (;;)
X	{
X		if (blobSetMaster == nil) break;	/* no more sets */
X		DisposeBlobSet (blobSetMaster);		/* changes the master list */
X	}
X}
X
X
X/*
X	Delete a blob from a blob list.	Should not be done lightly, e.g.,
X	if the blob is some other blob's glob.	Check to be sure that the
X	current glueCount is zero first.	All handles to the blob become
X	invalid.
X*/
X
XClobberBlob (b, bSet)
XBlobHandle		b;
XBlobSetHandle	bSet;
X{
XBlobHandle	b2;
X
X	if ((**bSet).firstBlob == b)	/* special case - first in list */
X	{
X		(**bSet).firstBlob = (**b).nextBlob;
X		if ((**bSet).lastBlob == b)	/* if true, set is now empty */
X			(**bSet).lastBlob = nil;
X	}
X	else
X	{
X		b2 = (**bSet).firstBlob;
X		for (;;)
X		{
X			if (b2 == nil) return;	/* NOT break! */
X			if ((**b2).nextBlob == b)
X			{
X				(**b2).nextBlob = (**b).nextBlob;
X				if ((**bSet).lastBlob == b)		/* clobbering last one */
X					(**bSet).lastBlob = b2;		/* back pointer up */
X				break;
X			}
X			b2 = (**b2).nextBlob;
X		}
X	}
X	DisposeBlob (b);
X}
X
X
X/*
X	Delete s from the match set of d
X*/
X
XClobberBlobMatch (b1, b2)
XBlobHandle	b1, b2;
X{
XMatchHandle	m1, m2;
X
X	m1 = (**b2).matches;
X	if (m1 != nil)		/* don't look if match set empty */
X	{
X		if ((**m1).mBlob == b1)	/* is it the first one in the list? */
X		{
X			(**b2).matches = (**m1).nextMatch;
X		}
X		else		/* it's not first one */
X		{
X			for (;;)
X			{
X				m2 = (**m1).nextMatch;
X				if (m2 == nil) return;		/* NOT break! */
X				if ((**m2).mBlob == b1) 	/* found it */
X				{
X					(**m1).nextMatch = (**m2).nextMatch;
X					break;
X				}
X				m1 = m2;
X			}
X		}
X		DisposHandle (m2);
X	}
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrPict.c
X/* -------------------------------------------------------------------- */
X/*						Blob Picture Creation Routines					*/
X/* -------------------------------------------------------------------- */
X
X
X# include	"BlobMgr.h"
X
X
X/*
X	Internal picture drawing scratch variables
X*/
X
Xstatic PicHandle	picHand;
Xstatic Rect			picRect = { -30000, -30000, 30000, 30000 };
X
X
XOpenBlob ()
X{
X	picHand = OpenPicture (&picRect);
X}
X
X
X/*
X	Close a picture blob.  The picture that was begin with OpenBlob is
X	installed in the blob record and the blob type is set to picture
X	(as opposed to proc) blob.  The picture is replayed with a frame
X	that is the union of the bounding boxes of the static and drag regions.
X	This will usually be sufficiently reasonable to avoid the problem
X	of overflow that sometimes results from scaling the clipping region
X	when a picture is drawn in a different size than the original.
X	The dragRect and statRect fields are set to those rects bounding
X	the parts of the picture corresponding to the two regions.  These
X	are used to cope in the event that the blob is moved or resized.
X	(See DrawBlob and DrawBlobPic.)
X
X	ClosePicBlob does NOT set the blob's regions.  That must be done
X	by the caller after calling ClosePicBlob.
X*/
X
Xstatic ClosePicBlob (b, dragRect, statRect)
XBlobHandle	b;
XRect		*dragRect, *statRect;
X{
XRect		pRect;
XPicHandle	p;
XRgnHandle	oldClip;
X
X	ClosePicture ();			/* stop saving picture definition */
X	DisposeBlobPic (b);			/* toss any pic the blob might have had */
X	UnionRect (dragRect, statRect, &pRect);
X	oldClip = NewRgn ();
X	GetClip (oldClip);
X	ClipRect (&pRect);				/* avoid clipping problem on picture */
X	p = OpenPicture (&pRect);		/* replay later on - this prevents */
X	DrawPicture (picHand, &picRect);	/* overflow if pic is scaled up */
X	ClosePicture ();
X	KillPicture (picHand);			/* don't need original picture now */
X	SetClip (oldClip);
X	DisposeRgn (oldClip);
X	(**b).bPicProc.bPic = p;
X
X	(**b).dragRect = *dragRect;		/* install original drag Rect */
X	(**b).statRect = *statRect;		/* install original static Rect */
X
X	SetBlobFlags (b, bPicMask);			/* set type as picture blob */
X}
X
X
X/*
X	Close a blob whose regions are defined by rectangles.
X*/
X
XCloseRectBlob (b, dragRect, statRect)
XBlobHandle	b;
XRect		*dragRect, *statRect;
X{
X	ClosePicBlob (b, dragRect, statRect);
X	SetBlobRects (b, dragRect, statRect);
X	if (BlobEnabled (b))
X		DrawBlob (b, inFullBlob);
X
X}
X
X
X/*
X	Close a blob.
X*/
X
XCloseRgnBlob (b, dragRgn, statRgn)
XBlobHandle	b;
XRgnHandle		dragRgn, statRgn;
X{
XRect		dragRect, statRect;
X
X	dragRect = (**dragRgn).rgnBBox;
X	statRect = (**statRgn).rgnBBox;
X	ClosePicBlob (b, &dragRect, &statRect);
X	SetBlobRgns (b, dragRgn, statRgn);
X	if (BlobEnabled (b))
X		DrawBlob (b, inFullBlob);
X
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrRand.c
X/*
X	Blob random number routines
X*/
X
X# include	<MacTypes.h>
X# define	nil		0L
X
X
Xstatic DefaultRand (max)
Xint		max;
X{
Xregister int		t;
X
X	t = Random ();
X	if (t < 0) t = -t;
X	return (t % (max + 1));
X}
X
X
Xstatic ProcPtr	bRandProc = DefaultRand;
X
X
X/*
X	return integer between zero and max (inclusive).	assumes max is
X	non-negative.
X*/
X
X
XBlobRand (max)
Xint		max;
X{
X	return ((*bRandProc) (max));
X}
X
X
X/*
X	Install procedure as random number generator for BlobRand
X*/
X
XSetBlobRand (f)
XProcPtr	f;
X{
X	bRandProc = (f == nil ? DefaultRand : f);
X}SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrSetProc.c
X/* -------------------------------------------------------------------- */
X/*						Blob Draw Proc Installation						*/
X/* -------------------------------------------------------------------- */
X
X
X# include	"BlobMgr.h"
X
X
X
X/*
X	Set a blob's draw procedure and regions.  This routine makes COPIES
X	of the regions passed in, so the caller should dispose of the regions
X	it passes in itself.
X*/
X
XSetProcRgnBlob (b, proc, dragRgn, statRgn)
XBlobHandle	b;
XProcPtr		proc;
XRgnHandle	dragRgn, statRgn;
X{
X	DisposeBlobPic (b);				/* toss any pic the blob might have */
X	(**b).bPicProc.bDrawProc = proc;/* install drawing procedure */
X	ClearBlobFlags (b, bPicMask);	/* set blob type to proc blob */
X	SetBlobRgns (b, dragRgn, statRgn);
X	if (BlobEnabled (b))
X		DrawBlob (b, inFullBlob);
X}
X
X
X/*
X	Set a blob's draw procedure and regions, where the regions are
X	defined by rectangles.
X*/
X
XSetProcRectBlob (b, proc, dragRect, statRect)
XBlobHandle	b;
XProcPtr		proc;
XRect		*dragRect, *statRect;
X{
X	DisposeBlobPic (b);				/* toss any pic the blob might have */
X	(**b).bPicProc.bDrawProc = proc;/* install drawing procedure */
X	ClearBlobFlags (b, bPicMask);	/* set blob type to proc blob */
X	SetBlobRects (b, dragRect, statRect);
X	if (BlobEnabled (b))
X		DrawBlob (b, inFullBlob);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrSetRgns.c
X/* -------------------------------------------------------------------- */
X/*						Blob Rect/Rgn Installation						*/
X/* -------------------------------------------------------------------- */
X
X
X# include	"BlobMgr.h"
X
X
X
X
X/*
X	Set a blob's regions.  This routine makes COPIES of the regions
X	passed in, so the caller should dispose of the regions it passes
X	in itself.  This routine relies on the fact that a blob should
X	never have nil handles for its regions (NewBlob installs empty
X	regions, so those regions are just changed here, not created).
X
X	The drag region is set equal to the drag region passed in.  The
X	static region is set to the difference of the static and drag
X	regions passed in.
X*/
X
XSetBlobRgns (b, dragRgn, statRgn)
XBlobHandle	b;
XRgnHandle	dragRgn, statRgn;
X{
X
X	CopyRgn (dragRgn, (**b).dragRgn);
X	DiffRgn (statRgn, dragRgn, (**b).statRgn);
X}
X
X
X/*
X	Set a blob's regions, where the regions are defined by rectangles.
X*/
X
XSetBlobRects (b, dragRect, statRect)
XBlobHandle	b;
XRect		*dragRect, *statRect;
X{
XRgnHandle	dragRgn, statRgn;
X
X	dragRgn = NewRgn ();
X	statRgn = NewRgn ();
X	RectRgn (dragRgn, dragRect);
X	RectRgn (statRgn, statRect);
X	SetBlobRgns (b, dragRgn, statRgn);
X	DisposeRgn (dragRgn);
X	DisposeRgn (statRgn);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrShuffle.c
X# include	"BlobMgr.h"
X
X
X/*
X	Shuffle the locations of the blobs in the given set.  If it is
X	undesirable to show all the redrawing that takes place during
X	this, HideBlobSet should be called before ShuffleBlobSet, and
X	ShowBlobSet after.
X	
X	ShuffleBlobSet deals with the following problem.  Suppose two blobs
X	to be switched are both visible.  The first is moved, i.e., hidden
X	and redrawn at the location of the second.  Then the second is moved,
X	i.e., hidden and redrawn at the previous location of the first.
X	But moving the second blob erases the newly drawn first blob!  To
X	avoid this, ShuffleBlobset hides the first blob before moving them,
X	and redraws it afterward.
X	
X	ShuffleBlobSet usually results in an update event being generated
X	since hiding a blob adds its region to the update region for the
X	window.  ValidRect can be called (with the window's portRect)
X	to defeat it.
X
X	The static region is often empty, which is why the drag regions are used
X	to determine the offsets.
X*/
X
X
XShuffleBlobSet (bSet)
XBlobSetHandle	bSet;
X{
Xregister BlobHandle	b1, b2;
Xregister int		size, h, v;
XBoolean				vis1 = false;
X
X	size = BlobSetSize (bSet);
X	for (b1 = FirstBlob (bSet); b1 != nil; b1 = NextBlob (b1))
X	{
X		b2 = GetBlobHandle (bSet, BlobRand (size - 1));
X		h = BDragBox (b1).left - BDragBox (b2).left;
X		v = BDragBox (b1).top - BDragBox (b2).top;
X		if (BlobEnabled (b1))
X		{
X			HideBlob (b1);
X			vis1 = true;
X		}
X		OffsetBlob (b1, inFullBlob, -h, -v);
X		OffsetBlob (b2, inFullBlob, h, v);
X		if (vis1)
X			ShowBlob (b1);
X	}
X}
X
X
X/*
X	Shuffle the globs attached to the blobs in the given set.
X*/
X
X
XShuffleGlobSet (bSet)
XBlobSetHandle	bSet;
X{
Xregister BlobHandle	b;
Xregister int		size;
X
X	size = BlobSetSize (bSet);
X	for (b = FirstBlob (bSet); b != nil; b = NextBlob (b))
X	{
X		SwapGlob (b, GetBlobHandle (bSet, BlobRand (size-1)));
X	}
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrTrackMouse.c
X# include	"BlobMgr.h"
X
X
X/*
X	Track the mouse while the button is held down.  Invert the given
X	blob region whenever the mouse is in the region.  Return true if
X	the mouse is released inside of the region, false otherwise.
X*/
X
XBoolean BTrackMouse (b, startPt, partCode)
XBlobHandle	b;
XPoint		startPt;
Xint			partCode;
X{
XBoolean		inBlob;
XRgnHandle	rgn;
XPoint		pt;
X
X	rgn = BCalcRegion (b, partCode);
X	pt = startPt;
X	inBlob = false;
X	for (;;)
X	{
X		if (PtInRgn (pt, rgn) != inBlob)	/* invert on change of state */
X		{
X			InvertRgn (rgn);
X			inBlob = !inBlob;
X		}
X		if (!StillDown ())
X			break;
X		GetMouse (&pt);
X	}
X	if (inBlob)		/* leave region uninverted */
X		InvertRgn (rgn);
X	DisposeRgn (rgn);
X	return (inBlob);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrTrans.c
X# include	"BlobMgr.h"
X
X
X/* ---------------------------------------------------------------------- */
X/*						Blob Transaction Routines						*/
X/* ---------------------------------------------------------------------- */
X
X
X/*
X	If the blob has a blob glued to its drag area, dissociate the glued blob.
X	Redraw the drag area.	Decrement the use count of the glued blob,
X	undimming it if necessary.
X*/
X
XUnglueGlob (b)
XBlobHandle	b;
X{
XBlobHandle	g;
X
X	g = (**b).glob;
X	if (g != nil)			/* skip if don't really have glued blob */
X	{
X		(**b).glob = nil;
X		DrawBlob (b, inDragBlob);
X		DecBlobGlue (g);	/* dec use count and undim if necessary */
X	}
X}
X
X
XUnglueGlobSet (bSet)
XBlobSetHandle	bSet;
X{
X	BlobLoopProc1 (&UnglueGlob, bSet);
X}
X
X
X/*
X	Glue donor blob d to receptor r.	Redraw the drag area of r.
X	Increment the use count of d, dimming it if necessary.	If r
X	already has a glue blob, unglue it first (undimming if necessary).
X*/
X
X
XGlueGlob (d, r)
XBlobHandle	d, r;
X{
XBlobHandle	g;
X
X	g = (**r).glob;					/* currently glued blob */
X	if (g != d)						/* ignore if has a glob and it's same */
X	{
X		if (g != nil)				/* if it has a glob already, then */
X			DecBlobGlue (g);		/* detach and undim if necessary */
X		(**r).glob = d;
X		IncBlobGlue (d);			/* increment use and dim if necessary */
X		DrawBlob (r, inDragBlob);	/* now have source in drag area - redraw */
X	}
X}
X
X
X/*
X	Transfer a glued blob from one blob to another (r1 -> r2).	Redraw
X	the drag areas of the blob losing the glued blob, and the one
X	receiving it, but the use count of the transferred blob does not
X	change, so it's not necessary to redraw it.
X*/
X
XTransferGlob (r1, r2)
XBlobHandle	r1, r2;
X{
XBlobHandle	g1, g2;
X
X	if (r1 != r2)				/* ignore unless different */
X	{
X		g1 = BGlob (r1);
X		g2 = BGlob (r2);
X		if (g1 != nil)			/* can't transfer if nothing there! */
X		{
X			BGlob (r1) = nil;	/* detach and redraw drag area */
X			DrawBlob (r1, inDragBlob);
X			if (g2 != nil)		/* toss this first if something there */
X				DecBlobGlue (g2);	/* undim if necessary */
X			BGlob (r2) = g1;
X			DrawBlob (r2, inDragBlob);	/* new donor in drag area - redraw */
X		}
X	}
X}
X
X
X/*
X	Swap the globs glued to r1 and r2.  If one of them doesn't actually
X	have a glob, this is equivalent to a transfer.
X*/
X
XSwapGlob (r1, r2)
XBlobHandle	r1, r2;
X{
XBlobHandle	g1, g2;
X
X	if (r1 != r2)				/* ignore unless different */
X	{
X		g1 = BGlob (r1);
X		g2 = BGlob (r2);
X		if (g1 != g2)			/* ignore if both have same glob */
X		{
X			BGlob (r1) = g2;
X			BGlob (r2) = g1;
X			DrawBlob (r1, inDragBlob);
X			DrawBlob (r2, inDragBlob);
X		}
X	}
X}
X
X
X/*
X	Glue the glob that's glued to r1 onto r2 as well.  No check
X	is made whether it's really gluable another time or not.
X*/
X
XDupGlob (r1, r2)
XBlobHandle	r1, r2;
X{
XBlobHandle	g;
X
X	if ((g = BGlob (r1)) != nil)
X		GlueGlob (g, r2);	/* duplicate blob */
X}
X
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrZTrans.c
X# include	"BlobMgr.h"
X
X
X/* -------------------------------------------------------------------- */
X/*				Blob Transaction Routines - with zooming				*/
X/* -------------------------------------------------------------------- */
X
X
X/*
X	Same as UnglueGlob, except that a rectangle is zoomed
X	from the destination blob back to the source blob.
X*/
X
XZUnglueGlob (b)
XBlobHandle	b;
X{
XBlobHandle	g;
XRect		r1, r2;
X
X	g = (**b).glob;
X	if (g != nil)			/* skip if don't really have glued blob */
X	{
X		(**b).glob = nil;
X		DrawBlob (b, inDragBlob);
X		r1 = BDragBox (b);
X		r2 = BDragBox (g);
X		ZoomRect (&r1, &r2);
X		DecBlobGlue (g);	/* dec use count and undim if necessary */
X	}
X}
X
X
XZUnglueGlobSet (bSet)
XBlobSetHandle	bSet;
X{
X	BlobLoopProc1 (&ZUnglueGlob, bSet);
X}
X
X
X/*
X	Same as GlueGlob, but zooms the donor to the receptor first
X*/
X
XZGlueGlob (d, r)
XBlobHandle	d, r;
X{
XRect		r1, r2;
X
X	r1 = BDragBox (d);
X	r2 = BDragBox (r);
X	ZoomRect (&r1, &r2);
X	GlueGlob (d, r);
X}
X
X
X/*
X	Same as DupGlob, but zooms from the first receptor to the second
X	before duplicating.
X	
X	This really should use the drag region of the first receptor's glob,
X	mapped to the shape and position of the receptor, rather than the
X	receptor's drag region - since it's the glob that's being transferred.
X	But that requires mapping (and scaling when it's put in).
X*/
X
XZDupGlob (r1, r2)
XBlobHandle	r1, r2;
X{
XRect		rect1, rect2;
X
X	if (BGlob (r1) != nil)
X	{
X		rect1 = BDragBox (r1);
X		rect2 = BDragBox (r2);
X		ZoomRect (&rect1, &rect2);
X		DupGlob (r1, r2);
X	}
X}
X
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BMgr/BMgrZoom.c
X# include	<QuickDraw.h>
X
X/*
X	Zooming parameters.  zoomSteps is the number of steps in the
X	interpolative series.  It must not be zero.  zoomShow is the number
X	of steps that show at any one time in the series.  It must be less
X	than or equal to zoomSteps.
X*/
X
X# define	zoomSteps	10
X# define	zoomShow	3
X
X
X/*
X	ZoomRect zooms a dotted rectangle from r1 to r2.	The effect is
X	transparent (screen no different afterwards).	The shape of the zoomed
X	rectangles changes for smooth interpolation.
X*/
X
XZoomRect (r1, r2)
XRect	*r1, *r2;
X{
Xregister int	r1left, r1top;
Xint				hDiff, vDiff, widDiff, htDiff;
Xint				l, t, r, b;
Xint				rWid, rHt;
Xint				i, j;
XRect			zoomRect[zoomSteps];
XPenState		ps;
X
X	r1left = r1->left;	/* these two statements make code shorter and */
X	r1top = r1->top;	/* impervious to mem mgr if rects passed by handle */
X	hDiff = r2->left - r1left;	/* positive if moving to right */
X	vDiff = r2->top - r1top;	/* positive if moving down */
X	rWid = r1->right - r1left;
X	rHt = r1->bottom - r1top;
X	widDiff = (r2->right - r2->left) - rWid;
X	htDiff = (r2->bottom - r2->top) - rHt;
X	/* set pen gray, mode xor */
X	GetPenState (&ps);				/* save current pen state */
X	PenPat (gray);
X	PenMode (patXor);
X	for (i = 0; i < zoomSteps; ++i)
X	{
X		j = i + 1;
X		l = r1left + (hDiff * j)	/ zoomSteps;
X		t = r1top + (vDiff * j)	/ zoomSteps;
X		r = l + rWid + (widDiff * j)	/ zoomSteps;
X		b = t + rHt + (htDiff * j)	/ zoomSteps;
X		SetRect (&zoomRect[i], l, t, r, b);
X		FrameRect (&zoomRect[i]);		/* draw in xor */
X		if (i >= zoomShow)
X			FrameRect (&zoomRect[i-zoomShow]);
X	}
X	for (i = zoomSteps - zoomShow; i < zoomSteps; ++i)
X	{
X		FrameRect (&zoomRect[i]);
X	}
X	/* set pen normal */
X	SetPenState (&ps);				/* restore pen state */
X		
X}
SHAR_EOF
exit