home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / comp / sources / acorn / 62 next >
Encoding:
Text File  |  1993-01-27  |  30.7 KB  |  1,087 lines

  1. Path: sparky!uunet!comp.vuw.ac.nz!waikato.ac.nz!aukuni.ac.nz!cs18.cs.aukuni.ac.nz!jwil1
  2. Newsgroups: comp.sources.acorn
  3. Subject: Source [C, Desklib]: Menu functions for DeskLib
  4. Message-ID: <1993Jan26.225014.19529@cs.aukuni.ac.nz>
  5. From: jwil1@cs.aukuni.ac.nz (TMOTA)
  6. Date: Tue, 26 Jan 1993 22:50:14 GMT
  7. Sender: jwil1@cs.aukuni.ac.nz (TMOTA)
  8. Organization: Computer Science Dept. University of Auckland
  9. Approved: cba@acorn.co.nz
  10. Lines: 1075
  11.  
  12. The following is additional C code which adds a menu creation/handling
  13. functionality to DeskLib. It works in much the same way as RISC OS Lib's
  14. "menu" code (same interface, etc).
  15.  
  16. I haven't had time to thoroughly test it or anything, but it should be OK.
  17. You will have to alter a couple of things (like the #includes of "Wimp_DL.h"
  18. instead of the DeskLib distribution name "Wimp", etc)
  19.  
  20. It comes in 4 files:
  21.   IconLib.c
  22.   IconLib.h
  23.   Menus.c
  24.   Menus.h
  25. which you will have to cut out of this file by hand.
  26.  
  27. --->8---cut here: IconLib.c
  28.  
  29. /* 
  30.   IconLib.c - routines over and above RISC_OSLib and DeskLib.
  31. */
  32.  
  33. /* ANSI includes */
  34. #include "stdlib.h"
  35.  
  36. /* DeskLib includes */
  37. #include "DeskLib.Wimp.h"
  38.  
  39. #include "IconLib.h"
  40.  
  41. void IconLib_DisposeIndData(icon_data *data, icon_flags flags)
  42. {
  43.   /* make sure this icon is indirected - exit quietly if not */
  44.   if ((data == NULL) || (!flags.data.indirected))
  45.     return;
  46.  
  47.   /* it's indirected - free the data */
  48.   switch (ICON_TYPE(flags.value))
  49.   {
  50.     case TXT_ONLY:
  51.     case TXT_AND_SPR:
  52.  
  53.       /* Free text buffer */
  54.       free(data->indirecttext.buffer);
  55.  
  56.       /* Free validation string, if there is one */
  57.       if ((data->indirecttext.validstring != NULL) &&
  58.       (data->indirecttext.validstring != (char *) 0xffffffff))
  59.     free(data->indirecttext.validstring);
  60.       break;
  61.  
  62.     case SPR_ONLY:
  63.       if (data->indirectsprite.nameisname)
  64.     free((void *) data->indirectsprite.name);
  65.       break;
  66.   }       
  67. }
  68.  
  69.  
  70. --->8---cut here: IconLib.h
  71.  
  72.  
  73. /* 
  74.   IconLib.h - routines over and above RISC_OSLib and DeskLib.
  75. */
  76.  
  77. /* DeskLib includes */
  78. #include "DeskLib.Wimp.h"
  79.  
  80. /* The following are used when examining icon flags/data.  A simple macro is
  81.    provide to find the type of an icon. */
  82.  
  83. #define NO_ICON_DATA 0
  84. #define TXT_ONLY     1
  85. #define SPR_ONLY     2
  86. #define TXT_AND_SPR  3
  87.  
  88. #define ICON_TYPE(flags) ((flags) & 3)
  89.  
  90. void IconLib_DisposeIndData(icon_data *data, icon_flags flags);
  91. /*
  92.   This will free any indirected data for this icon. It uses free(), so 
  93.   obviously expects that malloc() was used to get the memory in the first
  94.   place, so make sure this is the case if you use this function.
  95. */
  96.  
  97.  
  98.  
  99. --->8---cut here: Menus.c
  100.  
  101.  
  102.  
  103. /*
  104.   Menus.c
  105. */
  106.  
  107. /* ANSI includes */
  108. #include "stdlib.h"
  109. #include "string.h"
  110.  
  111. /* DeskLib includes */
  112. #include "Core.h"
  113. #include "DeskLib.Wimp.h"
  114. #include "DeskLib.Event.h"
  115. #include "WimpSWIs.h"
  116. #include "Error.h"
  117. #include "LinkList.h"
  118.  
  119. /* TimsLib includes */
  120. #include "lib.h"
  121. #include "IconLib.h"
  122.  
  123. #include "Menus.h"
  124.  
  125. /*
  126.    General Note:
  127.  
  128.    The typedef 'menu' is actually a wimp_menuptr, but cannot be used with
  129.    free, as the word before the wimp_menublock points to the parent of this
  130.    menu (if any), and the memory is allocated in one go (see typedef below).
  131.    Therefore to free storage you need:
  132.    free(((char *) menu) - 4);
  133.    i.e. don't bother, it's easier to use Menus_Dispose().
  134.  
  135.    This is so that I when I add routine to extend menus, it can be used to
  136.    extend submenus too.
  137. */
  138.  
  139. typedef struct
  140. {
  141.   menu_ptr   parent;
  142.   menu_block menublock;
  143. } menu_info;
  144.  
  145. static void Menus__InitItem(menu_item *item)
  146. {
  147.   static menu_item default_item;
  148.  
  149.   static BOOL first_time = TRUE;
  150.  
  151.   if (first_time)
  152.   {
  153.     first_time = FALSE;
  154.  
  155.     /* Initialise the menu flags */
  156.     default_item.menuflags.value = 0;
  157.  
  158.     /* Initialise the menu item to have no sub-menu */
  159.     default_item.submenu.value = -1;
  160.  
  161.     /* Initialise the icon flags */
  162.     default_item.iconflags.value = icon_TEXT | icon_FILLED;
  163.     default_item.iconflags.data.foreground = colour_BLACK;
  164.     default_item.iconflags.data.background = colour_WHITE;
  165.  
  166.     /* Initialise the icon data */
  167.     default_item.icondata.text[0] = '\0';
  168.   }
  169.  
  170.   memcpy(item, &default_item, sizeof(menu_item));
  171. }
  172.  
  173.  
  174. static void Menus__SetOption(menu_item *item, char opt)
  175. {
  176.   switch (opt)
  177.   {
  178.     case '!':  /* Tick this entry */
  179.       item->menuflags.data.ticked = TRUE;
  180.       break;
  181.  
  182.     case '~':  /* Fade this entry */
  183.       item->iconflags.data.shaded = TRUE;
  184.       break;
  185.  
  186.     case '=':  /* Add dotted line after this entry */
  187.       item->menuflags.data.dotted = TRUE;
  188.       break;
  189.  
  190.     case '>':  /* This entry has a dialogue box as a sub-menu */
  191.       item->menuflags.data.notifysub = TRUE;
  192.  
  193.       /* Set submenu flag - use a legal window handle, but not likely to be in use */
  194.       item->submenu.window = (window_handle) 0x7FFF; 
  195.       break;
  196.  
  197.     /* Ignore space */
  198.   }
  199. }
  200.  
  201. #define is_sep(c) (((c) == ',') || ((c) == '|'))
  202. #define is_opt(c) (((c) == '!') || ((c) == '>') || ((c) == '~') || ((c) == '=') || ((c) == ' '))
  203.  
  204. static int Menus__ReadItemName(char *item_name, char *src)
  205. {
  206.   int i = 0;
  207.  
  208.   /* A name can start with any character other than a 'sep' or an 'opt', and can
  209.      then contain any character other than a 'sep'.
  210.   */
  211.   if (!is_sep(src[0]) && !is_opt(src[0]))
  212.   {
  213.     do
  214.     {
  215.       /* Copy this character and move on */
  216.       item_name[i] = src[i];
  217.       i++;
  218.     } while (!is_sep(src[i]) && (src[i] != '\0'));
  219.   }
  220.  
  221.   /* Terminate the item name */
  222.   item_name[i] = '\0';
  223.  
  224.   /* Tell the caller how many characters we used */
  225.   return i;  
  226. }
  227.  
  228.  
  229. #define Menus__FirstEntry(menu) ((menu_item *) (((menu_ptr) (menu) + 1)))
  230.  
  231. menu Menus_New(char *name, char *desc)
  232. {
  233.   int entries = 1,
  234.       i = 0,
  235.       name_len,
  236.       max_name_len = 0;
  237.  
  238.   char item_name[80];
  239.  
  240.   menu_info *the_menu;
  241.   menu_item *item;
  242.  
  243.   /* Exit quietly if no description string */
  244.   if (desc == NULL)
  245.     return NULL;
  246.  
  247.   /* Find out how many entries are needed */
  248.   while (desc[i] != '\0')
  249.   {
  250.     if ((desc[i] == ',') || (desc[i] == '|'))
  251.       entries++;
  252.     i++;
  253.   }
  254.  
  255.   /* Get the memory for these entries */
  256.   the_menu = (menu_info *) malloc(sizeof(menu_info) + (entries * sizeof(menu_item)));
  257.  
  258.   /* Get pointer to first item, using some obscure pointer arithmetic */
  259.   item = Menus__FirstEntry(&the_menu->menublock);
  260.  
  261.   i = 0;
  262.  
  263.   /* Construct the menu entries */
  264.   while (desc[i] != '\0')
  265.   {
  266.     /* Initialise this menu item */
  267.     Menus__InitItem(item);
  268.  
  269.     /* Look for an option */
  270.     if (is_opt(desc[i]))
  271.     {
  272.       Menus__SetOption(item, desc[i]);
  273.       i++;
  274.     }
  275.  
  276.     /* Read the item name */
  277.     name_len = Menus__ReadItemName(item_name, desc + i);
  278.  
  279.     /* Copy the item name into the menu icon */
  280.  
  281.     if (name_len <= wimp_MAXNAME)
  282.     {
  283.       /* normal non-indirected icon */
  284.       strncpy(item->icondata.text, item_name, wimp_MAXNAME);
  285.     }
  286.     else
  287.     {
  288.       /* Too long - make into indirected icon */
  289.  
  290.       item->iconflags.data.indirected = TRUE;
  291.       item->icondata.indirecttext.buffer = save_str(item_name, 0);
  292.  
  293.       item->icondata.indirecttext.bufflen = name_len + 1;    /* for terminator */
  294.  
  295.       item->icondata.indirecttext.validstring = (char *) -1; /* no validation  */
  296.     }
  297.  
  298.     /* Check if this is the longest item so far */
  299.     if (name_len > max_name_len)
  300.       max_name_len = name_len;
  301.  
  302.     /* Move to next menu item */
  303.     i += name_len;
  304.  
  305.     /* Skip the separator, if there is one */
  306.     if (is_sep(desc[i]))
  307.       i++;
  308.  
  309.     item++;
  310.   }
  311.  
  312.   /* Set the 'last' flag on the last menu item */
  313.   item--;
  314.   item->menuflags.data.last = TRUE;
  315.  
  316.   /* Set up menu block */
  317.   strncpy(the_menu->menublock.title, name, wimp_MAXNAME);
  318.  
  319.   /* These attributes taken from Wimp Application notes (RO2 PRMS p1314) */
  320.   the_menu->menublock.titlefore = colour_BLACK;
  321.   the_menu->menublock.titleback = colour_GREY2;
  322.   the_menu->menublock.workfore  = colour_BLACK;
  323.   the_menu->menublock.workback  = colour_WHITE;
  324.   the_menu->menublock.width     = (max_name_len + 1) * 16; /* !!!Find a legal way!!! */
  325.   the_menu->menublock.height    = 44;
  326.   the_menu->menublock.gap       = 0;
  327.  
  328.   return (menu) &(the_menu->menublock);
  329. }
  330.  
  331. void Menus__Dispose(menu_ptr the_menu, BOOL recursive)
  332. {
  333.   menu_info *info;
  334.  
  335.   /* Get pointer to the first item */
  336.   menu_item *item = (menu_item *) (the_menu + 1);
  337.  
  338.   /* De-allocate any indirect data / sub-menus */
  339.   do
  340.   {
  341.     /* Does this item have a sub-menu (but not a dialogue box sub-menu) */
  342.     if (recursive && item->submenu.value >= 0x8000)
  343.       Menus__Dispose(item->submenu.menu, recursive);
  344.  
  345.     /* Free up any indirected data */
  346.     IconLib_DisposeIndData(&item->icondata, item->iconflags);
  347.  
  348.     /* Move to next item */
  349.     item++;
  350.   } while (item->menuflags.data.last == FALSE);
  351.  
  352.   /* Free the data for the menu block - this includes all the menu items, and
  353.      also the word preceeding the menu_block, as it points to the parent menu.
  354.   */
  355.   info = (menu_info *) (((char *) the_menu) - 4);
  356.   free(info);
  357. }
  358.  
  359. void Menus_Dispose(menu the_menu, BOOL recursive)
  360. {
  361.   /* Just a veneer for the slightly lower-level Menus__Dispose() */
  362.  
  363.   Menus__Dispose(&(((menu_info *) the_menu)->menublock), recursive);
  364. }
  365.  
  366. void Menus_SubMenu(menu parent, int entry, menu submenu)
  367. {
  368.   /* Get a pointer to the menu info block of the submenu */
  369.   menu_info *submenu_info = (menu_info *) (((int *) submenu) - 1);
  370.  
  371.   /* Get a pointer to the first entry */
  372.   menu_item *item = Menus__FirstEntry(parent);
  373.  
  374.   /* Plug the submenu into the specified menu entry */
  375.   item[entry-1].submenu.menu = (menu_ptr) submenu;
  376.  
  377.   /* Link the submenu to the parent */
  378.   submenu_info->parent = (menu_ptr) parent;
  379. }
  380.  
  381. void Menus_AddDialogBox(menu the_menu, int entry, window_handle window)
  382. {
  383.   /* Get a pointer to the first entry */
  384.   menu_item *item = Menus__FirstEntry(the_menu);
  385.  
  386.   /* Plug the submenu into the specified menu entry */
  387.   item[entry-1].submenu.window = window;
  388. }
  389.  
  390. void Menus_SetFlags(menu the_menu, int entry, BOOL tick, BOOL fade)
  391. {
  392.   /* Get a pointer to the first item */
  393.   menu_item *item = Menus__FirstEntry(the_menu);
  394.  
  395.   /* Set the flags as required */
  396.   item[entry-1].menuflags.data.ticked = tick;
  397.   item[entry-1].iconflags.data.shaded = fade;
  398. }
  399.  
  400. void Menus_MakeWritable(menu the_menu, int entry, 
  401.             char *buffer, int bufflen, char *validstring)
  402. {
  403.   char *the_text = NULL; /* Used to keep a copy of the current text of the entry */
  404.  
  405.   /* Get a pointer to the first item */
  406.   menu_item *item = Menus__FirstEntry(the_menu);
  407.  
  408.   /* Move to the entry we want */
  409.   item += (entry - 1);
  410.  
  411.   /* If it's currently a text (or text & sprite) icon, save the text string */
  412.  
  413.   switch (ICON_TYPE(item->iconflags.value))
  414.   {
  415.     case TXT_ONLY:
  416.     case TXT_AND_SPR:
  417.       if (item->iconflags.data.indirected)
  418.       {
  419.     the_text = save_str(item->icondata.indirecttext.buffer, 0);
  420.       }
  421.       else
  422.       {
  423.     the_text = (char *) malloc(wimp_MAXNAME + 1);
  424.     strncpy(the_text, item->icondata.text, wimp_MAXNAME);
  425.     the_text[wimp_MAXNAME] = '\0';
  426.       }
  427.   }
  428.  
  429.   /* Free any indirected data */
  430.   IconLib_DisposeIndData(&item->icondata, item->iconflags);
  431.  
  432.   /* Make the entry into a writable, indirected, text-only icon */
  433.   item->menuflags.data.writable   = TRUE;
  434.   item->iconflags.data.text       = TRUE;
  435.   item->iconflags.data.sprite     = FALSE; /* just to be safe */
  436.   item->iconflags.data.indirected = TRUE;
  437.  
  438.   /* Plug in the buffer and validation string */
  439.   item->icondata.indirecttext.buffer      = buffer;
  440.   item->icondata.indirecttext.bufflen     = bufflen;
  441.   item->icondata.indirecttext.validstring = validstring;
  442.  
  443.   /* Copy the old text back in and free the buffer, if necessary */
  444.   if (the_text != NULL)
  445.   {
  446.     strncpy(buffer, the_text, bufflen - 1);
  447.     free(the_text);
  448.   }
  449. }
  450.  
  451. void Menus_Show(menu the_menu, wimp_point mouse_pos, BOOL iconbar)
  452. {
  453.   /* If an iconbar menu, position bottom of menu 96 OS units above bottom
  454.      of screen.
  455.   */
  456.   if (iconbar)
  457.   {
  458.     /* Title bar is not included in the positioning of menu windows */
  459.  
  460.     int menu_height = 0;
  461.  
  462.     menu_ptr menu = (menu_ptr) the_menu;
  463.     /* Get a pointer to the first item */
  464.     menu_item *item = Menus__FirstEntry(the_menu);
  465.  
  466.     BOOL not_finished = TRUE;
  467.  
  468.     /* Process each menu entry */
  469.  
  470.     while (not_finished)
  471.     {
  472.       /* Add height of menu item for each item */
  473.     menu_height += menu->height;
  474.  
  475.       /* Add extra height for a separator */
  476.       if (item->menuflags.value & 2)
  477.     /* NB this value may be wrong for non-standard item heights/inter-item gaps */
  478.     menu_height += (6 * 4);
  479.  
  480.       /* If not the last item, add the inter-item gap */
  481.       if (item->menuflags.data.last)
  482.       {
  483.     menu_height += menu->gap;
  484.     not_finished = FALSE;
  485.       }
  486.  
  487.       /* Process the next item */
  488.       item++;
  489.     }
  490.  
  491.     /* Position menu */
  492.     mouse_pos.y = 96 + menu_height;
  493.   }
  494.  
  495.   /* Open the menu 64 OS units to the left of the mouse click, as recommended
  496.      by the RO2 PRMS, p1314.
  497.   */
  498.   Error_Check(Wimp_CreateMenu((menu_block *) the_menu, mouse_pos.x - 64, mouse_pos.y));
  499. }
  500.  
  501.  
  502. /* Menu attachment routines */
  503.  
  504. typedef struct handler_str
  505. {
  506.   union
  507.   {
  508.     int value;
  509.     struct 
  510.     {
  511.       unsigned int menu_maker   : 1; /* 1 => data union contains a menu_maker       */
  512.       unsigned int icon_handler : 1; /* 1 => Menu should be shown if select pressed */
  513.       unsigned int iconbar      : 1; /* 1 => Menu is attached to icon bar icon      */
  514.     } data;
  515.   } flags;
  516.  
  517.   union
  518.   {
  519.     menu       menu;
  520.     menu_maker maker;
  521.   } data;
  522.  
  523.   menu_handler handler_proc;
  524.   void *reference;
  525. } handler_str;
  526.  
  527. typedef struct icon_node
  528. {
  529.   linklist_header header;
  530.   icon_handle     icon;
  531.   handler_str     handler;
  532. } icon_node;
  533.  
  534. typedef struct window_node
  535. {
  536.   linklist_header  header;
  537.   window_handle    window;       /* event_ANY means catch all menu events */
  538.   linklist_header  icons_anchor;
  539.   handler_str      handler;
  540. } window_node;
  541.  
  542.  
  543. /* Contains tree of menu handlers */
  544. linklist_header handlers = {NULL, NULL};
  545.  
  546. /*
  547.    'submenu_handle' is used to store the window handle passed to 
  548.    Menus_AddDynamicDialogBox, which should be called in response to receiving a 
  549.    menu warning (requested by using the '>' option in the menu description string).
  550.  
  551.    Sequence of events is:
  552.      Message menu warning arrives.
  553.      Menus__MessageHandler calls appropriate user menu handler
  554.      User's menu handler calls Menus_AddDynamicDialogBox with handle to add.
  555.      Menus_AddDynamicDialogBox stores window handle and returns.
  556.      User's menu handler returns.
  557.      Menus__MessageHandler calls SWI Wimp_CreateSubmenu with the saved window handle,
  558.       and returns.
  559.  
  560.    i.e., a bit neater than all that dbox malarkey with Risc OS Lib.
  561.      (and much simpler for user).
  562. */
  563.  
  564. static window_handle submenu_handle;
  565.  
  566.  
  567. void Menus_AddDynamicDialogBox(window_handle window)
  568. {
  569.   /* A bit simple - provide a macro instead? */
  570.   submenu_handle = window;
  571. }
  572.  
  573. /* BOOL Menus__MessageHandler()
  574.  
  575.    This handles menu warning messages, and calls the appropriate user menu
  576.    handler to get the window handle to use as a sub-menu.
  577.  
  578.    It passes on the selection array to the user's handler, just like a normal
  579.    menu selection.
  580. */
  581.  
  582. static BOOL Menus__MessageHandler(event_pollblock *event, void *reference)
  583. {
  584.   /* If this is a menu warning message, process it */
  585.   if (event->data.message.header.action == message_MENUWARNING)
  586.   {
  587.     handler_str *handler = (handler_str *) reference;
  588.  
  589.     /* Get the window handle for the submenu */
  590.     handler->handler_proc(reference, event->data.message.data.words+3);
  591.  
  592.     /* Call Wimp_CreateSubMenu to show the submenu */
  593.     Error_Check(Wimp_CreateSubMenu((menu_block *) submenu_handle,
  594.                    event->data.message.data.words[1],
  595.                    event->data.message.data.words[2]));
  596.     return TRUE;
  597.   }
  598.   else
  599.     /* Pass on to next handler */
  600.     return FALSE;
  601. }
  602.  
  603. /* BOOL Menus__SelectionHandler()
  604.  
  605.    This will pass on a selection array to the relevant handler.
  606.    If adjust is pressed, the menu is re-opened.
  607.  
  608.    NB. Menu selection events are released, unless adjust is pressed.
  609.        This is in case the user wants to use them at other times.
  610. */
  611. static BOOL Menus__SelectionHandler(event_pollblock *event, void *reference)
  612. {
  613.   handler_str *handler = (handler_str *) reference;
  614.  
  615.   /* Get pointer info */
  616.   mouse_block mouse;
  617.   Error_Check(Wimp_GetPointerInfo(&mouse));
  618.  
  619.   /* Deal with selection */
  620.   handler->handler_proc(reference, event->data.selection);
  621.  
  622.   /* If adjust pressed, re-open the menu.
  623.      The position doesn't matter - the Wimp knows it's being re-opened and so it
  624.      works out the position for itself.
  625.   */
  626.   if (mouse.button.data.adjust)
  627.   {
  628.     wimp_point pos;
  629.     menu m;
  630.  
  631.     /* Call the menu maker, if necessary */
  632.     if (handler->flags.data.menu_maker)
  633.       m = (handler->data.maker)(reference);
  634.     else
  635.       m = handler->data.menu;
  636.  
  637.     pos.x = 0; pos.y = 0;
  638.  
  639.     Menus_Show(m, pos, FALSE);
  640.   }
  641.   else
  642.   {
  643.     /* Menu to be closed - release menu selection events... */
  644.     Event_Release(event_MENU, event_ANY, event_ANY, 
  645.           Menus__SelectionHandler, reference);
  646.     Event_Release(event_USERMESSAGE, event_ANY, event_ANY, 
  647.           Menus__MessageHandler, reference);
  648.   }
  649.  
  650.   /* We always handle menu selection events */
  651.   return TRUE;
  652. }
  653.   
  654.  
  655. /* BOOL Menus__EventHandler()
  656.  
  657.    This is called when a menu might be requested (ie a mouse click on a window
  658.    or icon user has registered a menu for.
  659.    If a menu is needed, it is created if necessary, and then shown.
  660.    Menu selection events are also claimed for passing to Menus__SelectionHandler().
  661. */
  662.  
  663. static BOOL Menus__EventHandler(event_pollblock *poll_block, void *reference)
  664. {
  665.   handler_str *handler = (handler_str *) reference;
  666.   mouse_block *mouse   = &poll_block->data.mouse;
  667.  
  668.   /* If menu was pressed, or if select was pressed and this menu is attached to
  669.      an icon, we should deal with this event, otherwise pass back to Event
  670.      module.
  671.   */
  672.   if ((mouse->button.data.menu) ||
  673.       ((handler->flags.data.icon_handler) && (mouse->button.data.select)))
  674.   {
  675.     /* Menu requested - call menu maker if necessary */
  676.     menu m;
  677.  
  678.     if (handler->flags.data.menu_maker)
  679.       m = (handler->data.maker)(reference);
  680.     else
  681.       m = handler->data.menu;
  682.  
  683.     /* Request menu selections and warning messages to be delivered to us... */
  684.     Event_Claim(event_MENU, event_ANY, event_ANY, 
  685.         Menus__SelectionHandler, (void *) handler);
  686.     Event_Claim(event_USERMESSAGE, event_ANY, event_ANY, 
  687.         Menus__MessageHandler, (void *) handler);
  688.  
  689.     /* And show the menu */
  690.     Menus_Show(m, mouse->pos, handler->flags.data.iconbar);
  691.  
  692.     return TRUE;
  693.   }
  694.   return FALSE;
  695.  
  696. }
  697.  
  698. static window_node *find_window_node(window_handle window)
  699. {
  700.   /* Looks through the handlers to see if one is registered for this 
  701.      window handle already.  Returns pointer to it if it is, NULL otherwise.
  702.   */
  703.   window_node *windownode = LinkList_FirstItem(&handlers);
  704.  
  705.   /* Search for specified window handle */
  706.   while ((windownode != NULL) && (windownode->window != window))
  707.     windownode = (window_node *) LinkList_NextItem(&windownode->header);
  708.  
  709.   /* Return what we found (possibly NULL => no window node found */
  710.   return windownode;
  711. }
  712.  
  713. static icon_node *find_icon_node(linklist_header *anchor, icon_handle icon)
  714. {
  715.   /* Looks through the given icon handlers to see if one is registered for this 
  716.      icon handle already.  Returns pointer to it if it is, NULL otherwise.
  717.   */
  718.   icon_node *iconnode = LinkList_FirstItem(anchor);
  719.  
  720.   /* Search for specified icon handle */
  721.   while ((iconnode != NULL) && (iconnode->icon != icon))
  722.     iconnode = (icon_node *) LinkList_NextItem(&iconnode->header);
  723.  
  724.   /* Return what we found (possibly NULL => no icon node found */
  725.   return iconnode;
  726. }
  727.  
  728. static handler_str *find_menu_handler(window_handle window, icon_handle icon)
  729. {
  730.   window_node *windownode;
  731.   icon_node   *iconnode;
  732.  
  733.   /* This returns a pointer to a handler_str for the caller to fill in.
  734.      It searches for an existing handler to overwrite, or failing that, creates
  735.      a new node in the tree for the specified window/icon handle pair, and returns
  736.      a pointer to the handler_str of that.
  737.      i.e. it's a 'constructive' find - if it can't find the entity it's looking for,
  738.      it makes it.
  739.   */
  740.  
  741.   /* Is there already a handler for this window? */
  742.  
  743.   windownode = find_window_node(window);
  744.  
  745.   if (windownode == NULL)
  746.   {
  747.     /* There isn't - so add one */
  748.  
  749.     windownode = (window_node *) malloc(sizeof(window_node));
  750.     windownode->window = window;
  751.     LinkList_InitItem(&windownode->icons_anchor); /* No icon specifics (yet) */
  752.  
  753.     /* If icon-specific, we should null the window-specific handler... */
  754.     if (icon != event_ANY)
  755.     {
  756.       /* The handler_proc is the flag - if it's NULL, there's no menu or
  757.      menu maker installed either.
  758.       */
  759.       windownode->handler.handler_proc = NULL;
  760.     }
  761.  
  762.     /* Add to head of list - except event_ANY, which should always be last */
  763.  
  764.     if (window == event_ANY)
  765.       LinkList_AddToTail(&handlers, &(windownode->header));
  766.     else
  767.       LinkList_AddToHead(&handlers, &(windownode->header));
  768.   }
  769.   else if (icon == event_ANY)
  770.   {
  771.     /* There is a window-specific handler already - remove it */
  772.     Event_Release(event_CLICK, window, event_ANY, Menus__EventHandler, 
  773.           (void *) &windownode->handler);
  774.   }
  775.  
  776.   /* If not interested in icon-specific handler, just return pointer to
  777.      window-specific handler */
  778.   if ((window == event_ANY) || (icon == event_ANY))
  779.     return &windownode->handler;
  780.  
  781.   /* Is there already a handler for this icon? */
  782.   iconnode = find_icon_node(&windownode->icons_anchor, icon);
  783.  
  784.   if (iconnode == NULL)
  785.   {
  786.     /* No icon handler for this icon, so add one */
  787.  
  788.     iconnode = (icon_node *) malloc(sizeof(icon_node));
  789.     iconnode->icon = icon;
  790.  
  791.     /* Add to the icon handler list */
  792.     LinkList_AddToTail(&windownode->icons_anchor, &(iconnode->header));
  793.   }
  794.   else
  795.   {
  796.     /* Remove the handler which is already present */
  797.     Event_Release(event_CLICK, window, icon, Menus__EventHandler, 
  798.           (void *) &iconnode->handler);
  799.   }
  800.  
  801.   return &iconnode->handler;
  802. }
  803.  
  804.  
  805. /* This is used to set the flag in the handler_str. */
  806. static BOOL Menus__menu_maker = FALSE;
  807.  
  808. void Menus_AttachMenu(menu the_menu, window_handle window, icon_handle icon,
  809.               menu_handler handler_proc, void *reference)
  810. {
  811.   handler_str *handler = find_menu_handler(window, icon);
  812.  
  813.   handler->flags.data.menu_maker   = Menus__menu_maker;
  814.   handler->flags.data.icon_handler = (icon != event_ANY);
  815.   handler->flags.data.iconbar      = (window == window_ICONBAR);
  816.   handler->data.menu               = the_menu;
  817.   handler->handler_proc            = handler_proc;
  818.   handler->reference               = reference;
  819.  
  820.   /* Claim mouse click events for given window/icon handle pair */
  821.   Event_Claim(event_CLICK, window, icon, Menus__EventHandler, (void *) handler);
  822. }
  823.  
  824.  
  825. void Menus_AttachMenuMaker(menu_maker maker_proc, 
  826.                window_handle window, icon_handle icon,
  827.                menu_handler handler_proc, void *reference)
  828. {
  829.   /* Bit of a cheat this - relies on union structure of handler_str, but why
  830.      duplicate code in a library?
  831.   */
  832.   Menus__menu_maker = TRUE;
  833.   Menus_AttachMenu((menu) maker_proc, window, icon, handler_proc, reference);
  834.   Menus__menu_maker = FALSE;
  835. }
  836.  
  837. void Menus_DetachMenu(window_handle window, icon_handle icon)
  838. /*
  839.    Detaches any menus or menu makers from the given window/icon.
  840.    If called for a window, detaches *all* handlers for it, including
  841.    icon specific ones.
  842.    If the window is event_ANY, it ONLY removes the 'catch-all' handler - it
  843.    does NOT remove all menu handlers.
  844.  
  845.    It is legal to call this even if no menus/makers are attached to the
  846.    specified window/icon.
  847.  
  848.    window    should be event_ANY  - if not window specific, or
  849.          a window handle      - to detach only from that window
  850.    icon      should be event_ANY  - if not icon-specific, or
  851.          an icon handle       - to detach ONLY from that icon
  852.          (NOTE: if icon != event_ANY, window MUST be defined)
  853. */
  854. {
  855.   window_node *windownode = find_window_node(window);
  856.   icon_node   *iconnode;
  857.  
  858.   if (windownode == NULL)
  859.     /* Couldn't find any handlers for specified window */
  860.     return;
  861.  
  862.   if (icon == event_ANY)
  863.   {
  864.     icon_node *tmp;
  865.  
  866.     /* Release the events claimed by this window-specific handler */
  867.     Event_Release(event_CLICK, window, event_ANY, Menus__EventHandler, 
  868.           (void *) &windownode->handler);
  869.  
  870.     /* Remove this window's node */
  871.     LinkList_Unlink(&handlers, &(windownode->header));
  872.  
  873.     /* Free the icon handler nodes for this window (if any exist) */
  874.  
  875.     iconnode = LinkList_FirstItem(&windownode->icons_anchor);
  876.  
  877.     /* Don't need to unlink each node as we're deleting the whole list
  878.        including the anchor anyway.
  879.     */
  880.     while (iconnode != NULL)
  881.     {
  882.       tmp = iconnode;
  883.       iconnode = LinkList_NextItem(&iconnode->header);
  884.       free(tmp);
  885.     }
  886.  
  887.     /* Free the window node itself */
  888.     free(windownode);
  889.   } 
  890.   else
  891.   {
  892.     /* Remove handler for this icon */
  893.     iconnode = find_icon_node(&windownode->icons_anchor, icon);
  894.     
  895.     /* If no handler for this window/icon handle pair, exit quietly */
  896.     if (iconnode == NULL)
  897.       return;
  898.  
  899.     /* Release the events claimed by this icon-specific handler */
  900.     Event_Release(event_CLICK, window, icon, Menus__EventHandler, 
  901.           (void *) &iconnode->handler);
  902.  
  903.     /* Remove the icon node from the window's list of icon handlers */
  904.     LinkList_Unlink(&(windownode->icons_anchor), &(iconnode->header));
  905.  
  906.     /* Free the memory used for this node */
  907.     free(iconnode);
  908.  
  909.     /* If this was the last icon handler for this window, and there is no
  910.        window-specific handler, delete the window node as well.
  911.     */
  912.     if ((LinkList_FirstItem(&windownode->icons_anchor) == NULL) &&
  913.     (windownode->handler.handler_proc == NULL))
  914.     {
  915.       LinkList_Unlink(&handlers, &(windownode->header));
  916.       free(windownode);
  917.     }
  918.   }
  919. }
  920.  
  921.  
  922.  
  923. --->8---cut here: Menus.h
  924.  
  925.  
  926.  
  927. /*
  928.    Menus.h - a replacement for RISC_OSLib's menu module.
  929.  
  930.    A bit tacky at the mo as menu entries are numbered from 1 when specifying
  931.    but the handlers receive offset numbered from 0. Will fix when I start using
  932.    this seriously.
  933.  
  934.    Why does Risc OS Lib offset convert a 0-based int array to a 1-based char array???
  935.  
  936. */
  937.  
  938. /* DeskLib includes */
  939. #include "Core.h"
  940. #include "Wimp_DL.h"
  941.  
  942. typedef int menu;
  943.  
  944. extern menu Menus_New(char *name, char *desc);
  945. /*
  946.   Creates a menu with the given name and description.  The description syntax
  947.   is the same as for RISC_OSLib's menu module.
  948. */
  949.  
  950. extern void Menus_Dispose(menu the_menu, BOOL recursive);
  951. /*
  952.   De-allocates memory associated with this menu.  If recursive is TRUE, also
  953.   recursively de-allocates all sub-menus of the_menu.
  954. */
  955.  
  956. extern void Menus_SubMenu(menu parent, int entry, menu submenu);
  957. /*
  958.   Attaches 'submenu' as a sub-menu onto the menu 'parent' at position 'entry'.
  959. */
  960.  
  961. extern void Menus_AddDialogBox(menu the_menu, int entry, window_handle window);
  962. /*
  963.   Attaches a window (dialogue box) as a submenu onto 'the_menu' at position
  964.   'entry'.  Usually called for a menu warning event, just before dbox is
  965.   shown.
  966. */
  967.  
  968. extern void Menus_SetFlags(menu the_menu, int entry, BOOL tick, BOOL fade);
  969. /*
  970.   This sets the flags on the given menu entry.
  971. */
  972.  
  973. #define Menus_TickEntry(the_menu, entry, tick) ((((menu_item *) (Menus_SysHandle(the_menu) + 1))[(entry)-1].menuflags.data.ticked) = (tick))
  974.  
  975. #define Menus_FadeEntry(the_menu, entry, fade) ((((menu_item *) (Menus_SysHandle(the_menu) + 1))[(entry)-1].iconflags.data.shaded) = (fade))
  976. /*
  977.   void Menus_TickEntry(menu the_menu, int entry, BOOL tick)
  978.   void Menus_FadeEntry(menu the_menu, int entry, BOOL fade)
  979.  
  980.   These are simple(!) macros for when you want to set the tick or fade flags on
  981.   their own.
  982. */
  983.  
  984.  
  985. extern void Menus_MakeWritable(menu the_menu, int entry, 
  986.             char *buffer, int bufferlength, char *validation);
  987. /*
  988.   This makes the specified menu entry writable.
  989. */
  990.  
  991. extern void Menus_Show(menu the_menu, wimp_point mouse_pos, BOOL iconbar);
  992. /*
  993.   This is used in response to a mouse button event with buttons = 4 (MENU
  994.   pressed).  The mouse_pos is used to position the menu - just pass the wimp_point
  995.   from the mouse button event straight through.
  996.   If iconbar is TRUE, it sets the vertical position correctly for an icon
  997.   bar menu as per the Acorn guideline in RO2 PRMs p1315 - 96 OS units above the
  998.   bottom of the screen.
  999. */
  1000.  
  1001. /*
  1002.  
  1003. extern menu_ptr Menus_SysHandle(menu the_menu);
  1004.  
  1005.   Returns a low-level handle to the menu, for manipulations not supported by
  1006.   this module.
  1007.  
  1008.   NB. Do NOT use free() on this pointer - it won't work!
  1009. */
  1010. #define Menus_SysHandle(menu) ((menu_ptr) (menu))
  1011.  
  1012. /* Attaching menus for automatic handling */
  1013.  
  1014. typedef void (*menu_handler)(void *reference, int *hit);
  1015. typedef menu (*menu_maker)(void *reference);
  1016.  
  1017. extern void Menus_AttachMenu(menu the_menu, window_handle window, icon_handle icon,
  1018.               menu_handler handler, void *reference);
  1019. /*
  1020.    the_menu  the menu to attach
  1021.    window    should be event_ANY  - if not window specific, or
  1022.          a window handle      - to attach only to that window
  1023.    icon      should be event_ANY  - if not icon-specific, or
  1024.          an icon handle       - to attach ONLY to that icon
  1025.          (NOTE: if icon != event_ANY, window MUST be defined)
  1026.   
  1027.    handler   is the address of your menu handler function
  1028.    reference is a handle for any user-data you want passed to the
  1029.         function whenever it is called.
  1030.  
  1031.    Note that menus attached to icons will be invoked by SELECT clicks as well
  1032.    as MENU clicks.
  1033. */  
  1034.  
  1035.  
  1036. extern void Menus_AttachMenuMaker(menu_maker maker_proc, 
  1037.                window_handle window, icon_handle icon,
  1038.                menu_handler handler, void *reference);
  1039. /*
  1040.    the_menu  the menu to attach
  1041.    window    should be event_ANY  - if not window specific, or
  1042.          a window handle      - to attach only to that window
  1043.    icon      should be event_ANY  - if not icon-specific, or
  1044.          an icon handle       - to attach ONLY to that icon
  1045.          (NOTE: if icon != event_ANY, window MUST be defined)
  1046.   
  1047.    handler   is the address of your menu handler function
  1048.    reference is a handle for any user-data you want passed to the
  1049.         function whenever it is called.
  1050.  
  1051.    Note that menus attached to icons will be invoked by SELECT clicks as well
  1052.    as MENU clicks.
  1053. */
  1054.  
  1055. extern void Menus_AddDynamicDialogBox(window_handle window);
  1056. /*
  1057.   This is used for sub-menu dialog boxes that are created on demand.
  1058.  
  1059.   This should be called from within the menu selection handler when
  1060.   a submenu is traversed that has been marked with a '>' option in the
  1061.   menu string.  The handler should create the window to show as a submenu
  1062.   (but not open it) and pass the handle to this function, then return.
  1063.   The rest is automagic!
  1064. */
  1065.  
  1066.  
  1067. extern void Menus_DetachMenu(window_handle window, icon_handle icon);
  1068. /*
  1069.    Detaches any menus or menu makers from the given window/icon.
  1070.    If called for a window, detaches *all* handlers for it, including
  1071.    icon specific ones.
  1072.    If the window is event_ANY, only removes the 'catch-all' handler - it
  1073.    does NOT remove all menu handlers.
  1074.  
  1075.    It is legal to call this even if no menus/makers are attached to the
  1076.    specified window/icon.
  1077.  
  1078.    window    should be event_ANY  - if not window specific, or
  1079.          a window handle      - to detach only from that window
  1080.    icon      should be event_ANY  - if not icon-specific, or
  1081.          an icon handle       - to detach ONLY from that icon
  1082.          (NOTE: if icon != event_ANY, window MUST be defined)
  1083. */
  1084.  
  1085. -- 
  1086. themasterofthearcanejwil1@cs.aukuni.ac.nzthismessagewassentonrecycledelectrons
  1087.