[comp.sources.amiga] v02i009: pop-up menu source code

derek@wisc.edu (Derek Zahn) (07/26/87)

    This is a niftly little hack that some people may want to include in
their programs!  It provides pop up menus that appear where-ever your
mouse is on the screen.  Thanx Derek!
    As he says, please send all bug reports/enhancements to him!
	-Doc

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	Makefile
#	README
#	popdoc
#	demo.c
#	pop.c
#	popmenu.h
# This archive created: Sat Jul 25 18:58:24 1987
# By:	Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > Makefile
demo: demo.o pop.o
  ln -O demo demo.o pop.o -lc
SHAR_EOF
cat << \SHAR_EOF > README
This is the source code to a module providing pop-up menu functionality
that is reasonably compatible with Intuition menus.  I hope somebody out
there likes it.  Any bug reports, discussion, enhancements, etc, quite
welcome.  The code was written for verson 3.4a of Manx C, but portability
to other C compilers should prove little trouble.

derek
SHAR_EOF
cat << \SHAR_EOF > popdoc
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 <intuition/intuition.h>
#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.
SHAR_EOF
cat << \SHAR_EOF > demo.c
/*
 *  Simple Demonstration Program Using Pop-up Menus
 *
 *  Written by Derek Zahn (Gambit Software, Madison WI), July 1987
 *
 *  This code is freely distributable and is blessed by its author for
 *  inclusion, in this form or any other, into Amiga programs,
 *  commercial or non-commercial.  If this is done, no credit must be
 *  given to me (although I wouldn't mind).
 *
 *  I think that the movmem() function used here is Manx-specific.  This
 *  code developed and tested under 1.2 and Manx C v. 3.40a
 */

/* include files */

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#include "popmenu.h"

extern LONG PopChoose();  /* the Pop-up Menu call */

extern VOID CloseLibrary(), CloseWindow(), ReplyMsg(), FreeMem();
extern struct IntuiMessage *GetMsg();
extern struct Window *OpenWindow();
extern APTR OpenLibrary(), AllocMem();

/* This is the window in which the pop-up menus will appear */

struct NewWindow demo_window = {
	100, 50,
	300, 50,
	(UBYTE) -1, (UBYTE) -1,
	MOUSEBUTTONS | CLOSEWINDOW,
	ACTIVATE | RMBTRAP | WINDOWCLOSE | WINDOWDRAG,
	NULL,
	NULL,
	(UBYTE *) "Pop-up menu demo",
	NULL,
	NULL,
	0, 0,
	0, 0,
	WBENCHSCREEN
};

/* Following are the two images that are used in the demo menu */

#define PIC2SIZE 240
USHORT *chipped_pic2;

USHORT demo_pic2_imageData[] =
{
0xFFFF,0xFFFF,0xFF80,0xFFFF,0xFFFF,0xFF80,
0xFC1F,0xC1FC,0x1F80,0xF80F,0x80F8,0x0F80,
0xF007,0x0070,0x0780,0xE082,0x0820,0x8380,
0xF007,0x0070,0x0780,0xF80F,0x80F8,0x0F80,
0xFC1F,0xC1FC,0x1F80,0xFEBF,0xF7FE,0xBF80,
0xFF7F,0xEBFF,0x7F80,0xFC1F,0xC1FC,0x1F80,
0xF80F,0x80F8,0x0F80,0xF007,0x0070,0x0780,
0xE082,0x0820,0x8380,0xF007,0x0070,0x0780,
0xF80F,0x80F8,0x0F80,0xFC1F,0xC1FC,0x1F80,
0xFFFF,0xFFFF,0xFF80,0xFFFF,0xFFFF,0xFF80,

0x0000,0x0000,0x0000,0x03E0,0x3E03,0xE000,
0x0410,0x4104,0x1000,0x0BE8,0xBE8B,0xE800,
0x1635,0x6356,0x3400,0x2EBA,0xEBAE,0xBA00,
0x1635,0x6356,0x3400,0x0BE8,0xBE8B,0xE800,
0x0410,0x4104,0x1000,0x03E0,0x3E03,0xE000,
0x03E0,0x3E03,0xE000,0x0410,0x4104,0x1000,
0x0BE8,0xBE8B,0xE800,0x1635,0x6356,0x3400,
0x2EBA,0xEBAE,0xBA00,0x1635,0x6356,0x3400,
0x0BE8,0xBE8B,0xE800,0x0410,0x4104,0x1000,
0x03E0,0x3E03,0xE000,0x0000,0x0000,0x0000
};
struct Image demo_pic2_image =
{
10,0,          /* LeftEdge, TopEdge */
41,20,2,      /* Width, Height, Depth */
NULL,        /* filled in later */
3,0,        /* PlanePick, PlaneOnOff */
NULL,        /* Pointer to next Image */
};

#define PIC1SIZE 96
USHORT *chipped_pic1;

