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);
}