TweetFollow Us on Twitter

Pop-up CDEF
Volume Number:4
Issue Number:9
Column Tag:Programmer's Workshop

Pop-up Menus CDEF

By James Plamondon, Berkeley, CA

Pop-up Menu

Control Definition Function

by James Plamondon

[James Plamondon has a BS in Computer Science from the University of New Mexico, in addition to a BS and Master's work in Geology. He has programmed professionally on the Mac for one year, and about two years before. He is currently working at Abacus Concepts, makers of StatView.]

The Challenge

I like a challenge as much as the next Guy. Like Sir Edmund Hillary, I sometimes accept challenges just because they’re there. But it’s always satisfying when I can do some programming which is fun for me, and beneficial to my employer as well.

In Inside Macintosh, Volume Five, on page 242, I found my most recent Everest. That page discusses how to use the new Toolbox function PopUpMenuSelect(). Near the end of the page is the brief notation: “[using PopUpMenuSelect()] could be handled by creating a pop-up menu control within the application.”

That sounded useful, so I called Mac Developer Services (on AppleLink) and asked for a copy of the pop-up menu control’s definition function To my surprise, it didn’t exist -- or at least, they didn’t have one.

There was the challenge. With a pop-up menu Control Definition Function (CDEF), pop-up menus could be used as easily as radio buttons or scroll bars. The application that I was writing for my employer at the time would also make use of pop-up menus, so I could even justify the time I spent on it! What a deal!

Why Use a CDEF?

Have you ever written code to draw a pushbutton -- such as the OK or Cancel buttons in a dialog? Have you ever written code to draw a scroll bar, hilite an up-arrow, or draw a thumb region being dragged?

Probably not. All of those operations are performed by Apple’s standard CDEFs. Because Apple includes these in all of its System files, you never have to worry about drawing or manipulating these controls. The CDEFs do all of the work, without your even paying attention to it.

What would happen if Apple didn’t provide these standard CDEFs? Every button would look and feel different. (Did I say “look and feel?” Sounds like a lawyer!) Imagine how different Mac software would be today if all pushbuttons, radio buttons, checkboxes, and scroll bars behaved differently. (And think how different MS-Windows would be!) The standardization of such controls greatly enhanced the Mac’s ease of use, both for the user and the programmer. All this, because of the much-neglected CDEF.

The pop-up menu CDEF described herein extends that standardization to pop-up menus, by making pop-up menus trivial to program, just as buttons and checkboxes are now. Just pop the CDEF into your program’s resource fork (or your System file), follow a few simple guidelines, and your pop-up menus will look as good as -- better than! -- Apple’s own.

The Language

The pop-up menu CDEF was written in MPW Pascal. It is about 4K in size, which is pretty big, for a CDEF. It would be alot smaller in Assembler; even C would probably produce a smaller CDEF. But everybody knows Pascal, and my employer uses Pascal, so it’s in Pascal.

CDEFs

CDEFs are described in loving detail on pages 328-332 of Inside Mac, Volume One. They are code resources of type ‘CDEF’, which are loaded into memory whenever a control using the CDEF is created. Like other _DEFs (MDEFs, LDEFs, etc.), the code resource has a single entry point at its first byte (i.e., no jump table). The entry point must be a function with the following definition:

FUNCTION MyControl(varCode: INTEGER;
 theControl: ControlHandle;
 message: INTEGER;
 param: LONGINT): LONGINT;

The function and argument names may be changed, of course, but their order and type must be as shown here (from IM v1 p329).

It is assumed (although not required) that there will be a separate message-handling routine for each message; all MyControl() needs to contain is a CASE statement, calling the different message-handling routines to handle the different messages. That is the organization this CDEF uses.

CDEF Messages

There are nine different messages a CDEF may receive, numbered 0 to 8. The first three, drawCntl, testCntl, and calcCRgns, must be handled by all CDEFs. When the CDEF gets a drawCntl message, it needs to draw the control. The ‘param’ argument will contain the part code of the part that needs to be redrawn, or 0 if the whole control is to be redrawn. You don’t need to draw anything if the control is invisible (which you can determine by looking at the control’s contrlVis field). If the control is inactive (contrlHilite = 255), then you need to draw the control differently (preferably by ‘greying it out’), to tell the user that it’s inactive.

The testCntl message is sent to find out which part of the control (if any) the given point is in. The ‘given point’ is passed in the ‘param’ LONGINT argument, in local coordinates. The part code of the control part in which the point lies should be returned by MyControl() -- or return 0 if the point is not in the control. This is the only message for which the pop-up menu CDEF returns a meaningful function result; for all other messages, it just returns 0.

The next two CDEF messages, initCntl and dispCntl, need only be implemented by the CDEF if each control handled by the CDEF needs to be initialized in some way, and then have its initialization voided when the control is disposed of. In this CDEF, for example, each control is allocated a memory block from the heap to hold color information and a handle to its pop-up menu. This storage is deallocated when the control is disposed of.

The next three CDEF messages, posCntl, thumbCntl, and dragCntl, need to be implemented only if your control has an indicator region (like the thumb of a scroll bar) that can be repositioned by the user. The pop-up menu CDEF has no such indicator, so when the CDEF receives these messages, it just returns without doing anything.

The last CDEF message, autoTrack, if this CDEF’s bread and butter. The autoTrack message is sent to the CDEF whenever the user clicks inside an active control. The ‘param’ field will contain the part code in which the mouse lies. The pop-up menu CDEF has only one part -- the pop-up menu box, part code 1 -- so it doesn’t even check the part code. It just calls PopUpMenuSelect(), updates the control’s data to reflect the user’s choice, and redraws the control with the newly-selected item in the pop-up box.

So, the CDEF does all the work. How, then, can we associate a control with this pop-up menu CDEF? By using the proper control procID, that’s how.

procIDs

Which CDEF a control uses is specified by the procID field of the control specification, whether in a ‘CNTL’ resource or (yuck!) in a NewControl() call. The procID contains both the resource ID of the CDEF to use for the control, and also a ‘variation code’ specifying what sub-type of control it is. The CDEF’s resource ID goes into the high three nibbles of the procID, while the variation code goes into the low nibble (a nibble is half a byte - really!):

Pushbuttons, checkboxes, and radio buttons all use the CDEF with resource ID zero (0). They have variation codes 0, 1, and 2, respectively. Since the high three nibbles hold 0 (the resource ID), the procID for these controls is the same as their variation code.

Scroll bars use CDEF 1, and have variation code 0. The resource ID (1) goes into bit 4, zero goes into the low nibble, and voila! you get a procID of 16, just like in the manual (IM v1 p315).

Our pop-up menu CDEF has resource ID 3, so the procID will be $0030 + x (that’s in hex; it’s 48 + x in decimal), where ‘x’ is the variation code (each hex letter represents a nibble). The pop-up menu CDEF doesn’t use variation codes, exactly; it uses variation code modifiers. This is because I wanted the user to be able to mix and match certain features of the CDEF. So, I use each of the four bits of the CDEFs variation code nibble as a flag for one of these features. The features, their bit positions, and their constants are:

Unused uses Bit 0 mUnused = 1

Resource list uses Bit 1 mRes = 2

Check item uses Bit 2 mCheck = 4

Command keys uses Bit 3 mKey = 8

With this system, assuming that popMenuProc = $30, you can specify a pop-up menu containing a list of resource names with the procID popMenuProc + mRes ($32). If you want the same pop-up menu, with the current item checked, you would use the procID popMenuProc + mRes + mCheck ($36). The standard pop-up menu control, without resource names, or checked items, would have the procID popMenuProc ($30).

Basic CDEF

According to the pop-up menu use guidelines (IM v5 p241-242), a pop-up menu should be inserted into the menu list just before PopUpMenuSelect() is called, and removed from the list right after the function returns. This is a pain in the behind, so I don’t do it that way. The pop-up menu and it’s sub-menus are inserted into the menu list when the control is defined, and removed when the control is disposed of. The guidelines say I can do this, if I really want to; so, I did.

mUnused

You can use this variation code modifier for whatever you want!

mRes

If you want the CDEF to find and install a list of resource names in the pop-up menu, you need to add mRes to the procID. This makes the CDEF look in the refCon field of the control, where it expects to find an OSType (such as ‘FONT’ or ‘DRVR’). After initialization, the refCon field is no longer used by the CDEF, so you get it back to use it however you want.

mCheck

If you want the currently-selected menu item to be checked, add mCheck to the procID. You must also set the check mark of the default menu item to the character you want to use as a check mark. If the default item is unmarked, a standard ‘’ mark will be used.

mKey

mKey is reserved for future expansion. More on that later.

The Pop-Up Menu CDEF

The CDEF was written to implement standard, resource name list, checked item, and sub-list pop-up menus (and combinations thereof). If color resources are available, and the CDEF is running on a color system, the menus and their controls will be displayed in living color. At initialization, the CDEF calls SysEnvirons(), and stores the hasColorQD field. Whenever drawing is to be done, this Boolean is checked. If true, the colors of the current menu and/or menu item are retrieved (from the menu color information table) and the control is drawn using those colors. Otherwise, you get black and white (sigh).

Note that a pop-up menu control that uses sub-menus will draw the control (menu) title and pop-up box in the mTitle color of the currently selected menu (or sub-menu). You can see this in the example program in the ‘Root’ menu. The currently-selected item will be drawn using that item’s colors, which may be different for each item in a menu. This is demonstrated in the “Thanks To” menu. Make sure that each menu title color has good contrast against its window’s content color, or the title will be hard to read.

The CDEF draws the title of the pop-up menu as the control’s title, left-justified in the control’s boundsRect. The CDEF draws the pop-up menu box to the left of the control title. Note that all of the examples’ pop-up menu titles have an extra blank space at the end of the title; this just makes the whole control look better.

The CDEF tries to make the pop-up box as wide as the menu it is controlling. If the menu is wider than the boundsRect will allow, then the pop-up box is clipped to the boundsRect. Menu items that are too long to fit in the clipped box are themselves clipped; an ellipsis is appended to such items to inform the user than text is missing. You can see this clipping by selecting ‘Monty “Montana-Unit” Cole’ in the “Thanks To” menu. Monty’s name is the widest item in the menu, and so it defines the width of the menu. Since the contrlRect is narrower than the menu (see that, when the menu pops up, it covers part of the OK button?), it is narrower than Monty’s name, too, so Monty’s name must be clipped. (Sorry, Monty.)

With the CDEF, a mouse-click in the pop-up box will always be inside the default item (as the guidelines require). The user will never click in a pop-up box, just to have the pop-up menu appear to the left of the mouse -- as might happen if the pop-up box were set to some “average” menu width, when the menu width might change (as with a font menu). When in doubt as to how wide to make the boundsRect, make it wider. Use ResEdit; it will call the CDEF to draw the control, and you can drag its image around. Neat!

The only way to make the left edges of pop-up boxes line up with other pop-up boxes is to leave the controls untitled, using staticText items as titles. The only way to make the right-hand edges of the pop-up boxes line up is to make the boundsRect of each of the controls no wider than that required by the narrowest control.

The Example Program

The example program is a very stripped Mac application. It simply initializes all of the usual managers, gets a dialog containing two buttons, a static text item, and three pop-up menus, and displays it. Notice that no filterProc is passed to ModalDialog(), and yet the pop-up menus get drawn, put up their menus where selected, and so on. The CDEF is doing all of the work!

After the example program exits the ModalDialog() loop, it simply disposes of the dialog and quits. In a real program, you’d get the values of the last selection from each pop-up menu control by calling GetCtlMax() to get the menuID of the menu from which the last selection was made and GetCtlMin() to get the item number of the last selection in that menu.

Using Pop-Up Menu Controls

To use a pop-up menu control in your program, you need to place the CDEF into your application resource file. You should also create two resources for each control: a ‘MENU’ resource, defining the menu to be popped-up (and further ‘MENU’ resources for its sub-menus, if any), and a ‘CNTL’ resource, defining the pop-up menu control. The pop-up ‘MENU’ resource is exactly like a regular ‘MENU’ resource, and its fields are all interpreted in the usual way. (See “Command-Key Equivalents” below for one possible exception: the menu title.) You can use regular ‘mctb’ resources to color your menus and their controls.

The pop-up menu’s ‘CNTL’ resource has the same fields as all other ‘CNTL’ resources, but a number of them are interpreted and used differently. We’ll go over these fields one at a time.

BoundsRect: The first field is an array of four integers, which define the rectangle bounding the control. If the control is to be used in a dialog, be sure to make the control’s boundsRect agree with the item rect in the dialog item list. Otherwise, everything gets confused.

Value: The control’s initial value must be set to the resource ID of the menu to be popped-up. The menu’s menuID must match its resource ID.

Visible: Same as always.

Max: Initially, this field must contain the menuID of the menu containing the default item. If the pop-up menu has sub-menus, this value may be different from the pop-up menu’s menuID. After the user has made a selection, this field will contain the menuID of the menu from which the item was chosen.

Min: Initially, this field must contain the item number of the default item. After the user has made a selection, this field will contain the item number of the chosen item.

ProcID: As discussed above, the procID field will contain popMenuProc plus some combination of mUnused, mRes, and mCheck (mKey will be discussed below).

RefCon: If the procID includes the mRes modifier, then this field must contain the OSType of the resource to be listed in the menu. ( See ‘CNTL’ 129 for an example.)

Title: Surprisingly enough, this is the title of the control.

There are two other fields of importance. These are fields of the ControlRecord allocated from the ‘CNTL’ resource. First is the contrlData field, which the CDEF uses to store control-specific information. Do not mess around with this field. The other field of note, contrlAction, contains (-1); this means that the CDEF has a default action procedure. (See IM v1 p323-324; also, v1 p328-332.)

Command-Key Equivalents

DO NOT USE COMMAND-KEY EQUIVALENTS IN YOUR POP-UP MENUS. They will not be recognized, and will only confuse the user. I think I know how to make command-key equivalents work in pop-up menu controls, but I didn’t try to implement the scheme.

I didn’t make command-key equivalents work for a number of reasons. First, command-key equivalents don’t really fit into the user interface, with regard to pop-up menus. Second, the implementation would require patching MenuKey() with GetTrapAddress() and SetTrapAddress(). Apple has posted signs all over these routines saying “future compatibility not guaranteed.” Patching them would be like driving the family car beyond the “End of County-Maintained Road” sign. Third, it would be a lot of work.

The mKey variation code modifier has been reserved for the hardy soul who ventures to implement them. If you manage to make it fly, I’d love to hear from you. You can reach me at Abacus Concepts, 1984 Bonita Avenue, Berkeley, CA 94704, (415) 540-1949.

Miscellaneous Notes

There are a few other points that need to be mentioned before you dive into the code. First, again, the resource ID and menu ID of the pop-up menu and each of its sub-menus must match.

Second, if you use sub-menus, make sure that the default menu item is not the parent of a sub-menu. Also, be sure not to use circular references in your sub-menus.

Third, don’t specify a ‘cctb’ resource for the pop-up menu control, because it will just get ignored. The control uses the menu color information table to color the control and its menus. Use ‘mctb’ resources to color your menus and their controls. Note that the pop-up control title is in essence the title of the pop-up menu, and is colored accordingly. Also note that the window’s content color is used instead of the menu bar color for the title’s background color.

Just before this article went to press, I added some code to allow the use of this CDEF with menu types other than the standard textMenuProc. The code requires that the MDEF defining the menu respond to two additional messages: the mItemRectMsg (512), and the mDrawItemMsg (513).

When a MDEF receives a mItemRectMsg, it should return the dimensions the given menu item in the given rectangle, with the topLeft point at (0,0) and the botRight point at (width, height).

When a MDEF receives a mDrawItemMsg, it should draw the given menu item in the given rectangle, clipped or scaled as needed.

I have not tested this code with as many custom MDEFs as I would like, but it works fine with those I’ve tested. It really should lock the MDEF before calling it, and restore its lock state after the MDEF returns, though. I’ll save that for version 2.0 ($300 upgrade!).

Acknowledgements

A number of people contributed to this project, and I would like to thank them: Dan, Jim, and Will at Abacus, for employing me; Bryan Stearns, for his text-clipping algorithm; my wife, for putting up with my 16-hour days; Mark Williams, who let me borrow his color Mac II; and most of all, Mark Bennet at Apple’s Mac Developer Technical Support, for his efforts above and beyond the call, even after 9pm, on a busy day, and with a nasty Spring cold. It’s people like him that make Apple special. Hip-Hip, Hooray!

#   File:       PopMenus.make
#   Target:     PopMenus
#   Sources:    PopMenus.p PopMenus.r
#   Created:    Thursday, April 14, 1988 3:21:49 AM

 
PopMenusƒƒPopMenus.r 
 PopMenuCDEF.r   
 PopMenuCDEF.CDEF
 Rez -rd PopMenus.r -o PopMenus

PopMenusƒƒPopMenus.p.o    
 PopMenuCDEF.CDEF
 PopMenus.r
Link  PopMenus.p.o 
 “{Libraries}”Runtime.o   
 “{PLibraries}”Paslib.o   
 -o PopMenus

PopMenuCDEF.CDEF ƒƒ PopMenuCDEF.p.o
 Link -sg PopMenuCDEF     
  -rt CDEF=1     
  -m MYCONTROL PopMenuCDEF.p.o
 “{Libraries}”Interface.o 
 “{PLibraries}”Paslib.o   
 -o PopMenuCDEF.CDEF

PopMenus.p.oƒ  PopMenus.p
 Pascal PopMenus.p -o PopMenus.p.o

PopMenuCDEF.p.o  ƒ PopMenuCDEF.p
 Pascal PopMenuCDEF.p -o PopMenuCDEF.p.o

# ###########################
# END OF FILE:  PopMenus.make #
# ###########################

(*****************************************************
Pop-up Menu Example Program
*****************************************************)

PROGRAM PopMenus;

USES MemTypes,
 Quickdraw,
 OSIntf,
 ToolIntf,
 PackIntf;

CONST
 myDLOGid =  128;{ DLOG resource ID}
 
 Activate = 2; { Activate button }
 PopMenu1 = 3; { popMenu control 1 }
 PopMenu2 = 4; { popMenu control 2 }
 PopMenu3 = 5; { popMenu control 3 }
 
 ON=  0;{ for HiliteControl }
 OFF    =  255;  { for HiliteControl }


VAR
 dPtr:  DialogPtr; { our test dialog }
 itemHit: INTEGER; { user’s choice }
 state: INTEGER; { ON or OFF}

(*****************************************************
doHitActivate: Toggles the activation state of the
 popMenu control.  Also toggles the Activate
 button’s title (Activate <=> Deactivate).
*****************************************************)

PROCEDURE doHitActivate(dPtr: DialogPtr;
 VAR state: INTEGER);
VAR
 ik:  INTEGER;
 ih:  Handle;
 ib:  Rect;

BEGIN
 { get handle to Activate button }
 GetDItem(dPtr, Activate, ik, ih, ib);
 
 { toggle state variable and button title }
 if (state = ON) then
 begin
 state := OFF;
 SetCTitle(ControlHandle(ih), ‘Activate’);
 end
 else begin
 state := ON;
 SetCTitle(ControlHandle(ih), ‘Deactivate’);
 end;
 
 { toggle the popMenu controls’ activation states }
 GetDItem(dPtr, PopMenu1, ik, ih, ib);
 HiliteControl(ControlHandle(ih), state);
 
 GetDItem(dPtr, PopMenu2, ik, ih, ib);
 HiliteControl(ControlHandle(ih), state);
 
 GetDItem(dPtr, PopMenu3, ik, ih, ib);
 HiliteControl(ControlHandle(ih), state);
END;  { doHitActivate }

(*****************************************************
Main
*****************************************************)

BEGIN
 { perform the ritual incantation }
 InitGraf(@thePort); 
 InitFonts; 
 FlushEvents(everyEvent, 0);
 InitWindows;    
 InitMenus; 
 TEInit;
 InitDialogs(NIL); 
 InitCursor;

 { read in the dialog from its resource template }
 dPtr := GetNewDialog(myDLOGid, NIL, POINTER(-1));
 
 { cycle through ModalDialog() until itemHit = OK }
 REPEAT
 ModalDialog(NIL, itemHit);
 
 { if the user hit the activate button, toggle }
 IF (itemHit = Activate) THEN BEGIN
 doHitActivate(dPtr, state);
 END;
 UNTIL itemHit = OK;
 
 DisposDialog(dPtr);
END.  { program PopMenus }

(*****************************************************
END OF FILE:  PopMenus.p
*****************************************************)

/*****************************************************
Pop-up Menu Example - Resources
*****************************************************/

#include “Types.r”
#include “PopMenuCDEF.r”

/* 1 = use color resources; 0 = don’t */
#define COLOR  1


/* include the CDEF */
data ‘CDEF’ (pmCDEFResID, “popMenu”) {
 $$resource(“PopMenuCDEF.CDEF”, ‘CDEF’, 1)
};

/* our sample dialog */
resource ‘DLOG’ (128) {
 {40, 70, 170, 440}, /* 130 tall, 370 wide */
 dBoxProc,
 visible,
 noGoAway,
 0x0,
 128,
 “”
};

/* the sample dialog’s item list: 3 pop-up menu ctls */
resource ‘DITL’ (128) {
 { /* array DITLarray: 4 elements */
 /* [1] */
 {10, 275, 30, 360},
 Button {
 enabled,
 “OK”
 },
 
 /* [2] */
 {40, 275, 60, 360},
 Button {
 enabled,
 “Deactivate”
 },
 
 /* [3] */
 {10, 10, 30, 270},
 Control {
 enabled,
 128
 },
 
 /* [4] */
 {40, 10, 60, 270},
 Control {
 enabled,
 129
 },
 
 /* [5] */
 {70, 10, 90, 270},
 Control {
 enabled,
 130
 },
 
 /* [6] */
 {90, 10, 125, 360},
 StaticText {
 disabled,
 “PopUp Menu Control CDEF example.  “
 “James Plamondon,  Abacus Concepts,  “
 “(415) 540-1949.”
 }
 }
};

resource ‘CNTL’ (128) {
 {10, 10, 30, 270},/* rect:  contrlRect*/
 128,   /* value: menu rsrc ID   */
 visible, /* vis:  standard */
 128,   /* max:  default menuID  */
 2,/* min:  default item #*/
 popMenuProc   /* ProcID:  3  */
 + mCheck,/* var: Check selection  */
 0,/* rfCon: for user’s use */
 “Thanks To: “   /* title: standard*/
};

resource ‘MENU’ (128) {
 128,
 textMenuProc,
 allEnabled,
 enabled,
 “Thanks To: “,
 { /* 11 items */
 “Mark Williams”,
 noIcon, noKey, noMark, plain;
 “Mark Bennet”,
 noIcon, noKey, appleChar, plain;
 “Joseph Daniel”,
 noIcon, noKey, noMark, plain;
 “Dr. Don Morrison”,
 noIcon, noKey, noMark, plain;
 “Andrew Stone”,
 noIcon, noKey, noMark, plain;
 “Eleanor Plamondon”,
 noIcon, noKey, noMark, plain;
 “Bruce Wampler”,
 noIcon, noKey, noMark, plain;
 “Patricia Guffey”,
 noIcon, noKey, noMark, plain;
 “Greta Shaw”,
 noIcon, noKey, noMark, plain;
 “Monty \”Montana-Unit\” Cole”,
 noIcon, noKey, noMark, plain;
 “Dr. Bernard Moret”,
 noIcon, noKey, noMark, plain
 }
};

resource ‘CNTL’ (129) {
 {40, 10, 60, 270},/* rect:  contrlRect*/
 129,   /* value: rsrc ID */
 visible, /* vis:  standard */
 129,   /* max: default  menuID  */
 2,/* min:  default item #*/
 popMenuProc   /* ProcID: 3 */
 + mRes /* var: add res names */
 + mCheck,/* var: Check selection  */
 ‘FONT’,/* rfCon: OSType  */
 “Fonts: “/* title: standard*/
};

resource ‘MENU’ (129) {
 129,
 textMenuProc,
 allEnabled,
 enabled,
 “Fonts: “,
 { /* 0 items */
 }
};

resource ‘CNTL’ (130) {
 {70, 10, 90, 270},/* rect:  contrlRect*/
 130,   /* value: rsrc ID */
 visible, /* vis:  standard */
 133,   /* max:  default menuID  */
 1,/* min: default  item #*/
 popMenuProc/* ProcID: 3  */
 + mCheck,/* var: Check selection  */
 0,/* rfCon: for user’s use */
 “Root: “ /* title: standard*/
};

resource ‘MENU’ (130) {
 130,
 textMenuProc,
 allEnabled,
 enabled,
 “Root: “,
 { /* 2 items */
 “Root Item1”,
 noIcon, parent, “\0D131”, plain;
 “Root Item2”,
 noIcon, parent, “\0D132”, plain
 }
};

resource ‘MENU’ (131) {
 131,
 textMenuProc,
 allEnabled,
 enabled,
 “”,
 { /* 2 items */
 “Sub-1 Item1”,
 noIcon, noKey, noMark, plain;
 “Sub-1 Item2”,
 noIcon, noKey, noMark, plain;
 “Sub-1 Item3”,
 noIcon, parent, “\0D133”, plain
 }
};

resource ‘MENU’ (132) {
 132,
 textMenuProc,
 allEnabled,
 enabled,
 “”,
 { /* 2 items */
 “Sub-2 Item1”,
 noIcon, noKey, noMark, plain;
 “Sub-2 Item2”,
 noIcon, noKey, noMark, plain;
 “Sub-2 Item3 (a very, very, very wide item)”,
 noIcon, noKey, noMark, plain
 }
};

resource ‘MENU’ (133) {
 133,
 textMenuProc,
 allEnabled,
 enabled,
 “”,
 { /* 2 items */
 “Sub-3 Item1”,
 noIcon, noKey, noMark, plain;
 “Sub-3 Item2”,
 noIcon, noKey, noMark, plain;
 “Sub-3 Item3”,
 noIcon, noKey, noMark, plain
 }
};

#ifCOLOR
resource ‘mctb’ (128) {
 { /* array MCTBArray: 12 elements */
 /* [1] */
 128, 0,/* menu 128, title*/
 { /* array: 4 elements */
 /* [1] */
 0, 0, $FFFF,
 /* [2] */
 0, 0, 0,
 /* [3] */
 0, $FFFF, 0,
 /* [4] */
 $FFFF, $FFFF, $FFFF
 },
 
 128, 1,/* menu 128, item 1 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $0040, $FFFF, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0,
 },
 
 128, 2,/* menu 128, item 2 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $0100, $8000, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0,
 },
 
 128, 3,/* menu 128, item 3 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $0400, $6000, 0,
 /* [3] */
 0, 0, 0, 
 /* [4] */
 0, 0, 0,
 },
 
 128, 4,/* menu 128, item 4 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $0800, $4000, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0,
 },
 
 128, 5,/* menu 128, item 5 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $1000, $2000, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0,
 },
 
 128, 6,/* menu 128, item 6 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $1800, $1800, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0,
 },
 
 128, 7,/* menu 128, item 7 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $2000, $1000, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0,
 },
 
 128, 8,/* menu 128, item 8 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $4000, $0400, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0,
 },
 
 128, 9,/* menu 128, item 9 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $6000, $0200, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0,
 },
 
 128, 10, /* menu 128, item 10 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $8000, $0100, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0,
 },
 
 128, 11, /* menu 128, item 11 */
 { /* array: 4 elements */
 /* [1] */
 $FFFF, 0, 0,
 /* [2] */
 $FFFF, $0040, 0,
 /* [3] */
 0, 0, 0,
 /* [4] */
 0, 0, 0, 
 }
 }
};