USHORT demo_pic1_imageData[] =
{
0xFFFF,0xFFFF,0xC000,0xFFFC,0x07FF,0xC000,
0xFFFB,0xFBFF,0xC000,0xE007,0xFC01,0xC000,
0xE007,0xFC01,0xC000,0xFFFB,0xFBFF,0xC000,
0xFFFC,0x07FF,0xC000,0xFFFF,0xFFFF,0xC000,

0x0000,0x0000,0x0000,0x0002,0x0000,0x0000,
0x0004,0x3400,0x0000,0x1FF8,0x6000,0x0000,
0x0000,0x63FE,0x0000,0x0004,0x6400,0x0000,
0x0002,0x0000,0x0000,0x0000,0x0000,0x0000
};

struct Image demo_pic1_image =
{
10,1,          /* LeftEdge, TopEdge */
34,8,2,      /* Width, Height, Depth */
NULL,  /* Set Later */
3,0,        /* PlanePick, PlaneOnOff */
NULL,        /* Pointer to next Image */
};

/* This is the list of demo menu MenuItems.  Note the use of the flags and */
/* the effects they have on what is displayed.                             */

struct IntuiText demo_text6_text = {
	(UBYTE) 0, (UBYTE) 1,
	JAM1,
	(SHORT) 2 + CHECKWIDTH, (SHORT) 2,
	NULL,
	(UBYTE *) "Mut Ex 3",
	NULL
};

struct MenuItem demo_text6 = {
	NULL,
	(SHORT) 0, (SHORT) 90,
	(SHORT) 100, (SHORT) 10,
	(USHORT) (ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT),
	(LONG) 0x0c0,
	(APTR) &demo_text6_text,
	NULL,
	(BYTE) 0,
	NULL,
	(USHORT) 0
};

struct IntuiText demo_text5_text = {
	(UBYTE) 0, (UBYTE) 1,
	JAM1,
	(SHORT) 2 + CHECKWIDTH, (SHORT) 2,
	NULL,
	(UBYTE *) "Mut Ex 2",
	NULL
};

struct MenuItem demo_text5 = {
	&demo_text6,
	(SHORT) 0, (SHORT) 80,
	(SHORT) 100, (SHORT) 10,
	(USHORT) (ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT),
	(LONG) 0x140,
	(APTR) &demo_text5_text,
	NULL,
	(BYTE) 0,
	NULL,
	(USHORT) 0
};

struct IntuiText demo_text4_text = {
	(UBYTE) 0, (UBYTE) 1,
	JAM1,
	(SHORT) 2 + CHECKWIDTH, (SHORT) 2,
	NULL,
	(UBYTE *) "Mut Ex 1",
	NULL
};

struct MenuItem demo_text4 = {
	&demo_text5,
	(SHORT) 0, (SHORT) 70,
	(SHORT) 100, (SHORT) 10,
	(USHORT) (ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT),
	(LONG) 0x180,
	(APTR) &demo_text4_text,
	NULL,
	(BYTE) 0,
	NULL,
	(USHORT) 0
};

struct IntuiText demo_text3_text = {
	(UBYTE) 2, (UBYTE) 1,
	JAM1,
	(SHORT) 2, (SHORT) 2,
	NULL,
	(UBYTE *) "Shadow",
	NULL
};

struct MenuItem demo_text3 = {
	&demo_text4,
	(SHORT) 0, (SHORT) 60,
	(SHORT) 100, (SHORT) 10,
	(USHORT) (ITEMTEXT),
	(LONG) 0,
	(APTR) &demo_text3_text,
	NULL,
	(BYTE) 0,
	NULL,
	(USHORT) 0
};

struct MenuItem demo_image3 = {
	&demo_text3,
	(SHORT) 5, (SHORT) 40,
	(SHORT) 60, (SHORT) 20,
	(USHORT) (HIGHBOX | ITEMENABLED),
	(LONG) 0,
	(APTR) &demo_pic2_image,
	NULL,
	(BYTE) 0,
	NULL,
	(USHORT) 0
};

struct MenuItem demo_image2 = {
	&demo_image3,
	(SHORT) 0, (SHORT) 30,
	(SHORT) 100, (SHORT) 10,
	(USHORT) (HIGHBOX),
	(LONG) 0,
	(APTR) &demo_pic1_image,
	NULL,
	(BYTE) 0,
	NULL,
	(USHORT) 0
};

struct IntuiText demo_text2_text = {
	(UBYTE) 0, (UBYTE) 1,
	JAM1,
	(SHORT) 2 + CHECKWIDTH, (SHORT) 2,
	NULL,
	(UBYTE *) "Text 2",
	NULL
};

struct IntuiText demo_text2_alt = {
	(UBYTE) 3, (UBYTE) 1,
	JAM1,
	(SHORT) 2 + CHECKWIDTH, (SHORT) 2,
	NULL,
	(UBYTE *) "tEXT 2",
	NULL
};

