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.
SimplePopupCDEF.c
/* |
File: SimplePopupCDEF.c |
Contains: A small sample of how to use the popupCDEF under System 7 |
Pretty straighforward |
A menu created with the popupCDEF is NOT always included in the |
menu list! The CDEF can (and will) remove it from the menu list |
when it needs to. That means that you CANNOT |
call GetMenuHandle(...) to get a handle to a popupCDEF menu handle. |
Look at the fuction here called GetPopUpMenuHandle to see how to do |
this the correct way. |
Written by: C.K. Haun |
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): |
8/9/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include "SimplePopupCDEF.h" |
#include <TextUtils.h> |
typedef struct popupPrivateData { |
MenuHandle mHandle; /* the popup menu handle */ |
short mID; /* the popup menu ID */ |
/* after these two public fields is the mPrivate private data, */ |
/* which may be any old size and should not be messed with */ |
}popupPrivateData; |
typedef popupPrivateData *popupPrivateDataPtr,**popupPrivateDataHdl; |
/* prototypes */ |
void InitalizeApp(void); |
void DoDiskEvents(long dinfo); /* hi word is error code, lo word is drive number */ |
void DrawMain(WindowPtr drawIt); |
Boolean DoSelected(long val); |
void SizeMain(WindowPtr theWindow); |
void InitAEStuff(void); |
void DoHighLevel(EventRecord *AERecord); |
void DoDaCall(MenuHandle themenu, long theit); |
void DoDocumentClick(WindowPtr theWindow); |
pascal OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn); |
pascal OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn); |
pascal OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn); |
pascal OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn); |
void SampleHelpDialog(void); |
WindowPtr AddNewWindow(short theID); |
MenuHandle GetPopUpMenuHandle(ControlHandle thisControl); |
void NilProc(void); |
/* one external */ |
extern void _DataInit(); /* this is the C initialization code */ |
/* globals */ |
Boolean gQuit, gInBackground; |
unsigned long gMySleep; |
ProcessSerialNumber gOurSN; |
short gHelpItem; |
#pragma segment Main |
void main() |
{ |
EventRecord myEventRecord; |
WindowPtr twindow; |
short fHit; |
windowCHandle tempWCH; |
// UnloadSeg((Ptr)_DataInit); /* throw out setup code */ |
InitalizeApp(); |
// UnloadSeg((Ptr)InitalizeApp); /* get rid of my initialization code */ |
do { |
WaitNextEvent(everyEvent, &myEventRecord, gMySleep, nil); |
switch (myEventRecord.what) { |
case nullEvent: |
/* no nul processing in this sample */ |
break; |
case updateEvt: |
/* always check to see if it's my window */ |
/* this may not seem necessary under 7.0, where it's unlikely or impossible for */ |
/* a DA to be in your layer, but there are others */ |
/* who can stick themselves into your window list, */ |
/* BalloonWriter comes quickly to mind */ |
tempWCH = (windowCHandle)GetWRefCon((WindowPtr)myEventRecord.message); |
(ProcPtr)((*tempWCH)->drawMe)((WindowPtr)myEventRecord.message); |
break; |
case mouseDown: |
/* first see where the hit was */ |
fHit = FindWindow(myEventRecord.where, &twindow); |
switch (fHit) { |
Rect limitRect; |
Str255 tempString; |
long back; |
case inDesk: /* if they hit in desk, then the process manager */ |
break; /* will switch us out, we don't need to do anything */ |
case inMenuBar: |
DoSelected(MenuSelect(myEventRecord.where)); |
break; |
case inSysWindow: |
/* pass to the system */ |
SystemClick(&myEventRecord, twindow); |
break; |
case inContent: |
/* Handle content and control clicks here */ |
if (FrontWindow()) { /* don't do this unless we have a window open, silly */ |
windowCHandle clicker; |
clicker = (windowCHandle)GetWRefCon(twindow); |
/* jump to the content function stored for this window */ |
HLock((Handle)clicker); /* lock it down so things don't get stupid */ |
(ProcPtr)((*clicker)->clickMe)(twindow); |
HUnlock((Handle)clicker); /* all done */ |
} |
break; |
case inDrag: |
DragWindow(twindow, myEventRecord.where, &qd.screenBits.bounds); |
break; |
case inGrow: |
/* Call GrowWindow here if you have a grow box */ |
SetPort(twindow); |
limitRect = qd.screenBits.bounds; |
limitRect.top = kMinHeight; |
GetWTitle(twindow, tempString); |
/* I'm not letting the user shrink the window so */ |
/* small that the title is truncated */ |
limitRect.left = StringWidth(tempString) + 120; |
back = GrowWindow(twindow,myEventRecord.where, &limitRect); |
if (back) {windowCHandle tempWCH = (windowCHandle)GetWRefCon(twindow); |
Rect sizeRect = ((WindowPtr)twindow)->portRect; |
InvalRect(&sizeRect); |
sizeRect.top = sizeRect.bottom - 16; |
sizeRect.left = sizeRect.right - 16; |
EraseRect(&sizeRect); |
InvalRect(&sizeRect); |
SizeWindow(twindow, back & 0xffff, back >> 16, true); |
(ProcPtr)((*tempWCH)->sizeMe)(twindow); |
} |
InvalRect(&twindow->portRect); |
break; |
case inGoAway: |
/* Click in Close box */ |
if (TrackGoAway(twindow, myEventRecord.where)) |
(ProcPtr)((*(windowCHandle)((WindowPeek)twindow)->refCon)->closeMe)(twindow); |
break; |
case inZoomIn: |
case inZoomOut: |
if (TrackBox(twindow, myEventRecord.where, fHit)) {windowCHandle tempWCH = (windowCHandle)GetWRefCon(twindow); |
SetPort(twindow); |
InvalRect(&twindow->portRect); |
ZoomWindow(twindow, fHit, true); |
(ProcPtr)((*tempWCH)->sizeMe)(twindow); |
} |
} |
case mouseUp: |
/* don't care */ |
break; |
/* same action for key or auto key */ |
case keyDown: |
case autoKey: |
if (myEventRecord.modifiers & cmdKey) |
DoSelected(MenuKey(myEventRecord.message & charCodeMask)); |
break; |
case keyUp: |
/* don't care */ |
break; |
case diskEvt: |
/* I don't do anything special for disk events, this just passes them */ |
/* to a function that checks for an error on the mount */ |
DoDiskEvents(myEventRecord.message); |
break; |
case activateEvt: |
if (myEventRecord.modifiers & activeFlag){ |
tempWCH = (windowCHandle)GetWRefCon((WindowPtr)myEventRecord.message); |
(ProcPtr)((*tempWCH)->drawMe)((WindowPtr)myEventRecord.message); |
} |
break; |
case networkEvt: |
/* don't care */ |
break; |
case driverEvt: |
/* don't care */ |
break; |
case app4Evt: |
switch ((myEventRecord.message >> 24) & 0x0FF) { /* high byte of message */ |
case suspendResumeMessage: /* suspend/resume is also an activate/deactivate */ |
gInBackground = (myEventRecord.message & kResumeMask) == 0; |
if(!gInBackground)InitCursor(); |
break; |
} |
break; |
default: |
break; |
/* This dispatches high level events (AppleEvents, for example) */ |
/* to our dispatch routine. This is NEW in the event loop for */ |
/* System 7 */ |
case kHighLevelEvent: |
DoHighLevel(&myEventRecord); |
break; |
} |
} |
while (gQuit != true); |
} |
/* DoDaCall opens the requested DA. It's here as a seperate routine if you'd */ |
/* like to perform some action or just know when a DA is opened in your */ |
/* layer. Can be handy to track memory problems when a DA is opened */ |
/* with an Option-open */ |
void DoDaCall(MenuHandle themenu, long theit) |
{ |
long qq; |
Str255 DAname; |
GetMenuItemText(themenu, theit, DAname); |
qq = OpenDeskAcc(DAname); |
} |
/* end DoDaCall */ |
/* DoDiskEvents just checks the error code from the disk mount, */ |
/* and puts up the 'Format' dialog (through DIBadMount) if need be */ |
/* You can do much more here if you care about what disks are */ |
/* in the drive */ |
void DoDiskEvents(long dinfo) /* hi word is error code, lo word is drive number */ |
{ |
short hival, loval, tommy; |
Point fredpoint = { |
40, 40 |
}; |
hival = HiWord(dinfo); |
loval = LoWord(dinfo); |
if (hival != noErr) /* something happened */ { |
tommy = DIBadMount(fredpoint, dinfo); |
} |
} |
/* draws my window. Pretty simple */ |
void DrawMain(WindowPtr drawIt) |
{ |
RgnHandle tempRgn; |
Rect scratchRect; |
BeginUpdate(drawIt); |
SetPort(drawIt); |
EraseRect(&drawIt->portRect); |
scratchRect = drawIt->portRect; |
scratchRect.top = scratchRect.bottom - 15; |
scratchRect.left = scratchRect.right - 15; |
tempRgn = NewRgn(); |
GetClip(tempRgn); |
ClipRect(&scratchRect); |
DrawGrowIcon(drawIt); |
SetClip(tempRgn); |
DrawControls(drawIt); |
DisposeRgn(tempRgn); |
EndUpdate(drawIt); |
} |
/* my menu action taker. It returns a Boolean which I usually ignore, but it */ |
/* mught be handy someday */ |
/* I usually use it in an application to determine if a keystroke was accepted */ |
/* by a menu or whether it should be passed along to any other key acceptors */ |
Boolean DoSelected(long val) |
{ |
short loval, hival; |
Boolean returnVal = false; |
loval = LoWord(val); |
hival = HiWord(val); |
switch (hival) { /* switch off the menu number selected */ |
case kAppleMenu: /* Apple menu */ |
if (loval != 1) { /* if this was not About, it's a DA */ |
DoDaCall(GetMenuHandle(kAppleMenu), loval); |
} else { |
Alert(kAboutBox, nil); /* do about box */ |
} |
returnVal = true; |
break; |
case kFileMenu: /* File menu */ |
switch (loval) { |
case kQuitItem: |
gQuit = true; /* only item */ |
returnVal = true; |
break; |
default: |
break; |
} |
break; |
case kEditMenu: |
/* edit menu junk */ |
/* don't care */ |
switch(loval){ |
default: |
break;} |
break; |
case kToolsMenu: |
/* add all your test stuff here */ |
switch(loval){ |
default: |
break;} |
break; |
case kHMHelpMenuID: /* Defined in Balloons.h */ |
/* I only care about this item. If anything else is returned here, I don't know what */ |
/* it is, so I leave it alone. Remember, the Help Manager chapter says that */ |
/* Apple reserves the right to add and change things in the Help menu */ |
if (loval == gHelpItem) |
SampleHelpDialog(); |
break; |
} |
HiliteMenu(0); |
return(returnVal); |
} |
void DoDocumentClick(WindowPtr theWindow) |
{ |
Point thePoint; |
ControlHandle theControl; |
GetMouse(&thePoint); |
if(FindControl(thePoint, theWindow, &theControl)){ |
/* ¥¥¥ NOTE! */ |
/* You MUST pass '(ProcPtr) -1' to TrackControl to have the */ |
/* popupCDEF menus automatically popped and tracked for you! */ |
if(TrackControl(theControl, thePoint,(ControlActionUPP) -1)){ |
Str255 myString; |
MenuHandle selectedMenu; |
Rect clearIt; |
SetRect(&clearIt,0,0,theWindow->portRect.right,20); |
EraseRect(&clearIt); |
/* if you selected something, I'll tell you about it */ |
MoveTo(5,16); |
GetIndString(myString,kGeneralStrings,kSayLastItem); |
DrawString(myString); |
selectedMenu = GetPopUpMenuHandle(theControl); |
GetMenuItemText(selectedMenu, GetControlValue(theControl),myString); |
DrawString(myString); |
} |
} |
} |
/* InitAEStuff installs my appleevent handlers */ |
void InitAEStuff(void) |
{ |
OSErr aevtErr = noErr; |
long aLong = 0; |
Boolean gHasAppleEvents = false; |
/* Check this machine for AppleEvents. If they are not here (ie not 7.0) |
* then we exit */ |
gHasAppleEvents = (Gestalt(gestaltAppleEventsAttr, &aLong) == noErr); |
/* The following series of calls installs all our AppleEvent Handlers. |
* These handlers are added to the application event handler list that |
* the AppleEvent manager maintains. So, whenever an AppleEvent happens |
* and we call AEProcessEvent, the AppleEvent manager will check our |
* list of handlers and dispatch to it if there is one. |
*/ |
if (gHasAppleEvents) { |
aevtErr = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, |
NewAEEventHandlerProc(AEOpenHandler),0, false); |
if (aevtErr) ExitToShell(); |
aevtErr = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, |
NewAEEventHandlerProc(AEOpenDocHandler),0, false); |
if (aevtErr) ExitToShell(); |
aevtErr = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, |
NewAEEventHandlerProc(AEQuitHandler), 0, false); |
if (aevtErr) ExitToShell(); |
aevtErr = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, |
NewAEEventHandlerProc(AEPrintHandler),0, false); |
if (aevtErr) ExitToShell(); |
} |
else ExitToShell(); |
} |
/* end InitAEStuff */ |
/* I'm not doing error handling in this sample for clarities sake, you should. Hah, */ |
/* easy for me to say, huh? */ |
void DoHighLevel(EventRecord *AERecord) |
{OSErr myErr; |
myErr=AEProcessAppleEvent(AERecord); |
} |
/* end DoHighLevel */ |
/* This is the standard Open Application event. */ |
pascal OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn) |
{WindowPtr myWindow; |
#pragma unused (messagein,reply,refIn) |
/* we of course don't do anything here in this simple app */ |
/* except open our window */ |
myWindow = AddNewWindow(kDocWindowResID); |
return(noErr); |
} |
/* end AEOpenHandler */ |
/* Open Doc, opens our documents. Remember, this can happen at application start AND */ |
/* anytime else. If your app is up and running and the user goes to the desktop, hilites one */ |
/* of your files, and double-clicks or selects Open from the finder File menu this event */ |
/* handler will get called. Which means you don't do any initialization of globals here, or */ |
/* anything else except open then doc. */ |
/* SO-- Do NOT assume that you are at app start time in this */ |
/* routine, or bad things will surely happen to you. */ |
pascal OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn) |
{ |
#pragma unused (messagein,refIn,reply) |
/* we of course don't do anything here */ |
return(errAEEventNotHandled); /* we have no docs, so no odoc events should come to us */ |
} |
pascal OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn) |
{ /* no printing handler in yet, so we'll ignore this */ |
/* the operation is functionally identical to the ODOC event, with the additon */ |
/* of calling your print routine. */ |
#pragma unused (messagein,refIn,reply) |
/* we of course don't do anything here */ |
return(errAEEventNotHandled); /* we have no docs, so no pdoc events should come to us */ |
} |
/* Standard Quit event handler, to handle a Quit event from the Finder, for example. */ |
/* ¥¥¥¥¥ DO NOT CALL EXITTOSHELL HERE ¥¥¥¥¥ or you will never have a happy life. */ |
/* OK, it's a few months after I wrote that comment, and I've seen a lot of code */ |
/* come through DTS that calls ExitToShell from quit handlers. Let me explain... */ |
/* When an AppleEvent Handler is called (like this quit handler) you are ALMOST */ |
/* 100% in your application world. A5 is right, you can call any toolbox function, */ |
/* you can call your own routines, everything _seems_ like you are in complete */ |
/* control. Well, almost but not quite. The routine has been dispatch to from the */ |
/* AppleEvent Manager's space, so you _must_ return to that at some point! */ |
/* Which is why you can't call ETS from here. When you call ExitToShell from an */ |
/* AE Handler, the most likely thing that happens is the FInder quits, and your */ |
/* application keeps running. Which ain't what you want, y'know? */ |
/* so, DON'T CALL EXITTOSHELL FROM AN APPLEEVENT HANDLER!!!!!!!!!!!!!! */ |
pascal OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn) |
{ |
#pragma unused (messagein,refIn,reply) |
gQuit = true; |
return(noErr); |
} |
/* This is my sample help dialog. Does not do anything, expand as you need */ |
void SampleHelpDialog(void) |
{ |
DialogPtr tdial = GetNewDialog(kSampHelp, nil, (WindowPtr)-1); |
short itemhit = 0; |
while (itemhit != 1) { |
ModalDialog(nil, &itemhit); |
} |
DisposeDialog(tdial); |
} |
#pragma segment Initialize |
void InitalizeApp(void) |
{ |
Handle myMenu; |
MenuHandle helpHandle,appleMenuHandle; |
StringHandle helpString; |
short count; |
long vers; |
MaxApplZone(); |
InitGraf(&qd.thePort); |
InitFonts(); |
InitWindows(); |
InitMenus(); |
TEInit(); |
InitDialogs(nil); |
InitCursor(); |
/* Check system version */ |
Gestalt(gestaltSystemVersion, &vers); |
vers = (vers >> 8) & 0xf; /* shift result over and mask out major version number */ |
if (vers < 7) { |
StopAlert(kBadSystem, nil); |
ExitToShell(); |
} |
InitAEStuff(); |
/* set up my menu junk */ |
myMenu = GetNewMBar(kMBarID); |
SetMenuBar(myMenu); |
appleMenuHandle = GetMenuHandle(kAppleMenu); |
AppendResMenu(appleMenuHandle, 'DRVR'); |
/* now install my Help menu item in the Help Manager's menu */ |
HMGetHelpMenuHandle(&helpHandle); /* Get the Hlpe menu handle */ |
count = CountMItems(helpHandle); /* How many items are there? */ |
helpString = GetString(kHelpString); /* get my help string */ |
DetachResource((Handle)helpString); /* detach it */ |
HNoPurge((Handle)helpString); |
MoveHHi((Handle)helpString); |
HLock((Handle)helpString); |
InsertMenuItem(helpHandle, *helpString, count + 1); /* insert my item in the Help menu */ |
gHelpItem = CountMItems(helpHandle); /* The number of the item */ |
DrawMenuBar(); |
GetCurrentProcess(&gOurSN); /* Get our process serial number for later use, if needed */ |
} |
#pragma segment Main |
WindowPtr AddNewWindow(short theID) |
{ |
windowCHandle setControls; |
WindowPtr tempWP; |
short cnt = 0; |
tempWP = GetNewWindow(theID, 0, (WindowPtr)-1); /* get a new window */ |
SetPort(tempWP); |
((WindowPeek)tempWP)->windowKind = kMyDocumentWindow; /* mark it as my document window */ |
setControls = (windowCHandle)NewHandleClear(sizeof(windowControl)); /* add our control structure to it */ |
SetWRefCon(tempWP,(long)setControls); /* stop stuffing refCon directly <ckh 1.0.3> */ |
HLock((Handle)setControls); /* lock it down while we fill it*/ |
/* add pointers to our procedures for drawing, saving, and closing */ |
/* This way, all I need is one dispatch point for drawing, closing */ |
/* or whatever, I don't have to case off the window kind to go to the */ |
/* correct routine. Kinda like object-oriented programming, but I won't */ |
/* admit that. */ |
(*setControls)->drawMe = (ProcPtr)DrawMain; |
(*setControls)->clickMe = (ProcPtr)DoDocumentClick; |
(*setControls)->sizeMe = (ProcPtr)SizeMain; |
(*setControls)->generalData = NewHandle(0); |
GetNewControl(128,tempWP); |
GetNewControl(129,tempWP); |
GetNewControl(130,tempWP); |
GetNewControl(131,tempWP); |
return(tempWP); |
} |
void SizeMain(WindowPtr theWindow) |
{ |
WindowPtr tempWP; |
GetPort(&tempWP); |
InvalRect(&theWindow->portRect); |
SetPort(tempWP); |
} |
void NilProc(void) |
{ |
} |
/* Since we can't call GetMenuHandle to access the menu handle, we have to */ |
/* look at the data stored in the control record */ |
MenuHandle GetPopUpMenuHandle(ControlHandle thisControl) |
{ |
popupPrivateDataHdl theMenuData = (popupPrivateDataHdl)(*thisControl)->contrlData; |
return((*theMenuData)->mHandle); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30