// ******************************************************************************************* // QuickDraw.c CLASSIC EVENT MODEL // ******************************************************************************************* // // This program opens a window in which the results of various QuickDraw drawing operations // are displayed. Individual line and text drawing, framing, painting, filling, erasing, // inverting, copying, etc., operations are chosen from a Demonstration pull-down menu. // // To keep the non-QuickDraw 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 'WIND' resources for the main window, and a small window used for the CopyBits // demonstration (purgeable) (initially visible). // // o An 'MBAR' resource and associated 'MENU' resources (preload, non-purgeable). // // o Two 'ICON' resources (purgeable) used for the boolean source modes demonstration. // // o Two 'PICT' resources (purgeable) used in the arithmetic source modes demonstration. // // o 'STR#' resources (purgeable) containing strings used in the source modes and text // drawing demonstrations. // // o Three 'ppat' resources (purgeable), two of which are used in various drawing, // framing, painting, filling, and erasing demonstrations. The third is used in the // drawing with mouse demonstration. // // o A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground, // doesActivateOnFGSwitch, and isHighLevelEventAware flags set. // // ******************************************************************************************* // .................................................................................. includes #include <Carbon.h> // ................................................................................... defines #define rMenubar 128 #define mAppleApplication 128 #define iAbout 1 #define mFile 129 #define iQuit 12 #define mDemonstration 131 #define iLine 1 #define iFrameAndPaint 2 #define iFillEraseInvert 3 #define iPolygonRegion 4 #define iText 5 #define iScrolling 6 #define iBooleanSourceModes 7 #define iArithmeticSourceModes 8 #define iHighlighting 9 #define iDrawWithMouse 10 #define iDrawingState 11 #define rWindow 128 #define rPixelPattern1 128 #define rPixelPattern2 129 #define rPixelPattern3 130 #define rDestinationIcon 128 #define rSourceIcon 129 #define rFontsStringList 128 #define rBooleanStringList 129 #define rArithmeticStringList 130 #define rPicture 128 #define MAX_UINT32 0xFFFFFFFF // .......................................................................... global variables Boolean gRunningOnX = false; Boolean gDone; WindowRef gWindowRef; Boolean gDrawWithMouseActivated; SInt16 gPixelDepth; Boolean gIsColourDevice = false; RGBColor gWhiteColour = { 0xFFFF, 0xFFFF, 0xFFFF }; RGBColor gBlackColour = { 0x0000, 0x0000, 0x0000 }; RGBColor gRedColour = { 0xAAAA, 0x0000, 0x0000 }; RGBColor gYellowColour = { 0xFFFF, 0xCCCC, 0x0000 }; RGBColor gGreenColour = { 0x0000, 0x9999, 0x0000 }; RGBColor gBlueColour = { 0x6666, 0x6666, 0x9999 }; // ....................................................................... function prototypes void main (void); void doPreliminaries (void); OSErr quitAppEventHandler (AppleEvent *,AppleEvent *,SInt32); void doEvents (EventRecord *); void doDemonstrationMenu (MenuItemIndex); void doLines (void); void doFrameAndPaint (void); void doFillEraseInvert (void); void doPolygonAndRegion (void); void doScrolling (void); void doText (void); void doBooleanSourceModes (void); void doArithmeticSourceModes (void); void doHighlighting (void); void doDrawWithMouse (void); void doDrawingState (void); void doDrawingStateProof (SInt16); void doGetDepthAndDevice (void); UInt16 doRandomNumber (UInt16,UInt16); // ************************************************************************************** main void main(void) { UInt32 seconds; MenuBarHandle menubarHdl; SInt32 response; MenuRef menuRef; EventRecord eventStructure; Boolean gotEvent; // ......................................................................... do prelimiaries 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); } gRunningOnX = true; } // ............................................................................. open window if(!(gWindowRef = GetNewCWindow(rWindow,NULL,(WindowRef)-1))) ExitToShell(); SetPortWindowPort(gWindowRef); UseThemeFont(kThemeSmallSystemFont,smSystemScript); // ...... get pixel depth and whether colour device for certain Appearance Manager functions doGetDepthAndDevice(); // ............................................................................... eventLoop gDone = false; while(!gDone) { gotEvent = WaitNextEvent(everyEvent,&eventStructure,MAX_UINT32,NULL); if(gotEvent) doEvents(&eventStructure); } } // *************************************************************************** doPreliminaries void doPreliminaries(void) { OSErr osError; MoreMasterPointers(32); 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; } // ********************************************************************************** doEvents void doEvents(EventRecord *eventStrucPtr) { SInt32 menuChoice; MenuID menuID; MenuItemIndex menuItem; WindowPartCode partCode; WindowRef windowRef; switch(eventStrucPtr->what) { case kHighLevelEvent: AEProcessAppleEvent(eventStrucPtr); break; case keyDown: if((eventStrucPtr->modifiers & cmdKey) != 0) { menuChoice = MenuEvent(eventStrucPtr); menuID = HiWord(menuChoice); menuItem = LoWord(menuChoice); if(menuID == mFile && menuItem == iQuit) gDone = true; } break; case mouseDown: if(partCode = FindWindow(eventStrucPtr->where,&windowRef)) { switch(partCode) { case inMenuBar: menuChoice = MenuSelect(eventStrucPtr->where); menuID = HiWord(menuChoice); menuItem = LoWord(menuChoice); if(menuID == 0) return; switch(menuID) { case mAppleApplication: if(menuItem == iAbout) SysBeep(10); break; case mFile: if(menuItem == iQuit) gDone = true; break; case mDemonstration: doDemonstrationMenu(menuItem); break; } break; case inDrag: DragWindow(windowRef,eventStrucPtr->where,NULL); break; case inContent: if(windowRef != FrontWindow()) SelectWindow(windowRef); else if(gDrawWithMouseActivated) doDrawWithMouse(); break; } } break; case updateEvt: windowRef = (WindowRef) eventStrucPtr->message; BeginUpdate(windowRef); EndUpdate(windowRef); break; } } // *********************************************************************** doDemonstrationMenu void doDemonstrationMenu(MenuItemIndex menuItem) { Rect portRect; Pattern whitePattern; gDrawWithMouseActivated = false; switch(menuItem) { case iLine: doLines(); break; case iFrameAndPaint: doFrameAndPaint(); break; case iFillEraseInvert: doFillEraseInvert(); break; case iPolygonRegion: doPolygonAndRegion(); break; case iText: doText(); break; case iScrolling: doScrolling(); break; case iBooleanSourceModes: doBooleanSourceModes(); break; case iArithmeticSourceModes: doArithmeticSourceModes(); break; case iHighlighting: doHighlighting(); break; case iDrawWithMouse: SetWTitle(gWindowRef,"\pDrawing with the mouse"); RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); gDrawWithMouseActivated = true; break; case iDrawingState: doDrawingState(); break; } HiliteMenu(0); } // *********************************************************************************** doLines void doLines(void) { Rect portRect, newClipRect; Pattern whitePattern, systemPattern, blackPattern; RgnHandle oldClipRgn; SInt16 a, b, c; RGBColor theColour; UInt32 finalTicks; PixPatHandle pixpatHdl; PenNormal(); RGBBackColor(&gBlueColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); newClipRect = portRect; InsetRect(&newClipRect,10,10); oldClipRgn = NewRgn(); GetClip(oldClipRgn); ClipRect(&newClipRect); // ................................ lines drawn with foreground colour and black pen pattern SetWTitle(gWindowRef,"\pDrawing lines with colours"); RGBBackColor(&gWhiteColour); FillRect(&portRect,&whitePattern); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); for(a=1;a<60;a++) { b = doRandomNumber(0,portRect.right - portRect.left); c = doRandomNumber(0,portRect.right - portRect.left); theColour.red = doRandomNumber(0,65535); theColour.green = doRandomNumber(0,65535); theColour.blue = doRandomNumber(0,65535); RGBForeColor(&theColour); PenSize(a * 2,1); MoveTo(b,portRect.top); LineTo(c,portRect.bottom); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(2,&finalTicks); } if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); // ........................................... lines drawn with system-supplied bit patterns SetWTitle(gWindowRef,"\pClick mouse for more lines"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); while(!Button()) ; SetWTitle(gWindowRef,"\pDrawing lines with system-supplied bit patterns"); FillRect(&portRect,&whitePattern); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); for(a=1;a<39;a++) { b = doRandomNumber(0,portRect.bottom - portRect.top); c = doRandomNumber(0,portRect.bottom - portRect.top); theColour.red = doRandomNumber(0,32767); theColour.green = doRandomNumber(0,32767); theColour.blue = doRandomNumber(0,32767); RGBForeColor(&theColour); GetIndPattern(&systemPattern,sysPatListID,a); PenPat(&systemPattern); PenSize(1, a * 2); MoveTo(portRect.left,b); LineTo(portRect.right,c); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(5,&finalTicks); } if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); // ........................................................ lines drawn with a pixel pattern SetWTitle(gWindowRef,"\pClick mouse for more lines"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); while(!Button()) ; SetWTitle(gWindowRef,"\pDrawing lines with a pixel pattern"); FillRect(&portRect,&whitePattern); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); if(!(pixpatHdl = GetPixPat(rPixelPattern1))) ExitToShell(); PenPixPat(pixpatHdl); for(a=1;a<60;a++) { b = doRandomNumber(0,portRect.right - portRect.left); c = doRandomNumber(0,portRect.right - portRect.left); PenSize(a * 2,1); MoveTo(b,portRect.top); LineTo(c,portRect.bottom); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(5,&finalTicks); } DisposePixPat(pixpatHdl); SetClip(oldClipRgn); DisposeRgn(oldClipRgn); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); // .................................................... lines drawn with pattern mode patXor SetWTitle(gWindowRef,"\pClick mouse for more lines"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); while(!Button()) ; SetWTitle(gWindowRef,"\pDrawing lines using pattern mode patXor"); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); RGBBackColor(&gRedColour); FillRect(&portRect,&whitePattern); PenSize(1,1); PenPat(GetQDGlobalsBlack(&blackPattern)); PenMode(patXor); InsetRect(&portRect,10,10); for(a = portRect.left,b = portRect.right;a < portRect.right + 1;a++,b--) { MoveTo(a,portRect.top); LineTo(b,portRect.bottom); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); } for(a = portRect.bottom,b = portRect.top;b < portRect.bottom + 1;a--,b++) { MoveTo(portRect.left,a); LineTo(portRect.right,b); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); } if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); } // *************************************************************************** doFrameAndPaint void doFrameAndPaint(void) { SInt16 a; Rect portRect, theRect; Pattern whitePattern; UInt32 finalTicks; Pattern systemPattern; PixPatHandle pixpatHdl; PenNormal(); PenSize(30,20); for(a=0;a<3;a++) { RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); // ........................................................................... preparation if(a == 0) { SetWTitle(gWindowRef,"\pFraming and painting with a colour"); RGBForeColor(&gRedColour); // set foreground colour to red } else if(a == 1) { SetWTitle(gWindowRef,"\pFraming and painting with a bit pattern"); RGBForeColor(&gBlueColour); // set foreground colour to blue RGBBackColor(&gYellowColour); // set foreground colour to yellow GetIndPattern(&systemPattern,sysPatListID,16); // get bit pattern for pen PenPat(&systemPattern); // set pen bit pattern } else if (a == 2) { SetWTitle(gWindowRef,"\pFraming and painting with a pixel pattern"); if(!(pixpatHdl = GetPixPat(rPixelPattern1))) // get pixel pattern for pen ExitToShell(); PenPixPat(pixpatHdl); // set pen pixel pattern } // .................................................................. framing and painting SetRect(&theRect,30,32,151,191); FrameRect(&theRect); // FrameRect MoveTo(30,29); DrawString("\pFrameRect"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); FrameRoundRect(&theRect,30,50); // FrameRoundRect MoveTo(170,29); DrawString("\pFrameRoundRect"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); FrameOval(&theRect); // FrameOval MoveTo(310,29); DrawString("\pFrameOval"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); FrameArc(&theRect,330,300); // FrameArc MoveTo(450,29); DrawString("\pFrameArc"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,-420,186); PaintRect(&theRect); // PaintRect MoveTo(30,214); DrawString("\pPaintRect"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); PaintRoundRect(&theRect,30,50); // PaintRoundRect MoveTo(170,214); DrawString("\pPaintRoundRect"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); PaintOval(&theRect); // PaintOval MoveTo(310,214); DrawString("\pPaintOval"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); PaintArc(&theRect,330,300); // PaintArc MoveTo(450,214); DrawString("\pPaintArc"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); if(a < 2) { SetWTitle(gWindowRef,"\pClick mouse for more"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); while(!Button()) ; } } DisposePixPat(pixpatHdl); } // ************************************************************************* doFillEraseInvert void doFillEraseInvert(void) { SInt16 a; Rect portRect, theRect; Pattern whitePattern, fillPat, backPat; PixPatHandle fillPixpatHdl, backPixpatHdl; UInt32 finalTicks; PenNormal(); PenSize(30,20); for(a=0;a<4;a++) { if(a < 3) { RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); } if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); // ........................................................................... preparation if(a == 0) { SetWTitle(gWindowRef,"\pFilling and erasing with colours"); RGBForeColor(&gBlueColour); // set blue colour for foreground RGBBackColor(&gRedColour); // set red colour for background GetIndPattern(&fillPat,sysPatListID,1); // get black bit pattern for fill functions BackPat(&whitePattern); // set white bit pattern for background } else if(a == 1) { SetWTitle(gWindowRef,"\pFilling and erasing with bit patterns"); RGBForeColor(&gBlueColour); // set blue colour for foreground RGBBackColor(&gYellowColour); // set yellow colour for background GetIndPattern(&fillPat,sysPatListID,37); // get bit pattern for fill functions GetIndPattern(&backPat,sysPatListID,19); // get bit pattern for background BackPat(&backPat); // set bit pattern for background } else if (a == 2) { SetWTitle(gWindowRef,"\pFilling and erasing with pixel patterns"); if(!(fillPixpatHdl = GetPixPat(rPixelPattern1))) // get pixel patt - fill functions ExitToShell(); if(!(backPixpatHdl = GetPixPat(rPixelPattern2))) // get pixel pattern - background ExitToShell(); BackPixPat(backPixpatHdl); // set pixel pattern - background } else if(a == 3) { SetWTitle(gWindowRef,"\pInverting"); BackPat(&whitePattern); SetRect(&theRect,30,15,570,29); EraseRect(&theRect); SetRect(&theRect,30,200,570,214); EraseRect(&theRect); } // ....................................................... filling, erasing, and inverting SetRect(&theRect,30,32,151,191); MoveTo(30,29); if(a < 2) { FillRect(&theRect,&fillPat); // FillRect DrawString("\pFillRect"); } else if(a == 2) { FillCRect(&theRect,fillPixpatHdl); // FillCRect DrawString("\pFillCRect"); } else if(a == 3) { InvertRect(&theRect); // InvertRect DrawString("\pInvertRect"); } QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); MoveTo(170,29); if(a < 2) { FillRoundRect(&theRect,30,50,&fillPat); // FillRoundRect DrawString("\pFillRoundRect"); } else if(a == 2) { FillCRoundRect(&theRect,30,50,fillPixpatHdl); // FillCRoundRect DrawString("\pFillCRoundRect"); } else if(a == 3) { InvertRoundRect(&theRect,30,50); // InvertRoundRect DrawString("\pInvertRoundRect"); } QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); MoveTo(310,29); if(a < 2) { FillOval(&theRect,&fillPat); // FillOval DrawString("\pFillOval"); } else if(a == 2) { FillCOval(&theRect,fillPixpatHdl); // FillCOval DrawString("\pFillCOval"); } else if(a == 3) { InvertOval(&theRect); // InvertOval DrawString("\pInvertOval"); } QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); MoveTo(450,29); if(a < 2) { FillArc(&theRect,330,300,&fillPat); // FillArc DrawString("\pFillArc"); } else if(a == 2) { FillCArc(&theRect,330,300,fillPixpatHdl); // FillCArc DrawString("\pFillCArc"); } else if(a == 3) { InvertArc(&theRect,330,300); // InvertArc DrawString("\pInvertArc"); } QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,-420,186); MoveTo(30,214); if(a < 3) { EraseRect(&theRect); // EraseRect DrawString("\pEraseRect"); } else { InvertRect(&theRect); // InvertRect DrawString("\pInvertRect"); } QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); MoveTo(170,214); if(a < 3) { EraseRoundRect(&theRect,30,50); // EraseRoundRect DrawString("\pEraseRoundRect"); } else { InvertRoundRect(&theRect,30,50); // InvertRoundRect DrawString("\pInvertRoundRect"); } QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); MoveTo(310,214); if(a < 3) { EraseOval(&theRect); // EraseOval DrawString("\pEraseOval"); } else { InvertOval(&theRect); // InvertOval DrawString("\pInvertOval"); } QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRect(&theRect,140,0); MoveTo(450,214); if(a < 3) { EraseArc(&theRect,330,300); // EraseArc DrawString("\pEraseArc"); } else { InvertArc(&theRect,330,300); // InvertArc DrawString("\pInvertArc"); } QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); if(a < 3) { SetWTitle(gWindowRef,"\pClick mouse for more"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); while(!Button()) ; } } DisposePixPat(fillPixpatHdl); DisposePixPat(backPixpatHdl); } // ************************************************************************ doPolygonAndRegion void doPolygonAndRegion(void) { Rect portRect, theRect; Pattern whitePattern, backPat; PixPatHandle fillPixpatHdl; PolyHandle polygonHdl; RgnHandle regionHdl; UInt32 finalTicks; SetWTitle(gWindowRef,"\pFraming, painting, filling, and erasing polygons and regions"); RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); // ............................................................................. preparation GetIndPattern(&backPat,sysPatListID,17); // get bit pattern for background BackPat(&backPat); // set bit pattern for background if(!(fillPixpatHdl = GetPixPat(rPixelPattern2))) // get pixel pattern for fill functions ExitToShell(); RGBForeColor(&gRedColour); // set red colour for foreground RGBBackColor(&gYellowColour); // set yellow colour for background PenNormal(); polygonHdl = OpenPoly(); // define polygon MoveTo(30,32); LineTo(151,32); LineTo(96,103); LineTo(151,134); LineTo(151,191); LineTo(30,191); LineTo(66,75); ClosePoly(); regionHdl = NewRgn(); // define region OpenRgn(); SetRect(&theRect,30,218,151,279); FrameRect(&theRect); SetRect(&theRect,30,316,151,377); FrameRect(&theRect); SetRect(&theRect,39,248,142,341); FrameOval(&theRect); CloseRgn(regionHdl); // ................................................. framing, painting, filling, and erasing FramePoly(polygonHdl); // FramePoly MoveTo(30,29); DrawString("\pFramePoly (colour)"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetPoly(polygonHdl,140,0); PaintPoly(polygonHdl); // PaintPoly MoveTo(170,29); DrawString("\pPaintPoly (colour)"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetPoly(polygonHdl,140,0); FillCPoly(polygonHdl,fillPixpatHdl); // FillCPoly MoveTo(310,29); DrawString("\pFillCPoly (pixel pattern)"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetPoly(polygonHdl,140,0); ErasePoly(polygonHdl); // ErasePoly MoveTo(450,29); DrawString("\pErasePoly (bit pattern)"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); FrameRgn(regionHdl); // FrameRgn MoveTo(30,214); DrawString("\pFrameRgn (colour)"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRgn(regionHdl,140,0); PaintRgn(regionHdl); // PaintRgn MoveTo(170,214); DrawString("\pPaintRgn (colour)"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRgn(regionHdl,140,0); FillCRgn(regionHdl,fillPixpatHdl); // FillCRgn MoveTo(310,214); DrawString("\pFillCRgn (pixel pattern)"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); OffsetRgn(regionHdl,140,0); EraseRgn(regionHdl); // EraseRgn MoveTo(450,214); DrawString("\pEraseRgn (bit pattern)"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); KillPoly(polygonHdl); DisposeRgn(regionHdl); DisposePixPat(fillPixpatHdl); BackPat(&whitePattern); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); } // ************************************************************************************ doText void doText(void) { Rect portRect, theRect; Pattern whitePattern; SInt16 windowCentre, a, fontNum, stringWidth; Str255 textString; UInt32 finalTicks; RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); SetWTitle(gWindowRef,"\pDrawing text with default source mode (srcOr)"); windowCentre = (portRect.right - portRect.left) / 2; SetRect(&theRect,windowCentre,portRect.top,portRect.right,portRect.bottom); RGBBackColor(&gBlueColour); FillRect(&theRect,&whitePattern); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); for(a=1;a<9;a++) { // ........................... set various text fonts, text styles, and foreground colours if(a == 1) { GetFNum("\pGeneva",&fontNum); TextFont(fontNum); TextFace(normal); RGBForeColor(&gRedColour); } else if(a == 2) TextFace(bold); else if(a == 3) { GetFNum("\pTimes",&fontNum); TextFont(fontNum); TextFace(italic); RGBForeColor(&gYellowColour); } else if(a == 4) TextFace(underline); else if(a == 5) { GetFNum("\pHelvetica",&fontNum); TextFont(fontNum); TextFace(normal); RGBForeColor(&gGreenColour); } else if(a == 6) TextFace(bold + italic); else if(a == 7) { GetFNum("\pCharcoal",&fontNum); TextFont(fontNum); TextFace(condense); RGBForeColor(&gBlackColour); } else if(a == 8) { TextFace(extend); } // ......................................................................... set text size if(a < 7) TextSize(a * 2 + 15); else TextSize(12); // .......... get a string and draw it in the set font, style, size, and foreground colour GetIndString(textString,rFontsStringList,a); stringWidth = StringWidth(textString); MoveTo(windowCentre - (stringWidth / 2),a * 46 - 10); DrawString(textString); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(30,&finalTicks); } // ............................................................. reset to Geneva 10pt normal GetFNum("\pGeneva",&fontNum); TextFont(fontNum); TextSize(10); TextFace(normal); // ............ erase a rectangle, get a string, and use TETextBox to draw it left justified SetRect(&theRect,portRect.left + 5,portRect.bottom - 55,portRect.left + 118, portRect.bottom - 5); EraseRect(&theRect); InsetRect(&theRect,5,5); GetIndString(textString,rFontsStringList,9); RGBForeColor(&gWhiteColour); TETextBox(&textString[1],textString[0],&theRect,teFlushLeft); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); } // ******************************************************************************* doScrolling void doScrolling(void) { Rect portRect, theRect; Pattern whitePattern; PixPatHandle pixpat1Hdl, pixpat2Hdl; RgnHandle oldClipHdl, regionAHdl, regionBHdl, regionCHdl, scrollRegionHdl; SInt16 a; UInt32 finalTicks; SetWTitle(gWindowRef,"\pScrolling pixels"); RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); if(!(pixpat1Hdl = GetPixPat(rPixelPattern1))) ExitToShell(); PenPixPat(pixpat1Hdl); PenSize(50,0); SetRect(&theRect,30,30,286,371); FrameRect(&theRect); SetRect(&theRect,315,30,571,371); FillCRect(&theRect,pixpat1Hdl); if(!(pixpat2Hdl = GetPixPat(rPixelPattern2))) ExitToShell(); BackPixPat(pixpat2Hdl); regionAHdl = NewRgn(); regionBHdl = NewRgn(); regionCHdl = NewRgn(); SetRect(&theRect,80,30,236,371); RectRgn(regionAHdl,&theRect); SetRect(&theRect,315,30,571,371); RectRgn(regionBHdl,&theRect); UnionRgn(regionAHdl,regionBHdl,regionCHdl); oldClipHdl = NewRgn(); GetClip(oldClipHdl); SetClip(regionCHdl); SetRect(&theRect,80,30,571,371); scrollRegionHdl = NewRgn(); for(a=0;a<371;a++) { ScrollRect(&theRect,0,1,scrollRegionHdl); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); theRect.top ++; Delay(1,&finalTicks); } SetRect(&theRect,80,30,571,371); BackPixPat(pixpat1Hdl); for(a=0;a<371;a++) { ScrollRect(&theRect,0,-1,scrollRegionHdl); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); theRect.bottom --; Delay(1,&finalTicks); } SetClip(oldClipHdl); DisposePixPat(pixpat1Hdl); DisposePixPat(pixpat2Hdl); DisposeRgn(oldClipHdl); DisposeRgn(regionAHdl); DisposeRgn(regionBHdl); DisposeRgn(regionCHdl); DisposeRgn(scrollRegionHdl); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); } // ********************************************************************** doBooleanSourceModes void doBooleanSourceModes(void) { Rect portRect, theRect; Pattern whitePattern; Handle destIconHdl, sourceIconHdl; SInt16 a, b; UInt32 finalTicks; BitMap sourceIconMap; Str255 sourceString; PixMapHandle destinationPixMapHdl; SetWTitle(gWindowRef,"\pBoolean source modes"); RGBForeColor(&gBlackColour); RGBBackColor(&gGreenColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); SetRect(&theRect,portRect.left,portRect.top,portRect.right, (portRect.bottom - portRect.top) / 2); RGBBackColor(&gWhiteColour); FillRect(&theRect,&whitePattern); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); destIconHdl = GetIcon(rDestinationIcon); sourceIconHdl = GetIcon(rSourceIcon); for(a=0;a<2;a++) { if(a == 1) { RGBForeColor(&gYellowColour); RGBBackColor(&gRedColour); } SetRect(&theRect,235,a * 191 + 30,299,a * 191 + 94); PlotIcon(&theRect,destIconHdl); MoveTo(235,a * 191 + 27); DrawString("\pDestination"); SetRect(&theRect,304,a * 191 + 30,368,a * 191 + 94); PlotIcon(&theRect,sourceIconHdl); MoveTo(304,a * 191 + 27); DrawString("\pSource"); } RGBForeColor(&gBlackColour); RGBBackColor(&gWhiteColour); for(a=0;a<2;a++) { if(a == 1) { RGBForeColor(&gYellowColour); RGBBackColor(&gRedColour); } for(b=0;b<8;b++) { SetRect(&theRect,b * 69 + 28,a * 191 + 121,b * 69 + 92,a * 191 + 185); PlotIcon(&theRect,destIconHdl); } } QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); RGBForeColor(&gBlackColour); RGBBackColor(&gWhiteColour); HLock(sourceIconHdl); sourceIconMap.baseAddr = *sourceIconHdl; sourceIconMap.rowBytes = 4; SetRect(&sourceIconMap.bounds,0,0,32,32); destinationPixMapHdl = GetPortPixMap(GetQDGlobalsThePort()); for(a=0;a<2;a++) { if(a == 1) { RGBForeColor(&gYellowColour); RGBBackColor(&gRedColour); } for(b=0;b<8;b++) { Delay(30,&finalTicks); SetRect(&theRect,b * 69 + 28,a * 191 + 121,b * 69 + 92,a * 191 + 185); CopyBits(&sourceIconMap, (BitMap *) *destinationPixMapHdl, &sourceIconMap.bounds, &theRect, b,NULL); GetIndString(sourceString,rBooleanStringList,b + 1); MoveTo(b * 69 + 28,a * 191 + 118); DrawString(sourceString); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); } } HUnlock(sourceIconHdl); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); } // ******************************************************************* doArithmeticSourceModes void doArithmeticSourceModes(void) { Rect portRect, sourceRect, destRect; Pattern whitePattern; PicHandle sourceHdl, destinationHdl; SInt16 a, b, arithmeticMode = 32; PixMapHandle currentPixMapHdl; Str255 modeString; UInt32 finalTicks; SetWTitle(gWindowRef,"\pCopyBits with arithmetic source modes"); RGBForeColor(&gBlackColour); RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); if(!(sourceHdl = GetPicture(rPicture))) ExitToShell(); SetRect(&sourceRect,44,21,201,133); HNoPurge((Handle) sourceHdl); DrawPicture(sourceHdl,&sourceRect); HPurge((Handle) sourceHdl); MoveTo(44,19); DrawString("\pSOURCE IMAGE"); if(!(destinationHdl = GetPicture(rPicture + 1))) ExitToShell(); HNoPurge((Handle) destinationHdl); for(a=44;a<403;a+=179) { for(b=21;b<274;b+=126) { if(a == 44 && b == 21) continue; SetRect(&destRect,a,b,a+157,b+112); DrawPicture(destinationHdl,&destRect); } } HPurge((Handle) destinationHdl); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); currentPixMapHdl = GetPortPixMap(GetWindowPort(gWindowRef)); for(a=44;a<403;a+=179) { for(b=21;b<274;b+=126) { if(a == 44 && b == 21) continue; Delay(60,&finalTicks); GetIndString(modeString,rArithmeticStringList,arithmeticMode - 31); MoveTo(a,b - 2); DrawString(modeString); SetRect(&destRect,a,b,a+157,b+112); CopyBits((BitMap *) *currentPixMapHdl, (BitMap *) *currentPixMapHdl, &sourceRect,&destRect, arithmeticMode + ditherCopy,NULL); arithmeticMode ++; QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); } } ReleaseResource((Handle) sourceHdl); ReleaseResource((Handle) destinationHdl); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); } // **************************************************************************** doHighlighting void doHighlighting(void) { Rect portRect, theRect; Pattern whitePattern; RGBColor oldHighlightColour; SInt16 a; UInt8 hiliteVal; UInt32 finalTicks; SetWTitle(gWindowRef,"\pHighlighting"); RGBForeColor(&gBlackColour); RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); LMGetHiliteRGB(&oldHighlightColour); for(a=0;a<3;a++) { MoveTo(50,a * 100 + 60); DrawString("\pClearing the highlight bit and calling InvertRect."); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(60,&finalTicks); SetRect(&theRect,44,a * 100 + 44,557,a * 100 + 104); hiliteVal = LMGetHiliteMode(); BitClr(&hiliteVal,pHiliteBit); LMSetHiliteMode(hiliteVal); if(a == 1) HiliteColor(&gYellowColour); else if(a == 2) HiliteColor(&gGreenColour); InvertRect(&theRect); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); MoveTo(50,a * 100 + 75); Delay(60,&finalTicks); DrawString("\pClick mouse to unhighlight. "); DrawString("\p(Note: The call to InvertRect reset the highlight bit ..."); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); while(!Button()) ; MoveTo(45,a * 100 + 90); DrawString("\p... so we clear the highlight bit again before calling InvertRect.)"); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); Delay(60,&finalTicks); LMSetHiliteMode(hiliteVal); InvertRect(&theRect); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); } HiliteColor(&oldHighlightColour); Delay(60,&finalTicks); MoveTo(50,350); DrawString("\pOriginal highlight colour has been reset."); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); } // *************************************************************************** doDrawWithMouse void doDrawWithMouse(void) { Rect portRect, drawRect; Pattern whitePattern, blackPattern; PixPatHandle pixpatHdl; Point initialMouse, previousMouse, currentMouse; UInt16 randomNumber; RGBColor theColour; RGBBackColor(&gWhiteColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); if(!(pixpatHdl = GetPixPat(rPixelPattern3))) ExitToShell(); PenPixPat(pixpatHdl); PenSize(1,1); PenMode(patXor); GetMouse(&initialMouse); drawRect.left = drawRect.right = initialMouse.h; drawRect.top = drawRect.bottom = initialMouse.v; GetMouse(&previousMouse); while(StillDown()) { GetMouse(¤tMouse); if(currentMouse.v != previousMouse.v || currentMouse.h != previousMouse.h) { FrameRect(&drawRect); if(currentMouse.h >= initialMouse.h) drawRect.right = currentMouse.h; if(currentMouse.v >= initialMouse.v) drawRect.bottom = currentMouse.v; if(currentMouse.h <= initialMouse.h) drawRect.left = currentMouse.h; if(currentMouse.v <= initialMouse.v) drawRect.top = currentMouse.v; FrameRect(&drawRect); } previousMouse.v = currentMouse.v; previousMouse.h = currentMouse.h; } FrameRect(&drawRect); theColour.red = doRandomNumber(0,65535); theColour.green = doRandomNumber(0,65535); theColour.blue = doRandomNumber(0,65535); RGBForeColor(&theColour); PenMode(patCopy); PenPat(GetQDGlobalsBlack(&blackPattern)); BackPixPat(pixpatHdl); randomNumber = doRandomNumber(0,3); if(randomNumber == 0) PaintRect(&drawRect); else if(randomNumber == 1) EraseRoundRect(&drawRect,50,50); else if(randomNumber == 2) PaintOval(&drawRect); else if(randomNumber == 3) PaintArc(&drawRect,0,doRandomNumber(0,360)); BackPat(&whitePattern); } // *************************************************************************** doDrawingState void doDrawingState(void) { Rect portRect, theRect; Pattern whitePattern; ThemeDrawingState themeDrawingState; SInt16 a; UInt32 finalTicks; RGBBackColor(&gBlueColour); GetWindowPortBounds(gWindowRef,&portRect); FillRect(&portRect,GetQDGlobalsWhite(&whitePattern)); SetWTitle(gWindowRef,"\pSaving and restoring the graphics port drawing state"); if(!gRunningOnX) SetThemeCursor(kThemeWatchCursor); NormalizeThemeDrawingState(); doDrawingStateProof(0); Delay(120,&finalTicks); GetThemeDrawingState(&themeDrawingState); theRect = portRect; theRect.right -= 300; SetThemeBackground(kThemeBrushListViewBackground,gPixelDepth,gIsColourDevice); EraseRect(&theRect); theRect.left += 150; SetThemeBackground(kThemeBrushListViewSortColumnBackground,gPixelDepth,gIsColourDevice); EraseRect(&theRect); SetThemePen(kThemeBrushListViewSeparator,gPixelDepth,gIsColourDevice); theRect.left -= 150; for(a=theRect.top;a<=theRect.bottom;a+=18) { MoveTo(theRect.left,a); LineTo(theRect.right - 1,a); } Delay(120,&finalTicks); doDrawingStateProof(1); Delay(120,&finalTicks); SetThemeDrawingState(themeDrawingState,true); doDrawingStateProof(2); if(!gRunningOnX) SetThemeCursor(kThemeArrowCursor); } // ********************************************************************** doDrawingStateProof void doDrawingStateProof(SInt16 phase) { Rect theRect; MoveTo(324,phase * 117 + 41); if(phase == 0) DrawString("\pBefore calls to SetThemePen and SetThemeBackground"); else if(phase == 1) DrawString("\pAfter calls to SetThemePen and SetThemeBackground"); else if(phase == 2) DrawString("\pAfter restoration of graphics port drawing state"); MoveTo(324,phase * 117 + 54); DrawString("\pPen pattern/colour"); MoveTo(462,phase * 117 + 54); DrawString("\pBackgrd pattern/colour"); SetRect(&theRect,324,phase * 117 + 58,438,phase * 117 + 132); PaintRect(&theRect); SetRect(&theRect,462,phase * 117 + 58,576,phase * 117 + 132); EraseRect(&theRect); QDFlushPortBuffer(GetWindowPort(FrontWindow()),NULL); } // *********************************************************************** doGetDepthAndDevice void doGetDepthAndDevice(void) { GDHandle deviceHdl; deviceHdl = GetMainDevice(); gPixelDepth = (*(*deviceHdl)->gdPMap)->pixelSize; if(((1 << gdDevType) & (*deviceHdl)->gdFlags) != 0) gIsColourDevice = true; } // **************************************************************************** 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 choose items from the Demonstration menu and click the mouse button when instructed to do so by the advisory text in the window's title bar.
In addition to the usual constants relating to menus and the window, constants are established for pixel pattern, icon, string list, and picture resource IDs.
The fields of the RGBColor global variables are assigned values representing the colours described by the variable names.
Random numbers are used by various functions in the demonstration. The call to SetQDGlobalsRandomSeed seeds the random number generator. randSeed is a QuickDraw global variable which holds the seed value for the random number generator. Unless randSeed is modified, the same sequence of numbers will be generated each time the program is run. The parameter to the GetDateTime call receives the number of seconds since midnight, January 1, 1904, a value that is bound to be different each time the program is run. Note that error handling in main(), as in other areas of the program, is somewhat rudimentary in that the program simply terminates.
Within the mouseDown case, at the inContent case, if the mouseDown is within the content region of the window when it is the front window and gDrawWithMouseActivated is true, the function doDrawWithMouse is called.
doDemonstrationMenu switches according to the user's choices in the Demonstration menu. In all but the iDrawWithMouse case, the only action taken is to call the relevant function. Note that the global variable gDrawWithMouseActivated is set to false at function entry, and is set to true within the iDrawWithMouse case (which executes if the user chooses the Draw With Mouse item). Also note that the window's background is filled with the white colour, using the white pattern, within this case.
doLines demonstrates line drawing using colours, bit patterns, pixel patterns, and with the Boolean pattern mode patXor. doLines also demonstrates modifying the graphics port's clipping region so as to clip drawing to that modified region. The first line sets the graphics pen's size, pattern, and pattern mode to the defaults. The next three lines fill the window's content area with blue. The next block sets the window's clipping region to a rectangle 10 pixels inside the port rectangle. The first two lines define such a rectangle. The next two lines save the current clipping region for later restoration. The call to ClipRect establishes the new clipping region by setting it in the graphics port object.
After the window title is set, FillRect is called with the white pattern with the background colour is set to white. This fill is clipped to the current clipping region, which is a rectangle 10 pixels inside the port rectangle. Within the for loop, random numbers between 0 and the width of the port rectangle are assigned to two variables which will be used to specify the starting and finishing horizontal coordinates for each of 60 drawn lines. The fields of an RGBColor variable are also assigned random values, this time between 0 and 65534 (one less than the maximum possible value for a UInt16). The call to RGBColor assigns this random colour as the requested foreground colour. The pen width is increased by two pixels. Finally, the call to MoveTo moves the pen to the random horizontal location at the top of the port rectangle, and the call to LineTo draws a line to the random horizontal location at the bottom of the port rectangle. The line drawing is clipped to the current clipping region.
This line drawing operation is similar to the previous one except that a system-supplied bit pattern is assigned to the graphics pen and the lines are drawn from left to right rather than top to bottom. The bit patterns are loaded by the call to GetIndPattern and are drawn from the 38 patterns in the 'PAT#' resource in the System file with resource ID sysPatListID (0). The call to PenPat assigns the specified bit pattern to the graphics pen. In this operation, the height of the pen, rather than the width, is increased by two each time around the for loop.
In this line drawing operation, before the for loop is entered, GetPixPat is called to allocate a PixPat structure and initialise it with information from the specified 'ppat' resource. The call to PenPixPat then assigns this pixel pattern to the graphics pen. After the last line is drawn, DisposePixPat is called to free the memory allocated by the GetPixPat call. At this point, the clipping region saved at the start of the function is restored, and all of the memory allocated by the NewRgn call is freed.
This block demonstrates a well-known but nonetheless exotic capability of the humble line when it operates in the pattern mode patXor. The content area is filled with red, following which the pen size and pen pattern are set to the defaults. The call to PenMode sets the pen mode to patXor The next four lines assign values to four variables which will be used to ensure that the starting and ending locations of each drawn line will be ten pixels inside the port rectangle. The for loops, proceeding clockwise, draw lines from points 10 pixels inside the periphery of the port rectangle through the centre of the rectangle to points on the opposite side of the rectangle. The effect of patXor on any destination pixel is to invert it. For example, assuming a white background and black pen colour, any white pixel in the path of the drawn lines will be turned black and any black pixel will be turned white. This produces a pattern known as a moire (watered silk) pattern.
doFrameAndPaint demonstrates the use of QuickDraw's framing and painting functions with the exception of those relating to polygons and regions. At the first two lines, the pen pattern and mode are set to the defaults and the pen size is set to 30 pixels wide and 20 pixels high. The for loop is traversed three times, once for framing and painting with a colour, once for framing and painting with a bit pattern, and once for framing and painting with a pixel pattern. The first action is to fill the port rectangle with the colour white using the white pattern.
The first time around the loop, RGBForeColor is called to set the requested foreground colour to red. The second time around the loop, RGBForeColor and RGBBackColor are called to set the requested foreground and background colours to, respectively, blue and yellow, GetIndPattern loads one of the system-supplied bit patterns, and PenPat makes that pattern the pen's current bit pattern. The third time around the loop, a call to GetPixPat loads a 'ppat' resource, creating a new PixPat structure, and a call to PenPixPat assigns that pixel pattern to the pen.
In this section, SetRect is used to assign the coordinates of a rectangle to the fields of a Rect structure, and OffsetRect is used to move the rectangle horizontally and vertically between the calls to the various framing and painting functions. Before doFrameAndPaint exits, DisposePixPat is called to free the memory allocated by the GetPixPat call.
doFillEraseInvert demonstrates the use of QuickDraw's filling, erasing, and inverting functions with the exception of those relating to polygons and regions. At the first two lines, the pen pattern and mode are set to the defaults and the pen size is set to 30 pixels wide and 20 pixels high. The for loop is traversed four times, once for filling and erasing with colours, once for filling and erasing with bit patterns, once for filling and erasing with a pixel patterns, and once for inverting. The first action, on the first three passes only, is to fill the port rectangle with the colour white using the white pattern.
The first time around the loop, RGBForeColor and RGBBackColor are called to set the requested foreground and background colours to, respectively, blue and red. In addition, the calls to GetIndPattern and BackPat set the background pattern to black. The second time around the loop, RGBForeColor and RGBBackColor are called to set the requested foreground and background colours to, respectively, blue and yellow. In addition, GetIndPattern is called twice, once to assign a bit pattern to a Pattern variable which will be passed as the second parameter in calls to FillRect, FillOval, etc., and once, in conjunction with BackPat, to assign a bit pattern to the graphics port's bkPixPat field. The third time around the loop, GetPixPat is called twice, once to assign a pixel pattern to a the variable which will be passed as the second parameter in calls to FillCRect, FillCOval, etc., and once, in conjunction with BackPixPat, to assign a pixel pattern to the graphics port's bkPixPat field. The fourth time around the loop, and preparatory to calls to the erasing functions, the call to BackPat sets the background pattern to white. (The calls to SetRect and EraseRect simply erase the existing text in the window.)
In this section, SetRect is used to assign the coordinates of a rectangle to the fields of a Rect structure, and OffsetRect is used to move the rectangle horizontally and vertically between the calls to the various filling, erasing, and inverting functions. Before doFillEraseInvert exits, DisposePixPat is called twice to free the memory allocated by the two GetPixPat calls.
doPolygonAndRegion demonstrates defining a polygon and a region and the use of some of QuickDraw's polygon and region framing, painting, filling, and erasing functions.
The calls to GetIndPattern and BackPat set the background pattern to one on the system-supplied bit patterns. The call to GetPixPat gets the pixel pattern to be used by the filling functions. The calls to RGBForeColor and RGBBackColor set the requested foreground and background colours. PenNormal sets the pen's size, pattern mode, and pattern to the defaults. The OpenPoly call initiates the recording of the polygon definition, the MoveTo and LineTo calls define the polygon, and ClosePoly stops the recording. Note that, in this demonstration, the last vertex is not joined to the first vertex. The NewRgn call allocates memory for a new region and a region pointer, initialises the contents of the region and make it an empty rectangle. OpenRgn initiates the recording of a region shape. The next seven lines create a region definition comprising two rectangles and an overlapping oval. CloseRgn terminates the recording.
In this section, OffsetPoly and OffsetRgn are used to move the polygon and region horizontally between the calls to the framing, filling, and erasing functions. OffsetPoly modifies the polygon's definition. OffsetRgn adjusts the coordinates of the region. Before doPolygonAndRegion exits, KillPoly is called to free all the memory allocated by OpenPoly, DisposeRgn is called to free all the memory allocated by NewRgn, DisposePixPat is called to free all the memory allocated by GetPixPat, and the background pattern is set to white.
doText draws text in various fonts, sizes and styles. In addition, the last block demonstrates drawing justified text within a specified rectangle using the TextEdit function TETextBox. Prior to the for loop, the variable windowCentre is assigned a value which represents a location midway across the port rectangle, and the right half of the content area is filled with blue. Within the first section of the for loop, the text font is changed using GetFNum and TextFont, the text style is changed using TextFace, and the foreground colour is changed. At the last two sections within the loop, the text size is changed using TextSize, a string is retrieved from a 'STR#' resource, the width of the string in pixels is determined, and the string is drawn centred laterally in the window. After the loop exits, the text font, size and style are returned to Geneva 10pt plain. At the final block, a small rectangle is defined at the bottom left of the content area. Because the current background colour is blue, the call to EraseRect erases the rectangle in that colour. The rectangle is then inset by five pixels all round. A string is then loaded from a 'STR#' resource and the foreground colour is set to white. Finally, TETextBox is called to draw the text within the specified rectangle with left justification. (Other available justification constants are teFlushRight and teCenter.)
doScrolling demonstrates scrolling pixels within a specified rectangle, with the operation clipped to a region comprising two unconnected rectangular areas. The first call to GetPixPat loads a 'ppat' resource. The call to PenPixPat assigns that pixel pattern to the pen, which is then made 50 pixels wide and zero pixels high. A framed rectangle is then drawn in the left half of the window. (Note that, because the pen height is set to zero, the two sides of the rectangle will be drawn but not the top and bottom.) A filled rectangle is then drawn in the right side of the window using the same pixel pattern. In the next block, another 'ppat' resource is retrieved. The call to BackPixPat makes this pixel pattern the background pixel pattern. The next block creates a region comprising two separate rectangles, the first one coincident with the "inside" of the framed rectangle and the second one coincident with the whole of the filled rectangle). The current clipping region is then saved and the newly created region is established as the current clipping region. The following call to SetRect defines a rectangle for the first parameter of the ScrollRect function. Laterally, this extends from the left inside of the framed rectangle to the right hand side of the filled rectangle. The call to NewRgn then creates the empty region required by the ScrollRect calls. In the first for loop, the pixels within the clipping region within the specified rectangle are scrolled downwards, the top of the rectangle being incremented downwards between calls to ScrollRect. ScrollRect fills the "vacated" areas with the background pattern . Between the for loops, the rectangle used by ScrollRect is redefined and the background pixel pattern is changed to the pixel pattern used to draw the original rectangles. The scrolling operation is then repeated, this time in an upwards direction. Before doScrolling exits, the saved clipping region is restored and all the memory allocated by the GetPixPat and NewRgn calls is freed.
doBooleanSourceModes demonstrates the effects of the Boolean source modes in both black-and-white and colour. The first block fills the content area with green and then fills the top half of the content area with white. This block leaves the foreground colour black and the background colour white. The next block loads two 32 bit by 32 bit 'ICON' resources. One icon contains the image of a cross and the other contains the image of a square. The first for loop calls PlotIcon four times, twice to draw the icons in the white area at the top of the window, and twice to draw them in the green area at the bottom of the window. The rectangle passed in the first parameter of the PlotIcon calls expands the icon to 64 pixels by 64 pixels. The calls to RGBForeColor and RGBBackColor cause the icons in the green area to be drawn using a foreground colour of yellow and a background colour of red. The foreground and background colours are reset to black and white before the second for loop is entered. The second for loop draws the cross icon eight times across the bottom of the white half of the window. The foreground and background colours are then changed to yellow and red before this process is repeated across the bottom of the green area of the window. The foreground and background colours are again reset to black and white. As a preamble to what is to come, note that there is no special data type for an icon. It is simply 128 bytes of bit data arranged as 32 rows of 4 bytes per row. All that is available is a handle to that 128 bytes of data. The intention is to cause the 128 bytes of data which constitutes the square icon to be regarded as bitmap data pointed to by the baseAddr field of a BitMap structure. That way, the CopyBits routine can be used to copy the bitmap into the graphics port. Because CopyBits is one of those functions which can move memory around, the first action is to lock the icon data in the heap. The address of the square icon image data is then assigned to the baseAddr field of a BitMap structure, the rowBytes field is assigned the value 4, and the bounds field is assigned a rectangle defining the normal icon size. The final for loop calls CopyBits to copy the bit image into the graphics port sixteen times, overdrawing the previously drawn cross icons. The call to SetRect within the inner for loop defines the expanded destination rectangle which governs the size at which the image will be drawn. This rectangle is passed in the destRect parameter of the CopyBits call. Note that, in the CopyBits call, the value passed in the tMode (transfer mode) parameter is incremented each time through the loop so that the square image overdraws the cross image once in each of the eight available Boolean source modes. The three lines following the CopyBits call retrieve the appropriate string containing the relevant source mode from the specified 'STR#' resource and draw this string above each copied image. The last line unlocks the icon image data.
doArithmeticSourceModes demonstrates the effects of the arithmetic source modes. Since CopyBits will be called, the foreground and background colours are set to black and white respectively. The call to FillRect clears the window to white. The first call to GetPicture loads a 'PICT' resource into a Picture structure. (Since the 'PICT' resource is purgeable, it is made non-purgeable immediately it is retrieved, used immediately, and immediately made purgeable again.) The call to DrawPicture draws the picture in the top left of the window, where it is labelled as the source image. The second call to GetPicture loads another 'PICT' resource which will be used as the destination image. The first for loop draws this picture in the window at eight separate locations, these locations being determined by the rectangle passed in the first parameter of the DrawPicture calls. The last for loop is traversed once for each of the eight arithmetic source modes. CopyBits is called eight times to overdraw the destination images with the source image. Note that the value in the tMode (transfer mode) parameter of the CopyBits call is incremented each time around the loop. Note also that, each time around the loop, a new string is retrieved from a 'STR#' resource and drawn above the destination image. Before doArithmeticSourceModes exits, ReleaseResource is called twice to free the memory allocated by the GetPicture calls.
doHighlighting demonstrates highlighting, first with the colour set by the user in the Appearance pane of the Appearance control panel (Mac OS 8/9) or in System Preferences (Mac OS X), and then with two colours set by the program. Firstly, the highlight colour set by the user is saved via a call to LMGetHiliteRGB. The for loop is traversed three times. On the second and third traverses, the highlight colour is changed. Within the for loop, a copy of the value at the low memory global HiliteMode is retrieved using LMGetHiliteMode, BitClr is called to clear the highlight bit, and LMSetHiliteMode is called to set to low memory global to this new value. At the if/else block, the highlight colour is changed if this is the second or third time around the loop. With the highlight bit cleared, InvertRect is called to invert a specified rectangle. Note that the call to InvertRect resets the highlight bit. Accordingly, when the user clicks the mouse button, the highlight bit is cleared once again before InvertRect is called once again. This second call restores the colour in the specified rectangle to the background colour. Before the doHighLighting function returns, it sets the highlight colour to the saved highlight colour.
doDrawWithMouse demonstrates the use of the mouse to define bounding rectangles for QuickDraw shape drawing functions. It also demonstrates the implementation of the "rubber band" rectangle commonly used to provide visual feedback to the user as he drags the mouse during such operations. (While the mouse button remains down, the "rubber-band" rectangle is continually erased and redrawn as the mouse is moved. It is erased when the mouse button is released.) doDrawWithMouse is called when a mouse-down occurs in the window while it is the front window, provided that the global variable gDrawWithMouseActivated is set to true. The call to GetPixPat loads a 'ppat' resource containing a small 8 pixel by 8 pixel pattern. This pixel pattern is assigned to the pen by the call to PenPixPat. The call to PenSize makes the pen size one pixel high by one pixel wide. The pen pattern mode is then set to patXOr. (Note: For a black-and-white "rubber band", replace the PenPixPat call with: PenPat(GetQDGlobalsGray(&grayPattern)); The call to GetMouse saves the initial mouse location to a Point variable. The contents of the fields of this variable will remain unchanged. Those coordinates are also used to initialise the left and top fields of the Rect variable drawRect. The next call to GetMouse assigns the initial location of the mouse to another Point variable. The contents of the fields of this variable will continually change as the mouse is dragged. The while loop continues to execute while the mouse button remains down. Within the loop, the current mouse location is retrieved and compared with the previous mouse location (the first if statement). If the mouse has moved: o FrameRect is called to draw the framed rectangle. o If the current mouse horizontal coordinate is greater than or equal to the initial horizontal mouse coordinate, the current mouse horizontal coordinate is assigned to the right field of the rectangle. o If the current mouse vertical coordinate is greater than or equal to the initial vertical mouse coordinate, the current mouse vertical coordinate is assigned to the bottom field of the rectangle. o If the current mouse horizontal coordinate is less than or equal to the initial horizontal mouse coordinate, the current mouse horizontal coordinate is assigned to the left field of the rectangle. o If the current mouse vertical coordinate is less than or equal to the initial vertical mouse coordinate, the current mouse vertical coordinate is assigned to the top field of the rectangle. o FrameRect is called again with the newly defined rectangle passed in. Because the drawing mode is patXor, the first call to FrameRect erases the old rectangle. Because FrameRect is only called if the mouse has moved, the flicker which would otherwise occur when the mouse is stationary is avoided. Below the if block, and preparatory to the next comparison of current and previous mouse location, the current mouse location becomes the previous mouse location. When the mouse button is released: o The final call to FrameRect erases the final "rubber-band" rectangle. o The foreground colour is set to a random colour, the pen pattern mode is set to patCopy, the pen pattern is set to black, and the background pixel pattern is set to that previously used to draw the "rubber band". o The rectangle as at mouse button release is used in calls to QuickDraw painting and erasing functions to draw rectangles, round rectangles, ovals, and arcs. Just which function is called depends on the value returned by the call to doRandomNumber. o The background pattern is set to white.
doDrawingState is similar to the function doDrawListView in the demonstration program Appearance, the difference being that, in doDrawingState, the drawing state is saved at entry and restored at exit. Note that the call to NormalizeThemeDrawingState or is included in this function for demonstration purposes only. Ordinarily, this function would be called (if required) at other points in an application. The call to GetThemeDrawingState saves the drawing state prior to the calls to the Appearance Manager functions SetThemeBackground and SetThemePen, which will change either the colour or the pattern settings in the graphics port. The call to SetThemeDrawingState restores the saved drawing state. The intervening code simply draws a Mac OS 8/9-type list view in the left half of the window. The calls to doDrawingStateProof are also for demonstration purposes only. As will be seen, this function simply draws rectangles in the right half of the window in the pen and background colours and patterns as they were after the call to NormalizeThemeDrawingState, after the calls to the Appearance Manager functions, and after the call to SetThemeDrawingState.
doDrawingStateProof is called by doDrawingState to draw rectangles in the right half of the window in the pen and background colours and patterns as they were after the call to NormalizeThemeDrawingState, after the calls to the Appearance Manager functions, and after the call to SetThemeDrawingState.
doRandomNumber are incidental to the demonstration. The use of the QuickDraw random number generator is quite adequate for the purposes of this demonstration. However, a professional programmer would not regard it as measuring up to the minimal standards of a "serious" random number generator. (See the article on random number generators at http://www.mactech.com/articles/mactech/Vol.08/08.03/RandomNumbers/index.html.)