struct MenuItem demo_text2 = {
	&demo_image2,
	(SHORT) 0, (SHORT) 20,
	(SHORT) 100, (SHORT) 10,
	(USHORT) (ITEMTEXT | HIGHIMAGE | ITEMENABLED | CHECKIT | MENUTOGGLE),
	(LONG) 0,
	(APTR) &demo_text2_text,
	(APTR) &demo_text2_alt,
	(BYTE) 0,
	NULL,
	(USHORT) 0
};

struct MenuItem demo_image1 = {
	&demo_text2,
	(SHORT) 0, (SHORT) 10,
	(SHORT) 100, (SHORT) 10,
	(USHORT) (HIGHCOMP | ITEMENABLED),
	(LONG) 0,
	(APTR) &demo_pic1_image,
	NULL,
	(BYTE) 0,
	NULL,
	(USHORT) 0
};

struct IntuiText demo_text1_text = {
	(UBYTE) 0, (UBYTE) 1,
	JAM1,
	(SHORT) 2, (SHORT) 2,
	NULL,
	(UBYTE *) "Text 1",
	NULL
};

struct MenuItem demo_text1 = {
	&demo_image1,
	(SHORT) 0, (SHORT) 0,
	(SHORT) 100, (SHORT) 10,
	(USHORT) (ITEMTEXT | HIGHBOX | ITEMENABLED),
	(LONG) 0,
	(APTR) &demo_text1_text,
	NULL,
	(BYTE) 0,
	NULL,
	(USHORT) 0
};

/* This menu is attached to the Right mouse button.  It shows the use of a */
/* title, and the POPPOINTREL and POPTIDY flags for menu positioning.      */

struct Menu demo_menu1 = {
	NULL,
	(SHORT) -1 * ((102 + POPTITLEHEIGHT) / 2),
	(SHORT) -1 * POPTITLEHEIGHT / 2,
	(SHORT) 100, (SHORT) 102 + POPTITLEHEIGHT,
	(USHORT) (MENUENABLED | POPTIDY | POPPOINTREL | POPRIGHTBUTTON | 
	  POPTRIGGERUP),
	(BYTE *) "Sample",
	&demo_text1
};

/* This menu is attached to the Left mouse button.  It is title-less (note */
/* that it is shorter than the one with the title).  This demonstrates the */
/* POPREMEMBER flag and the lack of POPTIDY.  Note that it uses the same   */
/* MenuItems as demo_menu1; this is only a demo, and that was easy         */

struct Menu demo_menu2 = {
	NULL,
	(SHORT) 0, (SHORT) 0,
	(SHORT) 100, (SHORT) 102,
	(USHORT) (MENUENABLED | POPPOINTREL | POPLEFTBUTTON | 
	  POPTRIGGERUP | POPREMEMBER),
	NULL,
	&demo_text1
};

/* In order to use the pop-up menus, the application MUST make the         */
/* following declarations.                                                 */

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

main()
{
	struct Window *win;
	SHORT val, class, code;
	struct IntuiMessage *message;

	/* The libraries MUST be opened in this or a similar manner in     */
	/* order to use pop-up menus.                                      */

	IntuitionBase = (struct IntuitionBase *) 
	  OpenLibrary("intuition.library", 0L);
	if(!IntuitionBase)
		exit(0);
	GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0L);
	if(!GfxBase) {
		CloseLibrary(IntuitionBase);
		exit(0);
	}

	win = OpenWindow(&demo_window);
	if(!win) {
		CloseLibrary(IntuitionBase);
		CloseLibrary(GfxBase);
	}

	/* make sure the image data is in CHIP ram */

	chipped_pic1 = (USHORT *) AllocMem((LONG) PIC1SIZE, MEMF_CHIP);
	movmem(&demo_pic1_imageData[0], chipped_pic1, PIC1SIZE);
	demo_pic1_image.ImageData = chipped_pic1;
	chipped_pic2 = (USHORT *) AllocMem((LONG) PIC2SIZE, MEMF_CHIP);
	movmem(&demo_pic2_imageData[0], chipped_pic2, PIC2SIZE);
	demo_pic2_image.ImageData = chipped_pic2;

	while(1) {
		Wait((ULONG) 1L << win->UserPort->mp_SigBit);
		while(message = GetMsg(win->UserPort)) {
			class = message->Class;
			code = message->Code;
			ReplyMsg(message);
			switch(class) {
			  case MOUSEBUTTONS:
			  	switch(code) {
				  case SELECTDOWN:
					val = (SHORT) PopChoose(&demo_menu2,
					  win);
					printf("val %d\n", val);
					break;
				  case MENUDOWN:
					val = (SHORT) PopChoose(&demo_menu1,
					  NULL);
					printf("val %d\n", val);
					break;
				  default:
				  	break;
				}
				break;
			  case CLOSEWINDOW:
			  	CloseWindow(win);
				CloseLibrary(IntuitionBase);
				CloseLibrary(GfxBase);
				FreeMem(chipped_pic1, (LONG) PIC1SIZE);
				FreeMem(chipped_pic2, (LONG) PIC2SIZE);
				exit(0);
			  default:
			  	break;
			}
		}

	}
}
SHAR_EOF
cat << \SHAR_EOF > pop.c
/*
 *  The functions for pop-up menus
 *
 *  Written by Derek Zahn (Gambit Software, Madison WI), July 1987
 *
 *  This code is freely distributable and is blessed by its author for
 *  inclusion, in this form or any other, into Amiga programs,
 *  commercial or non-commercial.  If this is done, no credit must be
 *  given to me (although I wouldn't mind).
 *
 *  This code was developed and tested under Manx Aztec C, version 3.40a
 *  with small code, small data, and short integers as part of the Gambit
 *  Software development environment.  It has been "unGambitized" for
 *  general use.  I am unfamiliar with other Amiga C compilers, so cannot
 *  speculate on any porting difficulties.  This file was created with a
 *  text editor (Z) whose tabstops were set to 8, so that it may be easily
 *  and intelligibly printed.  This code was developed under 1.2; I am
 *  not sure if it will work under 1.1, but can't see why not.
 *
 *  Note that there are some features that should be supported but are not,
 *  and some issues about the function and interface that make me nervous.
 *  These are explained in the appendix to the documentation.  I would
 *  greatly appreciate receiving any enhancements and modifications to this
 *  code, or suggestions therefor.  Comments on techniques and coding
 *  style are always appreciated.  Enjoy.
 */

