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 ---