[mod.mac.sources] BlobDemo2 - Blob Manager Demo source

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

#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	BlobDemo/DemoHeb.c
#	BlobDemo/DemoLib.c
#	BlobDemo/DemoMagicSquare.c
#	BlobDemo/DemoMain.c
#	BlobDemo/DemoMenu.c
#	BlobDemo/DemoPeg.c
mkdir BlobDemo
sed 's/^X//' << 'SHAR_EOF' > BlobDemo/DemoHeb.c
X/*
X	Blob Manager Demonstration:  Hebrew Alephbet module
X
X	26 July 1986	Paul DuBois
X*/
X
X
X# include	"BlobDemo.h"
X# include	<ControlMgr.h>
X# include	<ResourceMgr.h>
X
X
X# define	nLetters	22	/* 22 letters in Hebrew alephbet */
X# define	iconSize	32	/* standard icon is 32x32 */
X# define	sitmSize	16	/* sicn item is 16 x 16 */
X# define	rWidth		76	/* receptor blob width and height */
X# define	rHeight		22
X# define	rHGap		2	/* horizontal and vertical gaps */
X# define	rVGap		5	/* between receptor blobs */
X# define	dWidth		sitmSize	/* ditto for donors */
X# define	dHeight		sitmSize
X# define	dHGap		12
X# define	dVGap		4
X
X# define	pauseAns	1	/* what kind of pause? */
X# define	pauseMatch	2
X
Xtypedef int Sitm[sitmSize];
Xtypedef Sitm	Sicn[1];
Xtypedef Sicn **SicnHandle;
X
Xstatic GrafPtr			hebPort;
Xstatic BlobSetHandle	hebDonors;
Xstatic BlobSetHandle	hebReceptors;
X
Xstatic ControlHandle	matchCtl;
Xstatic ControlHandle	resumeCtl;
Xstatic ControlHandle	resetCtl;
Xstatic ControlHandle	shuffleCtl;
Xstatic ControlHandle	ansCtl;
X
Xstatic Boolean			paused = false;
Xstatic int				pauseType;
X
X
X/*
X	Plot sicn item in given rectangle.
X	This is exactly analogous to the ToolBox PlotIcon routine,
X	except that the item number within the sicn must be specified.
X*/
X
X
XPlotSicn (r, s, i)
XRect		*r;
XSicnHandle	s;
Xint		i;
X{
XGrafPtr	thePort;
XBitMap	bm;
XSitm	tmpSitm;
X
X	/* create a small bitmap */
X	BlockMove (&((**s)[i]), &tmpSitm, (long) sizeof (Sitm));
X	bm.baseAddr = (Ptr) &tmpSitm;
X	bm.rowBytes = 2;			/* items are 16 bits wide */
X	SetRect (&bm.bounds, 0, 0, sitmSize, sitmSize);
X	GetPort (&thePort);
X	CopyBits (&bm, &thePort->portBits, &bm.bounds, r, srcCopy, nil);
X}
X
X
XHebActivate (active)
XBoolean	active;
X{
X	if (active)
X	{
X		SetDragRects (hebPort);
X		SetBCPermissions (true, true, false, true, true);
X	}
X}
X
X
XHebUpdate (resized)
XBoolean	resized;
X{
Xint		mode;
XPattern	p;
X
X	DrawControls (hebPort);
X	DrawBlobSet (hebDonors);
X	GetBDimInfo (&p, &mode);
X	if (paused && pauseType == pauseMatch)
X		SetBDimInfo (black, patXor);
X	DrawBlobSet (hebReceptors);
X	SetBDimInfo (p, mode);
X}
X
X
XLightControls ()
X{
Xint	i;
X
X	i = (paused ? 0 : 255);
X	HiliteControl (matchCtl, 255 - i);
X	HiliteControl (resetCtl, 255 - i);
X	HiliteControl (ansCtl, 255 - i);
X	HiliteControl (resumeCtl, i);
X}
X
X
XHebMoveBlobs (bSet, n1, n2, x, y, xd)
XBlobSetHandle	bSet;
Xint				n1, n2, x, y, xd;
X{
X	for ( ; n1 <= n2; ++n1)
X	{
X		MoveBlob (GetBlobHandle (bSet, n1), inFullBlob, x, y);
X		x += xd;
X	}
X}
X
X
XHebMakeBlobs ()
X{
Xint			i;
Xint			h, v;
XRect		tRect, lRect, sitmRect;
XSicnHandle	letterSicn;
XStr255		rName;
XBlobHandle	b1, b2;
Xint			hMid;
Xint			x, y, delta;
X
X	hMid = hebPort->portRect.right / 2 - 4;
X	
X	letterSicn = (SicnHandle) GetResource ('SICN', letrSicnNo);
X	hebDonors = NewBlobSet ();
X	hebReceptors = NewBlobSet ();
X	for (i = 0; i < nLetters; ++i)
X	{
X		b1 = NewBlob (hebDonors, false, 1, false, 0L);
X		b2 = NewBlob (hebReceptors, false, 0, true, 0L);
X		NewBlobMatch (b1, b2);	/* assign donor -> receptor mapping */
X		GetIndString (&rName, nameStrNo, i+1);
X		OpenBlob ();	/* draw source blob */
X		SetRect (&sitmRect, 0, 0, sitmSize, sitmSize);
X		PlotSicn (&sitmRect, letterSicn, i);
X		CloseRectBlob (b1, &sitmRect, &sitmRect);
X
X		OpenBlob ();
X		SetRect (&tRect, 0, 5, rWidth-sitmSize-1, rHeight);
X		OffsetRect (&sitmRect, rWidth-sitmSize, 5);
X		TextBox (rName + 1, (long) rName[0], &tRect, 1);
X		EraseRect (&sitmRect);
X		FrameRect (&sitmRect);
X		CloseRectBlob (b2, &sitmRect, &tRect);
X
X	}
X	ReleaseResource (letterSicn);
X
X/*
X	now arrange the blobs on the board.
X*/
X	x = hMid - 5*(dWidth+dHGap) - dWidth/2;
X	delta = dWidth + dHGap;
X	y = 5;
X	HebMoveBlobs (hebDonors, 0, 10, x, y, delta);
X	y += dHeight + dVGap;
X	HebMoveBlobs (hebDonors, 11, 21, x, y, delta);
X
X	x = hMid - 3*(rWidth+rHGap);
X	y += dHeight + dVGap + 5;
X	delta = rWidth + rHGap;
X	HebMoveBlobs (hebReceptors, 0, 5, x, y, delta);
X	y += rHeight + rVGap;
X	HebMoveBlobs (hebReceptors, 6, 11, x, y, delta);
X	y += rHeight + rVGap;
X	HebMoveBlobs (hebReceptors, 12, 17, x, y, delta);
X	x = hMid - 2*(rWidth+rHGap);
X	y += rHeight + rVGap;
X	HebMoveBlobs (hebReceptors, 18, 21, x, y, delta);
X	/*ShuffleBlobSet (hebDonors);*/
X}
X
X
XHebMouse (thePt, t, mods)
XPoint	thePt;
Xlong	t;
Xint		mods;
X{
XControlHandle	ctl;
XBlobHandle		b, g;
Xint				mode;
XPattern			p;
X
X	if (!paused)
X	{
X		BlobClick (thePt, t, hebDonors, hebReceptors);
X		if (BlobSetQuiet (hebReceptors))	/* if all correct */
X		{									/* override normal */
X			paused = true;					/* control display */
X			pauseType = pauseAns;
X			LightControls ();
X			HiliteControl (resumeCtl, 255);
X			HiliteControl (resetCtl, 0);
X			FreezeBlobSet (hebReceptors);
X		}
X	}
X	if (FindControl (thePt, hebPort, &ctl))
X	{
X		if (TrackControl (ctl, thePt, nil))
X		{
X			if (ctl == matchCtl)
X			{
X				paused = true;
X				pauseType = pauseMatch;
X				GetBDimInfo (&p, &mode);	/* hilite by inverting */
X				SetBDimInfo (black, patXor);
X				BlobFeedback (hebReceptors, normalDraw, dimDraw);
X				SetBDimInfo (p, mode);
X				LightControls ();
X				if (BlobSetQuiet (hebReceptors))	/* if all correct */
X				{									/* override normal */
X					HiliteControl (resumeCtl, 255);	/* control display */
X					HiliteControl (resetCtl, 0);
X				}
X			}
X			else if (ctl == ansCtl)
X			{
X				paused = true;
X				pauseType = pauseAns;
X				LightControls ();
X				for (b = FirstBlob (hebReceptors); b != nil; b = NextBlob (b))
X				{
X					SetBRefCon (b, BGlob (b));	/* save current glob setting */
X					g = FirstBMatch (b);			/* attach real answer */
X					if (g != BGlob (b))
X					{
X						ZGlueGlob (g, b);
X					}
X				}
X			}
X			else if (ctl == resumeCtl)
X			{
X				paused = false;
X				LightControls ();
X				if (pauseType == pauseMatch)
X					ThawBlobSet (hebReceptors);
X				else
X				{
X					/* restore previous attachments */
X					for (b = FirstBlob (hebReceptors); b != nil; b = NextBlob (b))
X					{
X						g = (BlobHandle) GetBRefCon (b);
X						if (g != BGlob (b))
X						{
X							ZUnglueGlob (b);
X							GlueGlob (g, b);
X						}
X					}
X				}
X			}
X			else if (ctl == resetCtl)
X			{
X				paused = false;
X				LightControls ();
X				ThawBlobSet (hebReceptors);
X				ZUnglueGlobSet (hebReceptors);		/* clear and redraw */
X			}
X			else if (ctl == shuffleCtl)
X			{
X				ShuffleBlobSet (hebDonors);		/* shuffle letters */
X				ValidRect (&hebPort->portRect);
X			}
X		}
X	}
X}
X
X
XHebInit	()
X{
XRect	r;
Xint	i, j;
X
X	SkelWindow (hebPort	= GetDemoWind (hebWindRes),
X				HebMouse,		/* mouse clicks */
X				nil,			/* key clicks */
X				HebUpdate,		/* updates */
X				HebActivate,	/* activate/deactivate events */
X				nil,			/* close window */
X				DoWClobber,		/* dispose of window */
X				nil,			/* idle proc */
X				false);			/* irrelevant, since no idle proc */
X
X	HebMakeBlobs ();
X	i = hebPort->portRect.bottom - 25;
X	j = hebPort->portRect.right/2 - 205;
X	SetRect (&r, j, i, j+70, i+20);
X	matchCtl =
X		NewControl (hebPort, &r, "\pCheck", true, 0, 0, 0, pushButProc, nil);
X	OffsetRect (&r, 85, 0);
X	resumeCtl =
X		NewControl (hebPort, &r, "\pResume", true, 0, 0, 0, pushButProc, nil);
X	OffsetRect (&r, 85, 0);
X	ansCtl =
X		NewControl (hebPort, &r, "\pAnswer", true, 0, 0, 0, pushButProc, nil);
X	OffsetRect (&r, 85, 0);
X	resetCtl =
X		NewControl (hebPort, &r, "\pReset", true, 0, 0, 0, pushButProc, nil);
X	OffsetRect (&r, 85, 0);
X	shuffleCtl =
X		NewControl (hebPort, &r, "\pShuffle", true, 0, 0, 0, pushButProc, nil);
X
X	LightControls ();
X
X	EnableBlobSet (hebDonors);
X	EnableBlobSet (hebReceptors);
X	HebUpdate ();
X	ValidRect (&hebPort->portRect);
X}
X
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BlobDemo/DemoLib.c
X/*
X	Blob Manager Demonstration:  library routines
X*/
X
X# include	<QuickDraw.h>
X# include	"BlobDemo.h"
X
Xstatic int	charBlobSize = 18;
X
X
X
X/*
X	StrCpy copies the second argument into the first.
X	StrCat appends the second argument to the first.
X	MovesLeft creates a string that says "n Moves Left"
X
X	Both assume Pascal-style strings.
X*/
X
X
XStrCpy (dst, src)
XStringPtr	dst, src;
X{
Xint		i;
X
X	for (i = 0; i <= src[0]; ++i)
X		dst[i] = src[i];
X}
X
X
XStrCat (dst, src)
XStringPtr	dst, src;
X{
Xint		i, dlen, slen;
X
X	slen = src[0];
X	dlen = dst[0];
X	for (i = 1; slen > 0; ++i, --slen)
X		dst[dlen + i] = src[i];
X	dst[0] += src[0];
X}
X
X
XMovesLeft (n, s)
Xint			n;
XStringPtr	s;
X{
X	NumToString (n, s);
X	StrCat (s, (n == 1 ? "\p Move Left" : "\p Moves Left"));
X}
X
X
X/*
X	Create and dispose of offscreen port
X*/
X
XGrafPtr NewOffPort (r)
XRect	r;
X{
XGrafPtr	thePort, tmpPort;
XBitMap	*theMap;
Xlong	bitCount;
Xint		rowBytes;
XPtr		bits;
X
X	rowBytes = ((r.right-r.left + 15) / 8) & (~1);
X	bitCount = rowBytes * (r.bottom - r.top);
X	bits = NewPtr (bitCount);
X	if (bits)
X	{
X		thePort = (GrafPtr) NewPtr ((long) sizeof (GrafPort));
X		if (thePort)
X		{
X			theMap = (BitMap *) NewPtr ((long) sizeof (BitMap));
X			if (theMap)
X			{
X				theMap->bounds = r;
X				theMap->rowBytes = rowBytes;
X				theMap->baseAddr = bits;
X				GetPort	(&tmpPort);
X				OpenPort (thePort);
X				SetPortBits (theMap);
X				PortSize (r.right - r.left, r.bottom - r.top);
X				RectRgn (thePort->visRgn, &thePort->portRect);
X				ClipRect (&thePort->portRect);
X				EraseRect (&thePort->portRect);
X				SetPort	(tmpPort);
X				return (thePort);
X			}
X			DisposPtr (thePort);
X		}
X		DisposPtr (bits);
X	}
X	return (nil);
X}
X
X
XDisposeOffPort (thePort)
XGrafPtr	thePort;
X{
X	ClosePort (thePort);
X	DisposPtr (thePort->portBits.baseAddr);
X	DisposPtr (&thePort->portBits);
X	DisposPtr (thePort);
X}
X
X
X/*
X	Make a simulated push button.  It looks like a regular button,
X	except that it's oriented vertically rather than horizontally.
X	bSet is the blob set to add the button blob to, r defines the
X	bounds rect, and title is a string containing the text to be drawn
X	in the blob.
X
X	The glueMax and mustMatch fields are assumed to be zero and false.
X
X	The ShowPen call is necessary since both OpenBlob and OpenRgn
X	perform implicit HidePen's - without ShowPen, nothing will
X	be drawn!  Then must balance with HidePen after drawing.
X*/
X
XBlobHandle NewVButtonBlob (bSet, r, title, visible, refCon)
XBlobSetHandle	bSet;
XRect			*r;
XStringPtr		title;
Xlong			refCon;
X{
XBlobHandle	b;
XRgnHandle	rgn, rgn2;
XFontInfo	fInfo;
Xint			cHeight;	/* character height */
Xint			tHeight;	/* title height */
Xint			h, v;
Xint			i;
X
X/*
X	Make the regions defining the blob first.  The drag region is inset
X	by one so that the text in the middle can be dimmed without dimming
X	the frame.
X*/
X
X	rgn = NewRgn ();
X	OpenRgn ();
X	FrameRoundRect (r, 10, 10);
X	CloseRgn (rgn);
X	rgn2 = NewRgn ();
X	CopyRgn (rgn, rgn2);
X	InsetRgn (rgn2, 1, 1);
X
X	/*	Now make the blob and define its image	*/
X
X	b = NewBlob (bSet, visible, 0, false, refCon);
X	GetFontInfo (&fInfo);
X	cHeight = fInfo.ascent + fInfo.descent;
X	tHeight = title[0] * (cHeight + fInfo.leading) - fInfo.leading;
X	OpenBlob ();
X	v = (r->top + r->bottom - tHeight ) / 2 + fInfo.ascent;
X	FillRgn (rgn2, white);
X	for (i = 1; i <= title[0]; ++i)
X	{
X		h = (r->left + r->right - CharWidth (title[i])) / 2;
X		MoveTo (h, v);
X		DrawChar (title[i]);
X		v += cHeight + fInfo.leading;
X	}
X	FrameRoundRect (r, 10, 10);
X	CloseRgnBlob (b, rgn2, rgn);
X	DisposeRgn (rgn);
X	DisposeRgn (rgn2);
X	return (b);
X}
X
X
X/*
X	Determine the rectangle that defines each element of a grid,
X	and pass the rectangle to a blob-drawing proc.  The proc draws
X	whatever goes into the element.  Creates blobs a row at a time.
X*/
X
XMakeBlobGrid (rows, cols, hOff, vOff, hWidth, vHeight, hGap, vGap, p)
Xint		rows, cols, hOff, vOff, hWidth, vHeight, hGap, vGap;
XProcPtr	p;
X{
Xint	i, j;
Xint	h, v;
XRect	r;
X
X	for (j = 0; j < rows; ++j)
X	{
X		for (i = 0; i < cols; ++i)
X		{
X			h = hOff + i * (hGap + hWidth);
X			v = vOff + j * (vGap + vHeight);
X			SetRect (&r, h, v, h + hWidth, v + vHeight);
X			(*p) (&r);
X		}
X	}
X}
X
X
X/*
X	Draw a grid with the given number of rows and columns.  The upper
X	left-hand corner of the top element is at (hoff, voff).  The
X	height and width of each element is vHeight and hWidth.  The horizontal
X	and vertical gaps between elements are hGap and vGap.  A border is
X	also drawn around the whole grid.
X*/
X
XDrawGrid (rows, cols, hOff, vOff, hWidth, vHeight, hGap, vGap)
Xint	rows, cols, hOff, vOff, hWidth, vHeight, hGap, vGap;
X{
Xint	i;
Xint	h, v;
Xint	len;
XRect	r;
X
X	hOff -=	hGap;
X	vOff -=	vGap;
X	PenSize	(hGap, vGap);
X	/*	draw vertical lines	 */
X	len = rows * (vGap + vHeight);
X	for (i = 0; i <= cols; ++i)
X	{
X		h = hOff + i * (hGap + hWidth);
X		MoveTo (h, vOff);
X		LineTo (h, vOff	+ len);
X	}
X	/*	draw horizontal	lines  */
X	len	= cols * (hGap + hWidth);
X	for (i = 0; i <= rows; ++i)
X	{
X		v = vOff + i * (vGap + vHeight);
X		MoveTo (hOff, v);
X		LineTo (hOff + len, v);
X	}
X	PenNormal ();
X}
X
X
X/*
X	Drawing proc for character blobs
X*/
X
XDrawCharBlob (bDst, bSrc, partCode)
XBlobHandle	bDst, bSrc;
Xint			partCode;
X{
XRect		r;
Xchar		c;
X
X	r = BDragBox (bDst);
X	c = (char) LoWord (GetBRefCon (bSrc));
X	if (c == ' ')
X	{
X		EraseRoundRect (&r, 10, 10);
X		FrameRoundRect (&r, 10, 10);
X	}
X	else
X	{
X		TextBox (&c, 1L, &r, 1);
X	}
X}
X
X
X/*
X	Make a blob with a character in the middle (or a frame if the
X	character is a space).	Pass all the normal NewBlob parameters
X	except the reference constant, plus the horizontal and vertical
X	coordinates, and the character to be drawn in the blob.
X
X	The char is stored as the low word of the reference value.  The 
X	application can put whatever it wants in the high word.
X*/
X
X
XBlobHandle MakeCharBlob (bSet, enable, glueMVal, mustMatch, h, v, c)
XBlobSetHandle	bSet;
XBoolean			enable;
Xint				glueMVal;
XBoolean			mustMatch;
Xint				h, v;
Xchar			c;
X{
XBlobHandle		b;
XRect			r;
X
X	b = NewBlob (bSet, enable, glueMVal, mustMatch, (long) c);
X	SetRect (&r, h, v, h + charBlobSize, v + charBlobSize);
X	SetProcRectBlob (b, DrawCharBlob, &r, &r);
X	return (b);
X}
X
X
X/*
X	Set the size in which character blobs are created
X*/
X
XSetCharBlobSize (size)
Xint		size;
X{
X	charBlobSize = size;
X}SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BlobDemo/DemoMagicSquare.c
X/*
X	Blob Manager Demonstration:  Magic Square
X
X	All rows, columns and diagonals of a square must add up to 15.
X
X	This is a donorless scenario.  The receptors are created, then glued
X	to themselves initially so that they will each have a glob to drag
X	around.
X	
X	26 July	1986	Paul DuBois
X*/
X
X# include	"BlobDemo.h"
X# include	<ControlMgr.h>
X
X
X# define	vButton		5
X# define	vBoard		30	/* offset of top of board */
X# define	hGap		2
X# define	vGap		2
X# define	bSqSize		30	/* board square size */
X
X
Xstatic GrafPtr			magicPort;
Xstatic BlobSetHandle	receptors;
X
Xstatic Boolean			wait;
Xstatic ControlHandle	button;
Xstatic int				hMid;
Xstatic int				hBoard;
X
X
Xstatic Restart ()
X{
X
X	HiliteControl (button, 255);
X	ShuffleGlobSet (receptors);
X	wait = false;
X}
X
X
Xstatic Boolean TestConfig (pos1, pos2, pos3)
Xint		pos1, pos2, pos3;
X{
Xint		sum;
X
X	sum = GetBRefCon (BGlob (GetBlobHandle (receptors, pos1)))
X		+ GetBRefCon (BGlob (GetBlobHandle (receptors, pos2)))
X		+ GetBRefCon (BGlob (GetBlobHandle (receptors, pos3)));
X	return (sum == 15);
X}
X
X
Xstatic CheckStatus ()
X{
X	if (TestConfig (0, 1, 2)		/* top row	*/
X		&& TestConfig (3, 4, 5)		/* middle row */
X		&& TestConfig (6, 7, 8)		/* bottom row */
X		&& TestConfig (0, 3, 6)		/* left column */
X		&& TestConfig (1, 4, 7)		/* middle column */
X		&& TestConfig (2, 5, 8)		/* right column */
X		&& TestConfig (0, 4, 8)		/* diagonal */
X		&& TestConfig (2, 4, 6) )	/* diagonal */
X	{
X		HiliteControl (button, 0);
X		wait = true;
X	}
X}
X
X
Xstatic Mouse (pt, t, mods)
XPoint	pt;
Xlong	t;
Xint	mods;
X{
XBlobHandle		b, d;
XControlHandle	ctl;
X
X	if (FindControl	(pt, magicPort, &ctl))
X	{
X		if (TrackControl (ctl, pt, nil))
X			Restart ();
X	}
X	else if (!wait)
X		BlobClick (pt, t, nil, receptors);
X/*
X	Check status even if user just restarted, since the square may
X	by chance fall into a correct configuration.  In that case,
X	the CheckStatus will again enable the restart button.
X*/
X	CheckStatus ();
X}
X
X
Xstatic Update (resized)
XBoolean	resized;
X{
X	DrawControls (magicPort);
X	DrawBlobSet (receptors);
X}
X
X
Xstatic Activate	(active)
XBoolean	active;
X{
X	if (active)
X	{
X		SetDragRects (magicPort);
X		SetBCPermissions (false, false, false, true, true);
X	}
X}
X
X
X/*
X	Make receptor blob
X*/
X	
Xstatic MakeRBlob (r)
XRect	*r;
X{
XBlobHandle	b;
Xchar		c;
Xstatic long	rno = 0L;
X
X	b = NewBlob (receptors, false, infiniteGlue, false, ++rno);
X	OpenBlob ();
X	EraseRect (r);
X	FrameRect (r);
X	InsetRect (r, 5, 7);
X	c = rno + '0';
X	TextBox (&c, 1L, r, 1);
X	InsetRect (r, -3, -5);
X	InvertRect (r);
X	InsetRect (r, -2, -2);
X	CloseRectBlob (b, r, r);
X	GlueGlob (b, b);
X}
X
X
Xstatic MakeReceptors ()
X{
X	receptors =	NewBlobSet ();
X	MakeBlobGrid (3, 3, hBoard, vBoard, bSqSize, bSqSize,
X					hGap, vGap, MakeRBlob);
X}
X
X
XMagicInit	()
X{
XRect	r;
X
X	magicPort = GetDemoWind (magicWindRes);
X	SkelWindow (magicPort,
X				Mouse,			/* mouse clicks */
X				nil,			/* key clicks */
X				Update,			/* updates */
X				Activate,		/* activate/deactivate events */
X				nil,			/* close window */
X				DoWClobber,		/* dispose of window */
X				nil,			/* idle proc */
X				false);			/* irrelevant, since no idle proc */
X
X	hMid = magicPort->portRect.right / 2;
X	hBoard = hMid - (3 * (bSqSize + hGap) - hGap) / 2;
X	SetRect (&r, hMid - 40, vButton, hMid + 40, vButton + 20);
X	button = NewControl (magicPort, &r, "\pReset", true, 0, 0, 0,
X								pushButProc, nil);
X
X	MakeReceptors ();
X	ShowBlobSet (receptors);
X	Restart ();
X	ValidRect (&magicPort->portRect);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BlobDemo/DemoMain.c
X/*
X	Blob Manager Demonstration:  Main module
X
X	25 June 1986	Paul DuBois
X*/
X
X# include	<QuickDraw.h>
X
X
Xlong	diskInfoKludge;
X
X
X
Xmain ()
X{
Xint		i;
X
X	SkelInit ();					/* initialize TransSkel */
X
X	randSeed = TickCount ();
X
X	SetupMenus ();
X	
X/*
X	Initialize the various scenarios.  Any of them may be entirely
X	disabled simply by commenting out the initialization line.
X*/
X
X	WolfInit ();
X	TohInit ();
X	TttInit ();
X	StatesInit ();
X	PyrInit ();
X	PongInit ();
X	PegInit ();
X	MagicInit ();
X	HebInit ();
X	HangInit ();
X	FggInit ();
X	FshInit ();
X	FarInit ();
X	SwapInit ();
X	RadixInit ();
X	AnagramInit ();
X
X	AddWindowTitles ();				/* add window names to Windows menu */
X
X	SkelMain ();					/* loop 'til Quit selected */
X
X	DisposeBlobSets ();				/* shut down the Blob Manager */
X	SkelClobber ();					/* and TransSkel */
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BlobDemo/DemoMenu.c
X/*
X	Blob Manager Demonstration:  Handlers for standard menus (those
X	that stick around all the time).  Menus that exist only when
X	are particular window is in front are installed and removed by
X	the handler for that window.
X
X	12 July 1986	Paul DuBois
X*/
X
X# include	"BlobDemo.h"
X# include	<WindowMgr.h>
X# include	<MenuMgr.h>
X# include	<FontMgr.h>
X
X
Xstatic MenuHandle	fileMenu;
Xstatic MenuHandle	editMenu;
Xstatic MenuHandle	windMenu;
X
X
X/*
X	wPtr is used to map Windows menu items onto the windows associated
X	with each item.
X*/
X
Xstatic WindowPtr	wPtr[lastWindRes-firstWindRes];
Xstatic int			wCount = 0;
X
X
XDoAbout ()
X{
X	(void) Alert (aboutAlrtRes, nil);
X}
X
X
XDoFileMenu (item)
Xint		item;
X{
XHandle	h;
X
X	switch (item)
X	{
X		case getInfo:
X		{
X			h = GetResource ('TEXT', helpTextRes);
X			HLock (h);
X			TextDialog (textDlogRes, h, 0, 0, true);
X			HUnlock (h);
X			ReleaseResource (h);
X			break;
X		}
X		case quit:
X		{
X			SkelWhoa ();	/* tell SkelMain to quit */
X			break;
X		}
X	}
X}
X
X
XDoEditMenu (item)
Xint		 item;
X{
X	(void) SystemEdit (item - 1);	/* route to DA if appropriate */
X}
X
X
X/*
X	The Windows menu is used to select a given window and bring it
X	to the front.  It also makes it visible if it wasn't already.
X*/
X
XDoWindMenu (item)
Xint		item;
X{
X	SelectWindow (wPtr[item-1]);
X	ShowWindow (wPtr[item-1]);
X}
X
X
X/*
X	Add title of each window to the Windows menu.  Save the WindowPtr for
X	use later if the window is selected from the menu.
X*/
X
X
X
XAddWindowTitles ()
X{
XWindowPeek	theWind;
XStr255		title;
X
X	theWind = (WindowPeek) FrontWindow ();
X	while (theWind != nil)
X	{
X		GetWTitle (theWind, &title);
X		AppendMenu (windMenu, title);
X		wPtr[wCount++] = (WindowPtr) theWind;
X		theWind = theWind->nextWindow;
X	}
X}
X
X
X/*
X	Clobber proc for menu handlers
X*/
X
XDoMClobber (theMenu)
XHandle	theMenu;
X{
X	ReleaseResource (theMenu);
X}
X
X
X/*
X	Background task:  enable Edit menu when a DA window is in front.
X	Disable it when a demo window is in front.  Since this procedure
X	is executed continually, only mess with the menu bar on a change
X	of state.
X*/
X
XBackground ()
X{
Xstatic Boolean	editEnable = false;
XWindowPeek		wPeek;
X
X	if ( (wPeek = (WindowPeek) FrontWindow ()) == nil
X		|| wPeek->windowKind >= 0)	/* DA not in front */
X	{
X		if (editEnable)
X		{
X			DisableItem (editMenu, 0);
X			DrawMenuBar ();
X			editEnable = false;
X		};
X	}
X	else if (!editEnable)	/* DA window in front */
X	{
X		EnableItem (editMenu, 0);
X		DrawMenuBar ();
X		editEnable = true;
X	}
X}
X
X
X/*
X	Set up menus:  Tell LightSkel to make a default Apple menu handler,
X	create handlers for the File, Edit and Windows menus.  Install
X	background task that enables/disables Edit menu.
X*/
X
XSetupMenus ()
X{
X
X	SkelApple ("\pAbout BlobMgr DemoI", DoAbout);
X	fileMenu = GetMenu (fileMenuRes);
X	SkelMenu (fileMenu, DoFileMenu, DoMClobber);
X	editMenu = GetMenu (editMenuRes);
X	SkelMenu (editMenu, DoEditMenu, DoMClobber);
X	windMenu = GetMenu (windMenuRes);
X	SkelMenu (windMenu, DoWindMenu, DoMClobber);
X	SkelBackground (Background);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BlobDemo/DemoPeg.c
X/*
X	Blob Manager Demonstration:  Peg Solitaire module
X
X	31 July 1986		Paul DuBois
X*/
X
X
X# include	"BlobDemo.h"
X# include	<MenuMgr.h>
X
X
X# define	rows		7		/* number of rows on board */
X# define	columns		7		/* number of columns on board */
X# define	pieceSize	20		/* size of each piece */
X# define	vMessage	141		/* vertical position of message */
X
X/*
X	bits used in configuration information
X
X	bit 0	0 board position unused
X			1 board position used
X	bit 1	0 position does not have marble at start
X			1 position has marble at start
X	bit 2	0 position does not have marble at end
X			1 position has marble at end
X
X	bits 1 and 2 are irrelevant unless bit 0 is on
X*/
X
X# define	posUsedMask	1
X# define	inStartMask	2
X# define	inFinalMask	4
X
X
Xstatic GrafPtr		pegPort;
Xstatic MenuHandle	cnfgMenu;
X
X
Xstatic BlobSetHandle	boardBlobs = nil;		/* receptors */
Xstatic BlobHandle		board[columns][rows];
X
Xstatic int				hMid;
Xstatic int				cnfgNo;		/* current configuration */
Xstatic int				moves;		/* number of moves left */
Xstatic Boolean			pause;
Xstatic Str255			statusStr = "\p";
X
X
Xtypedef struct
X{
X	char	*lName;					/* name of configuration */
X	char	lCnfg[columns][rows];	/* board positions */
X} Layout;
X
X
Xstatic Layout centerToCenter =
X{
X	"\pCenter To Center",
X	{
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 3, 3, 3, 3, 3, 3, 3 },
X		{ 3, 3, 3, 5, 3, 3, 3 },
X		{ 3, 3, 3, 3, 3, 3, 3 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 0, 0, 3, 3, 3, 0, 0 }
X	}
X};
X
X
Xstatic Layout cornerToCorner =
X{
X	"\pCorner To Corner",
X	{
X		{ 0, 0, 1, 3, 3, 0, 0 },
X		{ 0, 3, 3, 3, 3, 3, 0 },
X		{ 3, 3, 3, 3, 3, 3, 3 },
X		{ 3, 3, 3, 3, 3, 3, 3 },
X		{ 3, 3, 3, 3, 3, 3, 3 },
X		{ 0, 3, 3, 3, 3, 3, 0 },
X		{ 0, 0, 3, 3, 7, 0, 0 }
X	}
X};
X
X
Xstatic Layout doubleCross =
X{
X	"\pDouble Cross",
X	{
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 0, 3, 1, 3, 1, 3, 0 },
X		{ 1, 1, 3, 3, 3, 1, 1 },
X		{ 3, 3, 3, 7, 3, 3, 3 },
X		{ 1, 1, 3, 3, 3, 1, 1 },
X		{ 0, 3, 1, 3, 1, 3, 0 },
X		{ 0, 0, 1, 3, 1, 0, 0 }
X	}
X};
X
X
Xstatic Layout finalScore =
X{
X	"\pFinal Score",
X	{
X		{ 0, 0, 7, 7, 7, 0, 0 },
X		{ 0, 7, 3, 3, 3, 7, 0 },
X		{ 7, 3, 7, 3, 7, 3, 7 },
X		{ 7, 3, 3, 5, 3, 3, 7 },
X		{ 7, 3, 3, 3, 3, 3, 7 },
X		{ 0, 7, 3, 7, 3, 7, 0 },
X		{ 0, 0, 7, 7, 7, 0, 0 }
X	}
X};
X
X
Xstatic Layout fiveCrosses =
X{
X	"\pFive Crosses",
X	{
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 0, 1, 3, 3, 3, 1, 0 },
X		{ 1, 3, 1, 3, 1, 3, 1 },
X		{ 3, 3, 3, 7, 3, 3, 3 },
X		{ 1, 3, 1, 3, 1, 3, 1 },
X		{ 0, 1, 3, 3, 3, 1, 0 },
X		{ 0, 0, 1, 3, 1, 0, 0 }
X	}
X};
X
X
Xstatic Layout footballTeam =
X{
X	"\pFootball Team",
X	{
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 1, 1, 3, 3, 3, 1, 1 },
X		{ 1, 1, 3, 5, 3, 1, 1 },
X		{ 1, 1, 1, 1, 1, 1, 1 },
X		{ 0, 0, 1, 1, 1, 0, 0 },
X		{ 0, 0, 1, 1, 1, 0, 0 }
X	}
X};
X
X
Xstatic Layout greekCross =
X{
X	"\pGreek Cross",
X	{
X		{ 0, 0, 1, 1, 1, 0, 0 },
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 1, 1, 1, 3, 1, 1, 1 },
X		{ 1, 3, 3, 7, 3, 3, 1 },
X		{ 1, 1, 1, 3, 1, 1, 1 },
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 0, 0, 1, 1, 1, 0, 0 }
X	}
X};
X
X
Xstatic Layout latinCross =
X{
X	"\pLatin Cross",
X	{
X		{ 0, 0, 1, 1, 1, 0, 0 },
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 1, 1, 3, 3, 3, 1, 1 },
X		{ 1, 1, 1, 7, 1, 1, 1 },
X		{ 1, 1, 1, 3, 1, 1, 1 },
X		{ 0, 0, 1, 1, 1, 0, 0 },
X		{ 0, 0, 1, 1, 1, 0, 0 },
X	}
X};
X
X
Xstatic Layout leTricolet =
X{
X	"\pLe Tricolet",
X	{
X		{ 0, 0, 7, 3, 7, 0, 0 },
X		{ 0, 3, 3, 7, 3, 3, 0 },
X		{ 7, 3, 3, 7, 3, 3, 7 },
X		{ 3, 7, 7, 1, 7, 7, 3 },
X		{ 7, 3, 3, 7, 3, 3, 7 },
X		{ 0, 3, 3, 7, 3, 3, 0 },
X		{ 0, 0, 7, 3, 7, 0, 0 }
X	}
X};
X
X
Xstatic Layout lonelyCross =
X{
X	"\pLonely Cross",
X	{
X		{ 0, 0, 7, 7, 7, 0, 0 },
X		{ 0, 7, 3, 3, 3, 7, 0 },
X		{ 7, 3, 3, 7, 3, 3, 7 },
X		{ 7, 3, 7, 5, 7, 3, 7 },
X		{ 7, 3, 3, 7, 3, 3, 7 },
X		{ 0, 7, 3, 3, 3, 7, 0 },
X		{ 0, 0, 7, 7, 7, 0, 0 }
X	}
X};
X
X
Xstatic Layout octagon =
X{
X	"\pOctagon",
X	{
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 0, 3, 3, 3, 3, 3, 0 },
X		{ 1, 3, 3, 3, 3, 3, 1 },
X		{ 3, 3, 3, 7, 3, 3, 3 },
X		{ 1, 3, 3, 3, 3, 3, 1 },
X		{ 0, 3, 3, 3, 3, 3, 0 },
X		{ 0, 0, 1, 3, 1, 0, 0 }
X	}
X};
X
X
Xstatic Layout pentagon =
X{
X	"\pPentagon",
X	{
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 1, 3, 3, 3, 3, 3, 1 },
X		{ 3, 3, 3, 7, 3, 3, 3 },
X		{ 1, 3, 3, 3, 3, 3, 1 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 0, 0, 1, 1, 1, 0, 0 }
X	}
X};
X
X
Xstatic Layout pyramidChefren =
X{
X	"\pPyramid of Chefren",
X	{
X		{ 0, 0, 1, 1, 1, 0, 0 },
X		{ 0, 1, 1, 1, 1, 1, 0 },
X		{ 1, 1, 1, 3, 1, 1, 1 },
X		{ 1, 1, 3, 7, 3, 1, 1 },
X		{ 1, 3, 3, 3, 3, 3, 1 },
X		{ 0, 1, 1, 1, 1, 1, 0 },
X		{ 0, 0, 1, 1, 1, 0, 0 },
X	}
X};
X
X
Xstatic Layout pyramidCheops =
X{
X	"\pPyramid of Cheops",
X	{
X		{ 0, 0, 1, 1, 1, 0, 0 },
X		{ 0, 1, 1, 3, 1, 1, 0 },
X		{ 1, 1, 3, 3, 3, 1, 1 },
X		{ 1, 3, 3, 7, 3, 3, 1 },
X		{ 3, 3, 3, 3, 3, 3, 3 },
X		{ 0, 1, 1, 1, 1, 1, 0 },
X		{ 0, 0, 1, 1, 1, 0, 0 },
X	}
X};
X
X
Xstatic Layout shrine =
X{
X	"\pShrine",
X	{
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 0, 1, 1, 3, 1, 1, 0 },
X		{ 1, 3, 3, 7, 3, 3, 1 },
X		{ 1, 1, 1, 3, 1, 1, 1 },
X		{ 1, 1, 1, 3, 1, 1, 1 },
X		{ 0, 1, 3, 3, 3, 1, 0 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X	}
X};
X
X
Xstatic Layout tiffanyLamp =
X{
X	"\pTiffany Lamp",
X	{
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 1, 3, 3, 3, 3, 3, 1 },
X		{ 1, 1, 1, 7, 1, 1, 1 },
X		{ 1, 1, 1, 3, 1, 1, 1 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X	}
X};
X
X
Xstatic Layout tiltedSquare =
X{
X	"\pTilted Square",
X	{
X		{ 0, 0, 1, 3, 1, 0, 0 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 1, 3, 3, 3, 3, 3, 1 },
X		{ 3, 3, 3, 5, 3, 3, 3 },
X		{ 1, 3, 3, 3, 3, 3, 1 },
X		{ 0, 0, 3, 3, 3, 0, 0 },
X		{ 0, 0, 1, 3, 1, 0, 0 }
X	}
X};
X
X
Xstatic Layout nullLayout =
X{
X	nil
X};
X
X
Xstatic Layout	*configurations [] =
X{
X	&centerToCenter,
X	&cornerToCorner,
X	&doubleCross,
X	&finalScore,
X	&fiveCrosses,
X	&footballTeam,
X	&greekCross,
X	&latinCross,
X	&leTricolet,
X	&lonelyCross,
X	&octagon,
X	&pentagon,
X	&pyramidChefren,
X	&pyramidCheops,
X	&shrine,
X	&tiffanyLamp,
X	&tiltedSquare,
X	&nullLayout
X};
X
X
Xstatic StatusMesg (s)
XStr255	s;
X{
XRect	r;
X
X	SetRect (&r, 0, vMessage, pegPort->portRect.right, vMessage + 12);
X	TextBox	(s+1, (long) s[0], &r, 1);
X	StrCpy (statusStr, s);
X}
X
X
X/*
X	Board position drawing procedure.  This draws a blank for those
X	positions that are unused.  For used positions, the blob is filled
X	with light gray if a peg should be in the position at the end of
X	play.  In the drag region, a hole is drawn if there is no glob,
X	otherwise a peg is drawn.  It does not matter what glob is attached,
X	only whether there is one.
X*/
X
Xstatic DrawBoardPos (bDst, bSrc, partCode)
XBlobHandle	bDst, bSrc;
Xint			partCode;		/* ignored - always draw entire blob */
X{
Xint		blobType;
XRect	r;
X
X	blobType = GetBRefCon (bDst);	/* indicates unused, used and present
X										in final, or used and not present
X										in final */
X	r = BStatBox (bDst);			/* blob outline */
X	EraseRect (&r);
X	if (blobType & posUsedMask)
X	{
X		if (blobType & inFinalMask)
X			FillRect (&r, gray);
X		if (BGlob (bDst) == nil)	/* no peg, just draw hole */
X		{
X			InsetRect (&r, 7, 7);
X			PaintOval (&r);
X		}
X		else
X		{
X			InsetRect (&r, 2, 2);	/* draw peg */
X			EraseOval (&r);
X			PenSize (2, 2);
X			FrameOval (&r);
X			PenNormal ();
X		}
X	}
X}
X
X
X/*
X	Build the board.  All positions are initially given a reference
X	value of 0, meaning an unused position.  This means that the board
X	is blank until a configuration is set up.  The blobs are built by
X	contructing the static and drag regions, without drawing anything,
X	since they are drawn by procedure.
X*/
X
Xstatic InitBoard ()
X{
Xint			h, v;
XRect		r, r2;
XBlobHandle	b;
X
X	boardBlobs = NewBlobSet ();
X	for (v = 0; v < rows; v++)
X	{
X		for (h = 0; h < columns; h++)
X		{
X			b = NewBlob (boardBlobs, false, infiniteGlue, true, (long) 0);
X			board[h][v] = b;
X			SetRect (&r, 0, 0, pieceSize, pieceSize);
X			OffsetRect (&r, h * pieceSize, v * pieceSize);
X			r2 = r;
X			InsetRect (&r2, 2, 2);
X			SetProcRectBlob (b, DrawBoardPos, &r2, &r);
X		}
X	}
X
X	EnableBlobSet (boardBlobs);
X}
X
X
X/*
X	Set the board to a given configuration.  The first board blob is
X	used as the general glob and as the match for final positions.
X*/
X
Xstatic SetupBoard (layout)
XLayout	*layout;
X{
Xint			h, v, val;
Xint			startCount = 0;
Xint			finalCount = 0;
XBlobHandle	b;
XStr255		s;
X
X	DisableBlobSet (boardBlobs);	/* turn off drawing temporarily */
X	for (v = 0; v < rows; v++)
X	{
X		for (h = 0; h < columns; h++)
X		{
X			b = board[h][v];
X			val = (int) layout->lCnfg[v][h];
X			SetBRefCon (b, val);
X			UnglueGlob (b);				/* clear any glob and match set */
X			DisposeBlobMatchSet (b);	/* from previous configuration */
X			if (val & posUsedMask)		/* check that position is used */
X			{
X				if (val & inStartMask)	/* position has marble at start */
X				{
X					startCount++;
X					GlueGlob (FirstBlob (boardBlobs), b);
X				}
X				if (val & inFinalMask)	/* position has marble at end */
X				{
X					finalCount++;
X					NewBlobMatch (FirstBlob (boardBlobs), b);
X				}
X			}
X		}
X	}
X
X	ShowBlobSet (boardBlobs);
X	moves = startCount - finalCount;
X	MovesLeft (moves, s);
X	StatusMesg (s);
X	pause = false;
X}
X
X
Xstatic PegPause (msg)
XStringPtr	msg;
X{
X	StatusMesg (msg);
X	pause = true;
X}
X
X
X/*
X	Check whether a board position is empty.  The coordinates must be
X	legal, the position must be used in the current configuration,
X	and the position must have a marble in it.
X*/
X
Xstatic Boolean BoardPosEmpty (h, v)
Xint		h, v;
X{
X	return (h >= 0 && h < columns && v >= 0 && v < rows
X			&& (GetBRefCon (board[h][v]) & posUsedMask)
X			&& BGlob (board[h][v]) == nil);
X}
X
X
Xstatic Boolean BoardPosFilled (h, v)
Xint		h, v;
X{
X	return (h >= 0 && h < columns && v >= 0 && v < rows
X			&& (GetBRefCon (board[h][v]) & posUsedMask)
X			&& BGlob (board[h][v]) != nil);
X}
X
X
X/*
X	Test whether a piece can move or not
X*/
X
Xstatic Boolean CanMove (h, v)
Xint		h, v;
X{
X	return ((BoardPosEmpty (h - 2, v) && BoardPosFilled (h - 1, v))
X			|| (BoardPosEmpty (h + 2, v) && BoardPosFilled (h + 1, v))
X			|| (BoardPosEmpty (h, v - 2) && BoardPosFilled (h, v - 1))
X			|| (BoardPosEmpty (h, v + 2) && BoardPosFilled (h, v + 1)));
X}
X
X
X/*
X	See if any more moves can be made.  True if stalemated.
X*/
X
Xstatic Boolean HaveStaleMate ()
X{
XBlobHandle	b;
Xint			h, v;
X
X	for (v = 0; v < rows; v++)
X	{
X		for (h = 0; h < columns; h++)
X		{
X			b = board[h][v];
X			if (GetBRefCon (b) & posUsedMask)
X			{
X				if (BGlob (b) != nil && CanMove (h, v))
X					return (false);		/* at least 1 move can be made */
X			}
X		}
X	}
X	return (true);
X}
X
X
Xstatic Mouse (pt, t, mods)
XPoint	pt;
Xlong	t;
Xint		mods;
X{
XStr255			s;
Xint				i;
X
X
X	if (!pause)
X	{
X		BlobClick (pt, t, nil, boardBlobs);
X		if (BClickResult () == bcXfer)
X		{
X			--moves;
X			if (BlobSetQuiet (boardBlobs))
X				PegPause ("\pYou Win");
X			else if (moves == 0)
X				PegPause ("\pYou Lose");
X			else if (HaveStaleMate ())
X				PegPause ("\pYou Can't Move");
X			else
X			{
X				MovesLeft (moves, s);
X				StatusMesg (s);
X			}
X		}
X	}
X}
X
X
X/*
X	Given a blob handle, find the board position that corresponds to it.
X	This is used to map hits in the blob set (a list) to the position in
X	the board (a 2-d array).
X*/
X
Xstatic FindBoardPos (b, h, v)
XBlobHandle	b;
Xint			*h, *v;
X{
Xint		i, j;
X
X	for (i = 0; i < columns; ++i)
X	{
X		for (j = 0; j < rows; ++j)
X		{
X			if (board[i][j] == b)
X			{
X				*h = i;
X				*v = j;
X				return;
X			}
X		}
X	}
X	/* shouldn't ever get here */
X}
X
X
X
X/*
X	When a piece is clicked on, the advisory checks whether the piece has
X	any legal moves available to it.  If so, it returns true, so that
X	BlobClick is allowed to drag the piece.  After the piece has been
X	dragged, the advisory checks whether the position it was dragged to
X	is legal.  If it is, the piece that was jumped is removed from the
X	board, and true is returned, so that BlobClick will complete the
X	tranfer of the dragged piece to its new position.
X
X	Messages passed to the advisory follow the pattern
X
X	{ { advRClick advRClick* } advXfer* }*
X
X	where * means 0 or more instances of the thing *'ed.  In particular,
X	the advXfer message is never seen without a preceding advRClick.
X*/
X
Xstatic Boolean Advisory (mesg, b)
Xint			mesg;
XBlobHandle	b;
X{
Xstatic int	h, v;	/* static to save board position of click on piece */
Xint			h2, v2;
X
X	switch (mesg)
X	{
X
X	case advRClick:	/* first click on piece */
X
X		FindBoardPos (b, &h, &v);	/* find where it is */
X		return (CanMove (h, v));
X
X	case advXfer:	/* Mouse released after dragging piece */
X
X		FindBoardPos (b, &h2, &v2);
X		if ( (	   (h2 == h - 2 && v2 == v)
X				|| (h2 == h + 2 && v2 == v)
X				|| (h2 == h && v2 == v - 2)
X				|| (h2 == h && v2 == v + 2)  )
X			&& BoardPosFilled ((h + h2) / 2, (v + v2) / 2))
X		{
X			UnglueGlob (board[(h + h2) / 2][(v + v2) / 2]);
X			return (true);
X		}
X		return (false);
X
X	}
X}
X
X
Xstatic DoConfiguration (item)
Xint		item;
X{
X	CheckItem (cnfgMenu, cnfgNo + 1, false);
X	SetupBoard (configurations [cnfgNo = item-1]);
X	CheckItem (cnfgMenu, cnfgNo + 1, true);
X}
X
X
Xstatic Activate (active)
XBoolean	active;
X{
XLayout				*l;
X
X	if (active)
X	{
X		SetDragRects (pegPort);
X		SetBCPermissions (false, true, false, false, false);	/* xfer only */
X		SetBCAdvisory (Advisory);
X		cnfgMenu = GetMenu (pegCnfgMenuRes);
X		for (l = configurations[0]; l->lName != nil; ++l)
X		{
X			AppendMenu (cnfgMenu, l->lName);
X		}
X		SkelMenu (cnfgMenu, DoConfiguration, DoMClobber);
X		CheckItem (cnfgMenu, cnfgNo+1, true);
X	}
X	else
X	{
X		SkelRmveMenu (cnfgMenu);
X		SetBCAdvisory (nil);
X	}
X}
X
X
Xstatic Update (resized)
XBoolean	resized;
X{
X	DrawBlobSet (boardBlobs);
X	StatusMesg (statusStr);
X}
X
X
XPegInit	()
X{
XRect	r;
X
X	SkelWindow (pegPort = GetDemoWind (pegWindRes),
X				Mouse,			/* mouse clicks */
X				nil,			/* key clicks */
X				Update,			/* updates */
X				Activate,		/* activate/deactivate events */
X				nil,			/* close window */
X				DoWClobber,		/* dispose of window */
X				nil,			/* idle proc */
X				false);			/* irrelevant, since no idle proc */
X
X	hMid = pegPort->portRect.right / 2;
X
X/*
X	Make blobs and set initial configuration to the Latin Cross, which
X	is good for learning some of the problems of peg solitaire.
X*/
X	InitBoard ();
X	SetupBoard (configurations[cnfgNo = 7]);
X	ValidRect (&pegPort->portRect);
X}
SHAR_EOF
exit