[mod.mac.sources] Marquee Rectangles, Revisited

macintosh@felix.UUCP (07/31/86)

I recently posted to this newsgroup a short algorithm to draw a marquee
rectangle - a moving dashed outline.  After self-adulating my
cleverness for a while, it dawned on me how truly useless that routine
was, since the marquee was destructive.  Therefore, I am now posting
the version into which I should have put enough thought to submit in
the first place!  This is in the form of a complete LightspeedC
application.  (Complete if you have TransSkel, that is.)

Click and drag the mouse repeatedly to select various rects that are
marqueed.  This happens on a Mondrian background to demonstrate that
the algorithm is non-destructive.

Paul DuBois     UUCP: {allegra,ihnp4,seismo}!uwvax!uwmacc!dubois    |
                ARPA: dubois@easter                               --+--
                                                                    |
"Well, we can't give an award to a _dead_ pig," said                |
the loud speaker.  "It's never been done."

--- cut ---

/*
	Marquee 1.1 - simple demonstration to draw rectangle which is outlined
	by a moving marquee.  Click and drag the mouse to select the
	rectangle to be outlined.  This version is non-destructive, unlike
	a version I released earlier.

	The project should include MacTraps, TransSkel.c (or a project built
	from TransSkel.c) and this file, Marquee.c.

	21 July 1986		Paul DuBois
*/

# include	<WindowMgr.h>

# define	nil		0L

WindowPtr	theWind;
Rect		mRect = { 0, 0, 0, 0 };
Pattern		marqueePat = { 0x0f, 0x87, 0xc3, 0xe1, 0xf0, 0x78, 0x3c, 0x1e };
Pattern		shiftPat = { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 };


/*
	Frame rectangle, using marquee pattern.  The rect is drawn in xor mode.
	This procedure should be called to draw the marquee initially, then
	MoveMarqueeRect should be called repeatedly to move the pattern.
	Finally, call MarqueeRect again to erase the marquee.  The display
	will end up the same as it was before.
*/

MarqueeRect (r)
Rect	*r;
{
PenState	p;

	GetPenState (&p);
	PenPat (marqueePat);
	PenMode (patXor);
	FrameRect (r);
	SetPenState (&p);
}


/*
	Frame rectangle, using pattern that moves the marquee.  Then shift
	the pattern and the marquee pattern, to keep the two in sync.
	This procedure must be called repeatedly to achieve the effect of
	motion.
*/

MoveMarqueeRect (r)
Rect	*r;
{
PenState	p;
Byte		c1, c2;
int			i;

	GetPenState (&p);
	PenPat (shiftPat);
	PenMode (patXor);
	FrameRect (r);
	SetPenState (&p);
	c1 = marqueePat[0];				/* shift patterns up for next call */
	c2 = shiftPat[0];
	for (i = 0; i < 7; ++i)			/* (could shift sideways instead) */
	{
		marqueePat[i] = marqueePat[i+1];
		shiftPat[i] = shiftPat[i+1];
	}
	marqueePat[7] = c1;
	shiftPat[7] = c2;
}


/*
	While mouse is down, draw selection rectangle in the current
	port.  Return the resultant rect in dstRect.  The rect is always
	clipped to the current portRect.
*/

DoSelectRect (startPoint, dstRect)
Point	startPoint;
Rect	*dstRect;
{
Point		pt, dragPt;
Rect		rClip;
GrafPtr		thePort;
PenState	ps;
int			i, count = 0;

	GetPort (&thePort);
	rClip = thePort->portRect;
	GetPenState (&ps);
	PenPat (marqueePat);
	PenMode (patXor);
	dragPt = startPoint;
	Pt2Rect (dragPt, dragPt, dstRect);
	FrameRect (dstRect);
	for (;;)
	{
		GetMouse (&pt);
		if (!EqualPt (pt, dragPt))	/* mouse has moved, change rect */
		{
			FrameRect (dstRect);
			dragPt = pt;
			Pt2Rect (dragPt, startPoint, dstRect);
			(void) SectRect (dstRect, &rClip, dstRect);
			FrameRect (dstRect);
			for (i = 0; i < 1000; ++i) { /* delay to avoid flicker */ }
			count = 5;
		}
		if (++count > 5)	/* make marquee move every so often */
		{
			MoveMarqueeRect (dstRect);
			PenPat (marqueePat);
			count = 0;
		}
		if (!StillDown ()) break;
	}
	FrameRect (dstRect);	/* erase last rect */
	SetPenState (&ps);
}


/*
	Draw selection rectangle as long as mouse is held down.  Use it
	for the marquee rectangle.
*/


Mouse (thePt, t, mods)
Point	thePt;
long	t;
int		mods;

{
	MarqueeRect (&mRect);			/* erase previous marquee */
	DoSelectRect (thePt, &mRect);	/* draw selection rectangle */
	MarqueeRect (&mRect);			/* draw new marquee */
}


Idle ()
{
	MoveMarqueeRect (&mRect);		/* make marquee move */
}


Clobber ()
{
	DisposeWindow (theWind);
}


/*
	Install window handler
*/

MarqueeInit ()
{
Rect	r;
int		SkelWhoa();

	SetRect (&r, 80, 60, 430, 250);
	theWind = NewWindow (nil, &r, "\pMarquee", true, noGrowDocProc, -1L,
							true, 0L);
	SkelWindow (theWind,
				Mouse,			/* select rectangle */
				nil,			/* ignore keyclicks */
				nil,			/* update proc */
				nil,			/* activate proc */
				SkelWhoa,		/* quit on click in close box */
				Clobber,		/* disposal proc */
				Idle,			/* idle proc */
				true);			/* idle only when frontmost */
}


/*
	return integer between zero and max (inclusive).  assumes max is
	non-negative.
*/

Rand (max)
int	max;
{
register int    t;

    t = Random ();
    if (t < 0) t = -t;
    return (t % (max + 1));
}


/*
	Draw some stuff so there is background - makes it obvious that
	the marquee stuff is non-destructive.
*/

DrawRects ()
{
int		i, sizeX, sizeY;
Point	pt1, pt2;
Rect	dstRect;

	sizeX = theWind->portRect.right;
	sizeY = theWind->portRect.bottom;
	for (i = 0; i < 20; ++i)
	{
		SetPt (&pt1, Rand (sizeX), Rand (sizeY));
		SetPt (&pt2, Rand (sizeX), Rand (sizeY));
		Pt2Rect (pt1, pt2, &dstRect);
		InvertRect (&dstRect);
	}
}


main ()

{
	SkelInit ();		/* initialize TransSkel */
	MarqueeInit ();		/* install window handler  */
	DrawRects ();		/* draw some background */
	SkelMain ();		/* handle events */
	SkelClobber ();		/* throw away handlers */
}

--- end ---