POP-UP MENU MODULE DOCUMENTATION Introduction It has been perceived by some that Intuition's requirement that menus be attached to the very top of screens is a flaw. Many windowing systems provide "pop-up" menus which can appear anywhere on the screen, typically for context-specific input of a multiple-choice selection. In the interests of experimenting with the Amiga's user interface (in this case extending it to include pop-up menus), the code bundled with this document is placed in the public domain for use, alteration, discussion, and enhancement. I hope someone somewhere finds it useful. Derek Zahn (my Amiga hat -- Gambit Software, Madison WI) USENET: ...!{allegra,heurikon,ihnp4,seismo,ucbvax}!uwvax!derek Files makefile: constructs the demonstration program 'demo' with 'make'. The command 'make pop.o' will just make the pop-up menu object module pop.c: source file for the pop-up menu module popmenu.h: include file containing essential constant definitions; should be included by any program using pop-up menus demo.c: example program making use of the pop-up menu module 1. Features This small chunk of code provides pop-up menu functionality that utilizes data structures compatible with the existing methods for specifying conventional Intuition menus. As will be described in sections 3 and 4 of this document, some flag values have been added to provide control useful for pop-up menus, some flag values are not meaningful here, some are unsupported, and some fields of the Menu structure are used in ways different from Intuition's. A potential user of the code should read and understand this document and examine the simple sample cases provided in the demonstration program. The interface, as will be seen in section 2, is a very simple blocking function call that will retrieve a pop-up menu selection from the specified menu. This interface differs considerably from the normal Intuition menu interface which sends messages to the user's IDCMP message port. The reason for this difference is as follows: The standard Intuition menu strip associated with a particular window is available for use any time the window is active (unless specifically shut off). The physical mechanism allowing the user to communicate her desire to make a menu choice, and then to do so, is built into Intuition at a fundamental level. This is not the case for an application-level pop-up menu facility. There is no clear universally acceptable method for removing from the application program the responsibility for recognizing the need to invoke the pop-up menu function. I had considered writing another level of interface to this code that would spawn off a Task which would be responsible for rendering and handling the pop-up menus in response to recognized human interaction, and then notify the program via the IDCMP port with a new set of IntuiMessage Classes (such as POPMENUVERIFY, POPMENUHIT, etc). However, I became bogged down in trying to devise a good scheme for registering pop-up menus and then detecting an appropriate time to bring up them up. I would welcome discussion on this point. Despite the lack of procedural alignment with Intuition menus (these pop-up menus are much more like AutoRequests), clearly there are instances where the interface is reasonably natural anyway. For instance, pop-up menus might be very handy for providing a quick way, in a word processing program, to change the font of a selected bunch of text -- using a pop-up menu saves the mouse movement required to make an Intuition menu selection, allows conservation of space that would be taken up by window gadgets to perform similar operations, and requires less memorization than keyboard-command equivalents (although Power Users may very well migrate there as they become familiar with the program). These pop-up menus are constructed with standard Intuition Menu and MenuItem structures. Most of the features provided by Intuition menus are supported in nearly identical fashion, as is described in sections 3 and 4. There are several notable exceptions, however; I do feel that this code is incomplete. The facility for providing sub-items is not supported, nor are multiple menus in a single pop-up menu. There is also no scheme for supporting extended selection. See the Appendix for a discussion of these lacks and other qualms I have about the code in its current form. 2. PopChoose The user interface: #include #include "popmenu.h" LONG PopChoose(menu, win) struct Menu *menu; struct Window *win; The argument 'win' specifies the window that is to act as the "parent" of the the pop-up menu to be rendered. If this value is NULL, the currently active window is used. The argument 'menu' is a specification for the pop-up menu, as described in sections 3 and 4 below. If an error is detected or no valid selection is made by the user, -1 is returned. If a selection is made, a non-negative value corresponding to the choice is returned, and is computed as follows: The first MenuItem in the Menu is numbered zero; thereafter, each is numbered one higher than the last as the list of MenuItems is traversed. This is the same scheme Intuition uses for the Code field of the class MENUHIT IntuiMessages. PopChoose() creates a new window in which it renders the menu. It is up to the caller to be sure that nobody is scribbling indiscriminately on the screen where the menu is to go. IMPORTANT POINT: In order to link the pop.o module into an application program, somewhere in that application the following global variables MUST be declared: struct IntuitionBase *IntuitionBase; struct GfxBase *GfxBase; Then, before PopChoose is called, IntuitionBase MUST be set to the result of successfully opening the intuition.library, and GfxBase MUST be set to the result of successfully opening the graphics.library. The potential for disaster if these steps are not properly taken is great indeed. If you do not understand exactly what is required, examine the code in the supplied demonstration program, 'demo.c'. 3. The Menu structure This section describes how the fields of the Menu structure are interpreted by PopChoose(). Some of the fields are interpreted differently than they are by Intuition; understand the differences before using the code. LeftEdge, TopEdge These fields contain the upper left corner values for the menu. Their meaning (and perhaps even value) is modified by the Flags POPPOINTREL, POPWINREL, POPTIDY, and POPREMEMBER, as described below. Width, Height The use of these fields differs considerably from Intuition. Here, they give the TOTAL size of the menu -- all of the MenuItems (and the title, if present) must fit inside the rectangle defined by these variables. MenuNext This field is ignored. MenuName Use of this field is confusing, as it affects the positioning of the MenuItems in the menu. If this field is NULL, there is no title present and the offsets in the MenuItem structures represent true offsets from the UL corner of the menu. If the field is non-null, it is assumed to point to a null-terminated character string which will be rendered in the top of the menu as the title. In this case, POPTITLEHEIGHT (defined in "popmenu.h") will be silently added to the the TopEdge of each of the MenuItems to take the title into account. This confusing behavior is the result of providing optional menu titles and still retaining compatibility with Intuition's notions about what the TopEdge field in a MenuItem means. FirstItem As with Intuition, this points to the list of MenuItem structures that make up the choices in the menu. This list may be empty. Flags Flags for Menus mentioned in the Intuition Reference Manual: MENUENABLED If this flag is not set in the menu passed to PopChoose(), -1 will be returned immediately. MIDRAWN This flag is unused. Menu flags (defined in "popmenu.h") added for use by the pop-up menus: By default, the values in LeftEdge and TopEdge are absolute offsets from the upper left corner of the screen. The following flags alter this. POPPOINTREL This flag, if set, specifies that the LeftEdge and TopEdge fields are to represent offsets from the pointer position when PopChoose() was called. POPWINREL This flag, if set, specifies that the LeftEdge and TopEdge fields represent offsets from the upper left corner of the "parent" window. The POPPOINTREL flag takes priority over POPWINREL if both are set. POPREMEMBER This flag, if set, takes effect if the menu passed to PopChoose() has previously been used to make a successful selection. Once this has happened and if the POPREMEMBER flag is set, the TopEdge and LeftEdge fields will be altered so that on the next call, the menu will appear relative to the pointer in such a manner that the last-selected item will be pre-selected. POPTIDY This flag, if set, makes sure that the whole menu appears on the screen by adjusting its location if part of it would not appear on the screen. It should be noted that this process can frustrate the intentions of the POPPOINTREL, POPWINREL, and POPREMEMBER flags by making the menu appear in an "unexpected" place; however, it does assure that the menu will be rendered and that all its items are on-screen. POPUSED This flag is for internal use by PopChoose() -- it indicates whether the next call to PopChoose() should treat the POPREMEMBER flag as being in effect. These next flags determine the conditions under which a menu selection is considered made. Note that it is ABSOLUTELY REQUIRED that at least one of { POPLEFTBUTTON, POPRIGHTBUTTON } be set AND at least one of { POPTRIGGERUP, POPTRIGGERDOWN } be set -- if these conditions are not met, the call to PopChoose() will never return! POPLEFTBUTTON Treat the left mouse button as "important" in determining when the user is through with the menu. Both this and POPRIGHTBUTTON may be set, in which case, both buttons are important. POPRIGHTBUTTON Treat the right mouse button as "important" in determining when the user is through with the menu. POPTRIGGERUP Send the menu away, making a selection if the pointer is over an item, and not if not, when the "important" button or buttons is/are released. Both this and POPTRIGGERDOWN may be set, in which case both are in effect. POPTRIGGERDOWN Send the menu away, making a selection if the pointer is over an item, and not if not, when the "important" button or buttons is/are depressed. 4. The MenuItem structure This section describes how the fields of the MenuItem structure are interpreted by PopChoose(). Their use is nearly identical to their use under Intuition. NextItem The next MenuItem in the list LeftEdge, TopEdge These values hold the coordinates of the upper left hand corner of the select box for the item, relative to the upper left hand corner of the menu itself. If the menu has a title, TopEdge will be silently incremented by POPTITLEHEIGHT for rendering purposes to make room for the title. This will be transparent to you (except when specifying the Height field of the Menu structure, as noted above). Width, Height These fields are the height and width of the select box for this item. MutualExclude Items with the the CHECKIT flag set (see below) may form mutual exclusion groups. If this item is not CHECKED and is selected, all items designated with the MutualExclude field will be un-CHECKED. This field is a bitmask where each bit refers to a single MenuItem in the list attached to the Menu. The first MenuItem corresponds to bit zero, the second to bit one, and so on. Setting a bit in this mask puts the corresponding MenuItem into a mutual exclusion group with this item. If you place an item in its own mutual exclusion group, it will never be CHECKED. In most cases this is not what you want, but who knows? ItemFill, SelectFill Exactly as described in the Intuition Reference Manual. Command This field is ignored - there are no keyboard equivalents for pop-up menus. SubItem In the current implementation, this field is ignored; menu sub-items are unsupported. NextSelect This field is unused -- extended selections are not supported. Flags There are no pop-up-menu-specific flags for the MenuItem structure. The following are the flags defined in the Intuition Reference Manual along with descriptions of how their use relates to pop-up menus. CHECKIT, CHECKED These flags provide checkmarking functionality identical to that of Intuition's menus. If the user tries to select a MenuItem that is CHECKED, -1 will ALWAYS be returned; that is the nature of checkmarking. HIGHCOMP, HIGHBOX, HIGHIMAGE, HIGHNONE Just as with Intuition, these specify the highlighting mode desired. If none of these are set, HIGHIMAGE is the default (because of the screwy way Intuition defines the flag values); if no alternate imagery is supplied in the SelectFill field, the effect will be the same as if HIGHNONE was chosen. ITEMTEXT This flag's function is identical to that explained in the Intuition Reference Manual. ITEMENABLED As in Intuition, if this flag is not set, the item is "ghosted" and unavailable for selection. An attempt by the user to select an item that is not ITEMENABLED will result in a return of -1. ISDRAWN, HIGHITEM, COMMSEQ These flags are unused. Appendix: Problems This code is not nearly perfect, although I confess my inability to provide either clearly better solutions to the problems that make me fidget or reasonably simple implementation schemes for the missing features. This Appendix details to some extent the concerns with which I send this code into the Amiga community. First, the inconsistencies in user interface. Although it still seems to me that the consistent way to do these menus would have involved the delivery of IntuiMessages, the current scheme seems workable in nearly all scenarios I have imagined; in fact, PopChoose() seems simpler to use the way it is now. Suggestions or enhancements for the interface would be welcome. Second, the POPREMEMBER business makes me a little nervous; altering the LeftEdge and TopEdge fields seems like a kludge. Third, the way the optional menu title is handled seems too strange. If the offsets of the MenuItems are going to be increased, it seems like the size of the window should be as well, in the interest of "transparency". But I couldn't bring myself to go around altering the window size.... Fourth, the code could probably be improved in a couple of ways: there is altogether too much looping through MenuItem lists, and as always there may be bugs lurking in the code somewhere. (clipping???) Fifth, there are several crucial features missing, with no clue how they might be added: subitems, extended select. In addition, the keyboard equivalents for Intuition menu items would be nice to support, but it is not at all clear to me how this would even be possible. Sixth, bugs. There are two that I know of. 1: although the case where the POPTIDY flag is not set and the menu extends past the bottom and/or right edge of the screen is supported (the window is altered in size), the case where the screen's top and/or left border(s) is/are violated is not handled; the menu will not be placed if this happens, and -1 returned (at least under 1.2 this is what happens -- I am not sure if the OpenWindow() call behaves the same under 1.1). Speaking of which, I just examined how the X window system (or at least the 'xterm' application) handles pop-up menus that fly off the screen. It appears to use the equivalent of my POPTIDY flag, except when the menu would violate the bottom edge of the screen. In this case, the menu is moved up as expected and THE POINTER IS MOVED so that it is where it should be in relation to the menu. A little voice in my head gasped and said, "Never, Ever, move the user's pointer!!!", but is this wrong or what? Should I add a POPMOVEPOINTER flag to work in conjunction with POPTIDY? 2: it is possible for the user to do the mouse operation that would end the menu request before the window is rendered. In this case, the event is not noticed and must be repeated. This could be remedied if I could figure out how to query the state of the mouse buttons just after the window is opened.