Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
CustomPC:B.c
/* |
File: CustomPC/B.c |
Contains: Sample showing how to implement custom PicComments and QuickDraw bottlenecks. |
See the ReadMe for code details. |
Written by: |
Copyright: Copyright © 1991-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
08/2000 JM Carbonized, non-Carbon code is commented out |
for demonstration purposes. |
7/9/1999 KG Updated for Metrowerks Codewarror Pro 2.1 |
03/97 v. 2.0 ik Rewritten as 'Custom PicComments/Bottlenecks' |
10/91 v. 1.0 dh Shipped as 'DTS Groupies' sample. |
*/ |
#include "CarbonPrefix.h" |
#include <Dialogs.h> |
#include <Fonts.h> |
#include <Menus.h> |
#include <Devices.h> |
#include <Resources.h> |
/*------ constants --------------------------------------------------------------------------*/ |
#define kCreatorType 'EGAD' /* Our creator type. */ |
#define rMenuBar 128 /* The menubar resource ID. */ |
#define mApple 128 /* Apple menu ID. */ |
#define iAbout 1 /* "AboutÉ" menu item index. */ |
#define mFile 129 /* File menu ID. */ |
#define iQuit 1 /* Quit menu item index. */ |
#define kMaxPICTs 50 /* Max. no. of pictures we handle. */ |
#define kCustomComment 100 /* Custom PicComment indicator. */ |
#define kSubPICTComment 200 /* Our (sub-picture) sub-PicComment. |
This comment indicates that we've |
stored a picture inside of a |
picture. We use it to extract |
the individual PICTs. */ |
/*------ types --------------------------------------------------------------------------*/ |
typedef struct TPICTRec { |
int numPICTs; /* The number of sub-pictures, */ |
PicHandle picture[kMaxPICTs]; /* and their PicHandles, */ |
Rect curPos[kMaxPICTs]; /* and their last drawn positions. */ |
} TPICTRec; |
/*------ globals --------------------------------------------------------------------------*/ |
static TPICTRec gPICTRec; /* Our global picture record. */ |
static Rect gPictsBounds; /* The bounds used by our pictures. */ |
static Boolean gQuitting; /* "Quitting?" flag. */ |
static WindowPtr gTheWindow; /* Our window's pointer. */ |
/*------ prototypes ------------------------------------------------------------------------*/ |
/*extern void CompositePictures(void); |
//extern pascal void CustomPicProc(int kind, int dataSize, Handle dataHandle); |
extern pascal void CustomPicProc(short kind, short dataSize, Handle dataHandle) |
extern void DisassemblePictures(void); |
extern void DoMenuCommand(long menuResult); |
extern void EventLoop(void); |
extern void MoveThePicts(Rect *wBounds); |
extern void MakeThePicts(void); |
extern void ShowThePicts(void);*/ |
void CompositePictures(void); |
pascal void CustomPicProc(short kind, short dataSize, Handle dataHandle); |
void DisassemblePictures(void); |
void DoMenuCommand(long menuResult); |
void EventLoop(void); |
void MoveThePicts(Rect *wBounds); |
void MakeThePicts(void); |
void ShowThePicts(void); |
/*------ CompositePictures ----------------------------------------------------------------*/ |
// CompositePictures groups all of the pictures in the global picture record |
// into one "composite" picture. It removes all of the old pictures and |
// stores the new one. |
/*----------------------------------------------------------------------------------------*/ |
void CompositePictures() |
{ |
PicHandle aPICT, groupPICT; |
RgnHandle oldClip; |
int idx; |
long dataSize; |
long ownerApp; |
short localPicComment; |
/* Save the old clipping region, and set a valid one so our grouped |
picture develops ok. */ |
oldClip = NewRgn(); |
GetClip(oldClip); |
ClipRect(&gPictsBounds); |
groupPICT = OpenPicture(&gPictsBounds); |
/* Create a picture to contain all the other ones, then draw those into |
it, separated by our PicComments. Kill the individual pictures as |
we go. Finally, close the composite picture. */ |
ownerApp = kCreatorType; |
localPicComment = kSubPICTComment; |
for(idx = 0; idx < gPICTRec.numPICTs; idx++) |
{ |
aPICT = gPICTRec.picture[idx]; |
/* We don't just use a single custom PicComment since another app may |
use the same comment and conflicts could result. (Not in this app, |
but in the real world.) We add six bytes to the handle and store the |
creator type of the app that made the picture followed by 2 bytes |
for a local PicComment kind within the app. If we used more than |
one PicComment in this app, this extra information would be |
necessary. */ |
dataSize = GetHandleSize((Handle) aPICT) +6; |
SetHandleSize((Handle) aPICT, dataSize); |
BlockMove((Ptr) *aPICT, (Ptr) *aPICT +6, dataSize -6); |
BlockMove(&ownerApp, (Ptr) *aPICT, 4); |
BlockMove(&localPicComment, (Ptr) *aPICT +4, 2); |
PicComment(kCustomComment, dataSize, (Handle) aPICT); |
/* Fix the original PicHandle so that we can draw our picture for apps |
that don't know about our custom comments. */ |
BlockMove((Ptr) *aPICT +6, (Ptr) *aPICT, dataSize -6); |
SetHandleSize((Handle) aPICT, dataSize -6); |
DrawPicture(aPICT, &(*aPICT)->picFrame); |
KillPicture(aPICT); |
gPICTRec.picture[idx] = NULL; |
} |
ClosePicture(); |
/* Restore the original clipping region and update our global picture |
record so that we have one consolidated picture, in the first slot. |
We set it's current position to (0, 0, 0, 0) so that we don't waste |
time erasing anything on the first draw. */ |
SetClip(oldClip); |
DisposeRgn(oldClip); |
gPICTRec.numPICTs = 1; |
gPICTRec.picture[0] = groupPICT; |
SetRect(&gPICTRec.curPos[0], 0, 0, 0, 0); |
} |
/*------ CustomPicProc ----------------------------------------------------------------*/ |
// CustomPicProc is our replacement for the port's StdCommentProc. |
// in the global picture record |
/*----------------------------------------------------------------------------------------*/ |
//pascal void CustomPicProc(int kind, int dataSize, Handle dataHandle) |
pascal void CustomPicProc(short kind, short dataSize, Handle dataHandle) |
{ |
int nextNum; |
long ownerApp; |
short localPicComment; |
Handle theHandle; |
/* If this is a custom PicComment, see if it's ours. In this app, |
we know it always will be, but when you import other pictures |
you can't be so sure. */ |
if (kind == kCustomComment && (gPICTRec.numPICTs < kMaxPICTs)) |
{ |
if (dataSize < 6) return; /* Not ours? */ |
BlockMove((Ptr) *dataHandle, &ownerApp, 4); |
BlockMove((Ptr) *dataHandle +4, &localPicComment, 2); |
if ((ownerApp != kCreatorType) || /* Not ours? */ |
(localPicComment != kSubPICTComment)) return; |
/* This is indeed our picture comment. Create a handle for the data we |
found, store it in our global picture record and bump the number of |
pictures we have. The reason that we clear the picture's curPos |
rect is so that we won't waste time erasing anything the first time |
we enter MoveTheGroupies. */ |
nextNum = gPICTRec.numPICTs; |
gPICTRec.picture[nextNum] = (PicHandle) dataHandle; |
SetRect(&gPICTRec.curPos[nextNum], 0, 0, 0, 0); |
/* After we create the handle for the data, we have to remember that |
we have 6 bytes of identifying "garbage" in front of the picture |
data. To remove that, BlockMove all the picture data to the |
beginning of the handle and reset the handle's size. This is kind |
of a hassle, but it's really best to store your custom PicComments |
this way. Otherwise, you may misinterpret someone elses comments |
or cause them to misinterpret yours. */ |
if (HandToHand((Handle *) &gPICTRec.picture[nextNum]) == noErr) |
{ |
++gPICTRec.numPICTs; |
theHandle = (Handle) gPICTRec.picture[nextNum]; |
BlockMove((Ptr) *theHandle +6, (Ptr) *theHandle, dataSize -6); |
SetHandleSize(theHandle, dataSize -6); |
} |
} |
} |
/*------ DisassemblePictures ----------------------------------------------------------------*/ |
// DisassemblePictures ungroups the first picture in the global picture |
// record. It replaces that picture with new pictures of every picture |
// it contained. All drawing is done within another "dummy" picture |
// so that nothing draws on the screen. The reason we can't use an empty |
// clipping region to do this is that PicComments will be clipped out along |
// with everything else, and we'd be hosed. (We need the PicComments!) |
// This code is written so that it installs the GrafProcs correctly for |
// both GrafPorts and CGrafPorts. |
/*----------------------------------------------------------------------------------------*/ |
void DisassemblePictures() |
{ |
GrafPtr curPort; |
//QDProcs theQDProcs; /* If we're using a GrafPortÉ */ |
CQDProcs theCQDProcs; /* If we're using a CGrafPortÉ */ |
PicHandle dummyPICT; |
/* Reset the number of pictures in our global picture record to zero. |
There's actually one picture there at this point (the composite |
one), but we must set this to zero so that our PicComment handler |
stores extracted pictures in the right place. */ |
gPICTRec.numPICTs = 0; |
/* Get the current port and the standard QDProcs or CQDProcs, |
depending on whether we have a GrafPort or CGrafPort. */ |
GetPort(&curPort); |
//if (curPort->portBits.rowBytes < 0) /* CGrafPortÉ */ |
if (GetPortBitMapForCopyBits(curPort)->rowBytes < 0) |
{ |
SetStdCProcs(&theCQDProcs); |
theCQDProcs.commentProc = NewQDCommentProc(CustomPicProc); |
//curPort->grafProcs = (QDProcsPtr) &theCQDProcs; |
SetPortGrafProcs(curPort, &theCQDProcs); |
} |
else /* GrafPortÉ */ |
{ |
/*SetStdProcs(&theQDProcs); |
theQDProcs.commentProc = NewQDCommentProc(CustomPicProc); |
//curPort->grafProcs = (QDProcsPtr) &theQDProcs; |
SetPortGrafProcs(curPort, &theQDProcs);*/ |
} |
/* Open our dummy picture and draw into it so that our PicComment |
handler is called to parse the picture. When finished, close the |
picture, kill it and remove our grafProcs. */ |
dummyPICT = OpenPicture(&(*gPICTRec.picture[0])->picFrame); |
DrawPicture(gPICTRec.picture[0], &(*gPICTRec.picture[0])->picFrame); |
ClosePicture(); |
KillPicture(dummyPICT); |
//curPort->grafProcs = NULL; |
SetPortGrafProcs(curPort, NULL); |
} |
/*------ DoMenuCommand ----------------------------------------------------------------*/ |
// DoMenuCommand handles our menu items. |
/*----------------------------------------------------------------------------------------*/ |
void DoMenuCommand(long menuResult) |
{ |
int menuID, menuItem; |
//Str255 daName; |
/*MenuHandle theMenu;*/ |
//GrafPtr savePort; |
/* Get the menu ID and item ID. */ |
menuID = (menuResult >>16) & 0xFFFF; |
menuItem = menuResult & 0xFFFF; |
/* Do what we're supposed to. */ |
switch (menuID) |
{ |
case mApple: /* Apple Menu */ |
switch (menuItem) |
{ |
case iAbout: /* -> Handle "AboutÉ" */ |
break; |
default: /* -> The rest are DAs. */ |
/*GetPort(&savePort); |
GetMenuItemText(GetMenuHandle(mApple), menuItem, daName); |
OpenDeskAcc((ConstStr255Param)daName); |
SetPort(savePort);*/ |
break; |
}; |
case mFile: /* File Menu */ |
switch (menuItem) |
{ |
case iQuit: |
gQuitting = true; /* -> Quit */ |
break; |
} |
} |
HiliteMenu(0); |
} |
/*------ EventLoop ----------------------------------------------------------------*/ |
// EventLoop is a main event loop. It calls WaitNextEvent and |
// other nice stuff. It also makes our pictures assemble, disassemble, |
// and move about. |
/*----------------------------------------------------------------------------------------*/ |
void EventLoop() |
{ |
EventRecord theEvent; |
WindowPtr whichWindow; |
short partCode; |
Rect dragRect; |
RgnHandle grayRgn; |
char key, time; |
unsigned long finalTicks; |
Rect tempRect1; |
/* Set up the rectangle for where we can drag windows. Initialize |
our "time-through-the-loop" counter to -1 so that it gets bumped |
to zero on the first pass. This will enable us assemble the |
grouped picture as we go through the first time. */ |
grayRgn = GetGrayRgn(); |
//dragRect = (*grayRgn)->rgnBBox; |
GetRegionBounds(GetGrayRgn(), &dragRect); |
time = -1; |
/* We have a counter which goes from 0-26 and is incremented each time |
we go through this code. At time = 0, We assemble the grouped image. |
At time = 12, we break all the PICTs out of it. At time = 27, we |
cycle back to time = 0. In between these life altering times, |
(at least for groupies), we draw all of our current pictures in |
random places. This clearly shows whether the PICTs are currently |
grouped or not. We go through this loop until the user quits. */ |
do |
{ |
//SetPort(gTheWindow); |
SetPortWindowPort(gTheWindow); |
time = ++time % 27; |
if (time == 0) |
{ |
CompositePictures(); /* Group the pictures. */ |
//EraseRect(&(gTheWindow)->portRect); |
EraseRect(GetPortBounds(GetWindowPort(gTheWindow), &tempRect1)); |
} |
if (time == 12) |
{ |
DisassemblePictures(); /* Ungroup the pictures. */ |
//EraseRect(&(gTheWindow)->portRect); |
EraseRect(GetPortBounds(GetWindowPort(gTheWindow), &tempRect1)); |
} |
/* Move all pictures so we can see their current state. */ |
//MoveThePicts(&(gTheWindow)->portRect); |
MoveThePicts(GetPortBounds(GetWindowPort(gTheWindow), &tempRect1)); |
/* Delay so our graphics don't flash. */ |
Delay((time < 12)? 40:10, &finalTicks); |
/* Handle any pending events. */ |
if (WaitNextEvent(everyEvent, &theEvent, 0, NULL)) |
switch (theEvent.what) |
{ |
case mouseDown: /* Handle mouse clicks. */ |
partCode = FindWindow(theEvent.where, &whichWindow); |
switch (partCode) |
{ |
case inContent: |
if (whichWindow != FrontWindow()) |
SelectWindow(whichWindow); |
break; |
case inDrag: |
DragWindow(whichWindow, theEvent.where, &dragRect); |
break; |
case inMenuBar: |
DoMenuCommand(MenuSelect(theEvent.where)); |
case inSysWindow: |
//SystemClick(&theEvent, whichWindow); |
break; |
} |
break; |
case updateEvt: /* Handle update events. */ |
BeginUpdate((WindowPtr) theEvent.message); |
EndUpdate((WindowPtr) theEvent.message); |
break; |
case keyDown: /* Handle key presses. */ |
case autoKey: |
key = (char) (theEvent.message & charCodeMask); |
if (((theEvent.modifiers & cmdKey) != 0) && (theEvent.what == keyDown)) |
DoMenuCommand(MenuKey(key)); |
break; |
} |
} |
while (!gQuitting); |
} |
/*------ MoveThePicts ----------------------------------------------------------------*/ |
// MoveThePicts moves the current picturesÉ somewhere randomly. It |
// first erases all the pictures in descending order. Then it redraws |
// them in new locations in ascending order. This way we don't wipe out |
// any of the new pictures when the old ones are erased. |
/*----------------------------------------------------------------------------------------*/ |
void MoveThePicts(Rect *wBounds) |
{ |
int newLeft, newTop, width, height, idx; |
float maxX, maxY; |
Rect picFrame, curPos; |
/* First erase all pictures in reverse order. Also, calculate their |
new locations and store those in their curPos fields. */ |
for (idx = gPICTRec.numPICTs -1; idx >= 0; idx--) |
{ |
curPos = gPICTRec.curPos[idx]; |
EraseRect(&curPos); |
picFrame = (*gPICTRec.picture[idx])->picFrame; |
width = picFrame.right -picFrame.left; |
height = picFrame.bottom -picFrame.top; |
/* To calculate new positions, we find the maximum position we can |
have for the picture's top left corner. Then, we find a random |
point that's bounded by (0, 0) and that maximum. Finally, we |
set this picture's current position so that it has this point for |
its top left corner. */ |
maxX = (wBounds->right - wBounds->left) -width; |
maxY = (wBounds->bottom - wBounds->top) -height; |
newTop = (((float) Random() +32767)/65534.0) * maxX; |
newLeft = (((float) Random() +32767)/65534.0) * maxY; |
curPos.top = newTop; |
curPos.left = newLeft; |
curPos.bottom = newTop +height; |
curPos.right = newLeft +width; |
gPICTRec.curPos[idx] = curPos; |
} |
/* Now draw all the pictures in their new positions. */ |
for (idx = 0; idx < gPICTRec.numPICTs; idx++) |
DrawPicture(gPICTRec.picture[idx], &gPICTRec.curPos[idx]); |
} |
/*------ MakeThePicts ----------------------------------------------------------------*/ |
// MakeThePicts creates the pictures that will be grouped. These can be |
// any QuickDraw pictures. For this example, I use four pictures; one |
// containing a square, one with a circle, one with a triangle and one |
// with some text. These are all stored in the global picture record. |
// This routine is only called once, to put some pictures into the works to |
// start with. |
/*----------------------------------------------------------------------------------------*/ |
void MakeThePicts() |
{ |
RgnHandle oldClip; |
PolyHandle trianglePoly; |
int fNum, vPos; |
/* Save the current clipping region so that we can restore it later. |
Set our own clipping region, so that we know we have a valid one. |
Also initialize the number of pictures in our global picture |
structure to zero. */ |
oldClip = NewRgn(); |
GetClip(oldClip); |
SetRect(&gPictsBounds, 0, 0, 150, 150); |
ClipRect(&gPictsBounds); |
gPICTRec.numPICTs = 0; |
/* Create a picture with a blue square in it. We set the curPos |
rectangle for all of these pictures to (0, 0, 0, 0) so that |
we don't do any unnecessary erasing the first time they enter |
MoveTheGroupies. */ |
gPICTRec.picture[0] = OpenPicture(&gPictsBounds); |
ForeColor(blueColor); |
PaintRect(&gPictsBounds); |
ClosePicture(); |
SetRect(&gPICTRec.curPos[0], 0, 0, 0, 0); |
++gPICTRec.numPICTs; |
/* Create a picture with a red circle in it. */ |
gPICTRec.picture[1] = OpenPicture(&gPictsBounds); |
ForeColor(redColor); |
PaintOval(&gPictsBounds); |
ClosePicture(); |
SetRect(&gPICTRec.curPos[1], 0, 0, 0, 0); |
++gPICTRec.numPICTs; |
/* Create a picture with a green triangle in it. */ |
gPICTRec.picture[2] = OpenPicture(&gPictsBounds); |
ForeColor(greenColor); |
trianglePoly = OpenPoly(); |
MoveTo(gPictsBounds.left, gPictsBounds.bottom); |
LineTo((gPictsBounds.right - gPictsBounds.left)/2, gPictsBounds.top); |
LineTo(gPictsBounds.right, gPictsBounds.bottom); |
LineTo(gPictsBounds.left, gPictsBounds.bottom); |
ClosePoly(); |
PaintPoly(trianglePoly); |
KillPoly(trianglePoly); |
ClosePicture(); |
SetRect(&gPICTRec.curPos[2], 0, 0, 0, 0); |
++gPICTRec.numPICTs; |
/* Create a picture with some text in it. */ |
gPICTRec.picture[3] = OpenPicture(&gPictsBounds); |
ForeColor(blackColor); |
GetFNum((ConstStr255Param) "\pTimes", (short*)&fNum); |
TextFont(fNum); |
TextSize(12); |
TextFont(bold); |
vPos = gPictsBounds.top +(gPictsBounds.bottom - gPictsBounds.top)/2; |
MoveTo(gPictsBounds.left +10, vPos +10); |
DrawString((ConstStr255Param) "\pCustom PicComments"); |
ClosePicture(); |
SetRect(&gPICTRec.curPos[3], 0, 0, 0, 0); |
++gPICTRec.numPICTs; |
/* Restore the original clipping region. */ |
SetClip(oldClip); |
DisposeRgn(oldClip); |
} |
/*------ ShowThePicts ----------------------------------------------------------------*/ |
// ShowThePicts starts the show. |
// First, we find the deepest display because the groupies are a colorful |
// bunch. Then we create a window and some pictures. Finally, we jump into |
// our main event loop. |
/*----------------------------------------------------------------------------------------*/ |
void ShowThePicts() |
{ |
Rect maxRect, deepRect, wBounds; |
GDHandle deepGDH; |
/* Find the bounds of the deepest device. We'll use this to determine |
where to put our window. Passing the maximum enclosing rectangle |
to GetMaxDevice assures that we find the deepest device available. */ |
SetRect(&maxRect, -32767, -32767, 32767, 32767); |
deepGDH = GetMaxDevice(&maxRect); |
deepRect = (*deepGDH)->gdRect; |
/* Create a window for our drawing, offset onto the deepest device. */ |
SetRect(&wBounds, 40, 40, 360, 340); |
OffsetRect(&wBounds, wBounds.left +deepRect.left, wBounds.top +deepRect.top); |
gTheWindow = NewWindow(nil, &wBounds, (ConstStr255Param) "\pCustomPC/B", true, noGrowDocProc, (WindowPtr) -1, false, 1234); |
//SetPort(gTheWindow); |
SetPortWindowPort(gTheWindow); |
/* Create our pictures to group, then go into the work loop. This |
loop continually groups the pictures, draws the grouped picture |
in different locations, ungroups the picture, draws the ungrouped |
pictures in different locations and repeats until the user quits. */ |
MakeThePicts(); |
EventLoop(); |
} |
/*------ CompositePictures ----------------------------------------------------------------*/ |
// CompositePictures groups all of the pictures in the global picture record |
// into one "composite" picture. It removes all the old pictures and |
// stores the new one. |
/*----------------------------------------------------------------------------------------*/ |
/* ------------------------------------- |
main. |
------------------------------------- */ |
void main(void) |
{ |
unsigned long randSeed; |
Handle menuBar; |
/* Initialize the toolbox routines. */ |
//InitGraf(&qd.thePort); |
//InitFonts(); |
//InitWindows(); |
//InitMenus(); |
//InitDialogs(nil); |
InitCursor(); |
/* Set up our menubar. */ |
menuBar = GetNewMBar(rMenuBar); /* Read menus into menu bar */ |
SetMenuBar(menuBar); /* and install them. */ |
DisposeHandle(menuBar); |
//AppendResMenu(GetMenuHandle(mApple), 'DRVR'); |
DrawMenuBar(); |
/* Initialize the random number seed for our hopping groupies and set |
our quitting flag to false. Call the routine that runs everything, |
then, quit. */ |
GetDateTime(&randSeed); |
gQuitting = false; |
ShowThePicts(); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-10