// ******************************************************************************************* // GWorldPicCursIcon.c CLASSIC EVENT MODEL // ******************************************************************************************* // // This program demonstrates offscreen graphics world, picture, cursor, cursor shape change, // animated cursor, and icon operations as a result of the user choosing items from a // Demonstration menu. It also demonstrates a modal dialog-based About. box containing a // picture. // // To keep the non-demonstration code to a minimum, the program contains no functions for // updating the window or for responding to activate and operating system events. // // The program utilises the following resources: // // o A 'plst' resource. // // o An 'MBAR' resource and associated 'MENU' resources (preload, non-purgeable). // // o A 'WIND' resource (purgeable) (initially visible). // // o An 'acur' resource (purgeable). // // o 'CURS' resources associated with the 'acur' resource (preload, purgeable). // // o Two 'cicn' resources (purgeable), one for the Icons menu item and one for drawing in the // window. // // o Two icon family resources (purgeable), both for drawing in the window. // // o A 'DLOG' resource (purgeable) and an associated 'DITL' resource (purgeable) and 'PICT' // resource for an About GWorldPicCursIcon. dialog box. // // o A 'STR#' resource (purgeable) containing transform constants. // // o A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground, // doesActivateOnFGSwitch, and isHighLevelEventAware flags set. // // ******************************************************************************************* // .................................................................................. includes #include <Carbon.h> // ................................................................................... defines #define rMenubar 128 #define rWindow 128 #define mAppleApplication 128 #define iAbout 1 #define mFile 129 #define iQuit 12 #define mDemonstration 131 #define iOffScreenGWorld1 1 #define iOffScreenGWorld2 2 #define iPicture 3 #define iCursor 4 #define iAnimatedCursor1 5 #define iAnimatedCursor2 6 #define iAnimatedCursorOSX 7 #define iIcon 8 #define rBeachBallCursor 128 #define rPicture 128 #define rTransformStrings 128 #define rIconFamily1 128 #define rIconFamily2 129 #define rColourIcon 128 #define rAboutDialog 128 #define kSleepTime 1 #define kBeachBallTickInterval 5 #define kCountingHandTickInterval 30 #define MAX_UINT32 0xFFFFFFFF #define topLeft(r) (((Point *) &(r))[0]) #define botRight(r) (((Point *) &(r))[1]) // .................................................................................. typedefs typedef struct { SInt16 numberOfFrames; SInt16 whichFrame; CursHandle frame[]; } animCurs, *animCursPtr, **animCursHandle; // .......................................................................... global variables Boolean gRunningOnX = false; WindowRef gWindowRef; Boolean gDone; SInt32 gSleepTime; RgnHandle gCursorRegion; Boolean gCursorRegionsActive = false; Boolean gAnimatedCursor1Active = false; Boolean gAnimatedCursor2Active = false; Boolean gAnimatedCursorOSXActive = false; animCursHandle gAnimCursHdl; SInt16 gAnimCursTickInterval; SInt32 gAnimCursLastTick; RGBColor gBlackColour = { 0x0000, 0x0000, 0x0000 }; RGBColor gWhiteColour = { 0xFFFF, 0xFFFF, 0xFFFF }; RGBColor gBeigeColour = { 0xF000, 0xE300, 0xC200 }; RGBColor gBlueColour = { 0x4444, 0x4444, 0x9999 }; // ....................................................................... function prototypes void main (void); void doPreliminaries (void); OSErr quitAppEventHandler (AppleEvent *,AppleEvent *,SInt32); void eventLoop (void); void doIdle (void); void doEvents (EventRecord *); void doMenuChoice (SInt32); void doOffScreenGWorld1 (void); void doOffScreenGWorld2 (void); void doPicture (void); void doCursor (void); void doChangeCursor (WindowRef,RgnHandle); void doAnimatedCursor1 (void); void doAnimatedCursor2 (void); Boolean doGetAnimCursor (SInt16,SInt16); void doIncrementAnimCursor (void); void doReleaseAnimCursor (void); void doAnimatedCursorOSX (void); void doIcon (void); void doAboutDialog (void); void doDrawStuff (void); UInt16 doRandomNumber (UInt16,UInt16); // ************************************************************************************** main void main(void) { UInt32 seconds; MenuBarHandle menubarHdl; SInt32 response; MenuRef menuRef; // ..................................................................... initialise managers doPreliminaries(); // ............................................................ seed random number generator GetDateTime(&seconds); SetQDGlobalsRandomSeed(seconds); // ............................................................... set up menu bar and menus menubarHdl = GetNewMBar(rMenubar); if(menubarHdl == NULL) ExitToShell(); SetMenuBar(menubarHdl); DrawMenuBar(); Gestalt(gestaltMenuMgrAttr,&response); if(response & gestaltMenuMgrAquaLayoutMask) { menuRef = GetMenuRef(mFile); if(menuRef != NULL) { DeleteMenuItem(menuRef,iQuit); DeleteMenuItem(menuRef,iQuit - 1); DisableMenuItem(menuRef,0); } menuRef = GetMenuRef(mDemonstration); if(menuRef != NULL) EnableMenuItem(menuRef,iAnimatedCursorOSX); gRunningOnX = true; } // ............................................................................. open window if(!(gWindowRef = GetNewCWindow(rWindow,NULL,(WindowRef)-1))) ExitToShell(); SetPortWindowPort(gWindowRef); TextSize(10); // ........................................................................ enter event loop eventLoop(); } // ************************************************************************** do preliminaries void doPreliminaries(void) { OSErr osError; MoreMasterPointers(64); InitCursor(); FlushEvents(everyEvent,0); osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication, NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler), 0L,false); if(osError != noErr) ExitToShell(); } // **************************************************************************** doQuitAppEvent OSErr quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon) { OSErr osError; DescType returnedType; Size actualSize; osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,NULL,0, &actualSize); if(osError == errAEDescNotFound) { gDone = true; osError = noErr; } else if(osError == noErr) osError = errAEParamMissed; return osError; } // ********************************************************************************* eventLoop void eventLoop(void) { EventRecord eventStructure; Boolean gotEvent; gDone = false; gSleepTime = MAX_UINT32; gCursorRegion = NULL; while(!gDone) { gotEvent = WaitNextEvent(everyEvent,&eventStructure,gSleepTime,gCursorRegion); if(gotEvent) doEvents(&eventStructure); else { if(eventStructure.what == nullEvent) doIdle(); } } } // ************************************************************************************ doIdle void doIdle(void) { if(gAnimatedCursor1Active || gAnimatedCursor2Active) doIncrementAnimCursor(); } // ********************************************************************************** doEvents void doEvents(EventRecord *eventStrucPtr) { WindowRef windowRef; WindowPartCode partCode; switch(eventStrucPtr->what) { case kHighLevelEvent: AEProcessAppleEvent(eventStrucPtr); break; case mouseDown: partCode = FindWindow(eventStrucPtr->where,&windowRef); switch(partCode) { case inMenuBar: doMenuChoice(MenuSelect(eventStrucPtr->where)); break; case inContent: if(windowRef != FrontWindow()) SelectWindow(windowRef); break; case inDrag: DragWindow(windowRef,eventStrucPtr->where,NULL); if(gCursorRegionsActive) doChangeCursor(windowRef,gCursorRegion); break; } break; case keyDown: if((eventStrucPtr->modifiers & cmdKey) != 0) doMenuChoice(MenuEvent(eventStrucPtr)); break; case updateEvt: BeginUpdate((WindowRef) eventStrucPtr->message); EndUpdate((WindowRef) eventStrucPtr->message); break; case osEvt: switch((eventStrucPtr->message >> 24) & 0x000000FF) { case suspendResumeMessage: if((eventStrucPtr->message & resumeFlag) == 1) SetThemeCursor(kThemeArrowCursor); break; case mouseMovedMessage: if(gCursorRegionsActive) doChangeCursor(FrontWindow(),gCursorRegion); break; } break; } } // ****************************************************************************** doMenuChoice void doMenuChoice(SInt32 menuChoice) { MenuID menuID; MenuItemIndex menuItem; menuID = HiWord(menuChoice); menuItem = LoWord(menuChoice); if(menuID == 0) return; if(gAnimatedCursor1Active || gAnimatedCursor2Active) { if(gAnimatedCursor2Active) doReleaseAnimCursor(); SetThemeCursor(kThemeArrowCursor); gSleepTime = MAX_UINT32; gAnimatedCursor1Active = false; gAnimatedCursor2Active = false; } if(gAnimatedCursorOSXActive) doAnimatedCursorOSX(); if(gCursorRegionsActive == true) { gCursorRegionsActive = false; DisposeRgn(gCursorRegion); gCursorRegion = NULL; } switch(menuID) { case mAppleApplication: if(menuItem == iAbout) doAboutDialog(); break; case mFile: if(menuItem == iQuit) gDone = true; break; case mDemonstration: switch(menuItem) { case iOffScreenGWorld1: doOffScreenGWorld1(); break; case iOffScreenGWorld2: doOffScreenGWorld2(); break; case iPicture: doPicture(); break; case iCursor: doCursor(); break; case iAnimatedCursor1: doAnimatedCursor1(); break; case iAnimatedCursor2: doAnimatedCursor2(); break; case iAnimatedCursorOSX: doAnimatedCursorOSX(); break; case iIcon: doIcon(); break; } break; } HiliteMenu(0); } // ************************************************************************ doOffScreenGWorld1 void doOffScreenGWorld1(void) { Rect portRect, sourceRect, destRect; GrafPtr windowPortPtr; GDHandle deviceHdl; QDErr qdErr; GWorldPtr gworldPortPtr; PixMapHandle gworldPixMapHdl, windowPixMapHdl; Boolean lockPixResult; // .......................................................................... draw in window SetWTitle(gWindowRef,"\pTime-consuming drawing operation"); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); doDrawStuff(); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); SetWTitle(gWindowRef,"\pClick mouse to repeat in offscreen graphics port"); QDFlushPortBuffer(GetWindowPort(gWindowRef),NULL); while(!Button()) ; if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); GetWindowPortBounds(gWindowRef,&portRect); RGBBackColor(&gBlueColour); EraseRect(&portRect); RGBForeColor(&gWhiteColour); MoveTo(190,180); DrawString("\pPlease Wait. Drawing in offscreen graphics port."); // ...................................... draw in offscreen graphics port and copy to window // ......................... save current graphics world and create offscreen graphics world GetGWorld(&windowPortPtr,&deviceHdl); qdErr = NewGWorld(&gworldPortPtr,0,&portRect,NULL,NULL,0); if(gworldPortPtr == NULL || qdErr != noErr) { SysBeep(10); return; } SetGWorld(gworldPortPtr,NULL); // ................... lock pixel image for duration of drawing and erase offscreen to white gworldPixMapHdl = GetGWorldPixMap(gworldPortPtr); if(!(lockPixResult = LockPixels(gworldPixMapHdl))) { SysBeep(10); return; } EraseRect(&portRect); // ................................................... draw into the offscreen graphics port doDrawStuff(); // ............................................................ restore saved graphics world SetGWorld(windowPortPtr,deviceHdl); // ................................................... set source and destination rectangles GetPortBounds(gworldPortPtr,&sourceRect); GetPortBounds(windowPortPtr,&destRect); // ............................................................. get window port's pixel map windowPixMapHdl = GetGWorldPixMap(windowPortPtr); // ............. ensure background colour is white and foreground colour in black, then copy RGBBackColor(&gWhiteColour); RGBForeColor(&gBlackColour); CopyBits((BitMap *) *gworldPixMapHdl, (BitMap *) *windowPixMapHdl, &sourceRect,&destRect,srcCopy,NULL); if(QDError() != noErr) SysBeep(10); // ................................................................................ clean up UnlockPixels(gworldPixMapHdl); DisposeGWorld(gworldPortPtr); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); SetWTitle(gWindowRef,"\pOffscreen Graphics Worlds, Pictures, Cursors and Icons"); QDFlushPortBuffer(GetWindowPort(gWindowRef),NULL); } // ************************************************************************ doOffScreenGWorld2 void doOffScreenGWorld2(void) { PicHandle picture1Hdl,picture2Hdl; Rect portRect; Rect sourceRect, maskRect, maskDisplayRect, dest1Rect, dest2Rect, destRect; GrafPtr windowPortPtr; GDHandle deviceHdl; QDErr qdErr; GWorldPtr gworldPortPtr; PixMapHandle gworldPixMapHdl, windowPixMapHdl; RgnHandle region1Hdl, region2Hdl, regionHdl; SInt16 a, sourceMode; RGBBackColor(&gBeigeColour); GetWindowPortBounds(gWindowRef,&portRect); EraseRect(&portRect); // ........................................ get the source picture and draw it in the window if(!(picture1Hdl = GetPicture(rPicture))) ExitToShell(); HNoPurge((Handle) picture1Hdl); SetRect(&sourceRect,116,35,273,147); DrawPicture(picture1Hdl,&sourceRect); HPurge((Handle) picture1Hdl); MoveTo(116,32); DrawString("\pSource image"); // ......................... save current graphics world and create offscreen graphics world GetGWorld(&windowPortPtr,&deviceHdl); SetRect(&maskRect,0,0,157,112); qdErr = NewGWorld(&gworldPortPtr,0,&maskRect,NULL,NULL,0); if(gworldPortPtr == NULL || qdErr != noErr) { SysBeep(10); return; } SetGWorld(gworldPortPtr,NULL); // ................... lock pixel image for duration of drawing and erase offscreen to white gworldPixMapHdl = GetGWorldPixMap(gworldPortPtr); if(!(LockPixels(gworldPixMapHdl))) { SysBeep(10); return; } GetPortBounds(gworldPortPtr,&portRect); EraseRect(&portRect); // ................................. get mask picture and draw it in offscreen graphics port if(!(picture2Hdl = GetPicture(rPicture + 1))) ExitToShell(); HNoPurge((Handle) picture2Hdl); DrawPicture(picture2Hdl,&maskRect); // .............................................................. also draw it in the window SetGWorld(windowPortPtr,deviceHdl); SetRect(&maskDisplayRect,329,35,485,146); DrawPicture(picture2Hdl,&maskDisplayRect); HPurge((Handle) picture2Hdl); MoveTo(329,32); DrawString("\pCopy of offscreen mask"); // ........................ define an oval-shaped region and a round rectangle-shaped region SetRect(&dest1Rect,22,171,296,366); region1Hdl = NewRgn(); OpenRgn(); FrameOval(&dest1Rect); CloseRgn(region1Hdl); SetRect(&dest2Rect,308,171,582,366); region2Hdl = NewRgn(); OpenRgn(); FrameRoundRect(&dest2Rect,100,100); CloseRgn(region2Hdl); SetWTitle(GetWindowFromPort(windowPortPtr),"\pClick mouse to copy"); QDFlushPortBuffer(GetWindowPort(gWindowRef),NULL); while(!Button()) ; // ............................................................. get window port's pixel map windowPixMapHdl = GetGWorldPixMap(windowPortPtr); // ........ set background and foreground colour, then copy source to destination using mask RGBForeColor(&gBlackColour); RGBBackColor(&gWhiteColour); for(a=0;a<2;a++) { if(a == 0) { regionHdl = region1Hdl; destRect = dest1Rect; sourceMode = srcCopy; MoveTo(22,168); DrawString("\pBoolean source mode srcCopy"); } else { regionHdl = region2Hdl; destRect = dest2Rect; sourceMode = srcXor; MoveTo(308,168); DrawString("\pBoolean source mode srcXor"); } CopyDeepMask((BitMap *) *windowPixMapHdl, (BitMap *) *gworldPixMapHdl, (BitMap *) *windowPixMapHdl, &sourceRect,&maskRect,&destRect,sourceMode + ditherCopy,regionHdl); if(QDError() != noErr) SysBeep(10); } // ................................................................................ clean up UnlockPixels(gworldPixMapHdl); DisposeGWorld(gworldPortPtr); ReleaseResource((Handle) picture1Hdl); ReleaseResource((Handle) picture2Hdl); DisposeRgn(region1Hdl); DisposeRgn(region2Hdl); SetWTitle(gWindowRef,"\pOffscreen Graphics Worlds, Pictures, Cursors and Icons"); QDFlushPortBuffer(GetWindowPort(gWindowRef),NULL); } // ********************************************************************************* doPicture void doPicture(void) { Rect portRect, pictureRect, theRect; OpenCPicParams picParams; RgnHandle oldClipRgn; PicHandle pictureHdl; SInt16 a, left, top, right, bottom, random; RGBColor theColour; PictInfo pictInfo; Str255 theString; RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); EraseRect(&portRect); // ................................................................ define picture rectangle pictureRect = portRect; pictureRect.right = (portRect.right - portRect.left) / 2; InsetRect(&pictureRect,10,10); // ..................................................................... set clipping region oldClipRgn = NewRgn(); GetClip(oldClipRgn); ClipRect(&pictureRect); // ......................................................... set up OpenCPicParams structure picParams.srcRect = pictureRect; picParams.hRes = 0x00480000; picParams.vRes = 0x00480000; picParams.version = -2; // .......................................................................... record picture pictureHdl = OpenCPicture(&picParams); RGBBackColor(&gBlueColour); EraseRect(&pictureRect); for(a=0;a<300;a++) { theRect = pictureRect; theColour.red = doRandomNumber(0,65535); theColour.green = doRandomNumber(0,65535); theColour.blue = doRandomNumber(0,65535); RGBForeColor(&theColour); left = doRandomNumber(10,theRect.right); top = doRandomNumber(10,theRect.bottom); right = doRandomNumber(left,theRect.right); bottom = doRandomNumber(top,theRect.bottom); SetRect(&theRect,left,top,right,bottom); PenMode(doRandomNumber(addOver,adMin)); random = doRandomNumber(0,5); if(random == 0) { MoveTo(left,top); LineTo(right - 1,bottom - 1); } else if(random == 1) PaintRect(&theRect); else if(random == 2) PaintRoundRect(&theRect,30,30); else if(random == 3) PaintOval(&theRect); else if(random == 4) PaintArc(&theRect,0,300); else if(random == 5) { TextSize(doRandomNumber(10,70)); MoveTo(left,right); DrawString("\pPICTURE"); } } // ............................. stop recording, draw picture, restore saved clipping region ClosePicture(); DrawPicture(pictureHdl,&pictureRect); SetClip(oldClipRgn); DisposeRgn(oldClipRgn); // .................................... display some information from the PictInfo structure RGBForeColor(&gBlueColour); RGBBackColor(&gBeigeColour); PenMode(patCopy); OffsetRect(&pictureRect,300,0); EraseRect(&pictureRect); FrameRect(&pictureRect); TextSize(10); if(GetPictInfo(pictureHdl,&pictInfo,recordFontInfo + returnColorTable,1,systemMethod,0)) SysBeep(10); MoveTo(380,70); DrawString("\pSome Picture Information:"); MoveTo(380,100); DrawString("\pLines: "); NumToString(pictInfo.lineCount,theString); DrawString(theString); MoveTo(380,115); DrawString("\pRectangles: "); NumToString((long) pictInfo.rectCount,theString); DrawString(theString); MoveTo(380,130); DrawString("\pRound rectangles: "); NumToString(pictInfo.rRectCount,theString); DrawString(theString); MoveTo(380,145); DrawString("\pOvals: "); NumToString(pictInfo.ovalCount,theString); DrawString(theString); MoveTo(380,160); DrawString("\pArcs: "); NumToString(pictInfo.arcCount,theString); DrawString(theString); MoveTo(380,175); DrawString("\pPolygons: "); NumToString(pictInfo.polyCount,theString); DrawString(theString); MoveTo(380,190); DrawString("\pRegions: "); NumToString(pictInfo.regionCount,theString); DrawString(theString); MoveTo(380,205); DrawString("\pText strings: "); NumToString(pictInfo.textCount,theString); DrawString(theString); MoveTo(380,220); DrawString("\pUnique fonts: "); NumToString(pictInfo.uniqueFonts,theString); DrawString(theString); MoveTo(380,235); DrawString("\pUnique colours: "); NumToString(pictInfo.uniqueColors,theString); DrawString(theString); MoveTo(380,250); DrawString("\pFrame rectangle left: "); NumToString(pictInfo.sourceRect.left,theString); DrawString(theString); MoveTo(380,265); DrawString("\pFrame rectangle top: "); NumToString(pictInfo.sourceRect.top,theString); DrawString(theString); MoveTo(380,280); DrawString("\pFrame rectangle right: "); NumToString(pictInfo.sourceRect.right,theString); DrawString(theString); MoveTo(380,295); DrawString("\pFrame rectangle bottom: "); NumToString(pictInfo.sourceRect.bottom,theString); DrawString(theString); QDFlushPortBuffer(GetWindowPort(gWindowRef),NULL); // ............................................ release memory occupied by Picture structure KillPicture(pictureHdl); } // ********************************************************************************** doCursor void doCursor(void) { Rect portRect, cursorRect; SInt16 a; RGBBackColor(&gBlueColour); GetWindowPortBounds(gWindowRef,&portRect); EraseRect(&portRect); cursorRect = portRect; for(a=0;a<3;a++) { InsetRect(&cursorRect,40,40); if(a == 0 || a == 2) RGBBackColor(&gBeigeColour); else RGBBackColor(&gBlueColour); EraseRect(&cursorRect); } RGBForeColor(&gBeigeColour); MoveTo(10,20); DrawString("\pArrow cursor region"); RGBForeColor(&gBlueColour); MoveTo(50,60); DrawString("\pIBeam cursor region"); RGBForeColor(&gBeigeColour); MoveTo(90,100); DrawString("\pCross cursor region"); RGBForeColor(&gBlueColour); MoveTo(130,140); DrawString("\pPlus cursor region"); gCursorRegion = NewRgn(); doChangeCursor(gWindowRef,gCursorRegion); gCursorRegionsActive = true; } // **************************************************************************** doChangeCursor void doChangeCursor(WindowRef windowRef,RgnHandle cursorRegion) { RgnHandle arrowCursorRgn; RgnHandle ibeamCursorRgn; RgnHandle crossCursorRgn; RgnHandle plusCursorRgn; Rect cursorRect; GrafPtr oldPort; Point mousePosition; arrowCursorRgn = NewRgn(); ibeamCursorRgn = NewRgn(); crossCursorRgn = NewRgn(); plusCursorRgn = NewRgn(); SetRectRgn(arrowCursorRgn,-32768,-32768,32766,32766); GetPort(&oldPort); SetPortWindowPort(windowRef); GetWindowPortBounds(windowRef,&cursorRect); LocalToGlobal(&topLeft(cursorRect)); LocalToGlobal(&botRight(cursorRect)); InsetRect(&cursorRect,40,40); RectRgn(ibeamCursorRgn,&cursorRect); DiffRgn(arrowCursorRgn,ibeamCursorRgn,arrowCursorRgn); InsetRect(&cursorRect,40,40); RectRgn(crossCursorRgn,&cursorRect); DiffRgn(ibeamCursorRgn,crossCursorRgn,ibeamCursorRgn); InsetRect(&cursorRect,40,40); RectRgn(plusCursorRgn,&cursorRect); DiffRgn(crossCursorRgn,plusCursorRgn,crossCursorRgn); GetGlobalMouse(&mousePosition); if(PtInRgn(mousePosition,ibeamCursorRgn)) { SetThemeCursor(kThemeIBeamCursor); CopyRgn(ibeamCursorRgn,cursorRegion); } else if(PtInRgn(mousePosition,crossCursorRgn)) { SetThemeCursor(kThemeCrossCursor); CopyRgn(crossCursorRgn,cursorRegion); } else if(PtInRgn(mousePosition,plusCursorRgn)) { SetThemeCursor(kThemePlusCursor); CopyRgn(plusCursorRgn,cursorRegion); } else { SetThemeCursor(kThemeArrowCursor); CopyRgn(arrowCursorRgn,cursorRegion); } DisposeRgn(arrowCursorRgn); DisposeRgn(ibeamCursorRgn); DisposeRgn(crossCursorRgn); DisposeRgn(plusCursorRgn); SetPort(oldPort); } // ************************************************************************* doAnimatedCursor1 void doAnimatedCursor1(void) { Rect portRect; Pattern whitePattern; BackColor(whiteColor); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); gAnimCursTickInterval = kCountingHandTickInterval; gSleepTime = gAnimCursTickInterval; gAnimatedCursor1Active = true; } // ************************************************************************* doAnimatedCursor2 void doAnimatedCursor2(void) { Rect portRect; Pattern whitePattern; SInt16 animCursResourceID, animCursTickInterval; BackColor(whiteColor); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); animCursResourceID = rBeachBallCursor; animCursTickInterval = kBeachBallTickInterval; if(doGetAnimCursor(animCursResourceID,animCursTickInterval)) { gSleepTime = animCursTickInterval; gAnimatedCursor2Active = true; } else SysBeep(10); } // *************************************************************************** doGetAnimCursor Boolean doGetAnimCursor(SInt16 resourceID,SInt16 tickInterval) { SInt16 cursorID, a = 0; Boolean noError = false; if((gAnimCursHdl = (animCursHandle) GetResource('acur',resourceID))) { noError = true; while((a < (*gAnimCursHdl)->numberOfFrames) && noError) { cursorID = (SInt16) HiWord((SInt32) (*gAnimCursHdl)->frame[a]); (*gAnimCursHdl)->frame[a] = GetCursor(cursorID); if((*gAnimCursHdl)->frame[a]) a++; else noError = false; } } if(noError) { gAnimCursTickInterval = tickInterval; gAnimCursLastTick = TickCount(); (*gAnimCursHdl)->whichFrame = 0; } return noError; } // ********************************************************************* doIncrementAnimCursor void doIncrementAnimCursor(void) { SInt32 newTick; static UInt32 animationStep; newTick = TickCount(); if(newTick < (gAnimCursLastTick + gAnimCursTickInterval)) return; if(gAnimatedCursor1Active) { SetAnimatedThemeCursor(kThemeCountingUpAndDownHandCursor,animationStep); animationStep++; } else if(gAnimatedCursor2Active) { SetCursor(*((*gAnimCursHdl)->frame[(*gAnimCursHdl)->whichFrame++])); if((*gAnimCursHdl)->whichFrame == (*gAnimCursHdl)->numberOfFrames) (*gAnimCursHdl)->whichFrame = 0; } gAnimCursLastTick = newTick; } // *********************************************************************** doReleaseAnimCursor void doReleaseAnimCursor(void) { SInt16 a; for(a=0;a<(*gAnimCursHdl)->numberOfFrames;a++) ReleaseResource((Handle) (*gAnimCursHdl)->frame[a]); ReleaseResource((Handle) gAnimCursHdl); } // *********************************************************************** doAnimatedCursorOSX void doAnimatedCursorOSX(void) { if(!gAnimatedCursorOSXActive) { QDDisplayWaitCursor(true); gAnimatedCursorOSXActive = true; } else { QDDisplayWaitCursor(false); gAnimatedCursorOSXActive = false; } } // ************************************************************************************ doIcon void doIcon(void) { Rect portRect, theRect; SInt16 a, b, stringIndex = 1; IconTransformType transform = 0; Str255 theString; Handle iconSuiteHdl; CIconHandle ciconHdl; RGBForeColor(&gBlueColour); RGBBackColor(&gBeigeColour); GetWindowPortBounds(gWindowRef,&portRect); EraseRect(&portRect); // .............................................................. PlotIconID with transforms MoveTo(50,28); DrawString("\pPlotIconID with transforms"); for(a=50;a<471;a+=140) { if(a == 190) transform = 16384; if(a == 330) transform = 256; for(b=0;b<4;b++) { if(a == 470 && b == 3) continue; GetIndString(theString,rTransformStrings,stringIndex++); MoveTo(a,b * 60 + 47); DrawString(theString); SetRect(&theRect,a,b * 60 + 50,a + 32,b * 60 + 82); PlotIconID(&theRect,0,transform,rIconFamily1); SetRect(&theRect,a + 40,b * 60 + 50,a + 56,b * 60 + 66); PlotIconID(&theRect,0,transform,rIconFamily1); SetRect(&theRect,a + 64,b * 60 + 50,a + 80,b * 60 + 62); PlotIconID(&theRect,0,transform,rIconFamily1); if(a >= 330) transform += 256; else transform ++; } } // .......................................................... GetIconSuite and PlotIconSuite MoveTo(50,275); LineTo(550,275); MoveTo(50,299); DrawString("\pGetIconSuite and PlotIconSuite"); GetIconSuite(&iconSuiteHdl,rIconFamily2,kSelectorAllLargeData); SetRect(&theRect,50,324,82,356); PlotIconSuite(&theRect,kAlignNone,kTransformNone,iconSuiteHdl); SetRect(&theRect,118,316,166,364); PlotIconSuite(&theRect,kAlignNone,kTransformNone,iconSuiteHdl); SetRect(&theRect,202,308,266,372); PlotIconSuite(&theRect,kAlignNone,kTransformNone,iconSuiteHdl); // .................................................................. GetCIcon and PlotCIcon MoveTo(330,299); DrawString("\pGetCIcon and PlotCIcon"); ciconHdl = GetCIcon(rColourIcon); SetRect(&theRect,330,324,362,356); PlotCIcon(&theRect,ciconHdl); SetRect(&theRect,398,316,446,364); PlotCIcon(&theRect,ciconHdl); SetRect(&theRect,482,308,546,372); PlotCIcon(&theRect,ciconHdl); } // ***************************************************************************** doAboutDialog void doAboutDialog(void) { DialogPtr dialogPtr; SInt16 itemHit; dialogPtr = GetNewDialog(rAboutDialog,NULL,(WindowRef)-1); ModalDialog(NULL,&itemHit); DisposeDialog(dialogPtr); } // ******************************************************************************* doDrawStuff void doDrawStuff(void) { Rect portRect, theRect; RGBColor theColour; SInt16 a, left, top, right, bottom, random; RGBBackColor(&gBlueColour); GetWindowPortBounds(gWindowRef,&portRect); EraseRect(&portRect); for(a=0;a<900;a++) { theRect = portRect; theColour.red = doRandomNumber(0,65535); theColour.green = doRandomNumber(0,65535); theColour.blue = doRandomNumber(0,65535); RGBForeColor(&theColour); left = doRandomNumber(0,theRect.right); top = doRandomNumber(0,theRect.bottom); right = doRandomNumber(left,theRect.right); bottom = doRandomNumber(top,theRect.bottom); SetRect(&theRect,left,top,right,bottom); PenMode(doRandomNumber(addOver,adMin)); random = doRandomNumber(0,3); if(random == 0) PaintRect(&theRect); else if(random == 1) PaintRoundRect(&theRect,doRandomNumber(10,100),doRandomNumber(10,100)); else if(random == 2) PaintOval(&theRect); else if(random == 3) PaintArc(&theRect,0,doRandomNumber(5,330)); QDFlushPortBuffer(GetWindowPort(gWindowRef),NULL); } } // **************************************************************************** doRandomNumber UInt16 doRandomNumber(UInt16 minimum, UInt16 maximum) { UInt16 randomNumber; SInt32 range, t; randomNumber = Random(); range = maximum - minimum + 1; t = (randomNumber * range) / 65536; return (t + minimum); } // *******************************************************************************************
When this program is run, the user should: o Invoke the demonstrations by choosing items from the Demonstration menu, clicking the mouse when instructed to do so by the text in the window's title bar. o Click outside and inside the window when the animated cursor demonstrations have been invoked. o Choose the AboutÉ item in the Apple menu to display the AboutÉ dialog. o Note that the Icons item in the Demonstration menu contains an icon. On Mac OS 8/9, if the first offscreen graphics world demonstration does not work when the monitor colour depth is set to Millions, increase the Minimum Heap Size set in the CodeWarrior project.
Constants are established for the resource IDs of 'acur', 'PICT', 'STR#', icon family, and 'cicn' resources, and a 'DLOG' resource. kSleeptime and MAX_UINT32 will be assigned to WaitNextEvent's sleep parameter at various points in the program. kBeachBallTickInterval represents the interval between frame changes for the first of three animated cursors. kCountingHandTickInterval represents the interval between frame changes for the second animated cursor.
The data type anumCurs is identical to the structure of an 'acur' resource.
In this program, the sleep and cursor region parameters in the WaitNextEvent call will be changed during program execution, hence the global variables gSleepTime and gCursorRegion. gCursorRegion will be assigned a reference to a region which will passed in the mouseRgn parameter of the WaitNextEvent call. This relates to the cursor shape changing demonstration. gAnimCursHdl will be assigned a handle to the animCurs structure used during the first animated cursor demonstration. gAnimCursTickInterval and gAnimCursLastTick also relate to the animated cursor demonstration.
Random numbers will be used in the function doPicture. The call to SetQDGlobalsRandomSeed seeds the random number generator with the value returned by the call to GetDateTime. Note that error handling here and in other areas of the program is somewhat rudimentary: the program simply terminates, sometimes with a call to SysBeep().
Before the event loop is entered, gSleepTime is set to MAX_UINT32. Initially, therefore, the sleep parameter in the WaitNextEvent call is set to the maximum possible UInt32 value. The global variable passed in the mouseRgn parameter of the WaitNextEvent call is assigned NULL so as to defeat the generation of mouse-moved events. When WaitNextEvent returns 0 with a null event, the function doIdle is called.
doIdle is called from the main event loop when WaitNextEvent returns 0 with a null event. If the active demonstration is one of the first two animated cursor demonstrations, the function doIncrementAnimCursor is called.
In the inDrag case, after the call to DragWindow, and provided the cursor shape changing demonstration is currently under way, the function doChangeCursor is called. The regions controlling the generation of mouse-moved events are defined in global coordinates, and are based on the window's port rectangle. Accordingly, when the window is moved, the new location of the port rectangle, in global coordinates, must be re-calculated so that the various cursor regions may be re-defined. The call to doChangeCursor re-defines these regions for the new window location and copies the reference to one of them, depending on the current location of the mouse cursor, to the global variable gCursorRegion. (Note that this call to doChangeCursor is also required, for the same reason, when a window is re-sized or zoomed.) In the case of a resume event, SetThemeCursor is called to ensure that the cursor is set to the arrow shape. In the case of a mouse-moved event (which occurs when the mouse cursor has moved outside the region whose reference is currently being passed in WaitNextEvent's mouseRgn parameter), doChangeCursor is called to change the region passed in the mouseRgn parameter according to the current location of the mouse.
The purpose of the code prior to the switch is to cancel any cursor demonstrations that may be currently under way. If the second of the first two animated cursor demonstrations is currently under way, doReleaseAnimCursor is called to release the memory associated with that animated cursor. If either of the first two animated cursor demonstrations is currently under way, SetThemeCursor is called to set the cursor shape to the arrow shape and WaitNextEvent's sleep parameter is then set to the maximum possible value. if the third animated cursor demonstration is currently under way, the function doAnimatedCursorOSX is called to terminate the Mac OS X wait cursor. If the cursor shape changing demonstration is currently under way, gCursorRegionsActive is set to false, the region containing the current cursor region is disposed of and the associated global variable is set to NULL, thus defeating the generation of mouse-moved events. Note that, if the user chooses the About... item in the Mac OS 8/9Apple or Mac OS X Application menu, doAboutDialog is called.
doWithoutOffScreenGWorld is the first demonstration.
As a prelude for what is to come, the function doDrawStuff is called to repeatedly paint some shapes in the window.
The call to GetGWorld saves the current graphics world, that is, the current graphics port and the current device. The call to NewGWorld creates an offscreen graphics world. The gworldPortPtr parameter receives a pointer to the offscreen graphics world's graphics port. 0 in the pixelDepth parameter means that the offscreen world's pixel depth will be set to the deepest device intersecting the rectangle passed as the boundsRect parameter. This rectangle becomes the offscreen port's port port rectangle, the offscreen pixel map's bounding rectangle, and the offscreen device's bounding rectangle. NULL in the cTable parameter causes the default colour table for the pixel depth to be used. The aGDevice parameter is set to NULL because the noNewDevice flag is not set. 0 in the flags parameter means that no flags are set. The call to SetGWorld sets the graphics port pointed to by gworldPortPtr as the current graphics port. (When the first parameter is a GWorldPtr, the current device is set to the device attached to the offscreen world and the second parameter is ignored.) GetGWorldPixMap gets a handle to the offscreen pixel map and LockPixels is called to prevent the base address of the pixel image from being moved when the pixel image is drawn into or copied from. The call to EraseRect clears the offscreen graphics port before the function doGWorldDrawing is called to draw some graphics in the offscreen port. With the drawing complete, the call to SetGWorld sets the (saved) window's graphics port as the current port and the saved device as the current device. The next two lines establish the source and destination rectangles (required by the forthcoming call to CopyBits) as equivalent to the offscreen graphics world and window port rectangles respectively. The calls to RGBForeColor and RGBBackColor set the foreground and background colours to black and white respectively, which is required to ensure that the CopyBits call will produce predictable results in the colour sense. The CopyBits call copies the image from the offscreen world to the window. The call to QDError checks for any error resulting from the last QuickDraw call (in this case, CopyBits). UnlockPixels unlocks the offscreen pixel image buffer and DisposeGWorld deallocates all of the memory previously allocated for the offscreen graphics world.
doWithoutOffScreenGWorld demonstrates the use of CopyDeepMask to copy a source pixel map to a destination pixel map using a pixel map as a mask, and clipping the copying operation to a designated region. Because mask pixel maps cannot come from the screen, an offscreen graphics world is created for the mask. The first block loads a 'PICT' resource and draws the picture in the window. The current graphics world is then saved and an offscreen graphics world the same size as the drawn picture is created. The offscreen graphics port is set as the current port, the pixel map is locked, and the offscreen port is erased. The second call to GetPicture loads the 'PICT' resource representing the mask and DrawPicture is called to draw the mask in the offscreen port. SetGWorld is then called again to make the window's graphics port the current port. The mask is then also drawn in the window next to the source image so that the user can see a copy of the mask in the offscreen graphics port. The next two blocks define two regions, one containing an oval and one a rounded rectangle. The references to these regions will be passed in the maskRgn parameter of two separate calls to CopyDeepMask. Before the calls to CopyDeepMask, the foreground and background colours are set to black and white respectively so that the results of the copying operation, in terms of colour, will be predictable. The for loop causes the source image to be copied to two locations in the window using a different mask region and Boolean source mode for each copy. The first time CopyDeepMask is called, the oval-shaped region is passed in the maskRgn parameter and the source mode srcCopy is passed in the mode parameter. The second time CopyDeepMask is called, the round rectangle-shaped region and srcOr are passed. QDError checks for any error resulting from the last QuickDraw call (in this case, CopyDeepMask). In the clean-up, UnlockPixels unlocks the offscreen pixel image buffer, DisposeGWorld deallocates all of the memory previously allocated for the offscreen graphics world, and the memory allocated for the picture resources and regions is released. Note that, because the pictures are resources obtained via GetPicture, ReleaseResource, rather than KillPicture, is used.
doPicture demonstrates the recording and playing back of a picture.
The window's port rectangle is copied to a local Rect variable. This rectangle is then made equal to the left half of the port rectangle, and then inset by 10 pixels all round. This is the picture rectangle The clipping region is then set to be the equivalent of this rectangle. (Before this call, the clipping region is very large. In fact, it is as large as the coordinate plane. If the clipping region is very large and you scale a picture while drawing it, the clipping region can become invalid when DrawPicture scales the clipping region - in which case the picture will not be drawn.)
This block assigns values to the fields of an OpenCPicParams structure. These specify the previously defined rectangle as the bounding rectangle, and 72 pixels per inch resolution both horizontally and vertically. The version field should always be set to -2.
OpenCPicture initiates the recording of the picture definition. The address of the OpenCPicParams structure is passed in the newHeader parameter. The picture is then drawn. Lines, rectangles, round rectangles, ovals, wedges, and text are drawn in random colours, and sizes.
The call to ClosePicture terminates picture recording and the call to DrawPicture draws the picture by "playing back" the "recording" stored in the specified Picture structure. The call to SetClip restores the saved clipping region and DisposeRgn frees the memory associated with the saved region.
The call to GetPictInfo returns information about the picture in a picture information structure. Information in some of the fields of this structure is then drawn in the right side of the window.
The call to KillPicture releases the memory occupied by the Picture structure.
doCursor's chief purpose is to assign true to the global variable gCursorRegionsActive, which will cause the function doChangeCursor to be called from within the main event loop provided the application is not in the background. In addition, it draws some rectangles in the window which visually represent to the user some cursor regions which will later be established by the doChangeCursor function. The last two lines sets the gCursorRegionsActive flag to true and create an empty region for the last parameter of the WaitNextEvent call in the main event loop. A reference to a cursor region will be copied to gCursorRegion in the function doChangeCursor.
doChangeCursor is called whenever a mouse-moved event is received and after the window is dragged. The first four lines create new empty regions to serve as the regions within which the cursor shape will be changed to, respectively, the arrow, I-beam, cross, and plus shapes. The SetRectRgn call sets the arrow cursor region to, initially, the boundaries of the coordinate plane. The next five lines establish a rectangle equivalent to the window's port rectangle and change this rectangle's coordinates from local to global coordinates so that the regions calculated from it will be in the required global coordinates. The call to InsetRect insets this rectangle by 40 pixels all round and the call to RectRgn establishes this as the I-beam region. The call to DiffRgn, in effect, cuts the rectangle represented by the I-beam region from the arrow region, leaving a hollow arrow region. The next six lines use the same procedure to establish a rectangular hollow region for the cross cursor and an interior rectangular region for the plus cursor. The result of all this is a rectangular plus cursor region in the centre of the window, surrounded by (but not overlapped by) a hollow rectangular cross cursor region, this surrounded by (but not overlapped by) a hollow rectangular I-beam cursor region, this surrounded by (but not overlapped by) a hollow rectangular arrow cursor region the outside of which equates to the boundaries of the coordinate plane. The call to GetGlobalMouse gets the point, in global coordinates, representing the mouse's current position. The next task is to determine the region in which the cursor is currently located. The calls to PtInRgn are made for that purpose. Depending on which region is established as the region in which the cursor in currently located, the cursor is set to the appropriate shape and the reference to that region is copied to the global variable passed in WaitNextEvent's mouseRgn parameter. That accomplished, the last four lines deallocate the memory associated with the regions created earlier in the function.
doAnimatedCursor1 responds to the user's selection of the Animated Cursor 1 item in the Demonstration menu. In this first animated cursor demonstration, the Appearance Manager function SetAnimatedThemeCursor will be used in the function doIncrementAnimCursor to increment the cursor frame. As preparatory measures, an appropriate frame change tick interval is assigned to gAnimCursTickInterval, the sleep parameter in the WaitNextEvent call is set to the same value (causing null events to be generated at that tick interval), and gAnimCurs1Active is set to true so that doIncrementAnimCursor will be called from the doIdle function.
doAnimatedCursor2 responds to the user's selection of the Animated Cursor 2 item in the Demonstration menu. In this second animated cursor demonstration, functions are utilised to retrieve 'acur' and 'CURS' resources, animate the cursor, and deallocate the memory associated with the animated cursor when the cursor is no longer required. DoAnimatedCursor2's major role is simply to call doGetAnimCursor with a "beach-ball" 'acur' resource as a parameter. After the screen has been cleared, the resource ID of the "beach-ball" 'acur' resource is assigned to the variable used as the first parameter in the later call to doGetAnimCursor. The next line assigns a value to the second parameter in the doGetAnimCursor call. This value controls the frame rate of the cursor, that is, the number of ticks which must elapse before the next frame (cursor) is displayed. (The best frame rate depends on the type of animated cursor used.) If the call to doGetAnimCursor is successful, the sleep parameter in the WaitNextEvent call is set to the same ticks value as that used to control the cursor's frame rate (causing null events to be generated at that tick interval), and the flag gAnimCurs2Active is set to true so that doIncrementAnimCursor will be called from the doIdle function. If the call to getAnimCursor fails, doAnimCursor simply plays the system alert sound and returns.
doGetAnimCursor retrieves the data in the specified 'acur' resource and stores it in an animCurs structure, retrieves the 'CURS' resources specified in the 'acur' resource and assigns the reference s to the resulting Cursor structures to elements in an array in the animCurs structure, establishes the frame rate for the cursor, and sets the starting frame number. GetResource is called to read the 'acur' resource into memory and return a handle to the resource. The handle is cast to type animCursHandle and assigned to the global variable gAnimCursHdl (a handle to a structure of type animCurs, which is identical to the structure of an 'acur' resource). If this call is not successful (that is, GetResource returns NULL), the function will simply exit, returning false. If the call is successful, noError is set to true before a while loop is entered. This loop will cycle once for each of the 'CURS' resources specified in the 'acur' resource, assuming that noError is not set to false at some time during this process. The ID of each cursor is stored in the high word of the specified element of the frame[] field of the animCurs structure. This is retrieved. The cursor ID is then used in the call to GetCursor to read in the resource (if necessary) and assign the handle to the resulting 68-byte Cursor structure to the specified element of the frame[] field of the animCurs structure. If this pass through the loop was successful, the array index is incremented; otherwise, noError is set to false, causing the loop and the function to exit. The first line within the if block assigns the ticks value passed to doGetAnimCursor to a global variable that will be utilised in the function doIncrementAnimCursor. The next line assigns the number of ticks since system startup to another variable which will also be utilised in the function doIncrementAnimCursor. The third line sets the starting frame number. At this stage, the animated cursor has been initialised and doIdle will call doIncrementAnimCursor whenever null events are received.
doIncrementAnimCursor is called whenever null events are received. The first line assigns the number of ticks since system startup to newTick. The next line checks whether the specified number of ticks have elapsed since the previous call to doIncrementAnimCursor. If the specified number of ticks have not elapsed, the function simply returns. Otherwise, the following occurs: o If the first animated cursor demonstration is under way, the Appearance Manager function SetThemeAnimatedCursor is called to increment the theme-compliant cursor frame. o If the second animated cursor demonstration is under way, SetCursor sets the cursor shape to that represented by the handle stored in the specified element of the frame[] field of the animCurs structure. This line also increments the frame counter field (whichFrame) of the animCurs structure. If whichFrame has been incremented to the last cursor in the series, the frame counter is re-set to 0. The last line retrieves and stores the tick count at exit for use at the first line the next time the function is called.
doReleaseAnimCursor deallocates the memory occupied by the Cursor structures and the 'acur' resource. Recall that doReleaseAnimCursor is called when the user clicks in the menu bar and that, at the same time, the gAnimatedCursor1Active and gAnimatedCursor2Active flags are set to false, the cursor is reset to the standard arrow shape, and WaitNextEvent's sleep parameter is reset to the maximum possible value.
doAnimatedCursorOSX utilises the function QDDisplayWaitCursor to turn the Mac OS X "spinning wheel" cursor on and off. This function is only available on OS X, so the stub library CarbonFrameworkLib has been added to the CodeWarrior project.
doIcon demonstrates the drawing of icons in a window using PlotIconID, PlotIconSuite, and PlotCIcon.
This block uses the function PlotIconID to draw an icon from an icon family with the specified ID fifteen times, once for each of the fifteen available transform types. PlotIconID automatically chooses the appropriate icon resource from the icon suite depending on the specified destination rectangle and the bit depth of the current device.
This block uses GetIconSuite to get an icon suite comprising only the 'ICN#', 'icl4', and 'icl8' resources from the icon family with the specified resource ID. PlotIconSuite is then called three times to draw the appropriate icon within destination rectangles of three different sizes. PlotIconSuite automatically chooses the appropriate icon resource from the icon suite depending on the specified destination rectangle and the bit depth of the current device. PlotIconSuite also expands the icon to fit the last two destination rectangles.
This block uses GetCIcon to load the specified 'cicn' resource and PlotCIcon to draw the colour icon within destination rectangles of three different sizes. PlotCIcon expands the 32 by 32 pixel icon to fit the last two destination rectangles.
doAboutDialog is called when the user chooses the About... item in the Apple menu. GetNewDialog creates a modal dialog. The dialog's item list contains a picture item, which fills the entire dialog window. The call to ModalDialog means that the dialog will remain displayed until the user clicks somewhere within the dialog, at which time DisposeDialog is called to dismiss the dialog and free the associated memory. A dialog rather than an alert is used to obviate the need for a button for dismissing the dialog.