resource ‘mctb’ (129) {
 { /* array MCTBArray: 1 elements */
 /* [1] */
 129, 0,
 { /* array: 4 elements */
 /* [1] */
 0, $4000, $4000,
 /* [2] */
 0, 0, 0,
 /* [3] */
 0, $4000, 0,
 /* [4] */
 $FFFF, $FFFF, $FFFF
 }
 }
};

resource ‘mctb’ (130) {
 { /* array MCTBArray: 1 elements */
 /* [1] */
 130, 0,
 { /* array: 4 elements */
 /* [1] */
 $0, $4000, $0,
 /* [2] */
 0, 0, 0,
 /* [3] */
 0, $4000, 0,
 /* [4] */
 $4000, 0, $4000
 }
 }
};

resource ‘mctb’ (131) {
 { /* array MCTBArray: 1 elements */
 /* [1] */
 131, 0,
 { /* array: 4 elements */
 /* [1] */
 $8000, $8000, $2000,
 /* [2] */
 $0, $0, $0,
 /* [3] */
 $0, $0, $0,
 /* [4] */
 $2000, $2000, $8000
 }
 }
};

resource ‘mctb’ (132) {
 { /* array MCTBArray: 1 elements */
 /* [1] */
 132, 0,
 { /* array: 4 elements */
 /* [1] */
 $1000, $C000, $C000,
 /* [2] */
 0, 0, 0,
 /* [3] */
 $4000, $6000, $FFFF,
 /* [4] */
 $C000, $1000, $1000
 }
 }
};

