home *** CD-ROM | disk | FTP | other *** search
- /*
- Menu.c
- */
- /*
-
- ##### # #
- # # # # #
- # # #
- # ## #### #### # ## ####
- # # # # # # # # # #
- # # # # # ### # # # #
- # # # # # # # # # #
- # ### # # # #### ##### ### ####
-
- -----------------------------------------------------------------------------
-
- This is source for use with the 'DeskLib' Wimp C programming library for
- Risc OS. I currently use v1.04 of DeskLib. This source is FreeWare, which
- means you can use it to write commercial applications, but you may not charge
- *in any way* for the distribution of this source. I (Tim Browse) retain
- all copyright on this source.
-
- This source is provided 'as is' and I offer no guarantees that is useful,
- bug-free, commented, that it will compile, or even that it exists.
-
- If it breaks in half, you own both pieces.
-
- All source © Tim Browse 1993
-
- -----------------------------------------------------------------------------
-
- */
-
- /* ANSI includes */
- #include "stdlib.h"
- #include "string.h"
-
- /* DeskLib includes */
- #include "DeskLib.Core.h"
- #include "DeskLib.Wimp.h"
- #include "DeskLib.Event.h"
- #include "DeskLib.EventMsg.h"
- #include "DeskLib.WimpSWIs.h"
- #include "DeskLib.Error.h"
- #include "DeskLib.LinkList.h"
-
- /* TimsLib includes */
- #include "lib.h"
- #include "IconLib.h"
-
- #include "Menu.h"
-
- /*
- General Note:
-
- The typedef 'menu' is actually a wimp_menuptr, but cannot be used with
- free, as the word before the wimp_menublock points to the parent of this
- menu (if any), and the memory is allocated in one go (see typedef below).
- Therefore to free storage you need:
- free(((char *) menu) - 4);
- i.e. don't bother, it's easier to use Menu_Dispose().
- */
-
- typedef struct
- {
- menu_ptr parent;
- menu_block menublock;
- } menu_info;
-
- BOOL menu_adjust_pressed = FALSE;
-
- window_handle menu_current_window = event_ANY;
- icon_handle menu_current_icon = event_ANY;
-
- static void Menu__InitItem(menu_item *item)
- {
- static menu_item default_item;
-
- static BOOL first_time = TRUE;
-
- if (first_time)
- {
- first_time = FALSE;
-
- /* Initialise the menu flags */
- default_item.menuflags.value = 0;
-
- /* Initialise the menu item to have no sub-menu */
- default_item.submenu.value = -1;
-
- /* Initialise the icon flags */
- default_item.iconflags.value = icon_TEXT | icon_FILLED;
- default_item.iconflags.data.foreground = colour_BLACK;
- default_item.iconflags.data.background = colour_WHITE;
-
- /* Initialise the icon data */
- default_item.icondata.text[0] = '\0';
- }
-
- memcpy(item, &default_item, sizeof(menu_item));
- }
-
-
- static void Menu__SetOption(menu_item *item, char opt)
- {
- switch (opt)
- {
- case '!': /* Tick this entry */
- item->menuflags.data.ticked = TRUE;
- break;
-
- case '~': /* Fade this entry */
- item->iconflags.data.shaded = TRUE;
- break;
-
- case '=': /* Add dotted line after this entry */
- item->menuflags.data.dotted = TRUE;
- break;
-
- case '>': /* This entry has a dialogue box as a sub-menu */
- item->menuflags.data.notifysub = TRUE;
-
- /* Set submenu flag - use a legal window handle, but not likely to be in use */
- item->submenu.window = (window_handle) 0x7FFF;
- break;
-
- /* Ignore space */
- }
- }
-
- #define is_sep(c) (((c) == ',') || ((c) == '|'))
- #define is_opt(c) (((c) == '!') || ((c) == '>') || ((c) == '~') || ((c) == '=') || ((c) == ' '))
-
- static int Menu__ReadItemName(char *item_name, char *src)
- {
- int i = 0;
-
- /* A name can start with any character other than a 'sep' or an 'opt', and can
- then contain any character other than a 'sep'.
- */
- if (!is_sep(src[0]) && !is_opt(src[0]))
- {
- do
- {
- /* Copy this character and move on */
- item_name[i] = src[i];
- i++;
- } while (!is_sep(src[i]) && (src[i] != '\0'));
- }
-
- /* Terminate the item name */
- item_name[i] = '\0';
-
- /* Tell the caller how many characters we used */
- return i;
- }
-
-
- #define Menu__FirstEntry(menu) ((menu_item *) (((menu_ptr) (menu) + 1)))
-
- menu Menu_New(char *name, char *desc)
- {
- int entries = 1,
- i = 0,
- name_len,
- max_name_len = strlen(name); /* In case title is longest string */
-
- char item_name[80];
-
- menu_info *the_menu;
- menu_item *item;
-
- /* Exit quietly if no description string */
- if (desc == NULL)
- return NULL;
-
- /* Find out how many entries are needed */
- while (desc[i] != '\0')
- {
- if ((desc[i] == ',') || (desc[i] == '|'))
- entries++;
- i++;
- }
-
- /* Get the memory for these entries */
- the_menu = (menu_info *) malloc(sizeof(menu_info) + (entries * sizeof(menu_item)));
-
- /* Get pointer to first item, using some obscure pointer arithmetic */
- item = Menu__FirstEntry(&the_menu->menublock);
-
- i = 0;
-
- /* Construct the menu entries */
- while (desc[i] != '\0')
- {
- /* Initialise this menu item */
- Menu__InitItem(item);
-
- /* Look for an option */
- if (is_opt(desc[i]))
- {
- Menu__SetOption(item, desc[i]);
- i++;
- }
-
- /* Read the item name */
- name_len = Menu__ReadItemName(item_name, desc + i);
-
- /* Copy the item name into the menu icon */
-
- if (name_len <= wimp_MAXNAME)
- {
- /* normal non-indirected icon */
- strncpy(item->icondata.text, item_name, wimp_MAXNAME);
- }
- else
- {
- /* Too long - make into indirected icon */
-
- item->iconflags.data.indirected = TRUE;
- item->icondata.indirecttext.buffer = save_str(item_name, 0);
-
- item->icondata.indirecttext.bufflen = name_len + 1; /* for terminator */
-
- item->icondata.indirecttext.validstring = (char *) -1; /* no validation */
- }
-
- /* Check if this is the longest item so far */
- if (name_len > max_name_len)
- max_name_len = name_len;
-
- /* Move to next menu item */
- i += name_len;
-
- /* Skip the separator, if there is one */
- if (is_sep(desc[i]))
- i++;
-
- item++;
- }
-
- /* Set the 'last' flag on the last menu item */
- item--;
- item->menuflags.data.last = TRUE;
-
- /* Set up menu block */
- strncpy(the_menu->menublock.title, name, wimp_MAXNAME);
-
- /* These attributes taken from Wimp Application notes (RO2 PRMS p1314) */
- the_menu->menublock.titlefore = colour_BLACK;
- the_menu->menublock.titleback = colour_GREY2;
- the_menu->menublock.workfore = colour_BLACK;
- the_menu->menublock.workback = colour_WHITE;
- the_menu->menublock.width = (max_name_len + 1) * 16; /* !!!Find a legal way!!! */
- the_menu->menublock.height = 44;
- the_menu->menublock.gap = 0;
-
- return (menu) &(the_menu->menublock);
- }
-
- menu Menu_ColourMenu(char *title, char *last_item)
- {
- menu colour_menu;
- menu_item *item;
- int i;
- char menu_desc[100]; /* Should be enough for really stoopid 'last_item' params */
-
- strcpy(menu_desc, "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,");
-
- if (last_item != NULL)
- /* Add a separator before last_item */
- strcat(menu_desc, "=");
-
- strcat(menu_desc, "15");
-
- if (last_item != NULL)
- {
- strcat(menu_desc, ",");
- strcat(menu_desc, last_item);
- }
-
- /* Create the menu */
- colour_menu = Menu_New(title, menu_desc);
-
- item = Menu__FirstEntry(colour_menu);
-
- /* Set the background colours of the menu items */
- for (i = 0; i <= 15; i++)
- item[i].iconflags.data.background = i;
-
- /* Set the foreground colours (always black or white) */
- for (i = 0; i <= 15; i++)
- if ((i <= 3) || (i == 9) || (i == 12) || (i >= 14))
- item[i].iconflags.data.foreground = colour_BLACK;
- else
- item[i].iconflags.data.foreground = colour_WHITE;
-
- return colour_menu;
- }
-
-
- void Menu__Dispose(menu_block *the_menu, BOOL recursive)
- {
- menu_info *info;
-
- /* Get pointer to the first item */
- menu_item *item = (menu_item *) (the_menu + 1);
-
- /* De-allocate any indirect data / sub-menus */
- do
- {
- /* Does this item have a sub-menu (but not a dialogue box sub-menu) */
- if (recursive && item->submenu.value >= 0x8000)
- Menu__Dispose(item->submenu.menu, recursive);
-
- /* Free up any indirected data */
- IconLib_DisposeIndData(&item->icondata, item->iconflags);
-
- /* Move to next item */
- item++;
- } while (item->menuflags.data.last == FALSE);
-
- /* Free the data for the menu block - this includes all the menu items, and
- also the word preceeding the menu_block, as it points to the parent menu.
- */
- info = (menu_info *) (((char *) the_menu) - 4);
- free(info);
- }
-
- void Menu_Dispose(menu the_menu, BOOL recursive)
- {
- /* Just a veneer for the slightly lower-level Menu__Dispose() */
- Menu__Dispose((menu_block *) the_menu, recursive);
- }
-
- void Menu_SubMenu(menu parent, int entry, menu submenu)
- {
- /* Get a pointer to the menu info block of the submenu */
- menu_info *submenu_info = (menu_info *) (((int *) submenu) - 1);
-
- /* Get a pointer to the first entry */
- menu_item *item = Menu__FirstEntry(parent);
-
- /* Plug the submenu into the specified menu entry */
- item[entry].submenu.menu = (menu_ptr) submenu;
-
- /* Link the submenu to the parent */
- submenu_info->parent = (menu_ptr) parent;
- }
-
- void Menu_AddDialogBox(menu the_menu, int entry, window_handle window)
- {
- /* Get a pointer to the first entry */
- menu_item *item = Menu__FirstEntry(the_menu);
-
- /* Plug the submenu into the specified menu entry */
- item[entry].submenu.window = window;
- }
-
- void Menu_SetFlags(menu the_menu, int entry, BOOL tick, BOOL fade)
- {
- /* Get a pointer to the first item */
- menu_item *item = Menu__FirstEntry(the_menu);
-
- /* Set the flags as required */
- item[entry].menuflags.data.ticked = tick;
- item[entry].iconflags.data.shaded = fade;
- }
-
- void Menu_MakeWritable(menu the_menu, int entry,
- char *buffer, int bufflen, char *validstring)
- {
- char *the_text = NULL; /* Used to keep a copy of the current text of the entry */
-
- /* Get a pointer to the first item */
- menu_item *item = Menu__FirstEntry(the_menu);
-
- /* Move to the entry we want */
- item += entry;
-
- /* If it's currently a text (or text & sprite) icon, save the text string */
-
- switch (ICON_TYPE(item->iconflags.value))
- {
- case TXT_ONLY:
- case TXT_AND_SPR:
- if (item->iconflags.data.indirected)
- {
- the_text = save_str(item->icondata.indirecttext.buffer, 0);
- }
- else
- {
- the_text = (char *) malloc(wimp_MAXNAME + 1);
- strncpy(the_text, item->icondata.text, wimp_MAXNAME);
- the_text[wimp_MAXNAME] = '\0';
- }
- }
-
- /* Free any indirected data */
- IconLib_DisposeIndData(&item->icondata, item->iconflags);
-
- /* Make the entry into a writable, indirected, text-only icon */
- item->menuflags.data.writable = TRUE;
- item->iconflags.data.text = TRUE;
- item->iconflags.data.sprite = FALSE; /* just to be safe */
- item->iconflags.data.indirected = TRUE;
-
- /* Plug in the buffer and validation string */
- item->icondata.indirecttext.buffer = buffer;
- item->icondata.indirecttext.bufflen = bufflen;
- item->icondata.indirecttext.validstring = validstring;
-
- /* Copy the old text back in and free the buffer, if necessary */
- if (the_text != NULL)
- {
- strncpy(buffer, the_text, bufflen - 1);
- free(the_text);
- }
- }
-
- void Menu_Show(menu the_menu, wimp_point mouse_pos, BOOL iconbar)
- {
- /* If an iconbar menu, position bottom of menu 96 OS units above bottom
- of screen.
- */
- if (iconbar)
- {
- /* Title bar is not included in the positioning of menu windows */
-
- int menu_height = 0;
-
- menu_ptr menu = (menu_ptr) the_menu;
- /* Get a pointer to the first item */
- menu_item *item = Menu__FirstEntry(the_menu);
-
- BOOL not_finished = TRUE;
-
- /* Process each menu entry */
-
- while (not_finished)
- {
- /* Add height of menu item for each item */
- menu_height += menu->height;
-
- /* Add extra height for a separator */
- if (item->menuflags.value & 2)
- /* NB this value may be wrong for non-standard item heights/inter-item gaps */
- menu_height += (6 * 4);
-
- /* If not the last item, add the inter-item gap */
- if (item->menuflags.data.last)
- {
- menu_height += menu->gap;
- not_finished = FALSE;
- }
-
- /* Process the next item */
- item++;
- }
-
- /* Position menu */
- mouse_pos.y = 96 + menu_height;
- }
-
- /* Open the menu 64 OS units to the left of the mouse click, as recommended
- by the RO2 PRMS, p1314.
- */
- Error_Check(Wimp_CreateMenu((menu_block *) the_menu, mouse_pos.x - 64, mouse_pos.y));
- }
-
-
- /* Menu attachment routines */
-
- typedef struct handler_str
- {
- union
- {
- int value;
- struct
- {
- unsigned int menu_maker : 1; /* 1 => data union contains a menu_maker */
- unsigned int icon_handler : 1; /* 1 => Menu should be shown if select pressed */
- unsigned int iconbar : 1; /* 1 => Menu is attached to icon bar icon */
- } data;
- } flags;
-
- union
- {
- menu menu;
- menu_maker maker;
- } data;
-
- menu_handler handler_proc;
- void *reference;
- } handler_str;
-
- typedef struct icon_node
- {
- linklist_header header;
- icon_handle icon;
- handler_str handler;
- } icon_node;
-
- typedef struct window_node
- {
- linklist_header header;
- window_handle window; /* event_ANY means catch all menu events */
- linklist_header icons_anchor;
- handler_str handler;
- } window_node;
-
-
- /* Contains tree of menu handlers */
- linklist_header handlers = {NULL, NULL};
-
- /*
- 'submenu_handle' is used to store the window handle passed to
- Menu_AddDynamicDialogBox, which should be called in response to receiving a
- menu warning (requested by using the '>' option in the menu description string).
-
- Sequence of events is:
- Message menu warning arrives.
- Menu__MessageHandler calls appropriate user menu handler
- User's menu handler calls Menu_AddDynamicDialogBox with handle to add.
- Menu_AddDynamicDialogBox stores window handle and returns.
- User's menu handler returns.
- Menu__MessageHandler calls SWI Wimp_CreateSubmenu with the saved window handle,
- and returns.
-
- i.e., a bit neater than all that dbox malarkey with Risc OS Lib.
- (and much simpler for user).
- */
-
- static window_handle submenu_handle;
-
-
- void Menu_AddDynamicDialogBox(window_handle window)
- {
- /* A bit simple - provide a macro instead? */
- submenu_handle = window;
- }
-
- /* BOOL Menu__MessageHandler()
-
- This handles menu warning messages, and calls the appropriate user menu
- handler to get the window handle to use as a sub-menu.
-
- It passes on the selection array to the user's handler, just like a normal
- menu selection.
- */
-
- BOOL menu_selection = TRUE;
-
- wimp_point menu_submenupos;
-
- static BOOL Menu__MessageHandler(event_pollblock *event, void *reference)
- {
- /* If this is a menu warning message, process it */
- if (event->data.message.header.action == message_MENUWARNING)
- {
- handler_str *handler = (handler_str *) reference;
-
- /* Get the window handle for the submenu */
-
- /* Tell caller this is a sub-menu warning message event */
- menu_selection = FALSE;
-
- /* Let them access submenu position if they want to */
- menu_submenupos.x = event->data.message.data.words[1];
- menu_submenupos.y = event->data.message.data.words[2];
-
- /* Initialise the submenu pointer/window handle */
- submenu_handle = -1;
-
- handler->handler_proc(handler->reference, event->data.message.data.words+3);
-
- menu_selection = TRUE;
-
- /* Call Wimp_CreateSubMenu to show the submenu, if the handler created one */
- if (submenu_handle != -1)
- Error_Check(Wimp_CreateSubMenu((menu_block *) submenu_handle,
- menu_submenupos.x, menu_submenupos.y));
- return TRUE;
- }
- else
- /* Pass on to next handler */
- return FALSE;
- }
-
-
- /* The following variable is used because the Wimp doesn't tell us when a menu is
- closed if a selection is not made from it (i.e. if the user clicks outside the
- menu. Therefore if menu events are to be claimed, and this variable is non-NULL,
- then the handler should be de-installed before the new handlersare installed.
- Otherwise the wrong handlers get called, as the Event module works on a first
- registered, first served basis.
- */
- static handler_str *installed_handler = NULL;
-
- /* BOOL Menu__SelectionHandler()
-
- This will pass on a selection array to the relevant handler.
- If adjust is pressed, the menu is re-opened.
-
- NB. Menu selection events are released, unless adjust is pressed.
- This is in case the user wants to use them at other times.
- */
- static BOOL Menu__SelectionHandler(event_pollblock *event, void *reference)
- {
- handler_str *handler = (handler_str *) reference;
-
- /* Get pointer info */
- mouse_block mouse;
- Error_Check(Wimp_GetPointerInfo(&mouse));
-
- /* Get mouse button information */
- if (mouse.button.data.adjust)
- menu_adjust_pressed = TRUE;
-
- /* Deal with selection */
- handler->handler_proc(handler->reference, event->data.selection);
-
- /* If adjust pressed, re-open the menu.
- The position doesn't matter - the Wimp knows it's being re-opened and so it
- works out the position for itself.
- */
- if (menu_adjust_pressed)
- {
- wimp_point pos;
- menu m;
-
- /* Call the menu maker, if necessary */
- if (handler->flags.data.menu_maker)
- m = (handler->data.maker)(handler->reference);
- else
- m = handler->data.menu;
-
- pos.x = 0; pos.y = 0;
-
- Menu_Show(m, pos, FALSE);
-
- menu_adjust_pressed = FALSE;
- }
- else
- {
- /* Menu to be closed - release menu selection events... */
- Event_Release(event_MENU, event_ANY, event_ANY,
- Menu__SelectionHandler, reference);
- EventMsg_Release(message_MENUWARNING, event_ANY, Menu__MessageHandler);
- /* Ensure they aren't released again */
- installed_handler = NULL;
- }
-
- /* We always handle menu selection events */
- return TRUE;
- }
-
-
- /* BOOL Menu__EventHandler()
-
- This is called when a menu might be requested (ie a mouse click on a window
- or icon user has registered a menu for.
- If a menu is needed, it is created if necessary, and then shown.
- Menu selection events are also claimed for passing to Menu__SelectionHandler().
- */
-
- static BOOL Menu__EventHandler(event_pollblock *poll_block, void *reference)
- {
- handler_str *handler = (handler_str *) reference;
- mouse_block *mouse = &poll_block->data.mouse;
-
- /* If menu was pressed, or if select was pressed and this menu is attached to
- an icon, we should deal with this event, otherwise pass back to Event
- module.
- */
- if ((mouse->button.data.menu) ||
- ((handler->flags.data.icon_handler) && (mouse->button.data.select)))
- {
- menu m;
-
- /* Menu requested - set up window/icon variables */
- menu_current_window = mouse->window;
- menu_current_icon = mouse->icon;
-
- /* Call menu maker if necessary */
- if (handler->flags.data.menu_maker)
- m = (handler->data.maker)(handler->reference);
- else
- m = handler->data.menu;
-
- /* De-install any handlers still installed... */
- if (installed_handler != NULL)
- {
- Event_Release(event_MENU, event_ANY, event_ANY,
- Menu__SelectionHandler, (void *) installed_handler);
- EventMsg_Release(message_MENUWARNING, event_ANY, Menu__MessageHandler);
- }
-
- /* Request menu selections and warning messages to be delivered to us... */
- Event_Claim(event_MENU, event_ANY, event_ANY,
- Menu__SelectionHandler, (void *) handler);
- EventMsg_Claim(message_MENUWARNING, event_ANY,
- Menu__MessageHandler, (void *) handler);
-
- /* Update the variable which records the currently installed handler */
- installed_handler = handler;
-
- /* And show the menu */
- Menu_Show(m, mouse->pos, handler->flags.data.iconbar);
-
- return TRUE;
- }
-
- return FALSE;
-
- }
-
- static window_node *find_window_node(window_handle window)
- {
- /* Looks through the handlers to see if one is registered for this
- window handle already. Returns pointer to it if it is, NULL otherwise.
- */
- window_node *windownode = LinkList_FirstItem(&handlers);
-
- /* Search for specified window handle */
- while ((windownode != NULL) && (windownode->window != window))
- windownode = (window_node *) LinkList_NextItem(&windownode->header);
-
- /* Return what we found (possibly NULL => no window node found */
- return windownode;
- }
-
- static icon_node *find_icon_node(linklist_header *anchor, icon_handle icon)
- {
- /* Looks through the given icon handlers to see if one is registered for this
- icon handle already. Returns pointer to it if it is, NULL otherwise.
- */
- icon_node *iconnode = LinkList_FirstItem(anchor);
-
- /* Search for specified icon handle */
- while ((iconnode != NULL) && (iconnode->icon != icon))
- iconnode = (icon_node *) LinkList_NextItem(&iconnode->header);
-
- /* Return what we found (possibly NULL => no icon node found */
- return iconnode;
- }
-
- static handler_str *find_menu_handler(window_handle window, icon_handle icon)
- {
- window_node *windownode;
- icon_node *iconnode;
-
- /* This returns a pointer to a handler_str for the caller to fill in.
- It searches for an existing handler to overwrite, or failing that, creates
- a new node in the tree for the specified window/icon handle pair, and returns
- a pointer to the handler_str of that.
- i.e. it's a 'constructive' find - if it can't find the entity it's looking for,
- it makes it.
- */
-
- /* Is there already a handler for this window? */
-
- windownode = find_window_node(window);
-
- if (windownode == NULL)
- {
- /* There isn't - so add one */
-
- windownode = (window_node *) malloc(sizeof(window_node));
- windownode->window = window;
- LinkList_InitItem(&windownode->icons_anchor); /* No icon specifics (yet) */
-
- /* If icon-specific, we should null the window-specific handler... */
- if (icon != event_ANY)
- {
- /* The handler_proc is the flag - if it's NULL, there's no menu or
- menu maker installed either.
- */
- windownode->handler.handler_proc = NULL;
- }
-
- /* Add to head of list - except event_ANY, which should always be last */
-
- if (window == event_ANY)
- LinkList_AddToTail(&handlers, &(windownode->header));
- else
- LinkList_AddToHead(&handlers, &(windownode->header));
- }
- else if (icon == event_ANY)
- {
- /* If there is a window-specific handler already, remove it */
- if (windownode->handler.handler_proc != NULL)
- Event_Release(event_CLICK, window, event_ANY, Menu__EventHandler,
- (void *) &windownode->handler);
- }
-
- /* If not interested in icon-specific handler, just return pointer to
- window-specific handler */
- if ((window == event_ANY) || (icon == event_ANY))
- return &windownode->handler;
-
- /* Is there already a handler for this icon? */
- iconnode = find_icon_node(&windownode->icons_anchor, icon);
-
- if (iconnode == NULL)
- {
- /* No icon handler for this icon, so add one */
-
- iconnode = (icon_node *) malloc(sizeof(icon_node));
- iconnode->icon = icon;
-
- /* Add to the icon handler list */
- LinkList_AddToTail(&windownode->icons_anchor, &(iconnode->header));
- }
- else
- {
- /* Remove the handler which is already present */
- Event_Release(event_CLICK, window, icon, Menu__EventHandler,
- (void *) &iconnode->handler);
- }
-
- return &iconnode->handler;
- }
-
-
- /* This is used to set the flag in the handler_str. */
- static BOOL Menu__menu_maker = FALSE;
-
- void Menu_AttachMenu(menu the_menu, window_handle window, icon_handle icon,
- menu_handler handler_proc, void *reference)
- {
- handler_str *handler = find_menu_handler(window, icon);
-
- handler->flags.data.menu_maker = Menu__menu_maker;
- handler->flags.data.icon_handler = (icon != event_ANY);
- handler->flags.data.iconbar = (window == window_ICONBAR);
- handler->data.menu = the_menu;
- handler->handler_proc = handler_proc;
- handler->reference = reference;
-
- /* Claim mouse click events for given window/icon handle pair */
- Event_Claim(event_CLICK, window, icon, Menu__EventHandler, (void *) handler);
- }
-
-
- void Menu_AttachMenuMaker(menu_maker maker_proc,
- window_handle window, icon_handle icon,
- menu_handler handler_proc, void *reference)
- {
- /* Bit of a cheat this - relies on union structure of handler_str, but why
- duplicate code?
- */
- Menu__menu_maker = TRUE;
- Menu_AttachMenu((menu) maker_proc, window, icon, handler_proc, reference);
- Menu__menu_maker = FALSE;
- }
-
- void Menu_DetachMenu(window_handle window, icon_handle icon)
- /*
- Detaches any menus or menu makers from the given window/icon.
- If called for a window, detaches *all* handlers for it, including
- icon specific ones.
- If the window is event_ANY, it ONLY removes the 'catch-all' handler - it
- does NOT remove all menu handlers.
-
- It is legal to call this even if no menus/makers are attached to the
- specified window/icon.
-
- window should be event_ANY - if not window specific, or
- a window handle - to detach only from that window
- icon should be event_ANY - if not icon-specific, or
- an icon handle - to detach ONLY from that icon
- (NOTE: if icon != event_ANY, window MUST be defined)
- */
- {
- window_node *windownode = find_window_node(window);
- icon_node *iconnode;
-
- if (windownode == NULL)
- /* Couldn't find any handlers for specified window */
- return;
-
- if (icon == event_ANY)
- {
- icon_node *tmp;
-
- /* Release the events claimed by this window-specific handler */
- Event_Release(event_CLICK, window, event_ANY, Menu__EventHandler,
- (void *) &windownode->handler);
-
- /* Remove this window's node */
- LinkList_Unlink(&handlers, &(windownode->header));
-
- /* Free the icon handler nodes for this window (if any exist) */
-
- iconnode = LinkList_FirstItem(&windownode->icons_anchor);
-
- /* Don't need to unlink each node as we're deleting the whole list
- including the anchor anyway.
- */
- while (iconnode != NULL)
- {
- tmp = iconnode;
- iconnode = LinkList_NextItem(&iconnode->header);
- free(tmp);
- }
-
- /* Free the window node itself */
- free(windownode);
- }
- else
- {
- /* Remove handler for this icon */
- iconnode = find_icon_node(&windownode->icons_anchor, icon);
-
- /* If no handler for this window/icon handle pair, exit quietly */
- if (iconnode == NULL)
- return;
-
- /* Release the events claimed by this icon-specific handler */
- Event_Release(event_CLICK, window, icon, Menu__EventHandler,
- (void *) &iconnode->handler);
-
- /* Remove the icon node from the window's list of icon handlers */
- LinkList_Unlink(&(windownode->icons_anchor), &(iconnode->header));
-
- /* Free the memory used for this node */
- free(iconnode);
-
- /* If this was the last icon handler for this window, and there is no
- window-specific handler, delete the window node as well.
- */
- if ((LinkList_FirstItem(&windownode->icons_anchor) == NULL) &&
- (windownode->handler.handler_proc == NULL))
- {
- LinkList_Unlink(&handlers, &(windownode->header));
- free(windownode);
- }
- }
- }
-
-