/* include files */

#include <exec/types.h>
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#include "popmenu.h"

/* Externally defined functions used in this module */

extern struct Window *OpenWindow();
extern struct IntuiMessage *GetMsg(); /* type coercion, true... */
extern VOID CloseWindow(), ReplyMsg(), Wait();
extern VOID RectFill(), Move(), Draw(), Text(), PrintIText(), DrawImage();

/* The following functions are defined in this module */

extern LONG PopChoose();  /* blocking user interface -- exported */
extern SHORT pop_computestate(); /* see who is selected, if anybody */
extern VOID pop_highlight(); /* highlight the specified item */
extern VOID pop_unhighlight(); /* unhighlight the specified item */
extern VOID pop_do_highlighting(); /* high or un high light the item */
extern VOID pop_render(); /* draws the title (if existent) and menu items */
extern VOID pop_draw_menuitem(); /* draws the menu item */
extern struct MenuItem *pop_getitem(); /* find a MenuItem struc */
extern SHORT pop_strlen(); /* local strlen() */

/* This is structure will be used to create a window for display of the    */
/* menu.  In my heart of hearts, I wanted to use graphics library          */
/* functions instead, but reason prevailed.  Note the use of the RMBTRAP   */
/* flag -- while the pop-up menu is being processed, there is no use for   */
/* the right button.  Perhaps this should only be set if the right button  */
/* has some bearing on the pop-up menu.                                    */

static
struct NewWindow pop_window = {
	0, 0, /* LeftEdge, TopEdge: will be filled in later */
	0, 0, /* Width, Height: will be filled in later */
	(UBYTE) -1, (UBYTE) -1, /* BlockPen, DetailPen */
	MOUSEBUTTONS | MOUSEMOVE, /* IDCMP flags */
	SMART_REFRESH | REPORTMOUSE | ACTIVATE | RMBTRAP, /* flags */
	NULL, /* no gadgets */
	NULL, /* checkmark inherited later */
	NULL, /* no title */
	NULL, /* Screen -- will be filled in later */
	NULL, /* No custom bitmap */
	0, 0, /* MinWidth, MinHeight -- no change in size necessary */
	0, 0, /* MaxWidth, MaxHeight -- no change in size necessary */
	CUSTOMSCREEN /* always use this value */
};

/* It is assumed that the following point to bases of opened libraries     */

extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;

/*
 * PopChoose(menu, win)
 * menu -- pointer to the menu to pop
 * win -- the window to which this menu relates.  NULL means the currently
 * active window.
 *
 * This function provides a blocking pop-up menu.  It returns (LONG) -1 if 
 * either an error occurred attempting to pop or if no selection was made
 * by the user.  If a selection was made, a LONG between 0 and n-1, where
 * n is the number of Menu Items.
 *
 * -1 is also returned if a selection of a checked item was made.
 *
 * Since this code opens a window, it is up to the caller to be sure that
 * no scribbling in droll ways is done while this code is in progress.
 */