resource ‘mctb’ (133) {
 { /* array MCTBArray: 1 elements */
 /* [1] */
 133, 0,
 { /* array: 4 elements */
 /* [1] */
 $8000, $0, $0,
 /* [2] */
 0, 0, 0,
 /* [3] */
 $4000, 0, $4000,
 /* [4] */
 $0, $8000, $0
 }
 }
};

resource ‘dctb’ (128) {
 0x0,
 0,
 { /* array ColorSpec: 3 elements */
 /* [1] */
 wContentColor, $FFFF, $FFFF, $FFFF,
 /* [2] */
 wFrameColor, 0, 0, $FFFF,
 /* [3] */
 wHiliteColor, $2000, $FFFF, $2000
 }
};

data ‘ictb’ (128) {
 /* (000) 0000: */ $”0040 0018"
 /* (004) 0004: */ $”0040 0018"
 /* (008) 0008: */ $”0000 0000"
 /* (012) 000C: */ $”0000 0000"
 /* (016) 0010: */ $”0000 0000"
 /* (020) 0014: */ $”800F 0040"
 
 /* (024) 0018: */ $”0000 0000"
 /* (028) 001C: */ $”0000"
 /* (030) 001E: */ $”0003"
 /* (032) 0020: */ $”0000 0000 0000 0000"
 /* (040) 0028: */ $”0001 4000 FFFF 4000"
 /* (048) 0030: */ $”0002 0000 0000 0000"
 /* (056) 0038: */ $”0003 0000 0000 FFFF”
 
 /* (064) 0040: */ $”0054"
 /* (066) 0042: */ $”0200"
 /* (068) 0044: */ $”0009"
 /* (070) 0046: */ $”FFFF 0000 0000"
 /* (076) 004C: */ $”FFFF FFFF FFFF”
 /* (082) 0052: */ $”0001"
 
 /* (084) 0054: */ $”0647 656E 6576 61"
};
#endif  /* COLOR */

