home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-11-30 | 75.9 KB | 2,525 lines | [TEXT/KAHL] |
- /* See the file Distribution for distribution terms.
- (c) Copyright 1994 Ari Halberstadt */
-
- /* Functions implementing a popup menu. To create a popup call
- PopupBegin and then PopupCurrentSet to set the initial item.
- Use PopupCurrent to find out which item was selected. There
- are quite a few other functions for controling how the popup
- is displayed: whether the popup is drawn, whether the popup
- is visible, enabling and disabling the popup, whether to
- use the window font to display the popup, setting the text
- style for the title, and more. The defaults are set by
- PopupBegin to be those recommended by Apple in IM-VI. This
- library is the basis for a popup menu 'CDEF'.
-
- You can use the popup CDEF to create a popup menu. The control's
- contrlData field will contain a handle to the popup. If you need to
- directly modify the popup, you can call the popup library routines
- on the handle. However, first call PopupVersion and make sure it
- is equal to kPopupVersion. If it isn't, then don't use the popup
- library to modify the control. In practice, it is better to use
- Control Manager routines to modify the popup.
-
- If you implement keyboard equivalents of menu commands, then you can
- use PopupHilite to hilite the title of the menu while executing the
- menu command.
-
- 94/11/26 aih
- - Supports control color tables.
- - Popup title colors are swapped when hiliting the
- title. This is how the system MDEF and other CDEFs
- behave. The title is simply inverted when drawing in
- black and white.
- - Removed support for old headers (pre-Universal Headers).
-
- 94/09/08 aih
- - Fixed disabling of items > 15.
-
- 94/07/16 aih
- - Fixed almost all color problems.
- - Improved right justification of menus, depending on system
- justification value.
-
- 94/07/12 aih
- - Adapted for color QuickDraw and offscreen graphics worlds.
- - Added support for color menu entries.
-
- 94/07/06 aih
- - Adapted for universal headers
-
- 94/03/15 aih
- - The current selection will display items with small icons properly.
- Formerly, this would display a large icon for items that had small or
- reduced icons, and wouldn't display the large icon once a different icon
- had been selected. Reduced icons still aren't displayed in the current
- selection, and I'm not sure why not, since I copy the command key character
- for reduced icons (0x1D) to the menu item used to draw the current
- selection.
- - When the useWFont variation code is used, the menu will be drawn in
- the font and size of the popup menu's port. Formerly, using the useWFont
- variation code produced incorrect results, but I fixed this by setting the
- system font and size low-memory globals instead of trying to change the
- font and size of the Window Manager's port.
- - The currently selected item is drawn in gray if it is disabled. Formerly,
- the item was only drawn in gray if it was disabled and if the menu item
- was drawn using the MDEF.
- - Updated "To Do" list.
- - Clicking in the popup's title will also pull down the menu. This
- is consistent with the operation of the system 7 popup CDEF.
- - Added support for the popupFixedWidth variation code.
-
- 93/12/28 aih
- - Continued cleaning up code, added some more comments.
- - Uses menu definition function to draw the current selection. This
- eliminated a couple of rectangles from the popup structure and
- means that the current selection will always be drawn correctly,
- even if it includes icons and command keys. A special one item menu
- is used to hold a copy of the currently selected menu item. This special
- menu is then used to calculate the height of the item and to draw the
- item.
-
- 93/12/26 aih
- - Major overhaul. Rewrote rectangle calculation code, since it wasn't
- working right. It's still not as simple as I'd like, but it's better
- than before. Also added attributes to make more compatible with Apple's
- popup CDEF (such as using the window font to display the menu).
-
- 93/12/24 aih
- - Converted to use handles.
- - Removed dependence on all other libraries, so that this library
- is completely self contained; the few external functions that were
- necessary (e.g., strcpy, TruncatePString) were copied or coded directly into
- this file. This will make this library more suitable for use as a
- CDEF since the linker won't pull in a lot of extra code from other
- libraries.
-
- 93/12/23 aih
- - updated for current version of libraries
- - added port parameter to PopupBegin
-
- 91/11/11 aih
- - Removed useless "PopupObj..." stuff
-
- 91/03/01-05 aih
- - Update events are handled
- - Added comment describing this file
- - The popup's title is allocated as a handle in the heap
- - The popup is allocated as a handle in the heap
- - Attribute values are only updated if nescessary
- - The function PopupDraw first draws the popup to an offscreen bitmap
- and then copies it to the popup's port. This eliminates flicker when
- the popup is used as a CDEF, since the Control Manager sends a draw
- message to a control whenever the control is clicked, even if no
- drawing is actually needed.
-
- 91/01/05 aih
- - Inserted this standard header in all files
-
- 90/12/15 Ari Halberstadt (aih)
- - Created this file */
-
- #include <Fonts.h>
- #include <GestaltEqu.h>
- #include <Icons.h>
- #include <LowMem.h>
- #include <Memory.h>
- #include <Palettes.h>
- #include <Resources.h>
- #include <Script.h>
- #include <TextEdit.h>
- #include <TextUtils.h>
- #include <ToolUtils.h>
- #include "PopupLib.h"
-
- /*-------------------------------------------------------------------------*/
- /* conditional compilation */
- /*-------------------------------------------------------------------------*/
-
- /* Define CACHE_OFFSCREEN as 1 to enable the image of the popup menu
- control to be cached offscreen for faster updates. If this is
- defined as 0, then the control is reimaged every time PopupDraw
- is called. We leave this disabled since it is difficult to detect
- when the cached image needs to be updated. */
- #define CACHE_OFFSCREEN (0)
-
- #ifndef NDEBUG
-
- /* Define DRAW_POPUP as 1 to enable drawing of the various parts of the
- the menu. You will normally want this to be enabled, but, while
- debugging, you may want to see only the outlines of the parts of
- the menu. To do so, you can set DRAW_POPUP to 0 and DRAW_RECTANGLES
- to 1. */
- #define DRAW_POPUP (1)
-
- /* To reduce flicker the popup is normally first drawn to an off-screen
- bitmap, then copied to the screen. To make debugging easier disable
- offscreen drawing. Then you can step through each draw routine to
- ensure that it is drawing correctly. */
- #define DRAW_OFFSCREEN (1)
-
- /* Define DRAW_RECTANGLES as 1 to enable framing of the various rectangles
- of the menu. This is useful when debugging since it clearly displays the
- rectangles, which otherwise can be tricky to calculate. */
- #define DRAW_RECTANGLES (0)
-
- /* To test right-justified menus without using a right-justified
- script system, define TEST_FLUSH_RIGHT as 1. */
- #define TEST_FLUSH_RIGHT (0)
-
- /* Another useful debugging trick is to define the following as something
- greater than 1. This helps spot and fix off-by-one errors caused by
- not taking into account the size of the frame and drop shadow.
- A value of at least 1/4" (18 pixels) provides good visual feedback
- and allows using a ruler to get rough measurements of areas. */
- #define kFrameSize (1) /* width of frame around popup */
- #define kShadowSize (1) /* width of shadow around popup */
-
- #else /* NDEBUG */
-
- #define DRAW_POPUP (1) /* if 1, draws the popup menu */
- #define DRAW_OFFSCREEN (1) /* if 1, uses offscreen bitmap */
- #define DRAW_RECTANGLES (0) /* if 1, draws frames around areas */
- #define TEST_FLUSH_RIGHT (0) /* if 1, menus are always right justified */
- #define kFrameSize (1) /* width of frame around popup */
- #define kShadowSize (1) /* size of shadow around popup */
-
- #endif /* NDEBUG */
-
- /*-------------------------------------------------------------------------*/
- /* constants */
- /*-------------------------------------------------------------------------*/
-
- #define kEllipses '…' /* character appended to long strings */
- #define kShadowOffset (3) /* amount to offset shadow from frame */
- #define kTriangleWidth (11) /* width of the triangle */
- #define kTriangleHeight (6) /* height of the triangle */
- #define kIconMarginH (4) /* horizontal margin around icon */
- #define kIconMarginV (2) /* vertical margin around icon */
- #define kTriangleMargin (5) /* margin around triangle */
- #define kTitleMargin (5) /* margin around title */
- #define kTitleMarginBottom (1) /* margin below title */
-
- /* special values for the command key field of a menu item */
- enum {
- kCmdHier = hMenuCmd,
- kCmdScript,
- kCmdIconReduced,
- kCmdIconSmall,
- kCmdReserved
- };
-
- /*-------------------------------------------------------------------------*/
- /* types */
- /*-------------------------------------------------------------------------*/
-
- /* used for translating a part code into a color part code */
- typedef struct {
- short fore;
- short back;
- short disabled;
- } PopupPartTableType ;
-
- /*-------------------------------------------------------------------------*/
- /* assertions */
- /*-------------------------------------------------------------------------*/
-
- #ifndef NDEBUG
-
- #define require(x) ((void) ((x) || PopupAssertFailed()))
- #define ensure(x) ((void) ((x) || PopupAssertFailed()))
- #define check(x) ((void) ((x) || PopupAssertFailed()))
-
- static int PopupAssertFailed(void)
- {
- DebugStr((StringPtr) "\pPopupAssertFailed");
- return(0);
- }
-
- static Boolean RectValid(const Rect *r)
- {
- if (r->top > r->bottom) return(false);
- if (r->left > r->right) return(false);
- return(true);
- }
-
- static short RectWidth(const Rect *r)
- {
- return(r->right - r->left);
- }
-
- static short RectHeight(const Rect *r)
- {
- return(r->bottom - r->top);
- }
-
- static Boolean RectWithin(const Rect *r1, const Rect *r2)
- {
- return( r1->left >= r2->left && r1->right <= r2->right &&
- r1->top >= r2->top && r1->bottom <= r2->bottom);
- }
-
- #else /* NDEBUG */
-
- #define require(x) ((void) 0)
- #define ensure(x) ((void) 0)
- #define check(x) ((void) 0)
-
- #endif /* NDEBUG */
-
- /*-------------------------------------------------------------------------*/
- /* math utilities */
- /*-------------------------------------------------------------------------*/
-
- /* return minimum of two signed long integers */
- static long lmin(long a, long b)
- {
- return(a < b ? a : b);
- }
-
- /* return maximum of two signed long integers */
- static long lmax(long a, long b)
- {
- return(a > b ? a : b);
- }
-
- /*-------------------------------------------------------------------------*/
- /* gestalt utilities */
- /*-------------------------------------------------------------------------*/
-
- /* return the version of the Macintosh system software */
- static short MacVersion(void)
- {
- OSErr err;
- long response;
-
- err = Gestalt(gestaltSystemVersion, &response);
- return(! err ? LoWord(response) : 0);
- }
-
- /* return the version of QuickDraw */
- static short MacVersionQD(void)
- {
- OSErr err;
- long response;
-
- err = Gestalt(gestaltQuickdrawVersion, &response);
- return(! err ? LoWord(response) : 0);
- }
-
- /* true if the Macintosh supports color QuickDraw */
- static Boolean MacHasColorQD(void)
- {
- return(MacVersionQD() >= gestalt8BitQD);
- }
-
- /* true if the Macintosh supports offscreen graphics worlds */
- static Boolean MacHasGWorlds(void)
- {
- /* GWorlds are supported if (MacVersionQD() >= gestalt32BitQD) but
- they behave sufficiently differently in systems before 7.0 that
- it's difficult to get them to work with both pre-7.0 and post-7.0
- systems, so we just support GWorlds in post-7.0 systems. */
- return(MacVersion() >= 0x0700);
- }
-
- /* true if the Macintosh has the icon utilities */
- static Boolean MacHasIconUtilities(void)
- {
- OSErr err;
- long response;
-
- // program_note: gestalt doesn't recognize the gestaltIconUtilitiesAttr,
- // even though the icon utilities are available (this is with system 7 pro),
- // so we just test for system 7.0
- //err = Gestalt(gestaltIconUtilitiesAttr, &response);
- //return(! err ? (response & (1<<gestaltIconUtilitiesPresent)) != 0 : false);
- return(MacVersion() >= 0x0700);
- }
-
- /*-------------------------------------------------------------------------*/
- /* graphics utilities */
- /*-------------------------------------------------------------------------*/
-
- /* return the pixel depth of the pixel map */
- static short GetPixMapDepth(PixMapHandle pixmap)
- {
- return((**pixmap).pixelSize);
- }
-
- /* return the pixel depth of the port */
- static short GetPortDepth(CGrafPtr port)
- {
- short depth;
-
- depth = 1;
- if ((port->portVersion & 0xC000) == 0xC000)
- depth = GetPixMapDepth(port->portPixMap);
- return(depth);
- }
-
- /* get the hilite color of the port */
- static void GetHiliteColor(CGrafPtr port, RGBColor *color)
- {
- if ((port->portVersion & 0xC000) == 0xC000)
- *color = (**(GVarHandle) (port->grafVars)).rgbHiliteColor;
- else
- LMGetHiliteRGB(color);
- }
-
- /* return the pixel depth of the graphics device */
- static short GetDeviceDepth(GDHandle device)
- {
- return(GetPixMapDepth((**device).gdPMap));
- }
-
- /* Return a handle to the video device with the smallest pixel depth
- that intersects the specified rectangle (given in global coordinates).
- If no video device intersects the specified rectangle, then NULL is
- returned. */
- static GDHandle GetMinDevice(const Rect *globalRect)
- {
- GDHandle mindevice;
- GDHandle curdevice;
- Rect deviceRect;
-
- mindevice = NULL;
- curdevice = GetDeviceList();
- while (curdevice) {
- if (TestDeviceAttribute(curdevice, screenDevice) &&
- TestDeviceAttribute(curdevice, screenActive))
- {
- deviceRect = (**curdevice).gdRect;
- if (SectRect(&deviceRect, globalRect, &deviceRect)) {
- if (! mindevice ||
- GetDeviceDepth(curdevice) <
- GetDeviceDepth(mindevice))
- {
- mindevice = curdevice;
- }
- }
- }
- curdevice = GetNextDevice(curdevice);
- }
- return(mindevice);
- }
-
- /* return the pixel map of the graphics world */
- static PixMapHandle GetPixMap(GWorldPtr gworld)
- {
- return(MacVersion() >= 0x0700 ? GetGWorldPixMap(gworld) : gworld->portPixMap);
- }
-
- /* get the text attributes of the current port */
- static void GetTextState(TextState *text)
- {
- GrafPtr port;
-
- GetPort(&port);
- text->font = port->txFont;
- text->face = port->txFace;
- text->mode = port->txMode;
- text->size = port->txSize;
- }
-
- /* set the text attributes of the current port */
- static void SetTextState(const TextState *text)
- {
- TextFont(text->font);
- TextFace(text->face);
- TextMode(text->mode);
- TextSize(text->size);
- }
-
- /* set the text attributes of the current port to their defaults */
- static void TextNormal(void)
- {
- TextFont(0);
- TextFace(0);
- TextSize(0);
- TextMode(srcOr);
- }
-
- /* get the color attributes of the current port */
- static void GetColorState(ColorState *color)
- {
- if (MacHasColorQD()) {
- GetForeColor(&color->fore);
- GetBackColor(&color->back);
- }
- }
-
- /* set the color attributes of the current port */
- static void SetColorState(const ColorState *color)
- {
- if (MacHasColorQD()) {
- RGBForeColor(&color->fore);
- RGBBackColor(&color->back);
- }
- }
-
- /*-------------------------------------------------------------------------*/
- /* rectangle utilities */
- /*-------------------------------------------------------------------------*/
-
- /* Convert the rectangle to global coordinates. */
- static void RectLocalToGlobal(Rect *r)
- {
- Point origin = { 0, 0 };
-
- LocalToGlobal(&origin);
- OffsetRect(r, origin.h, origin.v);
- }
-
- /* Flip the rectangle 'flip' horizontally (mirror image) relative to
- the rectangle 'within'. */
- static void RectFlip(Rect *flip, const Rect *within)
- {
- short offset;
-
- require(RectValid(flip));
- require(RectValid(within));
- require(within->left <= flip->left && flip->right <= within->right);
- offset = (within->right - flip->right) - (flip->left - within->left);
- flip->left += offset;
- flip->right += offset;
- }
-
- /* paint over the rectangle with the gray pattern */
- static void RectDisable(const Rect *r)
- {
- PenState pen;
- Pattern pat;
-
- require(RectValid(r));
- GetPenState(&pen);
- PenMode(patBic);
- GetIndPattern(&pat, sysPatListID, 4);
- PenPat(&pat);
- PaintRect(r);
- SetPenState(&pen);
- }
-
- /*-------------------------------------------------------------------------*/
- /* icon utilities */
- /*-------------------------------------------------------------------------*/
-
- /* PlotSICN draws a small black and white icon from a resource of type
- 'SICN' that defines a 16 by 16 bit image, in the specified rectangle.
- This is similar to the ToolBox routine PlotIcon. */
- static void PlotSICN(const Rect *bounds, Handle sicn)
- {
- SignedByte state;
- BitMap sicnBits;
- GrafPtr port;
-
- state = HGetState(sicn);
- MoveHHi(sicn);
- HLock(sicn);
- GetPort(&port);
- sicnBits.rowBytes = 2;
- sicnBits.baseAddr = *sicn;
- sicnBits.bounds = *bounds;
- CopyBits(&sicnBits, &port->portBits, bounds, bounds, srcCopy, NULL);
- HSetState(sicn, state);
- }
-
- /*-------------------------------------------------------------------------*/
- /* string utilities */
- /*-------------------------------------------------------------------------*/
-
- /* fit string to width by truncating it and adding an extra character,
- return width of string */
- static short TruncatePString(Str255 str, short maxwidth)
- {
- const char extra = kEllipses;
- short extrawidth;
- short width;
-
- require(maxwidth >= 0);
- if (MacVersion() >= 0x0700) {
- (void) TruncString(maxwidth, str, truncEnd);
- width = StringWidth(str);
- }
- else {
- width = StringWidth(str);
- if (width > maxwidth) {
- if (maxwidth == 0) {
- /* optimization */
- width = 0;
- *str = 0;
- }
- else {
- /* truncate one character (or multi-byte character)
- at a time from the end of the string */
- while (*str > 0) {
- str[*str] = extra;
- width = StringWidth(str);
- if (width <= maxwidth)
- break;
- while (CharByte((Ptr) str, *str) > 0) {
- check(*str > 1);
- --*str;
- }
- check(*str > 0);
- --*str;
- }
- }
- }
- }
- ensure(width <= maxwidth);
- ensure(width == StringWidth(str));
- return(width);
- }
-
- /*-------------------------------------------------------------------------*/
- /* menu utilities */
- /*-------------------------------------------------------------------------*/
-
- /* true if the menu item is enabled */
- static Boolean MenuItemEnabled(MenuHandle menu, short item)
- {
- unsigned long flags;
-
- require(0 <= item && item <= CountMItems(menu));
- flags = (**menu).enableFlags;
- return((flags & 1) != 0 && (item > 31 || (flags & (1UL << item)) != 0));
- }
-
- /* get info about the icon for the specified menu item */
- static void MenuItemIcon(MenuHandle menu, short item,
- ResType *iconType, short *iconID, Point *iconSize)
- {
- short itemCmd;
- short itemIcon;
- Rect iconRect;
- Handle iconRsrc;
-
- *iconID = 0;
- *iconType = 0;
- iconSize->h = iconSize->v = 0;
- if (item > 0) {
- GetItemCmd(menu, item, &itemCmd);
- GetItemIcon(menu, item, &itemIcon);
- if (itemIcon && itemCmd != kCmdScript) {
- *iconID = itemIcon + 256;
- if (itemCmd == kCmdIconReduced)
- iconSize->h = iconSize->v = 16;
- if (MacHasColorQD() && GetResource('cicn', *iconID)) {
- *iconType = 'cicn';
- iconRsrc = GetResource('cicn', *iconID);
- if (iconRsrc && *iconRsrc && itemCmd != kCmdIconReduced) {
- /* get icon's size from its pixmap */
- iconRect = (**(CIconHandle) iconRsrc).iconPMap.bounds;
- iconSize->h = iconRect.right - iconRect.left;
- iconSize->v = iconRect.bottom - iconRect.top;
- }
- }
- else if (itemCmd == kCmdIconSmall) {
- *iconType = 'SICN';
- iconSize->h = iconSize->v = 16;
- }
- else {
- *iconType = 'ICON';
- if (itemCmd != kCmdIconReduced)
- iconSize->h = iconSize->v = 32;
- }
- }
- }
- }
-
- /* If the menu item has a color associated with it, then sets the
- 'foreColor' and 'backColor' parameters to the menu item's colors
- and returns true. Otherwise, the 'foreColor' and 'backColor'
- parameters are not changed and the function returns false. */
- static Boolean MenuItemColor(MenuHandle menu, short item,
- RGBColor *foreColor, RGBColor *backColor)
- {
- MCEntryPtr mcentry; /* pointer to menu item's color entry */
- Boolean color; /* true if the item has a color */
-
- color = false;
- mcentry = GetMCEntry((**menu).menuID, item);
- if (item) {
- if (mcentry) {
- /* use item's color entry */
- if (foreColor) *foreColor = mcentry->mctRGB2;
- if (backColor) *backColor = mcentry->mctRGB4;
- color = true;
- }
- else {
- /* use default color entry */
- mcentry = GetMCEntry((**menu).menuID, 0);
- if (mcentry) {
- if (foreColor) *foreColor = mcentry->mctRGB3;
- if (backColor) *backColor = mcentry->mctRGB4;
- color = true;
- }
- }
- }
- else if (mcentry) {
- /* use title's color entry */
- if (foreColor) *foreColor = mcentry->mctRGB1;
- if (backColor) *backColor = mcentry->mctRGB4;
- color = true;
- }
- return(color);
- }
-
- /* this is similar to MenuItemColor, but returns the colors from the
- color table for the menu bar */
- static Boolean MenuItemColorDefault(short item,
- RGBColor *foreColor, RGBColor *backColor)
- {
- MCEntryPtr mcentry; /* pointer to menu item's color entry */
- Boolean color; /* true if the item has a color */
-
- color = false;
- mcentry = GetMCEntry(0, 0);
- if (mcentry) {
- color = true;
- if (item > 0) {
- /* use item entry */
- if (foreColor) *foreColor = mcentry->mctRGB3;
- if (backColor) *backColor = mcentry->mctRGB2;
- }
- else {
- /* use title entry */
- if (foreColor) *foreColor = mcentry->mctRGB1;
- if (backColor) *backColor = mcentry->mctRGB4;
- }
- }
- return(color);
- }
-
- /*-------------------------------------------------------------------------*/
- /* control utilities */
- /*-------------------------------------------------------------------------*/
-
- /* Return the control's color table, or NULL if it doesn't have
- a color table. */
- static CCTabHandle ControlColorTable(ControlHandle ctl)
- {
- AuxCtlHandle aux;
-
- return(GetAuxiliaryControlRecord(ctl, &aux) ? (**aux).acCTable : NULL);
- }
-
- /* If the part has an entry in the control's color table, then
- sets the 'color' parameter to the color specified in the table
- and returns true, otherwise the 'color' parameter is not changed
- and the function returns false. */
- static Boolean ControlCTPartColor(CCTabHandle cct, short part,
- RGBColor *color)
- {
- Boolean found;
- short i;
-
- found = false;
- if (cct) {
- for (i = 0; i <= (**cct).ctSize && ! found; i++) {
- if ((**cct).ctTable[i].value == part) {
- if (color)
- *color = (**cct).ctTable[i].rgb;
- found = true;
- }
- }
- }
- return(found);
- }
-
- /*-------------------------------------------------------------------------*/
- /* popup menu routines */
- /*-------------------------------------------------------------------------*/
-
- /* true if the popup is valid */
- Boolean PopupValid(PopupHandle popup)
- {
- return(popup && (**popup).version == kPopupVersion);
- }
-
- /*-------------------------------------------------------------------------*/
- /* port setup and restore */
- /*-------------------------------------------------------------------------*/
-
- /* Return the alignment for the font in which the popup menu is drawn. */
- static short PopupJust(PopupHandle popup)
- {
- GrafPtr savePort;
- short just;
-
- if ((**popup).attr.wfont) {
- GetPort(&savePort);
- SetPort((**popup).port);
- just = GetScript(FontScript(), smScriptJust);
- SetPort(savePort);
- }
- else
- just = GetSysDirection();
- return(just);
- }
-
- /* The menu definition function gets the font size in which to draw the
- popup menu from the font size of the window manager port. So, to
- set the system font, in addition to setting a low-memory global,
- we also have to set the font size for the window manager port. */
- static void SetSysFontSize(short size)
- {
- GrafPtr svport;
- GrafPtr wmgrPort;
-
- GetPort(&svport);
- GetWMgrPort(&wmgrPort);
- SetPort(wmgrPort);
- TextSize(size);
- SetPort(svport);
- }
-
- /* Returns the pixel depth of the port or device to which the popup is
- being drawn. */
- static short PopupPixelDepth(PopupHandle popup)
- {
- short depth;
-
- depth = GetPortDepth((CGrafPtr) (**popup).port);
- if ((**popup).draw.gdepth)
- depth = lmin(depth, (**popup).draw.gdepth);
- return(depth);
- }
-
- /* Remember the current drawing environment of the system font and size.
- Must be balanced with a call to PopupPortRestoreSystem. */
- static void PopupPortSetupSystem(PopupHandle popup)
- {
- /* If using the window's font, then set the system font and size
- to the font and size of the popup's port. */
- if ((**popup).attr.wfont) {
- (**popup).draw.save.sysfont = LMGetSysFontFam();
- (**popup).draw.save.syssize = LMGetSysFontSize();
- LMSetSysFontFam((**popup).port->txFont);
- LMSetSysFontSize((**popup).port->txSize);
- SetSysFontSize((**popup).port->txSize);
- }
- }
-
- /* Restore the current drawing environment of the system font and size. */
- static void PopupPortRestoreSystem(PopupHandle popup)
- {
- if ((**popup).attr.wfont) {
- LMSetSysFontFam((**popup).draw.save.sysfont);
- LMSetSysFontSize((**popup).draw.save.syssize);
- SetSysFontSize((**popup).draw.save.syssize);
- }
- }
-
- /* Remember the current drawing environment and set the drawing environment
- to that needed by the popup menu. Must be balanced with a call
- to PopupPortRestore. */
- static void PopupPortSetup(PopupHandle popup)
- {
- GrafPtr port;
- PenState pen;
- TextState text;
- ColorState color;
-
- require(! (**popup).state.drawset);
-
- #if TEST_FLUSH_RIGHT
- SetSysDirection(teFlushRight);
- #endif
-
- /* remember current port */
- GetPort(&port);
- (**popup).draw.save.port = port;
-
- /* save settings for popup's port */
- SetPort((**popup).port);
- GetPenState(&pen);
- GetTextState(&text);
- GetColorState(&color);
- (**popup).draw.save.pen = pen;
- (**popup).draw.save.text = text;
- (**popup).draw.save.color = color;
- if ((**popup).draw.gworld && port == (GrafPtr) (**popup).draw.gworld)
- SetGWorld((**popup).draw.gworld, NULL);
-
- /* reset text and pen settings to default system settings */
- TextNormal();
- PenNormal();
-
- /* if using the window's font, use font and font size of popup's port */
- if ((**popup).attr.wfont) {
- TextFont(text.font);
- TextSize(text.size);
- }
-
- /* save clip region and clip to the popup's maximum bounding rectangle */
- if ((**popup).draw.save.clip) {
- Rect maxbounds = (**popup).r.maxbounds;
- GetClip((**popup).draw.save.clip);
- ClipRect(&maxbounds);
- }
-
- (**popup).state.drawset = true;
- ensure((**popup).state.drawset);
- }
-
- /* Restore the drawing environment to its original state. */
- static void PopupPortRestore(PopupHandle popup)
- {
- GrafPtr port;
- PenState pen;
- TextState text;
- ColorState color;
- GrafPtr wmgrPort;
- TextState wmgrText;
-
- #if TEST_FLUSH_RIGHT
- SetSysDirection(0);
- #endif
-
- require((**popup).state.drawset);
- port = (**popup).draw.save.port;
- pen = (**popup).draw.save.pen;
- text = (**popup).draw.save.text;
- color = (**popup).draw.save.color;
- if ((**popup).draw.save.clip) {
- SetClip((**popup).draw.save.clip);
- SetEmptyRgn((**popup).draw.save.clip);
- }
- SetColorState(&color);
- SetTextState(&text);
- SetPenState(&pen);
- SetPort(port);
- (**popup).state.drawset = false;
- ensure(! (**popup).state.drawset);
- }
-
- /* structure for saving the drawing environment when drawing the current
- selection */
- typedef struct {
- TextState textState; /* saved text style */
- ColorState colorState; /* saved color state */
- } PopupDrawSelectionStructure;
-
- /* Setup the drawing environment for drawing the current selection string. */
- static void PopupDrawSelectionSetup(PopupHandle popup,
- PopupDrawSelectionStructure *save)
- {
- Rect selection; /* current selection rectangle */
- Style itemStyle; /* menu item's style */
- short itemCmd; /* item's command character */
- short itemScript; /* item's script code */
- long itemFondSize; /* item's font ID and size */
-
- /* save the text and color states */
- GetTextState(&save->textState);
- GetColorState(&save->colorState);
-
- /* set the font script of the menu item */
- if (! (**popup).attr.wfont) {
- GetItemCmd((**popup).menu, (**popup).state.current, &itemCmd);
- if (itemCmd == kCmdScript) {
- GetItemIcon((**popup).menu, (**popup).state.current, &itemScript);
- if (GetScript(itemScript, smScriptEnabled)) {
- itemFondSize = GetScript(itemScript, smScriptSysFondSize);
- TextFont(HiWord(itemFondSize));
- TextSize(LoWord(itemFondSize));
- }
- }
- }
-
- /* set the text style of the menu item */
- GetItemStyle((**popup).menu, (**popup).state.current, &itemStyle);
- TextFace(itemStyle);
- }
-
- /* restore the drawing environment after drawing the current selection */
- static void PopupDrawSelectionRestore(PopupHandle popup,
- const PopupDrawSelectionStructure *save)
- {
- SetTextState(&save->textState);
- SetColorState(&save->colorState);
- }
-
- /*-------------------------------------------------------------------------*/
- /* rectangle calculation routines */
- /*-------------------------------------------------------------------------*/
-
- /* set popup's rectangles and erase old popup if the frame has changed */
- static PopupRectanglesSet(PopupHandle popup, const PopupRectanglesType *r)
- {
- Rect bounds;
- RgnHandle eraseRgn;
-
- bounds = (**popup).r.bounds;
- if (! EqualRect(&bounds, &r->bounds)) {
- eraseRgn = NewRgn();
- if (eraseRgn && (**popup).draw.utilRgn) {
- /* to reduce flicker only erase the
- area that doesn't overlap */
- check(EmptyRgn((**popup).draw.utilRgn));
- RectRgn(eraseRgn, &bounds);
- RectRgn((**popup).draw.utilRgn, &r->bounds);
- DiffRgn(eraseRgn, (**popup).draw.utilRgn, eraseRgn);
- EraseRgn(eraseRgn);
- DisposeRgn(eraseRgn);
- SetEmptyRgn((**popup).draw.utilRgn);
- }
- else
- EraseRect(&bounds);
- }
- (**popup).r = *r;
- }
-
- /* calculate the popup's rectangles */
- void PopupCalculate(PopupHandle popup)
- {
- struct { /* margins around some items */
- struct { short left, right; } selection;
- struct { short left, right; } title;
- struct { short left, right; } triangle;
- } margin = {
- { kFrameSize, 0 }, /* selection */
- { kTitleMargin, kTitleMargin }, /* title */
- { 0, kTriangleMargin }, /* triangle */
- };
- struct { /* minimum widths of each item */
- short content;
- short hilite;
- short title;
- short triangle;
- short selection;
- } minwidth;
- short lineHeight; /* height of a line */
- short menuHeight; /* height of the selected menu item */
- short menuWidth; /* width of the menu */
- short maxwidth; /* for calculating maximum widths of items */
- short width; /* for calculating widths of items */
- Point iconSize; /* size of current item's icon */
- ResType iconType; /* resource type of current item's icon */
- short iconID; /* resource ID of current item's icon */
- FontInfo font; /* info about the current font size */
- Str255 title; /* the popup's title */
- Rect content; /* rectangle enclosing content (excludes frame) */
- PopupRectanglesType r; /* the calculated rectangles */
- PopupDrawSelectionStructure saveState; /* saved state for calculating menu height */
- GrafPtr port; /* the current port being drawn into */
-
- /* don't calculate if drawing is turned off */
- if (! (**popup).attr.draw) return;
-
- /* initialize settings */
- PopupPortSetup(popup);
- PopupTitle(popup, title);
- GetPort(&port);
-
- /* The following calls to CalcMenuSize and GetFontInfo have a bizarre
- requirement. When using a window font other than the system font
- (i.e., using the popupUseWFont variation code) the first call to
- CalcMenuSize would calculate the menu's size using the standard
- system font, instead of the font that the CDEF specified. The
- second call to CalcMenuSize calculates the menu's size using
- the correct font size. Similarly, if GetFontInfo were called before
- CalcMenuSize were called, GetFontInfo would return information about
- the wrong font. This seems either like a bug in the OS (both systems
- 6.0.5 and 7.0), or an attempt by Apple to work around some other
- bug in the OS. It took me a long time to figure out a work-around
- for this problem. */
-
- /* get width of menu, including the width of the triangle */
- PopupPortSetupSystem(popup);
- CalcMenuSize((**popup).menu);
- CalcMenuSize((**popup).menu);
- PopupPortRestoreSystem(popup);
- menuWidth = (**(**popup).menu).menuWidth + kTriangleWidth + kTriangleMargin;
-
- /* setup port for current selection string */
- PopupDrawSelectionSetup(popup, &saveState);
-
- /* get information about the font in which the current selection
- will be drawn */
- GetFontInfo(&font);
- lineHeight = font.ascent + font.descent + font.leading;
-
- /* calculate height of current selection */
- menuHeight = 0;
- MenuItemIcon((**popup).menu, (**popup).state.current,
- &iconType, &iconID, &iconSize);
- if (iconSize.v > 0)
- menuHeight = iconSize.v + kIconMarginV;
-
- /* the height must be at least as large as the minimum height
- of a line and the minimum height needed to display the down
- triangle */
- if (menuHeight < lineHeight)
- menuHeight = lineHeight;
- if (menuHeight < kTriangleHeight + 4)
- menuHeight = kTriangleHeight + 4;
-
- /* restore port */
- PopupDrawSelectionRestore(popup, &saveState);
-
- /* get information about the font in which the title will be drawn */
- GetFontInfo(&font);
- lineHeight = font.ascent + font.descent + font.leading;
-
- /* adjust margins */
-
- if (! *title || (**popup).attr.typein) {
- /* Type-in popups don't display the title or the current selection,
- so their margins aren't needed. If the title is empty then the
- title's margins can also be empty. */
- if ((**popup).attr.typein) {
- margin.triangle.left = 3;
- margin.triangle.right = 3;
- menuHeight = lineHeight;
- font.widMax = 0;
- }
- margin.title.left = 0;
- margin.title.right = 0;
- }
-
- /* for right justified popups we need a little extra space for the drop
- shadow between the current selection area and the title */
- if ((**popup).attr.just == teFlushRight)
- margin.selection.left += kShadowSize;
-
- /* calculate minimum widths */
-
- minwidth.triangle = kTriangleWidth;
- minwidth.title = (*title ? font.widMax : 0);
- minwidth.hilite = minwidth.title + margin.title.left + margin.title.right;
- minwidth.selection = font.widMax + minwidth.triangle + margin.triangle.left + margin.triangle.right;
- minwidth.content = minwidth.hilite + minwidth.selection +
- margin.selection.left + margin.selection.right;
-
- /* calculate rectangles */
-
- /* copy maxbounds rectangle */
- r.maxbounds = (**popup).r.maxbounds;
-
- /* calculate content rectangle; all the other rectangles will be
- calculated relative to this rectangle */
- content.top = r.maxbounds.top + kFrameSize;
- content.left = r.maxbounds.left + kFrameSize;
- content.bottom = content.top + menuHeight;
- content.right = content.left +
- lmax(r.maxbounds.right - r.maxbounds.left - 2 * kFrameSize - kShadowSize,
- minwidth.content);
-
- /* vertically center content in maxbounds */
- { short offset = ((r.maxbounds.bottom - r.maxbounds.top) -
- (content.bottom - content.top)) / 2;
- OffsetRect(&content, 0, lmax(0, offset - kShadowSize));
- }
-
- /* calculate hilite rectangle */
- r.hilite = content;
- r.hilite.left = content.left;
- r.hilite.right = content.right - minwidth.selection;
-
- /* calculate title rectangle */
- check(content.bottom - content.top >= lineHeight);
- r.title.top = content.top + (content.bottom - content.top - lineHeight) / 2;
- r.title.left = content.left + margin.title.left;
- r.title.bottom = r.title.top + lineHeight - kTitleMarginBottom;
- /* calculate width of title rectangle */
- if ((**popup).attr.typein || ! *title)
- width = 0; /* don't show title */
- else {
- /* popup has a title */
- maxwidth =
- r.hilite.right - r.hilite.left -
- margin.title.left - margin.title.right;
- check(maxwidth >= minwidth.title);
- if ((**popup).attr.title.width) {
- /* use fixed title width as specified by application */
- width = lmin(maxwidth, (**popup).attr.title.width);
- width = lmax(width, minwidth.title);
- }
- else {
- /* use actual width of title string */
- Style style = port->txFace;
- TextFace((**popup).attr.title.style);
- width = TruncatePString(title, maxwidth);
- TextFace(style);
- }
- check(width <= maxwidth);
- }
- check(width >= minwidth.title);
- r.title.right = r.title.left + width;
-
- /* adjust right edge of hilite rectangle now that width of title is known */
- r.hilite.right = r.title.right + margin.title.right;
-
- /* calculate selection rectangle, initially using the maximum possible width */
- r.selection = content;
- r.selection.left = r.hilite.right + margin.selection.left;
- r.selection.right = content.right - margin.selection.right;
-
- /* calculate width of current selection */
- if ((**popup).attr.typein)
- width = minwidth.selection;
- else if ((**popup).attr.fixedwidth)
- width = lmax(r.selection.right - r.selection.left, minwidth.selection);
- else {
- maxwidth = r.selection.right - r.selection.left;
- check(maxwidth >= minwidth.selection);
- width = lmin(maxwidth, menuWidth);
- width = lmax(width, minwidth.selection);
- }
-
- /* adjust right edge now that we know how wide everything is */
- r.selection.right = r.selection.left + width;
- content.right = r.selection.right + margin.selection.right;
-
- /* calculate triangle rectangle--center triangle vertically at
- right edge of selection rectangle */
- check(content.bottom - content.top >= kTriangleHeight);
- r.triangle.top = content.top + (content.bottom - content.top - kTriangleHeight) / 2;
- r.triangle.left = r.selection.right - kTriangleWidth - margin.triangle.right;
- r.triangle.bottom = r.triangle.top + kTriangleHeight;
- r.triangle.right = r.triangle.left + kTriangleWidth;
-
- /* calculate frame rectangle--surrounds content area, and includes the
- frame and shadow */
- r.bounds.top = content.top - kFrameSize;
- r.bounds.left = content.left - kFrameSize;
- r.bounds.right = content.right + kFrameSize;
- r.bounds.bottom = content.bottom + kFrameSize + kShadowSize;
- if ((**popup).attr.just != teFlushRight)
- r.bounds.right += kShadowSize; /* in teFlushRight shadow is part of content */
-
- /* mirror image rectangles if using a right justified menu */
- if ((**popup).attr.just == teFlushRight) {
- Rect maxbounds;
-
- /* copy and adjust maxbounds so flipping will work */
- maxbounds.top = r.maxbounds.top;
- maxbounds.left = r.maxbounds.left;
- maxbounds.bottom = r.maxbounds.top +
- lmax(r.maxbounds.bottom - r.maxbounds.top,
- r.bounds.bottom - r.bounds.top);
- maxbounds.right = r.maxbounds.left +
- lmax(r.maxbounds.right - r.maxbounds.left,
- r.bounds.right - r.bounds.left);
-
- /* flip the rectangles */
- RectFlip(&r.bounds, &maxbounds);
- RectFlip(&r.hilite, &maxbounds);
- RectFlip(&r.selection, &maxbounds);
- RectFlip(&r.title, &maxbounds);
- RectFlip(&r.triangle, &maxbounds);
- if (PopupJust(popup) != teFlushRight)
- RectFlip(&r.triangle, &r.selection);
- }
-
- /* make sure everything's ok */
- check(RectWidth(&r.hilite) >= minwidth.hilite);
- check(RectWidth(&r.title) >= minwidth.title);
- check(RectWidth(&r.selection) >= minwidth.selection);
- check(RectWidth(&r.triangle) >= minwidth.triangle);
- check(RectWithin(&r.hilite, &r.bounds));
- check(RectWithin(&r.title, &r.hilite));
- check(RectWithin(&r.selection, &r.bounds));
- check(RectWithin(&r.triangle, &r.selection));
- check(! RectWithin(&r.hilite, &r.selection));
- check(! RectWithin(&r.selection, &r.hilite));
-
- /* set the popup's rectangles and restore the environment */
- PopupRectanglesSet(popup, &r);
- PopupPortRestore(popup);
- }
-
- /*-------------------------------------------------------------------------*/
- /* more drawing routines */
- /*-------------------------------------------------------------------------*/
-
- /* Inserts the menu into the hierarchical menu list if necessary.
- This also sets the menu's color table if there is a 'mctb'
- resource with the same ID as the menu. True is returned if the
- menu was inserted into the menu table, in which case you should
- call PopupDeleteMenu when finished using the menu. */
- static Boolean PopupInsertMenu(PopupHandle popup)
- {
- Boolean inserted;
- SignedByte state;
- Handle mctb;
-
- require(PopupValid(popup));
- inserted = false;
- if (! GetMenuHandle((**(**popup).menu).menuID)) {
- inserted = true;
- InsertMenu((**popup).menu, -1);
- if (MacHasColorQD()) {
- mctb = GetResource('mctb', (**(**popup).menu).menuID);
- if (mctb && *mctb) {
- state = HGetState(mctb);
- MoveHHi(mctb);
- HLock(mctb);
- SetMCEntries(*(short *) *mctb, (MCTablePtr) (*mctb + sizeof(short)));
- HSetState(mctb, state);
- }
- }
- }
- return(inserted);
- }
-
- /* remove the menu from the menu list */
- static void PopupDeleteMenu(PopupHandle popup, Boolean inserted)
- {
- require(PopupValid(popup));
- if (inserted)
- DeleteMenu((**(**popup).menu).menuID);
- }
-
- /* Set the 'foreColor' and 'backColor' parameters to the foreground and
- background colors for the specified part of the control, and return
- true if should use color when drawing the part. The 'textMode'
- parameter is set to grayishTextOr if text should be grayed out,
- otherwise it is set to the text mode of the current port. The
- current port should be the port into which the popup is being
- drawn. */
- static Boolean PopupPartColor(PopupHandle popup, short part,
- RGBColor *foreColor, RGBColor *backColor, short *textMode)
- {
- const RGBColor whiteRGBColor = { 0, 0, 0 };
- const RGBColor blackRGBColor = { 65535, 65535, 65535 };
- const PopupPartTableType table[kPopupPartLast - kPopupPartFirst] = {
- { kPopupPartTitleFore, kPopupPartTitleBack, kPopupPartTitleDisabled, },
- { kPopupPartItemFore, kPopupPartItemBack, kPopupPartItemDisabled, },
- { kPopupPartIconFore, kPopupPartIconBack, kPopupPartIconDisabled, },
- { kPopupPartTriangleFore, kPopupPartTriangleBack, kPopupPartTriangleDisabled, },
- { kPopupPartFrameFore, kPopupPartFrameBack, kPopupPartFrameDisabled, },
- { kPopupPartShadowFore, kPopupPartShadowBack, kPopupPartShadowDisabled, },
- };
- const PopupPartTableType *entry; /* entry in table */
- short disabledTextMode; /* mode for drawing disabled text */
- Boolean enabled; /* true if the part is enabled */
- Boolean fore; /* true if got a foreground color */
- Boolean back; /* true if got a background color */
- Boolean color; /* true if got a color */
- Boolean gray; /* true if should select a gray color */
- GrafPtr port; /* for getting current text mode */
- CCTabHandle cct; /* control's color table */
- CCTabHandle dcct; /* default control color table */
- short item; /* menu item to get color for */
-
- require((**popup).state.drawset);
- require(kPopupPartFirst <= part && part < kPopupPartLast);
-
- /* only use color if the destination port is a color port
- (otherwise, the colors would be mapped to black, which
- would be pretty ugly) */
- *textMode = (**popup).port->txMode;
- if (PopupPixelDepth(popup) > 1) {
-
- check(MacHasColorQD());
-
- /* get default colors */
- *foreColor = blackRGBColor;
- *backColor = whiteRGBColor;
- disabledTextMode = *textMode;
- gray = false;
-
- /* get control color tables */
- dcct = ControlColorTable(NULL);
- cct = ControlColorTable((**popup).ctl);
- if (cct == dcct)
- cct = NULL;
-
- /* find entry in part code table */
- entry = table + (part - kPopupPartFirst);
-
- /* determine if part is enabled */
- enabled = (**popup).attr.enabled;
- if (enabled) {
- switch (part) {
- case kPopupPartItem:
- case kPopupPartIcon:
- enabled = MenuItemEnabled((**popup).menu, (**popup).state.current);
- break;
- }
- }
-
- /* get color for part */
- switch (part) {
- case kPopupPartTitle:
- case kPopupPartItem:
- case kPopupPartIcon:
- switch (part) {
- case kPopupPartTitle:
- disabledTextMode = grayishTextOr;
- item = 0;
- break;
- case kPopupPartItem:
- disabledTextMode = grayishTextOr;
- item = (**popup).state.current;
- break;
- case kPopupPartIcon:
- item = (**popup).state.current;
- break;
- }
- color = MenuItemColor((**popup).menu, item, foreColor, backColor);
- if (! color) {
- fore = ControlCTPartColor(cct, entry->fore, foreColor);
- back = ControlCTPartColor(cct, entry->back, backColor);
- if (! fore) fore = MenuItemColorDefault(item, foreColor, NULL);
- if (! back) back = MenuItemColorDefault(item, NULL, backColor);
- if (! fore) (void) ControlCTPartColor(dcct, cTextColor, foreColor);
- if (! back) (void) ControlCTPartColor(dcct, cBodyColor, backColor);
- }
- if (! enabled) {
- color = ControlCTPartColor(cct, entry->disabled, foreColor);
- if (! color)
- *textMode = disabledTextMode;
- }
- break;
- case kPopupPartTriangle:
- case kPopupPartFrame:
- case kPopupPartShadow:
- fore = ControlCTPartColor(cct, entry->fore, foreColor);
- back = ControlCTPartColor(cct, entry->back, backColor);
- switch (part) {
- case kPopupPartTriangle:
- if (! fore) (void) ControlCTPartColor(dcct, cTextColor, foreColor);
- if (! back) (void) ControlCTPartColor(dcct, cBodyColor, backColor);
- break;
- case kPopupPartFrame:
- if (! fore) (void) ControlCTPartColor(dcct, cFrameColor, foreColor);
- break;
- case kPopupPartShadow:
- if (! fore) (void) ControlCTPartColor(dcct, cFrameColor, foreColor);
- break;
- }
- if (! enabled)
- gray = ! ControlCTPartColor(cct, entry->disabled, foreColor);
- break;
- default:
- /* error */
- check(false);
- }
-
- /* derive a gray color from a blend of the foreground and background colors */
- if (gray) {
- if ((**popup).draw.gdevice)
- (void) GetGray((**popup).draw.gdevice, backColor, foreColor);
- else
- (void) GetGray(GetMainDevice(), backColor, foreColor);
- }
- }
- return(PopupPixelDepth(popup) > 1);
- }
-
- /* Set the foreground and background colors to the colors appropriate for
- drawing the specified part of the control. */
- static void PopupPartColorSet(PopupHandle popup, short part)
- {
- RGBColor foreColor;
- RGBColor backColor;
- short mode;
-
- if (PopupPartColor(popup, part, &foreColor, &backColor, &mode)) {
- RGBForeColor(&foreColor);
- RGBBackColor(&backColor);
- TextMode(mode);
- }
- }
-
- /* draw the frame around the popup menu */
- static void PopupDrawFrame(PopupHandle popup)
- {
- Rect frame;
- PenState pen;
- ColorState color;
-
- require((**popup).state.drawset);
-
- /* save state */
- GetPenState(&pen);
- GetColorState(&color);
-
- /* draw frame */
- PopupPartColorSet(popup, kPopupPartFrame);
- frame = (**popup).r.bounds;
- if ((**popup).attr.just == teFlushRight)
- frame.right = (**popup).r.selection.right + kFrameSize + kShadowSize;
- else
- frame.left = (**popup).r.selection.left - kFrameSize;
- frame.right -= kShadowSize;
- frame.bottom -= kShadowSize;
- PenSize(kFrameSize, kFrameSize);
- FrameRect(&frame);
-
- /* draw drop shadow */
- PopupPartColorSet(popup, kPopupPartShadow);
- PenSize(kShadowSize, kShadowSize);
- MoveTo(frame.right, frame.top + kShadowOffset);
- LineTo(frame.right, frame.bottom);
- LineTo(frame.left + kShadowOffset, frame.bottom);
-
- /* restore state */
- SetPenState(&pen);
- SetColorState(&color);
- }
-
- /* draw the triangle */
- static void PopupDrawTriangle(PopupHandle popup)
- {
- Rect triangle; /* triangle's rectangle */
- short height; /* height of triangle */
- short width; /* width of current line */
- short i; /* index to lines of triangle */
- ColorState color;
-
- require((**popup).state.drawset);
- GetColorState(&color);
- PopupPartColorSet(popup, kPopupPartTriangle);
- triangle = (**popup).r.triangle;
- height = kTriangleHeight;
- width = kTriangleWidth - 1;
- for (i = 0; i < height; i++) {
- MoveTo(triangle.left + i, triangle.top + i);
- LineTo(triangle.left + i + width, triangle.top + i);
- width -= 2;
- }
- SetColorState(&color);
- }
-
- /* draw the string within the bounding rectangle; if the string is too wide,
- it is truncated and an ellipses character is appended */
- static void PopupDrawString(PopupHandle popup, Str255 str, const Rect *bounds,
- short just)
- {
- FontInfo font;
- short width;
-
- require((**popup).state.drawset);
- require(RectValid(bounds));
- GetFontInfo(&font);
- width = TruncatePString(str, bounds->right - bounds->left);
- if (just == teFlushRight)
- MoveTo(bounds->right - width, bounds->bottom - font.descent);
- else
- MoveTo(bounds->left, bounds->bottom - font.descent);
- DrawString(str);
- }
-
- /* draw the current selection string and icon */
- static void PopupDrawSelection(PopupHandle popup)
- {
- PopupDrawSelectionStructure saveState; /* saved state */
- Str255 itemString; /* string of currently selected menu item */
- FontInfo font; /* information about the current font */
- Rect selectionRect; /* rectangle to draw string in */
- short selectionHeight; /* height of current selection rectangle */
- short lineHeight; /* height of a line of text */
- Rect iconRect; /* rectangle to draw icon in */
- Point iconSize; /* size of current item's icon */
- ResType iconType; /* resource type of current item's icon */
- short iconID; /* resource ID of current item's icon */
- Handle iconHandle; /* handle to current item's icon */
- SignedByte iconState; /* state of handle to icon */
- Boolean iconDraw; /* true if should draw the icon */
- IconTransformType iconTransform; /* transformation to apply when drawing icon */
-
- require((**popup).state.drawset);
- require(! (**popup).attr.typein);
-
- /* initially, set the rectangle within which to draw the string
- equal to the entire current selection rectangle */
- selectionRect = (**popup).r.selection;
-
- /* set up drawing environment */
- PopupDrawSelectionSetup(popup, &saveState);
-
- /* set the item's color */
- PopupPartColorSet(popup, kPopupPartItem);
- EraseRect(&selectionRect);
-
- /* calculate size of current item's icon */
- MenuItemIcon((**popup).menu, (**popup).state.current,
- &iconType, &iconID, &iconSize);
-
- /* exclude icon from selection rectangle */
- if (iconSize.h > 0) {
- if (PopupJust(popup) == teFlushRight)
- selectionRect.right -= iconSize.h + kIconMarginH;
- else
- selectionRect.left += iconSize.h + kIconMarginH;
- }
-
- /* exclude triangle from selection rectangle */
- if (PopupJust(popup) == teFlushRight)
- selectionRect.left += kTriangleWidth + kTriangleMargin;
- else
- selectionRect.right -= kTriangleWidth + kTriangleMargin;
-
- /* align the bounding box within which to draw the current selection
- string with the base line of the title string and with the edge
- of the current selection rectangle */
- GetFontInfo(&font);
- lineHeight = font.ascent + font.descent + font.leading;
- selectionHeight = selectionRect.bottom - selectionRect.top;
- check(selectionHeight >= lineHeight);
- selectionRect.top += (selectionHeight - lineHeight) / 2 - 1;
- selectionRect.bottom = selectionRect.top + lineHeight;
- if (PopupJust(popup) == teFlushRight)
- selectionRect.right -= font.widMax;
- else
- selectionRect.left += font.widMax;
-
- /* fix selection rectangle */
- if (selectionRect.right < selectionRect.left)
- selectionRect.right = selectionRect.left;
- if (selectionRect.left > selectionRect.right)
- selectionRect.left = selectionRect.right;
- check(RectValid(&selectionRect));
-
- /* draw the item string */
- GetMenuItemText((**popup).menu, (**popup).state.current, itemString);
- PopupDrawString(popup, itemString, &selectionRect, PopupJust(popup));
-
- /* calculate the icon's rectangle, but don't draw the icon if
- it overlaps the item's text or the triangle */
- iconDraw = true;
- iconRect = (**popup).r.selection;
- check(iconRect.bottom - iconRect.top >= iconSize.v);
- iconRect.top += (iconRect.bottom - iconRect.top - iconSize.v) / 2;
- iconRect.bottom = iconRect.top + iconSize.v;
- if (PopupJust(popup) == teFlushRight) {
- iconRect.right -= font.widMax;
- iconRect.left = iconRect.right - iconSize.h;
- if (iconRect.left < selectionRect.right ||
- iconRect.left < (**popup).r.triangle.right)
- {
- iconDraw = false;
- }
- }
- else {
- iconRect.left += font.widMax;
- iconRect.right = iconRect.left + iconSize.h;
- if (iconRect.right > selectionRect.left ||
- iconRect.right > (**popup).r.triangle.left)
- {
- iconDraw = false;
- }
- }
-
- /* determine transformation to apply when drawing icon */
- iconTransform = ttNone;
- if (! (**popup).attr.enabled ||
- ! MenuItemEnabled((**popup).menu, (**popup).state.current))
- {
- iconTransform = ttDisabled;
- }
-
- /* set the color in which to draw black and white icons */
- PopupPartColorSet(popup, kPopupPartIcon);
-
- /* draw the icon */
- if (iconDraw && iconType) {
- switch (iconType) {
- case 'SICN':
- OffsetRect(&iconRect, 0, -1);
- iconHandle = GetResource(iconType, iconID);
- if (iconHandle && *iconHandle) {
- iconState = HGetState(iconHandle);
- HNoPurge(iconHandle);
- PlotSICN(&iconRect, iconHandle);
- HSetState(iconHandle, iconState);
- }
- break;
- case 'ICON':
- OffsetRect(&iconRect, -1, 0);
- iconHandle = GetResource(iconType, iconID);
- if (iconHandle && *iconHandle) {
- iconState = HGetState(iconHandle);
- HNoPurge(iconHandle);
- PlotIcon(&iconRect, iconHandle);
- HSetState(iconHandle, iconState);
- }
- break;
- case 'cicn':
- check(MacHasColorQD());
- iconHandle = (Handle) GetCIcon(iconID);
- if (iconHandle) {
- if (*iconHandle) {
- if (MacHasIconUtilities())
- PlotCIconHandle(&iconRect, atNone, iconTransform, (CIconHandle) iconHandle);
- else
- PlotCIcon(&iconRect, (CIconHandle) iconHandle);
- }
- DisposeCIcon((CIconHandle) iconHandle);
- }
- break;
- }
- }
-
- /* restore drawing environment */
- PopupDrawSelectionRestore(popup, &saveState);
-
- /* gray over entire selection if item is disabled and we're drawing
- into a black and white port */
- if (PopupPixelDepth(popup) == 1 &&
- ! MenuItemEnabled((**popup).menu, (**popup).state.current))
- {
- selectionRect = (**popup).r.selection;
- RectDisable(&selectionRect);
- }
- }
-
- /* draw the title string */
- static void PopupDrawTitle(PopupHandle popup)
- {
- Str255 title; /* title string */
- Rect rtitle; /* title's text rectangle */
- Rect rhilite; /* title's hilite rectangle */
- TextState saveText; /* save text state */
- ColorState saveColor; /* saved color state */
- GrafPtr port; /* port we're drawing into */
-
- require((**popup).state.drawset);
- require(! (**popup).attr.typein);
-
- /* save port */
- GetPort(&port);
- GetTextState(&saveText);
- GetColorState(&saveColor);
-
- /* get rectangles */
- rhilite = (**popup).r.hilite;
- rtitle = (**popup).r.title;
-
- /* set the title's color */
- PopupPartColorSet(popup, kPopupPartTitle);
-
- /* set the text style */
- TextFace((**popup).attr.title.style);
-
- /* draw the title string */
- EraseRect(&rhilite);
- PopupTitle(popup, title);
- PopupDrawString(popup, title, &rtitle, (**popup).attr.just);
-
- /* restore port's settings */
- SetColorState(&saveColor);
- SetTextState(&saveText);
- }
-
- /* gray out menu if it's disabled */
- static void PopupDrawEnabled(PopupHandle popup)
- {
- Rect bounds;
-
- require(PopupValid(popup));
- require((**popup).state.drawset);
- if (PopupPixelDepth(popup) == 1 && ! (**popup).attr.enabled) {
- bounds = (**popup).r.bounds;
- RectDisable(&bounds);
- }
- }
-
- /* erase the entire popup menu */
- static void PopupErase(PopupHandle popup)
- {
- Rect bounds;
-
- require((**popup).state.drawset);
- bounds = (**popup).r.maxbounds;
- EraseRect(&bounds);
- }
-
- #if DRAW_RECTANGLES
-
- /* draw frames around the parts of the popup; this is useful when
- debugging */
- static void PopupDrawRectangles(PopupHandle popup)
- {
- struct {
- long maxbounds;
- long frame;
- long shadow;
- long title;
- long hilite;
- long selection;
- long triangle;
- } partColor = {
- blackColor,
- yellowColor,
- magentaColor,
- redColor,
- cyanColor,
- greenColor,
- blueColor,
- };
- PenState penState;
- long foreColor;
- Rect shadow;
- Rect frame;
- GrafPtr port;
-
- /* setup port */
- GetPort(&port);
- GetPenState(&penState);
- foreColor = port->fgColor;
- PenNormal();
-
- /* draw outline of frame and shadow */
- if (kShadowSize > 2 && kFrameSize > 2) {
-
- /* calculate frame */
- frame = (**popup).r.bounds;
- if ((**popup).attr.just == teFlushRight)
- frame.right = (**popup).r.selection.right + kFrameSize + kShadowSize;
- else
- frame.left = (**popup).r.selection.left - kFrameSize;
- frame.right -= kShadowSize;
- frame.bottom -= kShadowSize;
-
- /* draw outline of frame */
- ForeColor(partColor.frame);
- FrameRect(&frame);
- InsetRect(&frame, kFrameSize, kFrameSize);
- FrameRect(&frame);
- InsetRect(&frame, -kFrameSize, -kFrameSize);
-
- /* draw outline of shadow */
- ForeColor(partColor.shadow);
- shadow.top = frame.bottom;
- shadow.left = frame.left + kShadowOffset;
- shadow.bottom = shadow.top + kShadowSize;
- shadow.right = frame.right + kShadowSize;
- FrameRect(&shadow);
- shadow.top = frame.top + kShadowOffset;
- shadow.left = frame.right;
- shadow.bottom = frame.bottom + kShadowSize;
- shadow.right = shadow.left + kShadowSize;
- FrameRect(&shadow);
- }
-
- /* draw the other rectangles */
-
- ForeColor(partColor.maxbounds);
- frame = (**popup).r.maxbounds;
- FrameRect(&frame);
-
- ForeColor(partColor.title);
- frame = (**popup).r.title;
- FrameRect(&frame);
-
- ForeColor(partColor.hilite);
- frame = (**popup).r.hilite;
- FrameRect(&frame);
-
- ForeColor(partColor.selection);
- frame = (**popup).r.selection;
- FrameRect(&frame);
-
- ForeColor(partColor.triangle);
- frame = (**popup).r.triangle;
- FrameRect(&frame);
-
- /* restore port */
- SetPenState(&penState);
- ForeColor(foreColor);
- }
-
- #endif /* DRAW_RECTANGLES */
-
- /* draw the menu into the current port */
- static void PopupDrawDirect(PopupHandle popup)
- {
- Boolean inserted; /* true if inserted menu into menu list */
-
- require(PopupValid(popup));
-
- /* setup drawing environment */
- PopupPortSetup(popup);
-
- /* erase the entire popup */
- PopupErase(popup);
- if ((**popup).attr.visible) {
-
- /* insert menu into menu list so we can access its color table */
- inserted = PopupInsertMenu(popup);
-
- #if DRAW_POPUP
- /* draw all the parts of the popup */
- if (! (**popup).attr.typein) {
- PopupDrawTitle(popup);
- PopupDrawSelection(popup);
- }
- PopupDrawFrame(popup);
- PopupDrawTriangle(popup);
- PopupDrawEnabled(popup);
- #endif /* DRAW_POPUP */
-
- #if DRAW_RECTANGLES
- PopupDrawRectangles(popup);
- #endif /* DRAW_RECTANGLES */
-
- /* remove the menu from the menu list */
- PopupDeleteMenu(popup, inserted);
- }
-
- /* restore drawing environment */
- PopupPortRestore(popup);
- }
-
- /* callback for DeviceLoop function when drawing without an offscreen bitmap */
- static pascal void PopupDrawDeviceLoopCallBack(short depth, short flags,
- GDHandle device, long data)
- {
- PopupHandle popup;
-
- popup = (PopupHandle) data;
- (**popup).draw.gdevice = device;
- (**popup).draw.gdepth = depth;
- PopupDrawDirect(popup);
- }
-
- /* Draw the popup using a device loop. The popup's utilRgn should be
- set to the region enclosing the popup's maxbound's rectangle, in
- global coordinates. For greater efficiency, the popup's utilRgn
- can be set to the intersection of the popup's bounding rectangle
- and its port's visRgn. */
- static void PopupDrawDeviceLoop(PopupHandle popup)
- {
- if (MacHasColorQD() && (**popup).draw.utilRgn) {
- DeviceLoop((**popup).draw.utilRgn,
- PopupDrawDeviceLoopCallBack, (long) popup, 0);
- }
- else {
- (**popup).draw.gdevice = NULL;
- PopupDrawDirect(popup);
- }
- }
-
- /* draw the popup to an offscreen bitmap, then copy the bitmap to the screen */
- void PopupDraw(PopupHandle popup)
- {
- CGrafPtr saveCPort; /* saved graphics port */
- GDHandle saveGDH; /* saved graphics device */
- Boolean changed; /* if true, image needs to be redrawn */
- Boolean direct; /* true if should draw to onscreen port */
- GWorldPtr gworld; /* temporary pointer to offscreen graphics world */
- GDHandle gdevice; /* temporary pointer to graphics device of gworld */
- short gdepth; /* temporary storage for depth of graphics world */
- Rect popupRect; /* popup's bounding rectangle */
- GWorldFlags updateFlags; /* flags returned UpdateGWorld */
-
- require(PopupValid(popup));
-
- /* don't draw popup if drawing is disabled */
- if ( ! (**popup).attr.draw)
- return;
-
- /* get the popup's changed flag; the popup is reimaged if it changed */
- changed = (**popup).state.changed;
- (**popup).state.changed = false;
- #if ! CACHE_OFFSCREEN
- changed = true;
- #endif
-
- /* calculate mask region for CopyBits */
- if ((**popup).draw.utilRgn) {
- check(EmptyRgn((**popup).draw.utilRgn));
- CopyRgn((**popup).port->clipRgn, (**popup).draw.utilRgn);
- SectRgn((**popup).port->visRgn, (**popup).draw.utilRgn,
- (**popup).draw.utilRgn);
- }
-
- /* try to draw from the offscreen graphics world */
- direct = true;
- (**popup).draw.gdepth = 0;
- (**popup).draw.gdevice = NULL;
- if ((**popup).draw.gworld) {
-
- /* use an offscreen graphics world */
- check(MacHasGWorlds());
-
- /* get the popup's bounding rectangle, in global coordinates */
- popupRect = (**popup).r.maxbounds;
- RectLocalToGlobal(&popupRect);
-
- /* Determine if the image spans multiple monitors with different
- pixel depths. If so, then using CopyBits to draw the image from
- an offscreen bitmap causes the image to be displayed poorly
- on the monitor with the smaller pixel depth. In particular,
- on a 1-bit screen, colors are mapped to black, and a disabled popup
- (grayed out) may not be displayed at all. While there may be some
- trick to getting offscreen graphics worlds to work well in this
- situation, for now we just use a regular device loop to draw the
- image to each screen device. */
- direct = false;
- if (MacHasColorQD()) {
- GDHandle mindevice;
- GDHandle maxdevice;
- mindevice = GetMinDevice(&popupRect);
- maxdevice = GetMaxDevice(&popupRect);
- if ( maxdevice &&
- mindevice &&
- mindevice != maxdevice &&
- GetDeviceDepth(mindevice) !=
- GetDeviceDepth(maxdevice))
- {
- direct = true;
- }
- }
-
- if (! direct) {
-
- /* reset direct draw flag */
- direct = true;
-
- /* According to NIM:Imaging With QuickDraw, p6-9, we only
- need to call UpdateGWorld if the pixmap has been purged,
- after the window has been moved, and after update events.
- While it is simple enough to determine if the pixmap has
- been purged or if the window has been moved, it is not
- easy for a CDEF to determine when an update event has
- occurred. So, we always call UpdateGWorld whenever the
- popup is redrawn. It doesn't hurt (except for a possible
- performance hit) to call UpdateGWorld more than necessary. */
-
- /* update the offscreen graphics world */
- gworld = (**popup).draw.gworld;
- updateFlags = UpdateGWorld(&gworld, 0, &popupRect, NULL, NULL, 0);
- if (updateFlags != gwFlagErr) {
- (**popup).draw.gworld = gworld;
-
- /* lock the offscreen pixmap */
- if (LockPixels(GetPixMap(gworld))) {
-
- /* we're drawing offscreen */
- direct = false;
-
- /* get the popup's bounding rectangle, in local coordinates */
- popupRect = (**popup).r.maxbounds;
-
- /* determine if the popup changed, and redraw it to the
- offscreen graphics world if necessary */
- if (! changed)
- changed = ((updateFlags & reallocPix) != 0);
- if (changed) {
- gdevice = GetGWorldDevice(gworld);
- gdepth = (gdevice ? GetDeviceDepth(gdevice) : 1);
- (**popup).draw.gdevice = gdevice;
- (**popup).draw.gdepth = gdepth;
- GetGWorld(&saveCPort, &saveGDH);
- SetGWorld(gworld, NULL);
- SetOrigin(popupRect.left, popupRect.top);
- check(EqualRect(&gworld->portRect, &popupRect));
- ClipRect(&popupRect);
- PopupDrawDirect(popup);
- SetGWorld(saveCPort, saveGDH);
- }
-
- /* copy the offscreen pixmap to the onscreen port */
- check(EqualRect(&gworld->portRect, &popupRect));
- CopyBits(
- &((GrafPtr) gworld)->portBits,
- &(**popup).port->portBits,
- &popupRect, &popupRect,
- srcCopy, (**popup).draw.utilRgn);
-
- /* unlock the offscreen pixmap */
- UnlockPixels(GetPixMap(gworld));
- }
- }
- }
- }
-
- /* couldn't allocate an offscreen graphics world, or conditions weren't
- appropriate for drawing using the offscreen graphics world, but
- the popup can still be drawn directly to the onscreen port */
- if (direct)
- PopupDrawDeviceLoop(popup);
-
- /* clear the utility region to save memory */
- if ((**popup).draw.utilRgn)
- SetEmptyRgn((**popup).draw.utilRgn);
- }
-
- /* hilite the popup's title; useful for keyboard equivalents of commands */
- void PopupHilite(PopupHandle popup)
- {
- RGBColor foreColor;
- RGBColor backColor;
- RGBColor hiliteColor;
- short mode;
- Rect hilite;
- Boolean color;
- Boolean inserted;
-
- require(PopupValid(popup));
- if ( (**popup).attr.visible &&
- (**popup).attr.draw &&
- ! (**popup).attr.typein)
- {
- PopupPortSetup(popup);
- hilite = (**popup).r.hilite;
- inserted = PopupInsertMenu(popup);
- color = PopupPartColor(popup, kPopupPartTitle, &foreColor, &backColor, &mode);
- if (color) {
- GetHiliteColor((CGrafPtr) (**popup).port, &hiliteColor);
- HiliteColor(&foreColor);
- RGBForeColor(&foreColor);
- RGBBackColor(&backColor);
- LMSetHiliteMode(LMGetHiliteMode() & ~(1<<hiliteBit));
- InvertRect(&hilite);
- HiliteColor(&hiliteColor);
- }
- else
- InvertRect(&hilite);
- PopupDeleteMenu(popup, inserted);
- PopupPortRestore(popup);
- }
- }
-
- /*-------------------------------------------------------------------------*/
- /* event handling */
- /*-------------------------------------------------------------------------*/
-
- /* A handle to this structure is installed in the menu's menuProc field. */
- typedef struct {
- short jmp; /* M68K jmp instruction */
- MenuDefProcPtr mdef; /* address of our MDEF routine */
- Handle proc; /* original MDEF handle */
- short width; /* width of menu */
- } PatchMenuProcStructure, **PatchMenuProcHandle;
-
- /* Menu proc to patch the mSizeMsg, allowing us to set the menu width to
- include the triangle. This is installed just before calling
- PopupMenuSelect, and is removed immediately afterwards. */
- static pascal void PatchMenuProc(short message, MenuHandle menu,
- Rect *menuRect, Point hitPt, short *whichItem)
- {
- Handle proc;
- SignedByte state;
- PatchMenuProcHandle patch;
-
- patch = (PatchMenuProcHandle) (**menu).menuProc;
- proc = (**patch).proc;
- state = HGetState(proc);
- MoveHHi(proc);
- HLock(proc);
- CallMenuDefProc((MenuDefProcPtr) *proc, message, menu, menuRect, hitPt, whichItem);
- if (message == mSizeMsg)
- (**menu).menuWidth = (**patch).width;
- HSetState(proc, state);
- }
-
- /* True if point (in local coordinates) is within the popup menu.
- Call this before calling PopupSelect. */
- Boolean PopupWithin(PopupHandle popup, Point pt)
- {
- Rect hilite;
- Rect selection;
- Boolean result;
-
- require(PopupValid(popup));
- result = false;
- if ((**popup).attr.visible && (**popup).attr.enabled) {
- hilite = (**popup).r.hilite;
- selection = (**popup).r.selection;
- result = (PtInRect(pt, &selection) || PtInRect(pt, &hilite));
- }
- return(result);
- }
-
- /* call this when there's a mouse down in a popup menu */
- void PopupSelect(PopupHandle popup)
- {
- long chosen; /* item selected from menu */
- Point location; /* top left of menu */
- Boolean inserted; /* true if inserted menu into menu list */
- Rect selection; /* current selection rectangle */
- short oldMenuWidth; /* saved menu width */
- Handle oldMenuProc; /* saved menuProc */
- short menuWidth; /* width of menu */
- PatchMenuProcHandle patch;
-
- require(PopupValid(popup));
- if (StillDown()) {
-
- /* insert menu into menu list */
- inserted = PopupInsertMenu(popup);
-
- /* hilite title */
- PopupHilite(popup);
-
- /* setup port */
- PopupPortSetup(popup);
-
- /* setup system font and size */
- PopupPortSetupSystem(popup);
-
- /* calculate position for popup menu */
- location.h = (**popup).r.selection.left;
- location.v = (**popup).r.selection.top;
- LocalToGlobal(&location);
-
- /* adjust width of menu */
- CalcMenuSize((**popup).menu);
- oldMenuWidth = menuWidth = (**(**popup).menu).menuWidth;
- if (! (**popup).attr.typein) {
-
- /* make the menu wide enough to include the triangle */
- menuWidth += kTriangleWidth + kTriangleMargin;
- if ((**popup).attr.fixedwidth) {
- /* make menu fill all of current selection rectangle */
- menuWidth = (**popup).r.selection.right - (**popup).r.selection.left;
- }
- if (menuWidth < oldMenuWidth)
- menuWidth = oldMenuWidth;
-
- /* Though we patch the menu's mdef to ignore the width calculated
- by the mSizeMsg message, we still need to explicitely set the
- menu's width. This is necessary since PopupMenuSelect on
- non-color macs (e.g., a Plus running system 7) doesn't send
- the mSizeMsg message before drawing the menu, while on other
- macs (e.g., a Quadra running system 7 Pro) the mSizeMsg is
- sent by PopupMenuSelect. */
- (**(**popup).menu).menuWidth = menuWidth;
- }
-
- /* patch the menu's mdef to ignore the width calculated by
- the mSizeMsg message */
- oldMenuProc = (**(**popup).menu).menuProc;
- if ((**popup).menuProc) {
- patch = (PatchMenuProcHandle) (**popup).menuProc;
- (**patch).jmp = ASM_M68K_JMP;
- (**patch).mdef = PatchMenuProc;
- (**patch).proc = oldMenuProc;
- (**patch).width = menuWidth;
- (**(**popup).menu).menuProc = (**popup).menuProc;
- }
-
- /* let user select an item from the menu */
- chosen = PopUpMenuSelect((**popup).menu, location.v, location.h,
- (**popup).state.current);
-
- /* restore environment */
- (**(**popup).menu).menuProc = oldMenuProc;
- (**(**popup).menu).menuWidth = oldMenuWidth;
- PopupPortRestore(popup);
- PopupPortRestoreSystem(popup);
-
- /* unhilite the menu */
- PopupHilite(popup);
-
- /* remove the menu from the menu list */
- PopupDeleteMenu(popup, inserted);
-
- /* display the selected item */
- if (LoWord(chosen))
- PopupCurrentSet(popup, LoWord(chosen));
- }
- ensure(PopupValid(popup));
- }
-
- /*-------------------------------------------------------------------------*/
- /* getting and setting attributes */
- /*-------------------------------------------------------------------------*/
-
- /* recalculate and redraw the popup menu */
- static void PopupChanged(PopupHandle popup)
- {
- (**popup).state.changed = true;
- PopupCalculate(popup);
- PopupDraw(popup);
- }
-
- /* return the version of the library that created the popup menu */
- short PopupVersion(PopupHandle popup)
- {
- return((**popup).version);
- }
-
- /* return the currently selected menu item */
- short PopupCurrent(PopupHandle popup)
- {
- require(PopupValid(popup));
- return((**popup).state.current);
- }
-
- /* set the currently selected menu item */
- void PopupCurrentSet(PopupHandle popup, short current)
- {
- short i, nitems;
-
- require(PopupValid(popup));
- nitems = CountMItems((**popup).menu);
- for (i = 1; i <= nitems; i++)
- SetItemMark((**popup).menu, i, noMark);
- SetItemMark((**popup).menu, current, (**popup).attr.mark);
- if (current != (**popup).state.current) {
- (**popup).state.current = current;
- PopupChanged(popup);
- }
- }
-
- /* turn drawing on or off */
- void PopupDrawSet(PopupHandle popup, Boolean draw)
- {
- require(PopupValid(popup));
- (**popup).attr.draw = draw;
- }
-
- /* make popup visible or invisible */
- void PopupVisibleSet(PopupHandle popup, Boolean visible)
- {
- require(PopupValid(popup));
- if (visible != (**popup).attr.visible) {
- (**popup).attr.visible = visible;
- PopupChanged(popup);
- }
- }
-
- /* set the character used to mark the current menu item */
- void PopupMarkSet(PopupHandle popup, char mark)
- {
- require(PopupValid(popup));
- if (mark != (**popup).attr.mark) {
- (**popup).attr.mark = mark;
- SetItemMark((**popup).menu, (**popup).state.current, (**popup).attr.mark);
- }
- }
-
- /* enable or disable the menu */
- void PopupEnableSet(PopupHandle popup, Boolean enabled)
- {
- require(PopupValid(popup));
- if (enabled != (**popup).attr.enabled) {
- (**popup).attr.enabled = enabled;
- PopupChanged(popup);
- }
- else if (! enabled) {
- /* the popup has to be reimaged or it won't get drawn properly
- when using CopyBits to a black and white grafport */
- PopupChanged(popup);
- }
- }
-
- /* turn type-in style popup menu on or off */
- void PopupTypeInSet(PopupHandle popup, Boolean typein)
- {
- require(PopupValid(popup));
- if (typein != (**popup).attr.typein) {
- (**popup).attr.typein = typein;
- PopupChanged(popup);
- }
- }
-
- /* return rectangle enclosing all of popup; this is the rectangle
- enclosing the parts of the popup that are actually visible, and
- may be smaller than the rectangle specified with PopupBoundsSet. */
- void PopupBounds(PopupHandle popup, Rect *bounds)
- {
- require(PopupValid(popup));
- *bounds = (**popup).r.bounds;
- ensure(RectValid(bounds));
- }
-
- /* set popup's maximum bounding rectangle; all drawing is clipped
- to this rectangle */
- void PopupBoundsSet(PopupHandle popup, const Rect *maxbounds)
- {
- Rect oldmax;
-
- require(PopupValid(popup));
- require(RectValid(maxbounds));
- oldmax = (**popup).r.maxbounds;
- if (! EqualRect(&oldmax, maxbounds)) {
- if ((**popup).attr.draw) {
- PopupPortSetup(popup);
- PopupErase(popup);
- PopupPortRestore(popup);
- }
- (**popup).r.maxbounds = *maxbounds;
- PopupChanged(popup);
- }
- }
-
- /* return popup's title string */
- void PopupTitle(PopupHandle popup, Str255 title)
- {
- *title = 0;
- if ((**popup).attr.title.str) {
- BlockMove(*(**popup).attr.title.str, title,
- **(**popup).attr.title.str + 1);
- }
- }
-
- /* set popup's title string */
- void PopupTitleSet(PopupHandle popup, const Str255 title)
- {
- Str255 oldtitle;
-
- require(PopupValid(popup));
- if ((**popup).attr.title.str) {
- PopupTitle(popup, oldtitle);
- if (! EqualString(title, oldtitle, true, true)) {
- PtrToXHand(title, (**popup).attr.title.str, *title + 1);
- PopupChanged(popup);
- }
- }
- }
-
- /* set width of popup's title; the title is resized dynamically
- if the width is zero */
- void PopupTitleWidthSet(PopupHandle popup, short width)
- {
- require(width >= 0);
- if (width != (**popup).attr.title.width) {
- (**popup).attr.title.width = width;
- PopupChanged(popup);
- }
- }
-
- /* set the text style in which the popup's title will be drawn */
- void PopupTitleStyleSet(PopupHandle popup, Style style)
- {
- if (style != (**popup).attr.title.style) {
- (**popup).attr.title.style = style;
- PopupChanged(popup);
- }
- }
-
- /* set whether the popup will use the window's font for drawing the
- title, current selection, and menu */
- void PopupUseWFontSet(PopupHandle popup, Boolean wfont)
- {
- if (wfont != (**popup).attr.wfont) {
- (**popup).attr.wfont = wfont;
- PopupChanged(popup);
- }
- }
-
- /* set whether the popup will use a fixed width or will be resized
- dynamically */
- void PopupFixedWidthSet(PopupHandle popup, Boolean fixedwidth)
- {
- if (fixedwidth != (**popup).attr.fixedwidth) {
- (**popup).attr.fixedwidth = fixedwidth;
- PopupChanged(popup);
- }
- }
-
- /* set the justification style for drawing the popup */
- void PopupJustSet(PopupHandle popup, short just)
- {
- if (just != (**popup).attr.just) {
- (**popup).attr.just = just;
- PopupChanged(popup);
- }
- }
-
- /*-------------------------------------------------------------------------*/
- /* allocation and disposal */
- /*-------------------------------------------------------------------------*/
-
- /* Create a popup menu within the rectangle in the specified port.
- Drawing is initially off. Since the popup's title is initially empty,
- you should call PopupTitleSet if you want the popup to have a title.
- When you are finished configuring the popup, call PopupDrawSet to
- enable drawing and then call PopupCalculate. The popup's rectangles
- are only calculated when drawing is enabled. The popup menu allocates
- several utility handles, but will function, albeit not as well, even
- if it can't allocate any of the utility handles. To reduce flicker,
- the popup menu is drawn to an offscreen graphics world and then copied
- to the screen. The storage for the offscreen pixmap is kept in a
- relocatable and purgeable block. */
- PopupHandle PopupBegin(GrafPtr port, MenuHandle menu,
- const Rect *maxbounds, ControlHandle ctl)
- {
- PopupHandle popup;
- Rect globalbounds;
- void *tmp;
-
- require(menu != NULL);
- require(ctl != NULL);
- require(RectValid(maxbounds));
-
- /* allocate popup */
- popup = (PopupHandle) NewHandleClear(sizeof(PopupType));
- if (popup) {
-
- /* initialize internal state */
- (**popup).version = kPopupVersion;
- (**popup).port = port;
- (**popup).menu = menu;
- (**popup).ctl = ctl;
- (**popup).private.mHandle = menu;
- (**popup).private.mID = (**menu).menuID;
- (**popup).r.maxbounds = *maxbounds;
- (**popup).state.changed = true;
-
- /* initialize flags affecting display and operation of menu */
- (**popup).attr.visible = true;
- (**popup).attr.enabled = true;
- (**popup).attr.mark = checkMark;
- #if TEST_FLUSH_RIGHT
- (**popup).attr.just = teFlushRight;
- #else
- (**popup).attr.just = PopupJust(popup);
- #endif
-
- /* allocate title */
- tmp = NewHandleClear(1);
- (**popup).attr.title.str = tmp;
-
- /* allocate region for saving and restoring the clip region */
- tmp = NewRgn();
- (**popup).draw.save.clip = tmp;
-
- /* allocate glue handle for MDEF */
- tmp = NewHandle(sizeof(PatchMenuProcStructure));
- if (tmp)
- (**popup).menuProc = tmp;
-
- /* allocate utility region */
- tmp = NewRgn();
- (**popup).draw.utilRgn = tmp;
-
- /* Allocate an offscreen graphics world. The offscreen pixmap is
- purgeable, since we can always rebuild the data it contains and
- since we can always draw the popup to the onscreen port, though
- the image may flicker a bit. */
- #if DRAW_OFFSCREEN
- if (MacHasGWorlds()) {
- Rect globalBounds;
- GWorldPtr gworld;
- GrafPtr savePort;
- GetPort(&savePort);
- SetPort((**popup).port);
- globalBounds = (**popup).r.maxbounds;
- RectLocalToGlobal(&globalBounds);
- if (NewGWorld(&gworld, 0, &globalBounds, NULL, NULL, pixPurge) == noErr)
- (**popup).draw.gworld = gworld;
- SetPort(savePort);
- }
- #endif /* DRAW_OFFSCREEN */
- }
- ensure(! popup || PopupValid(popup));
- return(popup);
- }
-
- /* dispose of the popup menu */
- void PopupEnd(PopupHandle popup)
- {
- require(! popup || PopupValid(popup));
- if (popup) {
- if ((**popup).menuProc) DisposeHandle((**popup).menuProc);
- if ((**popup).draw.gworld) DisposeGWorld((**popup).draw.gworld);
- if ((**popup).draw.utilRgn) DisposeRgn((**popup).draw.utilRgn);
- if ((**popup).draw.save.clip) DisposeRgn((**popup).draw.save.clip);
- if ((**popup).attr.title.str) DisposeHandle((**popup).attr.title.str);
- DisposeHandle((Handle) popup);
- popup = NULL;
- }
- ensure(! PopupValid(popup));
- }
-