LONG
PopChoose(menu, win)
struct Menu *menu;
struct Window *win;
{
	struct Screen *screen; /* the window's screen */
	struct Window *popwin; /* the pop-up menu */
	struct IntuiMessage *message; /* our eyes and ears */
	struct MenuItem *sel_item; /* the selected item */
	SHORT pop_state, pop_newstate; /* menu selection state varaibles   */
	SHORT mouse_moved; /* keeps track of whether the mouse has moved   */
	SHORT finished; /* set when menu should be blown away */
	SHORT class; /* incoming IntuiMessage class */
	SHORT code; /* incoming IntuiMessage code */
	ULONG exclude; /* for handling mutual exclusion */

	/* Check to see that IntuitionBase and GfxBase are non-null.       */
	/* While this is not any sort of guarantee against disaster, it    */
	/* is better than nothing.                                         */

	if((IntuitionBase == NULL) || (GfxBase == NULL))
		return((LONG) (-1));

	/* One paranoid check */

	if(menu == NULL)
		return((LONG) (-1));

	/* If the menu is not MENUENABLED, nothing to do                   */

	if(!(menu->Flags & MENUENABLED))
		return((LONG) (-1));

	/* Form the menu window to blast forth into the Visual World. Note */
	/* the unconventional (and inconsistent with Intuition) ways that  */
	/* the Width and Height fields are used here.                      */

	pop_window.Width = menu->Width;
	pop_window.Height = menu->Height;

	if(win == NULL)
		win = IntuitionBase->ActiveWindow;
	if(win == NULL) /* panic */
		return((LONG) (-1));

	/* Inherit CheckMark from the "parent" window                      */

	if(win->CheckMark)
		pop_window.CheckMark = win->CheckMark;

	screen = win->WScreen;
	pop_window.Screen = screen;

	pop_window.LeftEdge = menu->LeftEdge;
	pop_window.TopEdge = menu->TopEdge;

	/* if we are supposed to return to the last-selected menu item and */
	/* such a beast exists, all other positioning information (except  */
	/* POPTIDY) will be circumvented.  The menu will appear under the  */
	/* pointer with the last-chosen item pre-selected, if this is      */
	/* possible given the POPTIDY flag and the screen constraints.     */
	/* In this case, the LeftEdge and TopEdge fields of the menu       */
	/* structure will have been altered (I know, ick!) to provide a    */
	/* relative offset with respect to the pointer to do the deed      */

	if((menu->Flags & POPREMEMBER) && (menu->Flags & POPUSED)) {
		pop_window.LeftEdge += screen->MouseX;
		pop_window.TopEdge += screen->MouseY;
	}
	else {
		if(menu->Flags & POPPOINTREL) {
			pop_window.LeftEdge += screen->MouseX;
			pop_window.TopEdge += screen->MouseY;
		}
		else if(menu->Flags & POPWINREL) {
			pop_window.LeftEdge += win->LeftEdge;
			pop_window.TopEdge += win->TopEdge;
		}
	}

	/* If the caller wishes us to be POPTIDY, the menu must completely */
	/* appear on the screen, whatever other effects this may have on   */
	/* menu positioning.  The left edge and top edge must be altered   */
	/* accordingly.  In the pathological case where the menu is larger */
	/* than the screen, -1 is returned.                                */
	/* If poptidiness is not a factor, the size of the window may have */
	/* to be altered if it shoots off the bottom or right edge of the  */
	/* screen.  There should be some similar mechanism to deal with    */
	/* the menu if it extends past the top or left edge of the screen; */
	/* as it stands now, the OpenWindow() call will fail, and the      */
	/* result may be even more dire under 1.1.  Use 1.2!               */

	if(menu->Flags & POPTIDY) {
		if((pop_window.Width > screen->Width) || 
		   (pop_window.Height > screen->Height))
		   	return((LONG) (-1));
		if(pop_window.LeftEdge + pop_window.Width > screen->Width)
			pop_window.LeftEdge = screen->Width-pop_window.Width;
		if(pop_window.TopEdge + pop_window.Height > screen->Height)
			pop_window.TopEdge=screen->Height-pop_window.Height;
		if(pop_window.LeftEdge < screen->LeftEdge)
			pop_window.LeftEdge = screen->LeftEdge;
		if(pop_window.TopEdge < screen->TopEdge)
			pop_window.TopEdge = screen->TopEdge;
	}
	else {
		if(pop_window.LeftEdge + pop_window.Width > screen->Width)
			pop_window.Width = screen->Width - 
			  pop_window.LeftEdge;
		if(pop_window.TopEdge + pop_window.Height > screen->Height)
			pop_window.Height = screen->Height -
			  pop_window.TopEdge;
	}

	/* There!  Finally, the window is ready to be displayed!  First,   */
	/* create it.                                                      */

	popwin = OpenWindow(&pop_window);
	if(popwin == NULL) /* all that work for nuthin' */
		return((LONG) (-1));

	/* Now, render the menu items and (possibly) the menu title.       */

	pop_render(popwin, menu);