/* end of resource file */


(*****************************************************
PopMenuIntf.p
   This file contains the Pascal interface for the
   constants used in PopUpCDEF.p, and in any other
   program or unit which uses pop-ups.
*****************************************************)

UNIT PopMenuIntf;

INTERFACE
CONST
 { VARIATION CODE MODIFIERS:}
 mUnused= 1;
 mRes   = 2;
 mCheck = 4;
 mKey   = 8;
 
 { part codes }
 inPopUpBox =   1;
 titlePart=  2;
 
 { MDEF message: get item dimensions }
 mItemRectMsg  = 512;
 
 { MDEF message: draw item in rect }
 mDrawItemMsg  = 513;
IMPLEMENTATION
END.  { PopMenuIntf }


(*****************************************************
PopMenuCDEF.p
   This file contains the Pascal source code for the
   routines needed to implement the pop-up menu
   button CDEF described (in passing) in Inside Mac,
   v5, p242.
*****************************************************)

UNIT PopMenuCDEF;

INTERFACE
USES
 {$U MemTypes.p  } MemTypes,
 {$U QuickDraw.p } QuickDraw,
 {$U OSIntf.p    } OSIntf,
 {$U ToolIntf.p  } ToolIntf,
 {$U PopMenuIntf.p } PopMenuIntf;


FUNCTION MyControl(varCode: INTEGER;
    theCntl: ControlHandle;
    message: INTEGER;
    param: LONGINT): LONGINT;
 

IMPLEMENTATION
CONST
 VISIBLE= 255;
 INVISIBLE=   0;
 INACTIVE = 255;
 ACTIVE =   0;
 DRAW_ALL =   0;
 NOT_IN_CTL =    0;
 L_PIXELS =  13;
 GREY   =  16;
 PARENT = $1B;


TYPE
 CtlDataRec = record
 popMenu: MenuHandle;
 menuProcID:INTEGER;
 hasColorQD:Boolean;
 markChar:Char;
 wFgColor:RGBColor;
 wBgColor:RGBColor;
 wContColor:RGBColor;
 mTitleColor:  RGBColor;
 mBgColor:RGBColor;
 iNameColor:RGBColor;
 iKeyColor: RGBColor;
 end;
 CtlDataPtr =  ^CtlDataRec;
 CtlDataHdl =  ^CtlDataPtr;
 
 StateRec = record
 savePort:GrafPtr;
 savePen: PenState;
 oldClip: RgnHandle;
 newClip: RgnHandle;
 end;
 

(*****************************************************
forward declarations
*****************************************************)

PROCEDURE doDrawCntl(theCntl: ControlHandle;
 vcLong, param: LONGINT);
 forward;
FUNCTIONdoTestCntl(theCntl: ControlHandle;
 param: LONGINT): LONGINT;
 forward;
PROCEDURE doCalcCRgns(theCntl: ControlHandle;
 param: LONGINT);
 forward;
PROCEDURE doInitCntl(theCntl: ControlHandle;
 vcLong: LONGINT);
 forward;
PROCEDURE doDispCntl(theCntl: ControlHandle;
 vcLong: LONGINT);
 forward;
PROCEDURE doAutoTrack(theCntl: ControlHandle;
 vcLong, param: LONGINT);
 forward;
 

(*****************************************************
MyControl: Main entry point.  Call appropriate
 message-handling routine.
*****************************************************)

FUNCTION MyControl(varCode: INTEGER;
 theCntl: ControlHandle;
 message: INTEGER;
 param: LONGINT): LONGINT;
VAR
 vcLong:LONGINT;

BEGIN
 MyControl := 0;
 vcLong := Ord4(varCode);
 
 CASE message OF
 drawCntl:
 doDrawCntl(theCntl, vcLong, param);
 testCntl:
 MyControl := doTestCntl(theCntl, param);
 calcCRgns:
 doCalcCRgns(theCntl, param);
 initCntl:
 doInitCntl(theCntl, vcLong);
 dispCntl:
 doDispCntl(theCntl, vcLong);
 autoTrack:
 doAutoTrack(theCntl, vcLong, param);
 END;  { case }
END;  { MyControl }



(*****************************************************
CallMDEF: Calls the given ProcPtr, passing it the
 given parameters.
*****************************************************)

PROCEDURE CallMDEF(message: INTEGER;
 theMenu: MenuHandle;
 menuRect: Rect;
 hitPt: Point;
 whichItem: INTEGER;
 MDEFProc: ProcPtr);
Inline 
 $205F,     { move.l (sp)+, a0; get address of proc}
 $4E90;     { jsr (a0)  ; call the proc}
 
 

(*****************************************************
GetItemRect: Get the given item’s rectangle.
*****************************************************)

PROCEDURE GetItemRect(theCntl: ControlHandle;
 menuID: INTEGER;
 menuItem: INTEGER;
 VAR boxRect: Rect);
VAR
 hitPt: Point;
 menuHdl: MenuHandle;
 mDefProc:Handle;
 
