home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
230.lha
/
QuickMenu
/
qmenu.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-04-04
|
19KB
|
546 lines
/*
* Quick Menu Package -- An easy way to make simple (but nice) menus.
*
* The client programmer will generally use the GenMenu()/FreeMenu()
* interface which creates and frees a whole menu strip. A menu strip
* -- both the main strip and menu item strips -- is defined by a
* NewMenu struct. Each NewMenu struct (defined in qmenu.h) is just a
* pointer to an array of strings and a pointer to an array of
* NewMenu's. The elements of the array of NewMenu's are paired with the
* strings in the array and represent the sub menus for that string.
* The string array is terminated with a null pointer.
*
* The package tries to do nice things, like automatically formatting
* the menu strings into blocks with their command keys, and placing
* markers pointing to subitem stips. The strings for the menu items
* can contain special control codes to control the optional
* characteristics of items. Special codes come at the front of item
* strings and are delimited with a special character (SPECIAL,
* defined as '!' by default) and are listed briefly below:
*
* c Checkmark item
* t Checkmark toggle
* + Checkmark checked
* b Highlight box
* n Highlight none
* d Disabled
* =C Set command key to "C"
* 0101... Set item exclude mask
*
* Any number of 1's and 0's after the special character will set the
* Mutual exclude bits for that item in the order they occur. So the
* first 1 or 0 will set bit 0 of the exclude mask, the second will
* set bit 1, etc. Any unset will be 0.
*
* Additionally, if the item string is "-", the item will be
* a non-selectable horizontal line (a "rule") that can be used to
* visually group similar items in the menu strip. There only need
* to be NewMenu structs in the array for text menu items, not rules,
* but don't forget to account for rules when counting MenuItem's
* since they take a slot.
*
*
* GenMenu() takes a pointer to a NewMenu struct and will create a main
* Menu strip from the strings in the struct, with menu items from the
* associated NewMenu's. It returns a pointer to a Menu struct which is
* the first element in the list for this strip.
*
* GenStrip() takes a pointer to an array of strings and creates a
* top-level set of Menu structs for the main menu strip.
*
* FreeMenu() frees a Menu strip created with either GenMenu() or
* GenStrip().
*
* GenItems() takes a pointer to a NewMenu struct and creates a single
* list of MenuItem's from the description. It also takes an X and Y
* offset for all the elements in the list.
*
* FreeMItem() frees structures created by the above function.
*
* AttachSubMenu() takes a single MenuItem and a NewMenu struct and
* attaches the MenuItems created from that NewMenu struct to the
* parent MenuItem. The parent should also have been created with
* GenItems() in order to work correctly. AttachSubMenu() places a
* little maker on the parent item to show that it has subitems. It
* returns 1 for sucess and 0 for failure.
*
* -- WARNING:
*
* The client programmer is completely responsible for keeping the
* menus within the bounds of the screen and for keeping within the
* limits of Intuition. The menu structures will not adjust as they
* hit the borders of the screen (which might be a nice enhancement).
* The strings in the NewMenu structures cannot be changed or deleted
* while the Menus are in use, although they can be used to create
* multiple Menus from the same strings.
*
**
* This code can be used and modified freely. Do let me know of any
* improvements or enhancements you may add, and be sure to give credit
* where credit is due (in the comments) if you redistribute this code.
*
* Stuart Ferguson 1/89
* (shf@well.UUCP)
*/
#include <intuition/intuition.h>
#include "qmenu.h"
/*
* Some useful macros for allocating and freeing structures and
* arrays of structures.
*/
#define NEW(typ) (typ*)AllocMem((long)sizeof(typ),0L)
#define FREI(p) FreeMem(p,(long)sizeof(*p))
#define NEW_N(typ,n) (typ*)AllocMem((long)((n)*sizeof(typ)),0L)
#define FREI_N(p,n) FreeMem(p,(long)((n)*sizeof(*p)))
char * AllocMem();
/*
* Definitions for formatting the menus. Glossary:
*
* TEXTCOLOR - color of the text items in the menus.
* MARKCOLOR - color of the subitem marker.
* RULECOLOR - color of the horizontal lines in the menus.
* RULEHEIGHT - vertical thickness of the horizontal rules.
* RULEGAP - vertical blank space around the rules.
* RULEMARGIN - horizontal blank space around the rules.
* TEXTGAP - vertical blank space around the text items.
* TEXTMARGIN - horizontal blank space around the text items.
* MAINWID - width of the text font on the main menu.
* MAINGAP - spacing between items on the main menu strip.
* SUBINDENT - overlap between items and their subitems.
* SUBDOWN - vertical shift between items and their subitems.
*/
#define TEXTCOLOR 2
#define MARKCOLOR 3
#define RULECOLOR 0
#define RULEHEIGHT 2
#define RULEGAP 4
#define RULEMARGIN 10
#define TEXTGAP 2
#define TEXTMARGIN 4
#define MAINWID 10
#define MAINGAP 10
#define SUBINDENT 18
#define SUBDOWN 0
/*
* Escape character for special control codes in text items strings.
*/
#define SPECIAL '!'
/*
* The extern "ta" is set by the client program for
* the font to use for these menus.
*/
extern struct TextAttr ta;
/*
* Generic templates to use for creating the dynamic menu structures.
*/
static struct IntuiText
generic_itext = {TEXTCOLOR, 0, JAM1, TEXTMARGIN/2, TEXTGAP/2, &ta,NULL,NULL},
generic_smark = {MARKCOLOR, 0, JAM1, 0, TEXTGAP/2, &ta, (UBYTE *) ";", NULL};
static struct Menu
generic_main = {NULL, 0, 0, 0, 10, MENUENABLED, NULL, NULL};
static struct MenuItem
generic_mitem = {
NULL, 0, 0, 0, 0,
ITEMTEXT | ITEMENABLED | HIGHCOMP,
0, NULL, NULL, 0, NULL, 0
};
/* Image struct with no imagery for the horizontal lines.
*/
static struct Image
generic_hrule = {0, 0, 1, RULEHEIGHT, 2, NULL, 0, RULECOLOR, NULL};
static struct MenuItem
generic_hitem = {
NULL, RULEMARGIN/2, 0, 0, 0,
ITEMENABLED | HIGHNONE,
0, NULL, NULL, 0, NULL, 0
};
/*
* Takes an array of strings and associated array of NewMenu structs
* (as a single NewMenu struct) and constructs a menu strip from the
* descripton. This is the main high-level call that most clients
* will make.
*/
struct Menu * GenMenu (nmen)
register struct NewMenu *nmen;
{
register short i, ok, n;
register struct Menu *mm;
register struct MenuItem *mi;
/* Count menus to be generated and create top level structure.
*/
for (n = 0; nmen->str[n]; n++) ;
mm = GenStrip (nmen->str);
if (!mm) return NULL;
/* Create the item strip for each main menu and attach to the
* top level Menu structure. Any failure causes the whole
* thing to crumble to dust.
*/
ok = 1;
nmen = nmen->submenu;
for (i = 0; i < n; i++) {
if (nmen->str) {
mi = GenItems (nmen, 0L, 0L);
mm[i].FirstItem = mi;
ok &= (mi != NULL);
}
nmen ++;
}
if (!ok) {
FreeMenu (mm);
return NULL;
}
return mm;
}
/*
* Generate a menu strip. Just creates the top level Menu structures,
* linked together and intialized. Takes an array of pointers to
* strings.
*/
struct Menu * GenStrip (str)
char **str;
{
register short i, x, num;
register struct Menu *mm, *m;
/*
* Create enough struct Menu's for the menu strip.
*/
for (num = 0; str[num]; num++) ;
mm = NEW_N (struct Menu, num);
if (!mm) return NULL;
/*
* Init the structures using the generic Menu struct as a template.
* NOTE: the size of the font for these item labels is unknown for
* windows on the Workbench screen, so a size should be used
* that will work with 60 & 80 column fonts.
*/
x = 0;
for (i = 0; i < num; i++) {
m = &mm[i];
*m = generic_main;
m->LeftEdge = x;
m->Width = strlen (str[i]) * MAINWID + TEXTMARGIN;
x += m->Width + MAINGAP;
m->MenuName = str[i];
m->NextMenu = m + 1;
}
mm[num - 1].NextMenu = NULL;
return mm;
}
/*
* Attach a submenu to a MenuItem. Takes the parent MenuItem and a
* NewMenu structure that will be attached as a sub-menu.
* Attaches a ";" mark at the end of the parent item and calls
* GenItems() to create the sub-menu strip.
*/
BOOL AttachSubMenu (mi, nmen)
register struct MenuItem *mi;
struct NewMenu *nmen;
{
register struct IntuiText *it;
/* Create an IntuiText with the marker and position it
* at the right edge of the item.
*/
if (!(it = NEW (struct IntuiText))) return FALSE;
*it = generic_smark;
it->LeftEdge = mi->Width - IntuiTextLength (it) - 2;
/* Create the subitem structure and attach to the main item.
*/
if (!(mi->SubItem = GenItems
(nmen, (LONG) (mi->Width - SUBINDENT), (LONG) SUBDOWN))) {
FREI (it);
return FALSE;
}
/* Only if it all worked attach the new text structure.
*/
((struct IntuiText *) mi->ItemFill)->NextText = it;
return TRUE;
}
/*
* Takes the given menu text item and skips past the special control
* codes while adjusting the associated MenuItem appropriately.
* Special codes are in the form of "!x", where "x" is:
*
* c Checkmark item
* t Checkmark toggle
* b Highlight box
* n Highlight none
* d Disabled
* + Checkmark checked
* =C Set command key to "C"
* 1010... Set item exclude mask
*
* Takes the mostly defined MenuItem and diddles it. Returns a pointer
* to the item text with control codes stripped.
*/
static UBYTE * ProcessSpecialStuff (str, mi)
char *str;
struct MenuItem *mi;
{
register LONG x;
register int i;
while (*str == SPECIAL) {
switch (*++str) {
case 'c':
mi->Flags |= CHECKIT;
break;
case 't':
mi->Flags |= CHECKIT | MENUTOGGLE;
break;
case 'b':
mi->Flags &= ~HIGHFLAGS;
mi->Flags |= HIGHBOX;
break;
case 'n':
mi->Flags &= ~HIGHFLAGS;
mi->Flags |= HIGHNONE;
break;
case 'd':
mi->Flags &= ~ITEMENABLED;
break;
case '+':
mi->Flags |= CHECKIT | CHECKED;
break;
case '=':
mi->Flags |= COMMSEQ;
mi->Command = *++str;
break;
case '0':
case '1':
x = 0;
i = 0;
while (*str == '0' || *str == '1')
x += (*str++ - '0') << (i++);
mi->MutualExclude = x;
str--;
break;
}
str++;
}
return (UBYTE*) str;
}
/*
* Construct a basic item list for a menu. Takes a NewMenu structure
* which contains a pointer to an array of pointers to strings and a
* pointer to an array of NewMenu structures. The strings contain the
* item text for each menu plus optional special control codes. If the
* string is "-", the item will be a horizontal rule rather than a text
* item. The NewMenu structures, if not NULL, are the sub-menu's for
* each menu in the array.
* "x" and "y" are the horizontal and vertical offsets of this set of
* MenuItems. These are set by AttachSubMenu() for positioning submenus
* under their parent items.
*/
struct MenuItem *GenItems (nmen, x, y)
struct NewMenu *nmen;
LONG x, y;
{
register struct MenuItem *mi, *cmi;
register struct IntuiText *itext;
struct Image *img;
register short i, len, max;
short n;
struct NewMenu *sub;
/* Count menu items (n) and allocate an array for the strip.
*/
for (n = 0; nmen->str[n]; n++) ;
if (!(mi = NEW_N (struct MenuItem, n))) return NULL;
/* Counts the number of rules in the menu ("-" strings)
* and allocates the structures for the lines and the text items.
*/
max = 0;
for (i = 0; i < n; i++) max += (*nmen->str[i] == '-');
if (n - max)
if (!(itext = NEW_N (struct IntuiText, n - max))) {
FREI_N (mi, n);
return NULL;
}
if (max)
if (!(img = NEW_N (struct Image, max))) {
FREI_N (mi, n);
if (n - max) FREI_N (itext, n - max);
return NULL;
}
/* Loop through text menu items and initialize the
* associated IntuiText structures. Compute the maximum
* width of the menu taking command keys into account while
* assigning all the other parts of the text MenuItem's.
*/
max = 0;
for (i = 0; i < n; i++) {
if (*nmen->str[i] == '-') continue; /* skip rules */
/* Init the text MenuItem to point to the assocd IntuiText.
*/
cmi = &mi[i];
*cmi = generic_mitem;
cmi->ItemFill = (APTR) itext;
/* Init the IntuiText and adjust the MenuItem from the
* flags set in the menu text string.
*/
*itext = generic_itext;
itext->IText = ProcessSpecialStuff (nmen->str[i], cmi);
/* Make a first cut at measuring the length of the item.
*/
len = IntuiTextLength (itext) + TEXTMARGIN;
/* If command key set, add to length.
*/
if (cmi->Flags & COMMSEQ) len += COMMWIDTH + MAINWID;
/* If this is a checkmark item, shift the text over to
* make room and add that to the length.
* Compute the max length.
*/
if (cmi->Flags & CHECKIT) {
itext->LeftEdge += CHECKWIDTH;
len += CHECKWIDTH;
}
if (len > max) max = len;
itext ++;
}
/* Secondary assignment loop. Position the text MenuItems and
* init the horizontal lines.
*/
for (i = 0; i < n; i++) {
cmi = &mi[i];
if (*nmen->str[i] != '-') {
cmi->LeftEdge = x;
cmi->TopEdge = y;
cmi->Width = max;
cmi->Height = ta.ta_YSize + TEXTGAP;
y += cmi->Height;
} else {
/* Rule items point to their Image structure
* and are just a little narrower than the
* menu itself.
*/
*img = generic_hrule;
img->Width = max - RULEMARGIN;
*cmi = generic_hitem;
cmi->TopEdge = y + RULEGAP/2;
y += RULEHEIGHT + RULEGAP;
cmi->ItemFill = (APTR) img;
img ++;
}
cmi->NextItem = cmi + 1;
}
mi[n - 1].NextItem = NULL;
/* Attach submenu's, if any.
*/
if (!(sub = nmen->submenu)) return mi;
/* Use "max" as a flag for the success of the attachments.
*/
max = 1;
for (i = 0; i < n; i++) {
if (*nmen->str[i] == '-') continue;
if (sub->str)
max &= AttachSubMenu (&mi[i], sub);
sub ++;
}
if (!max) {
FreeMItem (mi);
return NULL;
}
return mi;
}
/*
* Free a Menu structure created by GenStrip() that has items
* created with GenItems().
*/
void FreeMenu (mm)
struct Menu *mm;
{
register short i;
register struct Menu *t;
i = 0;
for (t = mm; t; t = t->NextMenu) {
if (t->FirstItem) FreeMItem (t->FirstItem);
i++;
}
FREI_N (mm, i);
}
/*
* Free a MenuItem structure created by GenItems().
*/
void FreeMItem (mi)
register struct MenuItem *mi;
{
register short nit, nimg;
register struct MenuItem *c;
register struct IntuiText *it = NULL, *it1;
struct Image *img = NULL;
/* Scan the MenuItem structures and count the number of images
* and IntuiText structures. Find the pointer to the first of
* each structure in the set. That will be the first element
* in the array that was allocated as a unit.
*/
nit = nimg = 0;
for (c = mi; c; c = c->NextItem) {
if (c->SubItem) FreeMItem (c->SubItem);
if (c->Flags & ITEMTEXT) {
it1 = (struct IntuiText *) c->ItemFill;
if (!it) it = it1;
nit++;
/* Free the subitem marker, if any.
*/
if (it1->NextText) FREI (it1->NextText);
} else {
if (!img) img = (struct Image *) c->ItemFill;
nimg++;
}
}
/* Free the arrays of structures of images and texts, as
* well as the main array of MenuItem structures themselves.
*/
if (nit) FREI_N (it, nit);
if (nimg) FREI_N (img, nimg);
FREI_N (mi, nit + nimg);
}