	/* Now, see if the pointer is over a selection.  The variable      */
	/* 'pop_state' will from this point on hold the value, in linear   */
	/* traversal order of the MenuItems (zero-indexed), the currently  */
	/* selected menu item, or -1 if none are selected.                 */

	pop_state = pop_computestate(popwin, menu);

	/* If one is indeed currently selected, highlight it.              */

	if(pop_state >= 0)
		pop_highlight(popwin, menu, pop_state);

	/* Here is the IDCMP loop that will process the pop-up menu.  Note */
	/* that on mousemove events, I don't care where it moved, just if  */
	/* it did -- pop_computestate() will figure out where by reaching  */
	/* into the Window structure.  Not Pure Programming, somehow, but  */
	/* blessed by the Intuition manual.                                */

	finished = 0;
	while(1) {
		mouse_moved = 0;
		Wait((ULONG) 1L << popwin->UserPort->mp_SigBit);
		while(message = GetMsg(popwin->UserPort)) {
			class = message->Class;
			code = message->Code;
			ReplyMsg(message);

			/* The only messages we should be getting are      */
			/* mouse button and move events.  Button events    */
			/* could signify the end of this routine's         */
			/* epheremal spotlight role.                       */

			switch(class) {
			  case MOUSEMOVE:
			  	mouse_moved = 1;
				break;
			  case MOUSEBUTTONS:
			  	switch(code) {
				  case SELECTDOWN:
				  	if((menu->Flags & POPLEFTBUTTON) &&
					   (menu->Flags & POPTRIGGERDOWN))
					   	finished = 1;
					break;
				  case SELECTUP:
				  	if((menu->Flags & POPLEFTBUTTON) &&
					   (menu->Flags & POPTRIGGERUP))
					   	finished = 1;
					break;
				  case MENUDOWN:
				  	if((menu->Flags & POPRIGHTBUTTON) &&
					   (menu->Flags & POPTRIGGERDOWN))
					   	finished = 1;
					break;
				  case MENUUP:
				  	if((menu->Flags & POPRIGHTBUTTON) &&
					   (menu->Flags & POPTRIGGERUP))
					   	finished = 1;
					break;
				  default: /* huh? */
				  	break;
				}
				break;
			  default: /* huh? */
			  	break;
			}
		}

		/* if the exit conditions have been met, we can return our */
		/* results with honor and dignity, having served.          */
		/* Note that if we are remembering the last selection, the */
		/* menu structure is mangled to make that possible.        */

		if(finished) {
			pop_state = pop_computestate(popwin, menu);
			if(pop_state >= 0) {
				if(menu->Flags & POPREMEMBER) {
					menu->Flags |= POPUSED;
					menu->LeftEdge = -1 * popwin->MouseX;
					menu->TopEdge =  -1 * popwin->MouseY;
				}

				/* Special things to do if the menu entry  */
				/* is of type CHECKIT                      */

				sel_item = pop_getitem(menu, pop_state);
				if(sel_item->Flags & CHECKIT) {
				  if(sel_item->Flags & CHECKED) {
				    pop_state = -1;
				    if(sel_item->Flags & MENUTOGGLE)
				      sel_item->Flags &= ~CHECKED;
				  }
				  else {
				    sel_item->Flags |= CHECKED;

				    /* Handle mutual exclusion */

				    exclude = sel_item->MutualExclude;
				    if(exclude) {
				      sel_item = menu->FirstItem;
				      while(sel_item) {
				        if(exclude & 1)
				          sel_item->Flags &= ~CHECKED;
				        exclude >>= 1;
				        sel_item = sel_item->NextItem;
				      }
				    }
				  }
				}
			}
			CloseWindow(popwin);
			return((LONG) pop_state);
		}

		/* if the mouse has moved, find out its new state and      */
		/* alter the highlighting accordingly.                     */

		if(mouse_moved) {
			pop_newstate = pop_computestate(popwin, menu);
			if(pop_newstate != pop_state) {
				if(pop_state >= 0)
					pop_unhighlight(popwin,
					  menu, pop_state);
				if(pop_newstate >= 0)
					pop_highlight(popwin, 
					  menu, pop_newstate);
				pop_state = pop_newstate;
			}
		}
	}
}

/*
 * pop_computestate()
 *
 * This function checks to see where the mouse pointer is in relation to
 * the various menu items in the menu.  If it is inside one of them, it
 * returns which one (indexed by its linear position in the MenuItem list
 * with 0 being the first one).  If not, returns -1.
 *
 * Possible future enhancement: keep a set of state variables containing
 * the UL and LR corners of the last-known select box; this would make
 * a quick check possible and would cut down the computation for short
 * mouse movements (the most common).
 */

static SHORT
pop_computestate(win, menu)
struct Window *win;
struct Menu *menu;
{
	register SHORT current = 0;
	register SHORT xval, yval;
	register struct MenuItem *item;

	/* Get the x and y vals of the mouse position */