BEGIN
 SetPt(hitPt, 0, 0);
 menuHdl := GetMHandle(menuID);
 mDefProc := menuHdl^^.menuProc;
 LoadResource(mDefProc);
 
 CallMDEF(mItemRectMsg,
 menuHdl,
 boxRect,
 hitPt,
 menuItem,
 ProcPtr(mDefProc^));
END;  { GetItemRect }
 

(*****************************************************
DrawMenuItem: Draw the given menu item in the
 given rectangle.
*****************************************************)

PROCEDURE DrawMenuItem(theCntl: ControlHandle;
 menuID: INTEGER;
 menuItem: INTEGER;
 boxRect: Rect);
VAR
 hitPt: Point;
 menuHdl: MenuHandle;
 mDefProc:Handle;
 
BEGIN
 SetPt(hitPt, 0, 0);
 menuHdl := GetMHandle(menuID);
 mDefProc := menuHdl^^.menuProc;
 LoadResource(mDefProc);
 
 CallMDEF(mDrawItemMsg,
 menuHdl,
 boxRect,
 hitPt,
 menuItem,
 ProcPtr(mDefProc^));
END;  { DrawMenuItem }
 
 
(*****************************************************
GetContentColor: Get the window’s content color.
*****************************************************)

PROCEDURE GetContentColor(wPtr: WindowPtr;
 VAR contColor: RGBColor);
VAR
 auxWinHdl: AuxWinHndl;
 winCTable: WCTabHandle;
 b_ignore:Boolean;
 i:INTEGER;
 
BEGIN
 b_ignore := GetAuxWin(wPtr, auxWinHdl);
 winCTable := WCTabHandle(auxWinHdl^^.
 awCTable);
 
 i := winCTable^^.ctSize;
 
 { search for wContentColor }
 while ((i >= 0) and (winCTable^^.ctTable[i].value
 <> wContentColor)) do begin
 i := i - 1;
 end;
 
 { if we didn’t find it, default to first entry }
 if (i < 0) then
 i := 0;
 
 contColor := winCTable^^.ctTable[i].rgb;
END;  { GetContentColor }
 

(*****************************************************
GetMenuColors: Initialize the control’s menu color
 information.  ctlData must be locked before calling
 this routine.
*****************************************************)

PROCEDURE GetMenuColors(theCntl: ControlHandle;
 ctlData: CtlDataHdl);
VAR
 WhiteRGB:RGBColor;
 BlackRGB:RGBColor;
 mbarPtr: MCEntryPtr;
 titlePtr:MCEntryPtr;
 itemPtr: MCEntryPtr;
 
BEGIN
 { default colors }
 WhiteRGB.red   := $FFFF;
 WhiteRGB.green := $FFFF;
 WhiteRGB.blue  := $FFFF;
 BlackRGB.red   := 0;
 BlackRGB.green := 0;
 BlackRGB.blue  := 0;
 
 with theCntl^^ do begin
 mbarPtr  := GetMCEntry(0, 0);
 titlePtr := GetMCEntry(contrlMax, 0);
 itemPtr  := GetMCEntry(contrlMax, contrlMin);
 end;
 
 { get defaults from mbar, or default to B&W }
 with ctlData^^ do begin
 if (mbarPtr = NIL) then
 begin
 if (titlePtr = NIL) then begin
 mTitleColor := BlackRGB;
 mBgColor    := WhiteRGB;
 end;
 
 if (itemPtr = NIL) then begin
 iNameColor  := BlackRGB;
 iKeyColor   := BlackRGB;
 end;
 end
 else if (titlePtr = NIL) then begin
 mTitleColor := mbarPtr^.mctRGB1;
 mBgColor := mbarPtr^.mctRGB2;
 
 if (itemPtr = NIL) then begin
 iNameColor  := mbarPtr^.mctRGB3;
 iKeyColor   := mbarPtr^.mctRGB3;
 end;
 end;
 
 { get colors and defaults from the title entry }
 if (titlePtr <> NIL) then begin
 mTitleColor := titlePtr^.mctRGB1;
 mBgColor := titlePtr^.mctRGB4;
 
 if (itemPtr = NIL) then begin
 iNameColor := titlePtr^.mctRGB3;
 iKeyColor  := titlePtr^.mctRGB3;
 end;
 end;
 
 { set the item colors }
 if (itemPtr <> NIL) then begin
 iNameColor := itemPtr^.mctRGB2;
 iKeyColor  := itemPtr^.mctRGB3;
 end;
 end;  { with ctlData^^ }
END;  { GetMenuColors }
 
 
(*****************************************************
InitColorInfo: Initialize the control’s color information.
*****************************************************)

PROCEDURE InitColorInfo(theCntl: ControlHandle;
 ctlData: CtlDataHdl);
VAR
 i:INTEGER;
 wPtr:  WindowPtr;
 
BEGIN
 HLock(Handle(ctlData));
 
 with ctlData^^ do begin
 wPtr := theCntl^^.contrlOwner;
 
 { get the window’s content color }
 GetContentColor(wPtr, wContColor);
 
 { save the window’s current fg and bg colors }
 GetForeColor(wFgColor);
 GetBackColor(wBgColor);
 
 { get the menu’s and current item’s colors }
 GetMenuColors(theCntl, ctlData);
 end;
 
 HUnlock(Handle(ctlData));
END;  { InitColorInfo }

 
(*****************************************************
GetTitleRect: Get the title of the pop-up menu.
*****************************************************)

PROCEDURE GetTitleRect(theCntl: ControlHandle;
 VAR titleRect: Rect);
VAR
 fInfo: FontInfo;
 height:INTEGER;
 
BEGIN
 GetFontInfo(fInfo);
 
 with fInfo do begin
 height := ascent + descent + leading;
 end;
 
 { define the title’s rect }
 with theCntl^^ do begin
 SetRect(titleRect, contrlRect.left,
    contrlRect.top,
    contrlRect.left +
      StringWidth(contrlTitle),
    contrlRect.top + height);
 
 with titleRect do begin
 if (bottom > contrlRect.bottom - 1) then
 bottom := contrlRect.bottom - 1;
 
 if (right > contrlRect.right - 1) then
 right := contrlRect.right - 1;
 end;  { with titleRect }
 end;  { with theCntl^^ }
END;  { GetTitleRect }

 
(*****************************************************
GetBoxRect: Get the box surrounding the pop-up
 box.
*****************************************************)

PROCEDURE GetBoxRect(theCntl: ControlHandle;
 VAR boxRect: Rect);
VAR
 leftEdge:INTEGER;
 popMenu: MenuHandle;
 fInfo: FontInfo;
 height:INTEGER;
 menuProcID:INTEGER;
 ctlData: CtlDataHdl;
 
BEGIN
 ctlData := CtlDataHdl(theCntl^^.contrlData);
 menuProcID := ctlData^^.menuProcID;
 
 if (menuProcID = textMenuProc) then
 begin
 GetFontInfo(fInfo);
 
 with fInfo do begin
 height := ascent + descent + leading;
 end;
 
 with theCntl^^ do begin
 { find the left edge of the pop-up box }
 leftEdge := contrlRect.left +
 StringWidth(contrlTitle);
 
 popMenu := ctlData^^.popMenu;
 
 { defend against Menu Manager bug }
 CalcMenuSize(popMenu);
 
 { define the pop-up box’s rect }
 SetRect(boxRect,
 leftEdge,
  contrlRect.top,
  leftEdge +
   popMenu^^.menuWidth +
 2,
 contrlRect.top + height + 1);
 end;  { with theCntl^^ }
 end  { menuProc = nil }
 else begin
 GetItemRect(theCntl,
 theCntl^^.contrlMax,
 theCntl^^.contrlMin,
 boxRect);
 end;  { else }
 
 with theCntl^^ do begin
 with boxRect do begin
 if (bottom > contrlRect.bottom - 1) then
 bottom := contrlRect.bottom - 1;
 
 if (right > contrlRect.right - 1) then
 right := contrlRect.right - 1;
 end;  { with boxRect }
 end;  { with theCntl^^ }
END;  { GetBoxRect }

 
(*****************************************************
GetCtlRect: Get the box surrounding the pop-up box
 and its title.
*****************************************************)

PROCEDURE GetCtlRect(theCntl: ControlHandle;
 VAR ctlRect: Rect);
VAR
 boxRect: Rect;
 titleRect: Rect;
 
BEGIN
 GetBoxRect(theCntl, boxRect);
 GetTitleRect(theCntl, titleRect);
 
 UnionRect(boxRect, titleRect, ctlRect);
 
 with ctlRect do begin
 SetRect(ctlRect, left, top,
 right + 1, bottom + 1);
 end;
END;  { GetCtlRect }

 
(*****************************************************
InstallMenus: Recursive routine to install a menu and
 its sub-menus, if any.  It is only called once
 (from doInitCntl()).
*****************************************************)

PROCEDURE InstallMenus(rsrcID: INTEGER);
VAR
 mh:    MenuHandle;
 ni:    INTEGER;
 i:INTEGER;
 c:Char;
 
BEGIN
 mh := GetMenu(rsrcID);
 InsertMenu(mh, -1);
 ni := CountMItems(mh);
 
 { look for parent items }
 for i := 1 to ni do begin
 GetItemCmd(mh, i, c);
 
 { if it’s a parent item, recurse on its child }
 if (c = CHR(PARENT)) then begin
 GetItemMark(mh, i, c);
 InstallMenus(ORD(c));
 end;
 end;
