(*********************************************************************************	Apple Macintosh Developer Technical Support**	Source file for the GMonde application**	Program:	GMonde*	File:		GMonde.p - Pascal Implementation**	by:			Forrest Tanaka**	Copyright © 1988-1991 Apple Computer, Inc.*	All rights reserved.** ------------------------------------------------------------------------------**     GMonde is a sample program showing a fairly typical use of GWorlds.  A* single window shows up and displays a set of color ramps.  When the user* clicks and drags in the window, a selection marquee follows the mouse with one* corner at the location that the mouse was first pressed and the opposite* corner at the current location of the mouse.  As the user drags the selection* marquee, the classic Òmarching antsÓ animation occurs.  The color ramp image* isnÕt damaged by the selection marquee being dragged over it.  When the user* releases the mouse button, the selection marquee remains at the location and* size that the user selected, and the marching ants animation continues.**     The color ramp image is stored in an off-screen GWorld stored in the* gOffScreen global variable.  During any update events, this GWorld is copied* to the window specified by the gDemoWindow global variable.**     When the user begins the drag the dragger, a temporary off-screen GWorld* is created and the source image in gOffScreen is copied to it.  The selection* marquee is drawn into the temporary GWorld, and then the temporary GWorld is* copied to the window.  When the user moves the mouse with the mouse button* still held down, the original GWorld is copied into the temporary GWorld, the* selection marquee is drawn into the temporary GWorld at the location of the* mouse, and then the temporary GWorld is copied to the window.  This is done as* long as the mouse button is held down and the user moves the mouse.  Once the* user releases the mouse, the temporary GWorld is disposed of.**     The GWorld routines can assure that an off-screen GWorld is set up so that* a CopyBits call between the GWorld and a portion of a screen is as fast as* possible.  To do this, the color table of the GWorld must be equal to the* color table of the destination screen on a Color QuickDraw machine, the pixel* depth of the GWorld must equal the pixel depth of the destination screen, and* the alignment of the pixels in memory in the GWorld must be the same as the* alignment of the pixels on the Screen.  If you pass 0 for a pixel depth,* NewGWorld creates a GWorld that satisfies all three of these requirements.* UpdateGWorld maintains this if you pass it 0 for a pixel depth.  This* application takes advantage of this capability.  When gOffScreen is created,* the portRect of gDemoWindow is converted to global coordinates and passed to* NewGWorld, along with a pixel depth of 0.  gOffScreen can then be copied to* gDemoWindow at the fastest possible speed.**     When gDemoWindow is dragged to a new screen location, the pixels of* gOffScreen might not have the same memory alignment as the pixels of* gDemoWindow.  To bring gOffScreen back into alignment, UpdateGWorld is called* with a pixel depth of 0 after gDemoWindow is dragged to a new location.**     If the user switches out of GMonde and uses the Monitors control panel to* switch to a new pixel depth or to switch between Grays mode and Colors mode,* gOffScreen must be updated to these new characteristics so that a CopyBits* call from gOffScreen to gDemoWindow remains as fast as possible.  UpdateGWorld* is again used to do this.  But how can you tell that the screen depth or color* table has changed?  An update event can tell you this.  When the user switches* the depth or color table, the entire screen is redrawn and every window gets* an update event.  When gDemoWindow receives an update event, UpdateGWorld is* called just in case the event was caused by a change in the screen depth or* color table.  You might want to put more intelligence in this by saving the* current screen depth of the deepest screen the gDemoWindow intersects, then* checking the screen depth on every update event.  If the screen depth changes,* then call UpdateGWorld.  For GMonde, I decided not to do this.**     Under system software version 7.0, this application can run on a Macintosh* without Color QuickDraw.  All the GWorld routines work, albeit only a pixel* depth of 1 is supported.  RGBForeColor works, although the specified colors* are thresholded to black or white.  Also, version 2 PICTs can be drawn on a* Macintosh without Color QuickDraw, though GMonde doesnÕt show that.  Version* 1.0 did though.  I debated about that decision, but figured all it took was a* DrawPicture to prove that it works.  I thought it was more interesting that* RGBForeColor works.  Nodamene?*******************************************************************************){$R-}PROGRAM GMonde;(******************************************************************************** Used Units*******************************************************************************)	USES		ToolIntf, QDOffscreen, OSUtils, GestaltEqu, Script, AppleEvents;(******************************************************************************** Constant Declarations*******************************************************************************)	CONST		(* Resource IDs *)		rDemoWindID    = 128; {Demonstration window WIND resource ID}		rMarqueePattID = 128; {Marquee pattern PAT  resource ID}		(* Specifications for drawing the color ramps *)		kMaxRampCount = 256; {Number of steps in a ramp}		kRampWidth    = 16;  {Width of a ramp in pixels}		kMinScrollTime = 1;    {# ticks between marquee pattern scrolls}		kResumeMask    = 1;    {Mask used to tell if got suspend/resume event}		kUseTempMem    = TRUE; {Pass to CreateOffScreen; GWorld in temp mem} (******************************************************************************** Global Variable Definitions*******************************************************************************)	VAR		gDemoWindow:  WindowPtr; {Pointer to display window}		gDemoDevice:  GDHandle;  {Handle to the normal GDevice}		gOffScreen:   GWorldPtr; {Pointer to off-screen GWorld}		gMarqueeRect: Rect;      {Position of the dragger in window coordinates}		gMarqueePatt: Pattern;   {Marquee diagonal stripe pattern}		gQuitting:    Boolean;   {TRUE if GMonde is quitting}		gHasGWorlds:  Boolean;   {TRUE if GWorlds are implemented}		gHasCQD:      Boolean;   {TRUE Color QuickDraw is implemented}(******************************************************************************** DoneRequiredParams - Done processing required params; OK?** DoneRequiredParams checks to see if the AppleEvent specified by the* anAppleEvent parameter has any required parameters that we havenÕt yet* processed.  If there arenÕt any left, then noErr is returned.  If there are* required parameters that havenÕt been processed yet, then errAEEventNotHandled* is returned.  If any other errors occur, then that error code is returned.*******************************************************************************)	FUNCTION DoneRequiredParams (anAppleEvent: AppleEvent): OSErr;		VAR			typeCode:   DescType; {Type of AppleEvent attribute found; ignored}			actualSize: Size;     {Actual size of parameters; ignored}			error:      OSErr;	BEGIN		(* Are there any required parameters in AppleEvent we didnÕt process? *)   	error := AEGetAttributePtr (anAppleEvent, keyMissedKeywordAttr,				typeWildCard, (*<*)typeCode, NIL, 0, (*<*)actualSize);		IF error = errAEDescNotFound THEN			(* No required parameters left, so no error *)			DoneRequiredParams := noErr		ELSE IF error = noErr THEN			(* There was at least one required parameter we didnÕt process *)			DoneRequiredParams := errAEEventNotHandled		ELSE			(* Some other error happened *)			DoneRequiredParams := error	END;(******************************************************************************** HandleAEquit - Handler for 'quit' AppleEvent** This is the AppleEvent handler for the 'quit' AppleEvent as passed in the* quitAppleEvent parameter by the AppleEvent Manager.  The DoQuit routine is* called which causes this application to quit at the start of the next* iteration of the main event loop.** Though the quit AppleEvent doesnÕt contain any parameters, the standard thing* to do in reaction to any AppleEvent is to check to see if there are any* required parameters in the AppleEvent that this routine doesnÕt recognise.* DoneRequiredParms checks for this condition and returns an error if there are* in fact required parameters in the AppleEvent or if some other error occurs* during the check.*******************************************************************************)	PROCEDURE HandleAEquit (quitAppleEvent: AppleEvent;	                        reply:          AppleEvent;	                        handlerRefCon:  LongInt);	BEGIN		(* quit AE has no parms, but check in case the client requires any *)		IF DoneRequiredParams (quitAppleEvent) = noErr THEN			gQuitting := TRUE	END;(******************************************************************************** StartUp - Start up our application**     The usual things that have to be done as soon as an application starts are* done here.  A few of the managers are initialized, and then the graphics* environment is checked.**     The global variable gHasCQD is set to true if the machine GMonde is* running on has Color QuickDraw, false if not.  This is determined through* Gestalt.  If Gestalt isnÕt working for some reason, then SysEnvirons is used* instead.  The global variable gHasGWorlds is set to true if the GWorlds are* implemented on the machine that GMonde is running on.  GWorlds are implemented* if Gestalt returns gestalt32BitQD or greater, and if Gestalt returns a value* between gestaltOriginalQD and gestalt8BitQD, exclusive.  Gestalt returns a* value in this range if GMonde is running on a classic QuickDraw machine* running system software version 7.0 or greater.**     The AppleEvent ÔquitÕ handler is installed here.  When an incoming ÔquitÕ* AppleEvent is processed, control is routed to the HandleAEquit function thatÕs* defined elsewhere in this program.*******************************************************************************)	PROCEDURE StartUp;		VAR			trashEvent: EventRecord; {Just used for EventAvailÕs pleasure; ignored}			marquee:    PatHandle;   {Diagonal striped marquee pattern resource}			qdVersion:  LongInt;     {Version of QuickDraw on this machine}			aeAttr:     LongInt;     {AppleEvents attributes}			result:     OSErr;	BEGIN		InitGraf (@thePort);		InitFonts;		InitWindows;		InitMenus;		(* Find out if GWorlds and CQD are implemented on this machine *)		result := Gestalt (gestaltQuickdrawVersion, (*<*)qdVersion);		gHasGWorlds := ((qdVersion > gestaltOriginalQD) AND (qdVersion <				gestalt8BitQD)) OR (qdVersion >= gestalt32BitQD);		gHasCQD := qdVersion >= gestalt8BitQD;		(* Copy the marquee pattern into gMarqueePatt, or 50% gray if not found *)		marquee := GetPattern (rMarqueePattID);		IF marquee <> NIL THEN			BlockMove (Ptr(marquee^), Ptr(@gMarqueePatt), SIZEOF (Pattern))		ELSE			BlockMove (Ptr(gray), Ptr(@gMarqueePatt), SIZEOF (Pattern));		(* Install the AppleEvent handler *)		result := Gestalt (gestaltAppleEventsAttr, (*<*)aeAttr);		IF (result = noErr) & BTST (aeAttr, gestaltAppleEventsPresent) THEN			result := AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,					@HandleAEquit, 0, FALSE);		(* The initial marquee rectangle is empty *)		SetRect ((*<*)gMarqueeRect, 0, 0, 0, 0)	END;(******************************************************************************** CreateDemoWindow - Create a centered demonstration window**     A new window is created and displayed on the main screen.  It is* horizontally centered and on the upper third of the main screen.  A pointer to* this window is returned.  This window is also set as the current GrafPort* before it returns.  If the window couldnÕt be created for some reason, nil is* returned.**     On Color QuickDraw machines, a color window is created.  On machines* without Color QuickDraw, a normal window is created.  The gHasCQD global* variable is used to determine whether Color QuickDraw is implemented on this* machine or not, so gHasCQD must be properly initialized before* CreateDemoWindow is called.*******************************************************************************)	FUNCTION CreateDemoWindow: WindowPtr;		VAR			aWindow:   WindowPtr; {Pointer to new Marching Ants window}			windStore: Ptr;       {Pointer to WindowRecord memory}	BEGIN		(* Assume failure, as I always do in life *)		CreateDemoWindow := NIL;		(* Create window storage before calling GetNewWindow to avoid heap fragmentation *)		windStore := NewPtr (SizeOf (WindowRecord));		IF windStore <> NIL THEN			BEGIN				(* If machine has CQD, make color window, otherwise make normal window *)				IF gHasCQD THEN					aWindow := GetNewCWindow (rDemoWindID, windStore, WindowPtr(-1))				ELSE					aWindow := GetNewWindow (rDemoWindID, windStore, WindowPtr(-1));				IF aWindow <> NIL THEN					BEGIN						(* Center window on the main screen *)						MoveWindow (aWindow, ((screenBits.bounds.right - screenBits.								bounds.left) - (aWindow^.portRect.right - aWindow^.								portRect.left)) DIV 2, ((screenBits.bounds.bottom -								screenBits.bounds.top - GetMBarHeight) - (aWindow^.								portRect.bottom - aWindow^.portRect.top)) DIV 3 +								GetMBarHeight, FALSE);						(* Make the window visible and make it the current GrafPort *)						ShowWindow (aWindow);						SetPort (aWindow);						CreateDemoWindow := aWindow					END				ELSE					DisposPtr (windStore)			END	END;(******************************************************************************** CreateOffScreen - Create an off-screen GWorld to buffer a windowÕs image**     An off-screen GWorld is created so that it can be used to buffer the image* in the window specified by basePort.  To do that, I take advantage of* NewGWorldÕs pixel-alignment capabilities.  If you pass zero as the pixel depth* in the depth parameter and convert the portRect of basePort to global* coordinates, the GWorld is created a pixel depth equal to the deepest screen* that the portRect intersects, a color table to match that of the screenÕs, and* pixels aligned so that a CopyBits call to copy pixels between the screen and* this GWorld is as fast as possible.**     Starting with 32-Bit QuickDraw 1.2 (which was first included with system* software version 6.0.5), you could ask NewGWorld to allocate the pixel image* in temporary memory (formerly called ÒMultiFinder Temporary MemoryÓ) by* passing useTempMem in the GWorld flags.  If you pass true in ÔtemporaryÓ,* NewGWorld will be called with the useTempMem flag set.  Otherwise, NewGWorld* will be called normally.  As usual, pixel images allocated in temporary memory* should only be used for transient GWorlds.**     I set the clip region of the new GWorld to be coincident with its portRect* as a matter of habit as soon as I create a GWorld.  The reason for this is* because UpdateGWorld, at least as of this writing, resizes the clip region of* a GWorld if you specify the stretchBits flag.  NewGWorld initializes the clip* region to the entire QuickDraw coordinate plane.  When UpdateGWorld resizes* the GWorld larger with the stretchBits flag on, it also resizes the GWorldÕs* clip region proportionately larger.  If the clip region already covers the* entire coordinate plane, the coordinates will get wrapped around and the clip* region will be set to an empty region, and so nothing can be drawn into that* GWorld.  By setting the clip region to the portRect right from the start, this* problem never happens.*******************************************************************************)	FUNCTION CreateOffScreen (basePort:  GrafPtr;	                          depth:     Integer;	                          temporary: Boolean): GWorldPtr;		VAR			currPort:    CGrafPtr;    {Pointer to the saved port}			currGDevice: GDHandle;    {Handle to the current GDevice}			offScreen:   GWorldPtr;   {Pointer to our GWorld}			offRect:     Rect;        {portRect of basePort in global coordinates}			tempFlags:   GWorldFlags; {Flag which indicates whether temp memory is to be used}			result:      QDErr;       {Error return from NewGWorld}	BEGIN		GetGWorld ((*<*)currPort, (*<*)currGDevice);		SetPort (basePort);		(* Globalize portRect of basePort *)		offRect := basePort^.portRect;		IF depth = 0 THEN			BEGIN				LocalToGlobal ((*×*)offRect.topLeft);				LocalToGlobal ((*×*)offRect.botRight)			END;		(* Set tempFlags to reflect desire for temporary memory *)		IF temporary THEN			tempFlags := [useTempMem]		ELSE			tempFlags := [];		(* Create the new off-screen port and set it as the current port *)		result := NewGWorld ((*<*)offScreen, depth, offRect, NIL, NIL, tempFlags);		IF result = noErr THEN			BEGIN				SetGWorld (offScreen, NIL);				(* Good idea to set clip region to the portRect of the new GWorld *)				ClipRect (offScreen^.portRect);				(* Clear the new GWorld to white *)				IF LockPixels (GetGWorldPixMap (offScreen)) THEN					BEGIN						EraseRect (offScreen^.portRect);						UnlockPixels (GetGWorldPixMap (offScreen))					END			END		ELSE			offScreen := NIL;		(* Reset current GrafPort & GDevice to what it was when we were called *)		SetGWorld (currPort, currGDevice);		CreateOffScreen := offScreen	END;(******************************************************************************** PaintImage - Paint color ramps into the current GrafPort**     PaintImage paints a series of color ramps into the current GrafPort.  To* showcase the error diffusion dithering that CopyBits can perform on Color* QuickDraw machines, the image isnÕt drawn directly to the current GrafPort if* Color QuickDraw is available.  Instead, a temporary 32-bit deep GWorld is* created and the ramps are drawn into it.  When thatÕs done, the 32-bit deep* GWorld is copied to the current GrafPort with the ditherMode modifier on.  If* there isnÕt enough memory for the temporary GWorld or if Color QuickDraw isnÕt* available, then the ramps are drawn directly to the current GrafPort.*******************************************************************************)	PROCEDURE PaintImage;		VAR			rampWorld:    GWorldPtr; {32-bit deep GWorld to draw ramps into}			savedPort:    CGrafPtr;  {Ptr to saved CGrafPort for later restoring}			savedGDevice: GDHandle;  {Handle to saved GDevice for later restoring}			rampColor:    RGBColor;  {Color to use when drawing a step of the ramp}			rampFactor:   Integer;   {Value to set a color component to in a ramp}			pinFactor:    Integer;   {Value to pin a color component to in a ramp}			x:            Integer;   {Horizontal coordinate to draw ramp step}			y:            Integer;   {Vertical coordinate to draw ramp step}	BEGIN		GetGWorld ((*<*)savedPort, (*<*)savedGDevice);			(* Create the offscreen to draw ramps if Color QuickDraw available *)		IF gHasCQD THEN			BEGIN				(* Try to make the temporary GWorld in application heap *)				rampWorld := CreateOffScreen (GrafPtr(savedPort), 32,						NOT kUseTempMem);					(* If that didnÕt work, try allocating GWorld in temporary memory *)				IF rampWorld = NIL THEN					rampWorld := CreateOffScreen (GrafPtr(savedPort), 32,							kUseTempMem);				(* If got GWorld, set it, otherwise trash it *)			IF rampWorld <> NIL THEN				IF LockPixels (GetGWorldPixMap (rampWorld)) THEN					SetGWorld (rampWorld, NIL)				ELSE					BEGIN						DisposeGWorld (rampWorld);						rampWorld := NIL					END			END		ELSE			rampWorld := NIL;			(* Pin component values at $0000 first, then at $FFFF *)		FOR pinFactor := 0 DOWNTO -1 DO			BEGIN				(* Loop on y coordinate over entire GrafPort *)				FOR y := 0 TO kMaxRampCount DO					BEGIN						(* Calculate value of components that are dynamic in a ramp *)						rampFactor := y * 65535 DIV kMaxRampCount;						IF pinFactor = -1 THEN							BEGIN								(* If pinning at $FFFF, use 1Õs comp of rampFactor *)								rampFactor := BXOR (rampFactor, $FFFF);								x := kRampWidth							END						ELSE							x := 0;							rampColor.red := rampFactor;						rampColor.green := rampFactor;						rampColor.blue := rampFactor;						RGBForeColor (rampColor);						MoveTo (x, y);						Line (kRampWidth, 0);							x := x + kRampWidth * 2;						rampColor.blue := pinFactor;						RGBForeColor (rampColor);						MoveTo (x, y);						Line (kRampWidth, 0);							x := x + kRampWidth * 2;						rampColor.green := pinFactor;						RGBForeColor (rampColor);						MoveTo (x, y);						Line (kRampWidth, 0);							x := x + kRampWidth * 2;						rampColor.blue := rampFactor;						RGBForeColor (rampColor);						MoveTo (x, y);						Line (kRampWidth, 0);							x := x + kRampWidth * 2;						rampColor.red := pinFactor;						RGBForeColor (rampColor);						MoveTo (x, y);						Line (kRampWidth, 0);							x := x + kRampWidth * 2;						rampColor.green := rampFactor;						RGBForeColor (rampColor);						MoveTo (x, y);						Line (kRampWidth, 0);							x := x + kRampWidth * 2;						rampColor.blue := pinFactor;						RGBForeColor (rampColor);						MoveTo (x, y);						Line (kRampWidth, 0);							x := x + kRampWidth * 2;						rampColor.green := pinFactor;						RGBForeColor (rampColor);						MoveTo (x, y);						Line (kRampWidth, 0);					END			END;			(* If drawing into rampWorld, then CopyBits it to the current GrafPort *)		IF rampWorld <> NIL THEN			BEGIN				SetGWorld (savedPort, savedGDevice);				CopyBits (GrafPtr(rampWorld)^.portBits, GrafPtr(savedPort)^.						portBits, rampWorld^.portRect, savedPort^.portRect, srcCopy +						ditherCopy, NIL);				UnlockPixels (GetGWorldPixMap (rampWorld));				DisposeGWorld (rampWorld)			END	END;(******************************************************************************** ScrollPattern - Scroll a pattern for the marchine ants effect**     This routine rotates an old eight-byte pattern down one pixel, moving the* bottom row of pixels to the top.  This is done to with the marquee pattern so* that the ants march.*******************************************************************************)	PROCEDURE ScrollPattern (VAR aPattern: Pattern);		VAR			tempByte: Byte; {Holds one byte of pattern for rotation}	BEGIN		tempByte := aPattern [7];		BlockMove (Ptr(@aPattern [0]), Ptr(@aPattern [1]), SIZEOF (Pattern) - 1);		aPattern [0] := tempByte;	END;(******************************************************************************** TrackMarquee - Track the dragging of the marquee**     As long as the user holds down the mouse button, a selection marquee is* drawn with one corner at the location that the mouse was pressed and the* opposite corner at the location that the mouse currently is.  The off-screen* image that the dragger is being drawn over (you could think of this as the* documentÕs data) must be specified by srcImage.  The window that the user will* see the image and the selection marquee must be specified by dstImage.  Every* time the mouse is moved, srcImage is copied into a temporary GWorld.  This is* where the actual marquee animation takes place.  The marquee is then drawn* into the temporary GWorld, then that is copied to the window specified by* dstImage.  The next time the mouse is moved, srcImage is again copied into the* temporary GWorld which erases the old marquee and the process repeats.**     To optimize the speed of the tracking, only the portion of the GWorlds and* screen that are affected by the marquee are copied.  So if the selection* marquee is small, only a small portion of the GWorlds are copied.  The maxRect* local variable holds the union of the marquee in the previous iteration and* the current iteration, and itÕs this rectangle thatÕs used as the bounds of* the CopyBits.  That assures that the marquee in the previous iteration is* erased.*******************************************************************************)	PROCEDURE TrackMarquee (srcImage:      GWorldPtr;	                        dstImage:      WindowPtr;	                        startPt:       Point;	                        VAR finalRect: Rect);		VAR			newPt:          Point;     {Current location of mouse in local coords}			oldPt:          Point;     {Mouse loc in last iteration; local coords}			currPort:       CGrafPtr;  {Current GrafPort for restoring}			currGDevice:    GDHandle;  {Handle to current GDevice for restoring}			animationWorld: GWorldPtr; {Temporary GWorld used for smooth animation}			scrollTime:     LONGINT;   {Time of last scroll}			marqueeRect:    Rect;      {Rectangle of marquee}			maxRect:        Rect;      {Union of current marqueeRect and previous}	BEGIN		SetPort (dstImage);		GetGWorld ((*<*)currPort, (*<*)currGDevice);		(* Create the animation offscreen *)		animationWorld := CreateOffScreen (dstImage, 0, NOT kUseTempMem);		(* If that didnÕt work, try allocating the GWorld in temporary memory *)		IF animationWorld = NIL THEN			animationWorld := CreateOffScreen (dstImage, 0, kUseTempMem);		(* If THAT didnÕt work, give up *)		IF animationWorld <> NIL THEN			BEGIN				IF LockPixels (GetGWorldPixMap (animationWorld)) THEN					BEGIN						(* Prime the animation world *)						SetGWorld (animationWorld, NIL);						IF LockPixels (GetGWorldPixMap (srcImage)) THEN							BEGIN								CopyBits (GrafPtr(srcImage)^.portBits,										GrafPtr(animationWorld)^.portBits,										srcImage^.portRect, animationWorld^.portRect,										srcCopy, NIL);								UnlockPixels (GetGWorldPixMap (srcImage))							END;						(* Draw 1st frame to window so that old marquee is erased *)						SetGWorld (CGrafPtr(dstImage), currGDevice);						CopyBits (GrafPtr(animationWorld)^.portBits, dstImage^.								portBits, animationWorld^.portRect, dstImage^.portRect,								srcCopy + ditherCopy, NIL);						(* Prime the loop variables *)						oldPt := startPt;						newPt := startPt;						scrollTime := 0;						Pt2Rect (startPt, startPt, (*<*)marqueeRect);						maxRect := marqueeRect;						(* Keep tracking until the mouse button is released *)						WHILE StillDown DO							BEGIN								GetMouse ((*×*)newPt);								(* Only redraw the marquee if the mouse moved *)								IF NOT EqualPt (oldPt, newPt) THEN									BEGIN										(* Erase the old temporary image *)										SetGWorld (animationWorld, NIL);										IF LockPixels (GetGWorldPixMap (srcImage)) THEN											BEGIN												CopyBits (GrafPtr(srcImage)^.portBits,														GrafPtr(animationWorld)^.portBits,														marqueeRect, marqueeRect, srcCopy,														NIL);												UnlockPixels (GetGWorldPixMap (srcImage))											END;										(* Draw the new dragger position *)										SetGWorld (animationWorld, NIL);										PenPat (gMarqueePatt);										Pt2Rect (startPt, newPt, (*<*)marqueeRect);										FrameRect (marqueeRect);										UnionRect (marqueeRect, maxRect, (*<*)maxRect);										(* Draw the new frame to the window *)										SetGWorld (CGrafPtr(dstImage), currGDevice);										CopyBits (GrafPtr(animationWorld)^.portBits,												dstImage^.portBits, maxRect, maxRect,												srcCopy + ditherCopy, NIL);										oldPt := newPt;										maxRect := marqueeRect									END;								(* If enough time since last ant march, march ants *)								IF (TickCount - scrollTime) > kMinScrollTime THEN									BEGIN										ScrollPattern (gMarqueePatt);										PenPat (gMarqueePatt);										FrameRect (marqueeRect);										scrollTime := TickCount									END							END;						UnlockPixels (GetGWorldPixMap (animationWorld))					END;				DisposeGWorld (animationWorld);			END;		(* Reset current GWorld to what it was at routine entry *)		SetGWorld (currPort, currGDevice);		(* Rtn dragged rect in finalRect, or set empty if dragged rect empty *)		IF EqualPt (startPt, newPt) THEN			SetRect ((*<*)finalRect, 0, 0, 0, 0)		ELSE			Pt2Rect (startPt, newPt, (*<*)finalRect)	END;(******************************************************************************** UpdateOffScreen - Update GWorld to screen characteristics of a window**     UpdateOffScreen updates the off-screen GWorld specified by offScreen so* that it has the same depth and color table of the deepest screen that* intersects the window specified by baseWindow.  Also, the pixels of offScreen* are properly aligned so that a CopyBits call from the off-screen to the screen* is as fast as possible.  If enough of a change is made to offScreen, then a* completely new GWorld will be made by UpdateGWorld, and a pointer to it is* returned back in offScreen.**     This routine is really a showcase for the UpdateGWorld routine.  ItÕs this* routine, introduced with 32-Bit QuickDraw 1.0, that handles updating the* characteristics of the GWorld to the screen characteristics and pixel* alignment of a rectangle on the screen, normally a windowÕs portRect.**     You can see that IÕm passing clipPix in the GWorld flags.  I never resize* the GWorld, so it would seem that asking UpdateGWorld to clip the existing* image to the new size of the GWorld is a fine waste of time.  But what IÕm* asking UpdateGWorld to do is to retain the existing image.  If I had passed an* empty set for the GWorld flags, UpdateGWorld could throw out the existing* image if it sees fit to do so.  The result of UpdateGWorld is returned in* returnFlags.**     If UpdateGWorld had to change the depth of the GWorld, it returns the* newDepth flag.  If it had to change the color table of the GWorld, it returns* the mapPix flag. I decided that, for this sample code, I would completely* redraw the picture into the GWorld if either of these things had to be done.* UpdateGWorld is completely capable of dithering the image to the new depth and* color table, but that only helps you if UpdateGWorld makes the GWorld* shallower than it was--it doesnÕt do much for you if it made the GWorld* deeper.  ThatÕs why I decided to redraw the picture in either case.*******************************************************************************)	FUNCTION UpdateOffScreen (VAR offScreen:   GWorldPtr;	                          VAR returnFlags: GWorldFlags;	                          baseWindow:      WindowPtr): QDErr;		VAR			windRect:    Rect;     {portRect of baseWindow in global coordinates}			currPort:    CGrafPtr; {Pointer to the current GrafPort}			currGDevice: GDHandle; {Handle to the current GDevice}			error:       OSErr;    {Result of QuickDraw operations}	BEGIN		(* Convert baseWindowÕs portRect to global coordinates *)		windRect := baseWindow^.portRect;		LocalToGlobal ((*×*)windRect.topLeft);		LocalToGlobal ((*×*)windRect.botRight);		(* Update the GWorld so that it has the same characteristics as the screen *)		returnFlags := UpdateGWorld ((*×*)offScreen, 0, windRect, NIL, NIL,				[clipPix]);		(* If an error occurs, get the error code *)		IF gwFlagErr IN returnFlags THEN			error := LongInt(returnFlags)		ELSE			BEGIN				error := noErr;				(* Redraw our picture if updated GWorld has new depth or clut *)				IF (newDepth IN returnFlags) OR (mapPix IN returnFlags) THEN					BEGIN						GetGWorld ((*<*)currPort, (*<*)currGDevice);						SetGWorld (offScreen, NIL);						IF LockPixels (GetGWorldPixMap (offScreen)) THEN							BEGIN								PaintImage;								UnlockPixels (GetGWorldPixMap (offScreen))							END;						SetGWorld (currPort, currGDevice)					END			END;		UpdateOffScreen := error	END;(******************************************************************************** MainEventLoop - Main event loop**     This function collects events, does something on events that it knows* about, and exits when the user clicks on the close box of the window.**     If the user clicks in the content region of the window, then TrackMarquee* is called to allow the user to drag out a selection marquee.**     If the user clicks in the title bar, the user is allowed to drag the* window around in the usual manner.  After the window is moved, UpdateOffScreen* is called so that if the window was moved to a screen with a depth different* than the one it was on, then the off-screen GWorld containing the image of the* ramps is updated to the depth of that screen.  If the window is moved so that* it crosses more than one screen, then the off-screen GWorld is set to the* depth and color table of the deepest screen thatÕs crossed.  That leads to an* interesting situation if the window is moved so that itÕs partially on a* screen of a deeper depth than the one it was entirely contained on because the* depth of the GWorld changes, yet the part of the image that remains on the old* screen is from the old depth of the GWorld.  That can lead to some weird* window updating problems.  So in that case, I invalidate the entire window so* that all of the image is redrawn from the new GWorld depth.**     If an update event comes in for the display window, UpdateOffScreen is* called in case the update event is caused by the user using the Monitors* control panel to change the depth or color table of the screen that the window* is on.  Similar to the case in which the window is dragged so that itÕs partly* on a deeper screen, the entire window is invalidated if the GWorldÕs depth or* color table changes.**     If a suspend event comes in, then the GWorld is copied to the window so* that the selection marquee, if any, is erased.  When GMonde is resumed, the* usual marching ants animation resumes.**     If thereÕs an active selection marquee, then the marquee pattern is* rotated one pixel and the marquee rectangle is redrawn using this rotated* pattern.  This is done once per iteration of the main event loop.  To make the* ants march as smoothly as possible, the WaitNextEvent sleep time is set to 0* to give GMonde as much time as possible.  If there is no active selection,* then itÕs kind of impolite to ask for that much time, so the sleep time is set* to 60 if thereÕs no selection.*******************************************************************************)	PROCEDURE MainEventLoop;		VAR			anEvent:     EventRecord; {Incoming events}			evtWind:     WindowPtr;   {Pointer to window relevant to event}			clickArea:   Integer;     {Area that mouse was clicked (i.e. menu bar)}			clickPos:    Point;       {Location of mouse click in local coords}			osEvtKind:   Byte;        {OSEvent kind; mouse-moved or suspend/resume}			resultFlags: GWorldFlags; {GWorld flags on return of UpdateGWorld call}			screenRect:  Rect;        {Rectangle of screen in global coordinates}			scrollTime:  LongInt;     {TickCount at last marquee patt scroll}			dreamTime:   LongInt;     {# ticks that WaitNextEvent can hold us up}			wereInFront: Boolean;     {True if GMonde is front-most application}			error:       QDErr;       {Result of QuickDraw operations}			result:      OSErr;       {Result of AppleEvent operations}	BEGIN		(* Initialize our variables for the main event loop *)		gQuitting := FALSE;		error := noErr;		scrollTime := 0;		wereInFront := TRUE;		(* Continue the main event loop until user quits or an error occurs *)		InitCursor;		WHILE (NOT gQuitting) AND (error = noErr) DO			BEGIN				(* If marquee is active, then need more time to march the ants *)				IF EmptyRect (gMarqueeRect) THEN					dreamTime := 60				ELSE					dreamTime := 0;				(* If a non-null event, handle it; otherwise, just march the ants *)				IF WaitNextEvent (everyEvent, (*<*)anEvent, dreamTime, NIL) THEN					BEGIN						(* React to content clicks, title clicks, close box clicks *)						IF anEvent.what = mouseDown THEN							BEGIN								clickArea := FindWindow (anEvent.where, (*<*)evtWind);								IF clickArea = inContent THEN									BEGIN										(* Click in content rgn; user dragging marquee *)										clickPos := anEvent.where;										GlobalToLocal ((*×*)clickPos);										TrackMarquee (gOffScreen, evtWind, clickPos,												(*<*)gMarqueeRect)									END								ELSE IF clickArea = inDrag THEN									BEGIN										(* Let the user drag the window around *)										screenRect := GetGrayRgn^^.rgnBBox;										DragWindow (evtWind, anEvent.where, screenRect);										(* If wind moved to other screen, update GWorld *)										error := UpdateOffScreen ((*×*)gOffScreen,												(*<*)resultFlags, evtWind);										IF error = noErr THEN											IF (newDepth IN resultFlags) OR (mapPix IN													resultFlags) THEN												BEGIN													(* Wind in scrn w/ diff depth; redraw *)													SetPort (evtWind);													InvalRect (evtWind^.portRect)												END									END								ELSE IF clickArea = inGoAway THEN									(* Click in close box means weÕre outta here *)									IF TrackGoAway (evtWind, anEvent.where) THEN										gQuitting := TRUE							END						ELSE IF anEvent.what = updateEvt THEN							BEGIN								evtWind := WindowPtr(anEvent.message);								(* Might be update because of depth or clut changing *)								error := UpdateOffScreen ((*×*)gOffScreen,										(*<*)resultFlags, evtWind);								IF error = noErr THEN									(* If scrn depth/clut changed, redraw whole window *)									IF (newDepth IN resultFlags) OR (mapPix IN											resultFlags) THEN										BEGIN											SetPort (evtWind);											InvalRect (evtWind^.portRect)										END;								BeginUpdate (evtWind);								SetPort (evtWind);								(* Now draw the GWorld to the window *)								IF LockPixels (GetGWorldPixMap (gOffScreen)) THEN									BEGIN										CopyBits (GrafPtr(gOffScreen)^.portBits, evtWind^.												portBits, gOffScreen^.portRect, evtWind^.												portRect, srcCopy + ditherCopy, NIL);										UnlockPixels (GetGWorldPixMap (gOffScreen))									END;								EndUpdate (evtWind)							END						ELSE IF anEvent.what = osEvt THEN							BEGIN								osEvtKind := BAnd (BRotR (anEvent.message, 24), $00FF);								IF (osEvtKind = suspendResumeMessage) AND (BAnd (anEvent.message,										kResumeMask) <> 0) THEN									wereInFront := TRUE								ELSE									BEGIN										evtWind := FrontWindow;										SetPort (evtWind);										IF LockPixels (GetGWorldPixMap (gOffScreen)) THEN											BEGIN												CopyBits (GrafPtr(gOffScreen)^.portBits,														evtWind^.portBits, gOffScreen^.														portRect, evtWind^.portRect, srcCopy +														ditherCopy, NIL);												UnlockPixels (GetGWorldPixMap (gOffScreen))											END;										wereInFront := false									END							END						ELSE IF anEvent.what = kHighLevelEvent THEN							result := AEProcessAppleEvent (anEvent)					END;				(* If have marquee & enough time since last ant march, march ants *)				IF (NOT EmptyRect (gMarqueeRect)) AND (TickCount - scrollTime >						kMinScrollTime) AND wereInFront THEN					BEGIN						ScrollPattern (gMarqueePatt);						PenPat (gMarqueePatt);						FrameRect (gMarqueeRect);						scrollTime := TickCount					END			END	END;(******************************************************************************)(* Main Program                                                               *)(******************************************************************************)BEGIN	StartUp;	IF gHasGWorlds THEN		BEGIN			gDemoWindow := CreateDemoWindow;			IF gDemoWindow <> NIL THEN				BEGIN					SetPort (gDemoWindow);					gOffScreen := CreateOffScreen (gDemoWindow, 0, NOT kUseTempMem);					IF gOffScreen <> NIL THEN						BEGIN							GetGWorld (GWorldPtr(gDemoWindow), gDemoDevice);							SetGWorld (gOffScreen, NIL);							IF LockPixels (GetGWorldPixMap (gOffScreen)) THEN								BEGIN									PaintImage;									UnlockPixels (GetGWorldPixMap (gOffScreen))								END;							SetGWorld (GWorldPtr(gDemoWindow), gDemoDevice);							MainEventLoop						END				END		ENDEND.