	xval = win->MouseX;
	yval = win->MouseY;

	/* If there is a title, decrement the yval by the correct amount */

	if(menu->MenuName)
		yval -= POPTITLEHEIGHT;

	/* First, see if the pointer is even in the window */

	if((xval < 0) || (yval < 0) ||
	   (xval > win->Width) || (yval > win->Height))
	   	return(-1);

	/* search through the list of menu items, checking the select box  */
	/* of each.  If containment is detected, the job is done.          */

	item = menu->FirstItem;
	while(item) {
		if((xval >= item->LeftEdge) && (yval >= item->TopEdge) &&
		   (xval <= item->LeftEdge + item->Width) &&
		   (yval <= item->TopEdge + item->Height)) {

		   	/* We have found the quarry; now, the result only  */
			/* depends on the MenuItem's ITEMENABLED flag.     */

			if(item->Flags & ITEMENABLED)
		   		return(current);
			else
				return(-1);
		}
		current++;
		item = item->NextItem;
	}

	/* If the list is exhausted, return the sad news */

	return(-1);
}

/*
 * pop_highlight()
 *
 * highlight a menu item
 */

static VOID
pop_highlight(win, menu, state)
struct Window *win;
struct Menu *menu;
SHORT state;
{
	pop_do_highlighting(win, menu, state, 0);
}

/*
 * pop_unhighlight()
 *
 * unhighlight a menu item
 */

static VOID
pop_unhighlight(win, menu, state)
struct Window *win;
struct Menu *menu;
SHORT state;
{
	pop_do_highlighting(win, menu, state, 1);
}

/*
 * pop_do_highlighting()
 *
 * Highlight or unhighlight a menu item, given its traversal number.  Assumes
 * this is a rational value -- if it isn't, Watch Out.
 */

static VOID
pop_do_highlighting(win, menu, state, mode)
struct Window *win;
struct Menu *menu;
SHORT state;
SHORT mode; /* 0 means to highlight, 1 means to unhighlight */
{
	register struct MenuItem *item;
	struct RastPort *rp;
	SHORT offset = 0;

	if(menu->MenuName)
		offset = POPTITLEHEIGHT;

	/* Get the correct MenuItem structure */

	item = pop_getitem(menu, state);

	rp = win->RPort;

	/* Now, do the highlighting!  The action to be taken depends on    */
	/* the type of highlighting desired for this item.                 */
	/* The way that the flags for highlighting works is truly bizarre  */

	if((item->Flags & HIGHNONE) == HIGHNONE)
		return;

	if(item->Flags & HIGHCOMP) {
		SetDrMd(rp, COMPLEMENT);
		RectFill(rp, (LONG) item->LeftEdge, (LONG) (item->TopEdge +
		  offset), (LONG) (item->LeftEdge + item->Width - 1),
		  (LONG) (item->TopEdge + item->Height + offset));
	}
	else if(item->Flags & HIGHBOX) {
		SetDrMd(rp, COMPLEMENT);
		Move(rp, (LONG) item->LeftEdge, (LONG) (item->TopEdge + 
		  offset));
		Draw(rp, (LONG) (item->LeftEdge + item->Width - 1),
		  (LONG) (item->TopEdge + offset));
		Draw(rp, (LONG) (item->LeftEdge + item->Width - 1),
		  (LONG) (item->TopEdge + item->Height + offset));
		Draw(rp, (LONG) item->LeftEdge,
		  (LONG) (item->TopEdge + item->Height + offset));
		Draw(rp, (LONG) item->LeftEdge, (LONG) 
		  (item->TopEdge + offset));
	}

	/*  Otherwise, the mode is HIGHIMAGE */

	else
		pop_draw_menuitem(win, item, !mode, offset);
}

/*
 * pop_render()
 *
 * renders the menu title (if existent) and the menu items
 */

static VOID
pop_render(win, menu)
struct Window *win;
struct Menu *menu;
{
	struct MenuItem *item;
	struct RastPort *rp;
	SHORT offset = 0;

	rp = win->RPort;

	/* Fill the background with color 1, like Intuition Menus */

	SetAPen(rp, 1L);
	RectFill(rp, 0L, 0L, (LONG) win->Width, (LONG) win->Height);

	/* First, if there is a Title for this menu, render it in the top */
	/* of the menu.                                                   */

	if(menu->MenuName) {
		SetDrMd(rp, JAM1);
		SetAPen(rp, 0L);
		SetBPen(rp, 1L);
		Move(rp, 4L, 7L);
		Text(rp, menu->MenuName, (LONG) pop_strlen(menu->MenuName));
		SetDrMd(rp, COMPLEMENT);
		RectFill(rp,0L,0L, (LONG) win->Width, (LONG) POPTITLEHEIGHT);
		SetDrMd(rp, JAM1);
		offset = POPTITLEHEIGHT;
	}