END;  { InstallMenus }

 
(*****************************************************
RemoveMenus: Recursive routine to remove a menu
 and its sub-menus, if any.  It is only called once
 (from doDispCntl()).
*****************************************************)

PROCEDURE RemoveMenus(menuID: INTEGER);
VAR
 mh:    MenuHandle;
 ni:    INTEGER;
 i:INTEGER;
 c:Char;
 
BEGIN
 mh := GetMHandle(menuID);
 ni := CountMItems(mh);
 
 { look for parent items }
 for i := 1 to ni do begin
 GetItemCmd(mh, i, c);
 
 { if it’s a parent item, recurse on its child }
 if (c = CHR(PARENT)) then begin
 GetItemMark(mh, i, c);
 RemoveMenus(ORD(c));
 end;
 end;
 
 { delete the menu from the menu list }
 DeleteMenu(menuID);
 ReleaseResource(Handle(mh));
END;  { RemoveMenus }

 
(*****************************************************
ShrinkString: Make the given string fit in the given
 box.  From a program by Bryan Stearns.
*****************************************************)

PROCEDURE ShrinkString(VAR s: Str255; r: Rect);
VAR
 s_pix: INTEGER;
 s_len: INTEGER;
 room:  INTEGER;

BEGIN
 { how much room do we have? }
 room := (r.right - r.left) - L_PIXELS;
 
 { watch for weirdness }
 if (room < 0) then begin
 room := 0;
 s[0] := CHR(0);
 end;
 
 { get the width of the string }
 s_pix := StringWidth(s);
 
 { will it fit? }
 if (s_pix > room) then begin
 s_len := LENGTH(s);
 room := room - CharWidth(‘ ’);
 
 repeat
 s_pix := s_pix - CharWidth(s[s_len]);
 s_len := s_len - 1;
 until ((s_pix < room) or (LENGTH(s) = 0));
 
 s_len := s_len + 1;
 s[s_len] := ‘ ’;
 s[0] := CHR(s_len);
 end;
END;  { ShrinkString }



 
(*****************************************************
DrawTitle: Draw the title of the pop-up menu control.
*****************************************************)

PROCEDURE DrawTitle(theCntl: ControlHandle);
VAR
 titleRect: Rect;
 ctlData: CtlDataHdl;
 fInfo: FontInfo;
 baseline:INTEGER;

BEGIN
 with theCntl^^ do begin
 ctlData := CtlDataHdl(contrlData);
 
 { if we need to draw in color, set the colors }
 with ctlData^^ do begin
 if (hasColorQD) then begin
 if (contrlHilite = titlePart) then
 begin
 RGBForeColor(wContColor);
 RGBBackColor(mTitleColor);
 end
 else begin
 RGBForeColor(mTitleColor);
 RGBBackColor(wContColor);
 end;
 end;
 end;
 
 { get the control’s title box, and erase it }
 GetTitleRect(theCntl, titleRect);
 EraseRect(titleRect);
 
 { get info about the current font }
 GetFontInfo(fInfo);
 
 { define baseline }
 with fInfo do begin
 baseline := contrlRect.top + ascent;
 end;
 
 { move to baseline }
 MoveTo(titleRect.left + 1, baseline);
 
 { draw control title (= the pop-up menu’s title) }
 DrawString(contrlTitle);
 
 { if we drew in color, restore the colors }
 with ctlData^^ do begin
 if (hasColorQD) then
 begin
 RGBForeColor(wFgColor);
 RGBBackColor(wBgColor);
 end
 else if (contrlHilite = titlePart) then begin
 InvertRect(titleRect);
 end;
 end;
 end;
END;  { DrawTitle }


 
(*****************************************************
DrawDropShadow: Draw the shadow around the
 pop-up box of the pop-up menu control.
*****************************************************)

PROCEDURE DrawDropShadow(
 theCntl: ControlHandle;
 boxRect: Rect);
VAR
 ctlData: CtlDataHdl;

BEGIN
 ctlData := CtlDataHdl(theCntl^^.contrlData);
 
 { if we need to draw in color, set the colors }
 with ctlData^^ do begin
 if (hasColorQD) then begin
 RGBForeColor(mTitleColor);
 RGBBackColor(mBgColor);
 end;  { if }
 end; { with ctlData^^ }
 
 with boxRect do begin
 { draw the drop shadow }
 MoveTo(right, top + 2);
 LineTo(right, bottom);
 LineTo(left + 2, bottom);
 end;  { with boxRect }
 
 { if we drew in color, restore the colors }
 with ctlData^^ do begin
 if (hasColorQD) then begin
 RGBForeColor(wFgColor);
 RGBBackColor(wBgColor);
 end;  { if }
 end;  { with ctlData^^ }
END;  { DrawDropShadow }

(*****************************************************
DrawPopBox: Draw the pop-up box of the pop-up
 menu control.  Also draws drop shadow.
*****************************************************)

PROCEDURE DrawPopBox(theCntl: ControlHandle;
 vcLong: LONGINT);
VAR
 boxRect: Rect;
 itemStr: Str255;
 ctlData: CtlDataHdl;
 fInfo: FontInfo;
 baseline:INTEGER;
 menuProcID:INTEGER;
 
BEGIN
 ctlData := CtlDataHdl(theCntl^^.contrlData);
 menuProcID := ctlData^^.menuProcID;
 
 if (menuProcID = textMenuProc) then
 begin { standard textMenuProc }
 with theCntl^^ do begin
 ctlData := CtlDataHdl(contrlData);
 GetBoxRect(theCntl, boxRect);
 
 { erase the box and shadow }
 with boxRect do begin
 SetPt(botRight, right + 2,
 bottom + 2);
 EraseRect(boxRect);
 SetPt(botRight, right - 2,
 bottom - 2);
 end;  { with }
 
 { get current selection string }
 GetItem(GetMHandle(contrlMax),
 contrlMin,
 itemStr);
 
 { make the string fit in the boxRect }
 ShrinkString(itemStr, boxRect);
 
 { if color, set the colors }
 with ctlData^^ do begin
 if (hasColorQD) then begin
   RGBForeColor(mTitleColor);
   RGBBackColor(mBgColor);
 end;
 end;
 
 { frame the box }
 FrameRect(boxRect);
 
 { get info about the current font }
 GetFontInfo(fInfo);
 
 { define baseline }
 with fInfo do begin
 baseline := contrlRect.top +
 ascent;
 end;
 
 { if color, set the colors }
 with ctlData^^ do begin
 if (hasColorQD) then begin
   RGBForeColor(iNameColor);
 end;
 end;
 
 
 with boxRect do begin
 { draw the string in the popup box }
 MoveTo(left+L_PIXELS, baseline);
 DrawString(itemStr);
 end;  { with boxRect }
 
 { if color, restore the colors }
 with ctlData^^ do begin
 if (hasColorQD) then begin
 RGBForeColor(wFgColor);
 RGBBackColor(wBgColor);
 end;
 end;
 end;  { with theCntl^^ }
 end
 else begin { non-standard menuProc }
 GetBoxRect(theCntl, boxRect);
 DrawMenuItem(theCntl,
 theCntl^^.contrlMax,
 theCntl^^.contrlMin,
 boxRect);
 end;
 
 DrawDropShadow(theCntl, boxRect);
END;  { DrawPopBox }


 
(*****************************************************
DrawDisabled: Invert the pop-up menu control’s title.
*****************************************************)

PROCEDURE DrawDisabled(theCntl: ControlHandle);
VAR
 greyPat: PatHandle;
 ctlRect: Rect;
 
BEGIN
 { get the grey pattern from the System file }
 greyPat:=PatHandle(GetResource(‘PAT ‘,GREY));
 PenPat(greyPat^^);
 ReleaseResource(Handle(greyPat));
 
 { set the pen mode }
 PenMode(patBic);
 
 GetCtlRect(theCntl, ctlRect);
 PaintRect(ctlRect);
END;  { DrawDisabled }


 
(*****************************************************
SaveState: Save the current drawing environment.
*****************************************************)

PROCEDURE SaveState(theCntl: ControlHandle;
 VAR theState: StateRec);
VAR
 ctlData: CtlDataHdl;

BEGIN
 { lock the control handle }
 HLock(Handle(theCntl));
 
 with theCntl^^ do begin
 with theState do begin
 { save current grafPort; set to owner }
 GetPort(savePort);
 SetPort(contrlOwner);
 
 { allocate space for clipping regions }
 oldClip := NewRgn;
 newClip := NewRgn;
 
 { save old clipping region }
 GetClip(oldClip);
 
 { set newClip region to given rectangle }
 RectRgn(newClip, contrlRect);
 
 { newClip: intersection of rect and region }
 SectRgn(oldClip, newClip, newClip);
 
 { set grafPorts’ clip region to the result  }
 SetClip(newClip);
 
 { save current pen state; normalize pen }
 GetPenState(savePen);
 PenNormal;
 
 { if we have color, get the menu color info }
 ctlData := CtlDataHdl(contrlData);
 if (ctlData^^.hasColorQD) then begin
 HLock(Handle(ctlData));
   GetMenuColors(theCntl, ctlData);
 HUnlock(Handle(ctlData));
 end;
 end;  { with theState }
 end;  { with theCntl^^ }
 
 { unlock the control handle }
 HUnlock(Handle(theCntl));