	/* now render all of the menu items */

	item = menu->FirstItem;
	while(item) {
		pop_draw_menuitem(win, item, 0, offset);
		item = item->NextItem;
	}
}

/* Area fill patterns */

static USHORT pop_ghost_pattern[] = {
	0x1111, 0x4444
};
static USHORT pop_normal_pattern[] = {
	0xffff, 0xffff
};

/*
 * pop_draw_menuitem()
 *
 * Draws the specified menuitem in the given rastport.  The mode argument
 * says what to draw -- 0 means draw the ItemFill, 1 the SelectFill.
 */

static VOID
pop_draw_menuitem(win, item, mode, offset)
struct Window *win;
struct MenuItem *item;
SHORT mode;
SHORT offset;
{
	APTR fill;
	struct RastPort *rp;

	/* first, figure out what to do, and return if it is a NULL thing */

	if(!mode)
		fill = item->ItemFill;
	else
		fill = item->SelectFill;

	if(!fill)
		return;

	rp = win->RPort;

	/* First, erase what may already be there, just to be sure that    */
	/* everything works out all right.                                 */

	SetAPen(rp, 1L);
	SetDrMd(rp, JAM1);
	RectFill(rp, (LONG) item->LeftEdge, (LONG) (item->TopEdge +
	  offset), (LONG) (item->LeftEdge + item->Width), (LONG)
	  (item->TopEdge + item->Height + offset));

	/* If the item is checkmarked, draw the checkmark.  Intuition made */
	/* sure that the CheckMark field of the window structure exists    */

	if(item->Flags & CHECKIT)
		if(item->Flags & CHECKED)
			DrawImage(rp, win->CheckMark, (LONG)  item->LeftEdge,
			  (LONG) (item->TopEdge + offset + 1));

	/* Now, draw the item itself -- depending on the Flag value, it    */
	/* could be either an Image or an IntuiText                        */

	if(item->Flags & ITEMTEXT)
		PrintIText(rp, fill, (LONG) item->LeftEdge, 
		  (LONG) (item->TopEdge + offset));
	else
		DrawImage(rp, fill, (LONG) item->LeftEdge, 
		  (LONG) (item->TopEdge + offset));

	/* If the ITEMENABLED flag is not set, "ghost" the item.           */

	if(!(item->Flags & ITEMENABLED)) {
		SetAPen(rp, 1L);
		SetDrMd(rp, JAM1);
		SetAfPt(rp, (USHORT *) pop_ghost_pattern, 1L);
		RectFill(rp, (LONG) item->LeftEdge, (LONG) (item->TopEdge +
		  offset), (LONG) (item->LeftEdge + item->Width), (LONG)
		  (item->TopEdge + item->Height + offset));
		SetAfPt(rp, (USHORT *) pop_normal_pattern, 1L);
	}
}

/*
 * pop_getitem()
 *
 * given the traversal number of a menu item in a menu (assumes, BTW, that
 * the arguments are valid), return a pointer to the MenuItem structure
 */

static struct MenuItem *
pop_getitem(menu, which)
struct Menu *menu;
SHORT which;
{
	struct MenuItem *item;

	item = menu->FirstItem;
	while(which--)
		item = item->NextItem;
	return(item);
}

/*
 * pop_strlen()
 *
 * a home-brewed strlen to prevent it being necessary to hook in whatever
 * huge object file in which the c library's strlen() resides.
 */

static SHORT
pop_strlen(str)
char *str;
{
	register SHORT count = 0;

	for(; *str++; count++);
	return(count);
}

/* :-) */
SHAR_EOF
cat << \SHAR_EOF > popmenu.h
/*
 *  Constant definitions needed by the pop-up menu code.
 *
 *  Written by Derek Zahn (Gambit Software, Madison WI), July 1987
 *
 *  This code is freely distributable and is blessed by its author for
 *  inclusion, in this form or any other, into Amiga programs,
 *  commercial or non-commercial.  If this is done, no credit must be
 *  given to me (although I wouldn't mind).
 */

/* This value should be added to the Height field of the Menu structure */
/* if a title is supplied in the MenuName field.                        */

#define POPTITLEHEIGHT 10

/* These flags will go in the Flags field of the Menu structure         */

#define POPVERIFY 0x0002L  /* for possible future expansion */
#define POPRELEASE 0x0004L  /* for possible future expansion */
#define POPTIDY 0x0008L
#define POPPOINTREL 0x0010L
#define POPWINREL 0x0020L
#define POPREMEMBER 0x0040L
#define POPUSED 0x0080L
#define POPMOVEPOINTER 0x0200L /* for possible future expansion */
#define POPLEFTBUTTON 0x1000L
#define POPRIGHTBUTTON 0x2000L
#define POPTRIGGERDOWN 0x4000L
#define POPTRIGGERUP 0x8000L
SHAR_EOF
#	End of shell archive
exit 0