END;  { SaveState }


(*****************************************************
RestoreState: Restore the saved drawing environment.
*****************************************************)

PROCEDURE RestoreState(theCntl: ControlHandle;
 VAR theState: StateRec);
BEGIN
 with theState do begin
 { restore saved states }
 SetClip(oldClip);
 SetPenState(savePen);
 SetPort(savePort);
 
 { dispose of regions }
 DisposeRgn(oldClip);
 DisposeRgn(newClip);
 end;  { with }
END;  { RestoreState }


(*****************************************************
doDrawCntl: Draw the pop-up menu box and title.
*****************************************************)

PROCEDURE doDrawCntl(theCntl: ControlHandle;
 vcLong, param: LONGINT);
VAR
 theState:StateRec;

BEGIN
 if (theCntl^^.contrlVis = VISIBLE) then begin
 { save the current drawing environment }
 SaveState(theCntl, theState);
 
 { lock the control }
 HLock(Handle(theCntl));
 
 { draw the control }
 DrawTitle(theCntl);
 DrawPopBox(theCntl, vcLong);
 
 { if inactive, grey out the control }
 if (theCntl^^.contrlHilite=INACTIVE) then begin
 DrawDisabled(theCntl);
 end;
 
 { unlock the control }
 HUnlock(Handle(theCntl));
 
 { restore the saved drawing environment }
 RestoreState(theCntl, theState);
 end;  { if VISIBLE }
END;  { doDrawCntl }

************************************
doTestCntl: Determine in which part of the control (if
 any) the given point (in ‘param’) lies.     
*****************************************************)

FUNCTION doTestCntl(theCntl: ControlHandle;
 param: LONGINT): LONGINT;
VAR
 boxRect: Rect;
 
BEGIN
 if (theCntl^^.contrlHilite <> INACTIVE) then
 begin  { control is active }
 GetBoxRect(theCntl, boxRect);
 
 if PtInRect(Point(param), boxRect) then
 doTestCntl := inPopUpBox
 else
 doTestCntl := NOT_IN_CTL;
 end
 else  { control is inactive }
 doTestCntl := NOT_IN_CTL;
END;  { doTestCntl }



(*****************************************************
doCalcCRgns: Calculate the region the control
 occupies in its window.
*****************************************************)

PROCEDURE doCalcCRgns(theCntl: ControlHandle;
 param: LONGINT);
VAR
 boxRect: Rect;

BEGIN
 if (BitAnd(param, $80000000) = $80000000) then
 begin { wants indicator region - we have none }
 param := BitAnd(param, $0FFFFFFF);
 SetEmptyRgn(RgnHandle(param));
 end
 else begin
 param := BitAnd(param, $0FFFFFFF);
 
 { set the given region to boxRect }
 GetBoxRect(theCntl, boxRect);
 RectRgn(RgnHandle(param), boxRect);
 end;
END;  { doCalcCRgns }


 
(*****************************************************
doInitCntl: Do any initialization required for the given
 control.
*****************************************************)

PROCEDURE doInitCntl(theCntl: ControlHandle;
 vcLong: LONGINT);
VAR
 popMenu: MenuHandle;
 dfltMenu:MenuHandle;
 ctlRect: Rect;
 ctlData: CtlDataHdl;
 world: SysEnvRec;
 error: OSErr;
 markChar:Char;
 menuProcID:INTEGER;
 
BEGIN
 { lock the control record down  }
 HLock(Handle(theCntl));

 with theCntl^^ do begin
 { allocate a relocatable block }
 ctlData := CtlDataHdl(NewHandle(sizeof(
 CtlDataRec)));
 
 { is color QuickDraw running? }
 error := SysEnvirons(1, world);
 ctlData^^.hasColorQD := world.hasColorQD;
 
 { store a handle to the control data  }
 contrlData := Handle(ctlData);
 
 { erase the control’s rectangle }
 EraseRect(contrlRect);
 
 { get a handle to the ‘MENU’ resource }
 popMenu := MenuHandle(
 GetResource(‘MENU’,contrlValue));
 
 { save the menuProc ID }
 ctlData^^.menuProcID := HiWord(
 Ord4(popMenu^^.menuProc));
 
 { load pop-up menu, and its sub-menus }
 InstallMenus(contrlValue);
 popMenu := GetMHandle(contrlValue);
 
 { save the pop-up menu’s menu handle }
 ctlData^^.popMenu := popMenu;
 
 { append resource names to the menu? }
 if ((BitAnd(vcLong, mRes) = mRes) and
 (contrlRfCon <> 0)) then begin
 AddResMenu(popMenu,
 OSType(contrlRfCon));
 end;
 
 { does the user want to use a check mark? }
 if (BitAnd(vcLong,mCheck)=mCheck) then
 begin
 { get a handle to the default menu }
 dfltMenu := GetMHandle(contrlMax);
 
 { get the default menu item’s mark char }
 GetItemMark(dfltMenu,
 contrlMin,
 markChar);
 
 { if no mark char, default to checkMark }
 if (markChar = CHR(noMark)) then begin
 markChar := CHR(checkMark);
 
 { set the default item’s mark }
 SetItemMark(dfltMenu,
 contrlMin,
 markChar);
 end;
 
 { save the default item }
 ctlData^^.markChar := markChar;
 end;
 
 { if we have color, initialize the color info }
 if (world.hasColorQD) then begin
 InitColorInfo(theCntl, ctlData);
 end;
 
 { flag the default action proc }
 contrlAction := POINTER(-1);
 end;  { with theCntl }
 
 { unlock the control record before SetCTitle }
 HUnlock(Handle(theCntl));
END;  { doInitCntl }



(*****************************************************
doDispCntl: Do any de-allocation required for the
 given control.
*****************************************************)

PROCEDURE doDispCntl(theCntl: ControlHandle;
 vcLong: LONGINT);
VAR
 popMenu: MenuHandle;
 ctlData: CtlDataHdl;
 
BEGIN
 ctlData := CtlDataHdl(theCntl^^.contrlData);
 popMenu := ctlData^^.popMenu;
 
 { remove the pop-up and its sub-menus }
 RemoveMenus(popMenu^^.menuID);
END;  { doDispCntl }



(*****************************************************
doAutoTrack: This is the default action procedure for
 all controls of this type.  TrackControl() will
 place the value inPopBox in contrlHilite
 before calling doAutoTrack, so the old
 value will be lost before we can save it here.
*****************************************************)

PROCEDURE doAutoTrack(theCntl: ControlHandle;
 vcLong, param: LONGINT);
VAR
 popMenu: MenuHandle;
 menuResult:LONGINT;
 menuID:INTEGER;
 menuItem:INTEGER;
 boxRect: Rect;
 globalPt:Point;
 default: INTEGER;
 saveTable: MCTableHandle;
 ctlData: CtlDataHdl;

BEGIN
 { lock control handle before dereferencing }
 HLock(Handle(theCntl));
 
 with theCntl^^ do begin
 { set hiliting to titlePart }
 contrlHilite := titlePart;
 
 { invert the title rect }
 DrawTitle(theCntl);
 
 { get the pop-up box’s rectangle }
 GetBoxRect(theCntl, boxRect);
 
 { get the topLeft point, and convert to global  }
 SetPt(globalPt, boxRect.left, boxRect.top);
 LocalToGlobal(globalPt);
 
 { get a handle to the pop-up menu }
 ctlData := CtlDataHdl(contrlData);
 popMenu := ctlData^^.popMenu;
 
 { determine the default item }
 if (contrlMax = popMenu^^.menuID) then
 default := contrlMin
 else
 default := 1;

 { let the Menu Manager do the hard stuff }
 with globalPt do begin
 menuResult := PopUpMenuSelect(
 popMenu,
 v, h + 1, default);
 end;
 
 { what menu was the selection made from? }
 menuID := HiWord(menuResult);
 menuItem := LoWord(menuResult);
 
 { was a menu selection made? }
 if ((menuID <> 0) and ((menuID <> contrlMax)
 or (menuItem<>contrlMin))) then begin
 { check the current selection }
 if (BitAnd(vcLong, mCheck) =
 mCheck) then begin
 { unmark previous selection }
 SetItemMark(
 GetMHandle(contrlMax),
 contrlMin,
 CHR(noMark));
 
 { mark current selection }
 SetItemMark(
 GetMHandle(menuID),
 menuItem,
 ctlData^^.markChar);
 end;  { if mCheck }
 
 { update the MenuSelect() results }
 contrlMax := menuID;
 contrlMin := menuItem;
 
 { redraw the pop-up box }
 DrawPopBox(theCntl, vcLong);
 end;  {if selection made }
 end;  { with }
 
 { unlock control handle before returning }
 HUnlock(Handle(theCntl));
END;  { doAutoTrack }

END.  { PopMenuCDEF.p }

/*****************************************************
Pop-up Menu Control “Rez” File Constants

‘#include’ this file in any Rez source file that uses
 pop-up menu controls.
*****************************************************/

/* standard pop-up menu control procID  */
#define popMenuProc      48 /* #48 dec, $30 hex     */

/* CDEF’s resource ID*/
#define pmCDEFResID3

/* VARIATION CODE MODIFIERS: */
#define mUnused  1 /* allow sub-menus*/
#define mRes2  /* add res names  */
#define mCheck   4 /* check menu item*/
#define mKey8  /* reserved  */

/* parent of a sub-menu  */
#define parent   “\$1B” 

/* MARK CHARACTERS */
#define cmdChar  “\$11” /* command mark*/
#define checkChar“\$12” /* check mark*/
#define diamondChar“\$13” /* diamond mark    */
#define appleChar“\$14” /* apple mark*/

/*****************************************************
END OF FILE:  PopMenusCDEF.r
*****************************************************/

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Dragon Dictate 6.0 - Premium voice-recog...
With Dragon Dictate speech recognition software, you can use your voice to create and edit text or interact with your favorite Mac applications. Far more than just speech-to-text, Dragon Dictate lets... Read more
OmniFocus 3.11.7 - GTD task manager with...
OmniFocus is an organizer app. It uses projects to organize tasks naturally, and then add tags to organize across projects. Easily enter tasks when you’re on the go, and process them when you have... Read more
rekordbox 6.5.1.0009 - Professional DJ m...
rekordbox is the best way of preparing and managing your tracks, be it at home, in the studio, or even on the plane! It allows you to import music from other music-management software using the... Read more
1Password 7.8.1 - Powerful password mana...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
Ableton Live 10.1.35 - Record music usin...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
Microsoft Office 365, 2019 16.48 - Popul...
Microsoft Office 365. The essentials to get it all done. Unmistakably Office, designed for Mac Get started quickly with new, modern versions of Word, Excel, PowerPoint, Outlook and OneNote-... Read more
Adobe After Effects 18.1 - Create profes...
After Effects is available as part of Adobe Creative Cloud for $52.99/month (or $20.99/month for a single app license). The new, more connected After Effects can make the impossible possible. Get... Read more
Adobe Audition 14.1 - Professional post-...
Audition is available as part of Adobe Creative Cloud for as little as $20.99/month (or $9.99/month if you're a previous Audition customer). Adobe Audition empowers you to create and deliver... Read more
Adobe Animate 21.0.5 - Animation authori...
Animate is available as part of Adobe Creative Cloud for as little as $20.99/month (or $9.99/month if you're a previous Flash Professional customer). Animate (was Flash CC) lets you share work... Read more
Adobe Photoshop 22.3.1 - Professional im...
You can download Photoshop for Mac as a part of Creative Cloud for only $20.99/month (or $9.99/month if you have purchased an earlier software version). Adobe Photoshop is a recognized classic of... Read more

Latest Forum Discussions

See All

Moonlight Sculptor is an upcoming MMORPG...
Kakao Games and XL Games – who you might be familiar with from their previous game ArcheAge – have announced that their MMORPG Moonlight Sculptor is now available to pre-order for iOS and Android devices. Moonlight Sculptor has previously launched... | Read more »
MU Archangel is now open for pre-registr...
MU Archangel is now open for pre-registration in Southeast Asia following its massive success in other territories. Players from Singapore, Thailand, Malaysia, Indonesia, and the Philippines (except Vietnam) can now join in on the fun by applying... | Read more »
Compete, a new social media app you can...
Whoever told you you can’t get rich making videos has obviously never heard of Compete, Competitive Media Technologies Limited’s hot new social media app where you can rake in all the dough just by doing what you love. Video monetization that... | Read more »
Bethesda has released a new DOOM mobile...
Bethesda Softworks has released a new DOOM game out of the blue exclusively for mobile devices. It’s called Mighty DOOM and is currently only available as an early access title on Android but will be expanding to more users in the future. [Read... | Read more »
Anagraphs is a word puzzle game with a t...
Cinq-Mars Media has released its word puzzle game Anagraphs for iOS and Android devices. The game released last week after a short delay in getting it onto the appropriate platforms. [Read more] | Read more »
These are the top 5 best iPhone games li...
Fortnite has been the big hitter in mobile gaming this year, and it's not hard to see why. Thanks to some excellent marketing, and a polished experience that almost anyone can enjoy, it's really taken the App Store by storm. But there are other... | Read more »
The top 5 best iPhone games like Pokemon...
Pokemon GO is still the, if you'll excuse the pun, go-to game if you want some AR action on your phone. But it's not the only choice out there, and if you've got a hankering for something a bit different, then your eyes might already have started... | Read more »
The top 5 best iPhone games like Starcra...
Starcraft sits at the top of the RTS tree for a number of very good reasons. It also isn't on mobile, again, for a number of very good reasons. But that doesn't mean you can't find a way to indulge your sci-fi, competitive, massive, or engaging RTS... | Read more »
Apple Arcade: Ranked - Top 25 [Updated 4...
In case you missed it, I am on a quest to rank every Apple Arcade game there is. [Read more] | Read more »
The top 5 best iPhone games like The Roo...
The Room has had a massive impact on the world of mobile gaming. Not only is it a brilliant adventure, it also shows how the touchscreen controls on your iPhone can be turned into something far more elegant and tactile than just a bunch of buttons... | Read more »

Price Scanner via MacPrices.net

B&H is offering clearance prices on lefto...
Apple reseller B&H Photo has clearance 2020 13″ 1.4GHz Intel-based MacBook Pros on sale today for $200-$300 off Apple’s original MSRP with prices starting at only $1099. Expedited shipping is... Read more
Roundup of Today’s Best MacBook Deals: M1 Mac...
Apple resellers are offering sale prices on Apple’s M1-powered 13″ MacBook Airs ranging up to $190 off MSRP. Here’s where to pick one up today, and as always, keep an eye on our 13″ MacBook Air Price... Read more
Apple AirPods Pro drop to new low price of on...
Amazon has Apple’s AirPods Pro on sale today for a new low price of only $197 shipped. That’s $52 off MSRP and the lowest price currently available for a set of AirPods Pro from any Apple reseller.... Read more
Apple restocks clearance 13″ Intel-based MacB...
Apple has clearance, Certified Refurbished, 2020 13″ Intel-based MacBook Airs available starting at only $809 and up to $280 off original MSRP. Each MacBook features a new outer case, comes with a... Read more
OWC drops prices on 2020 Intel multi-core Mac...
Other World Computing has clearance 2020 Intel-based Mac minis on sale starting at only $499. Both 4-core and 6-core models are in stock today. These are new, unopened, factory-sealed minis: – 3.6GHz... Read more
Save $50 off Apple’s 10.9″ iPad Air today at...
B&H Photo has new 10.9″ Apple iPad Airs in stock and on sale today for up to $50 off MSRP. Expedited shipping is free to most addresses in the US. Note that some sale prices may be restricted to... Read more
Rare Apple sale: Get a HomePod mini for $10 o...
Apple reseller Expercom has the Space Gray HomePod mini on sale today for $89 shipped. Their price is $10 off Apple’s MSRP, and it’s currently the only sale price available for a HomePod mini among... Read more
Apple has M1 Mac minis available starting at...
Apple has a full line of standard configuration M1 Mac minis available in their Certified Refurbished section starting at only $589 and up to $140 off MSRP. Each mini comes with Apple’s one-year... Read more
New sale at Amazon: $55-$70 discounts on Appl...
Amazon has 7.9″ iPad minis on sale today for up to $70 off Apple’s MSRP, each including free shipping. Prices start at $344. These are the same iPad minis sold by Apple in their retail and online... Read more
Apple offering 13″ M1 MacBook Airs for as lit...
Apple has a full line of 2020 13″ M1 MacBook Airs available, Certified Refurbished, starting at only $849 and up to $190 off original MSRP. These are the cheapest M1 MacBook Airs for sale today at... Read more

Jobs Board

*Apple* Valley 20hr Teller - Wells Fargo (Un...
…or scheduled + Ability to stand for extended periods of time **Street Address** **MN- Apple Valley:** 14325 Cedar Ave - Apple Valley, MN **Disclaimer** All offers Read more
*Apple* Valley 20hr Teller - Wells Fargo (Un...
…or scheduled + Ability to stand for extended periods of time **Street Address** **MN- Apple Valley:** 14325 Cedar Ave - Apple Valley, MN **Disclaimer** All offers Read more
Desktop Support Technician - *Apple* / Mac...
…infrastructure and internal desktop systems. Must have an IT background that includes Apple / Mac support. **Overview:** + Responds to routine technical questions or Read more
Geek Squad *Apple* Consultation Professiona...
**801042BR** **Job Title:** Geek Squad Apple Consultation Professional **Job Category:** Store Associates **Store Number or Department:** 000214-Willowbrook-Store Read more
*Apple* Mobility Specialist - Best Buy (Unit...
**800879BR** **Job Title:** Apple Mobility Specialist **Job Category:** Store Associates **Store Number or Department:** 000803-Lansing-Store **Job Description:** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.