chiark / gitweb /
Initial revision
[ssr] / StraySrc / Libraries / Steel / c / menu
1 /*
2  * menu
3  *
4  *  A working menu system
5  *
6  * © 1993-1998 Straylight
7  */
8
9 /*----- Licensing note ----------------------------------------------------*
10  *
11  * This file is part of Straylight's Steel library.
12  *
13  * Steel is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2, or (at your option)
16  * any later version.
17  *
18  * Steel is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with Steel.  If not, write to the Free Software Foundation,
25  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26  */
27
28 #include "menu.h"
29 #include "mem.h"
30 #include "wimp.h"
31 #include "werr.h"
32 #include "msgs.h"
33 #include "resspr.h"
34 #include "event.h"
35 #include "wimpt.h"
36 #include "os.h"
37 #include "swis.h"
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41
42 #include "dll.h"
43
44 #ifndef _dll_NODLL
45   extern void _dllEntry(menu__menuproc)(void *handle,char hits[]);
46   extern menu _dllEntry(menu__menumaker)(void *handle);
47 #endif
48
49 typedef struct menu__menustr
50 {
51   int items;
52   int maxlen;
53   BOOL submenu;
54   char *indirect;
55   int indbytes;
56   wimp_menustr *syshandle;       /* This points to the actual structure,   */
57                                  /* and 1 word ahead of a back-pointer to  */
58                                  /* this structure                         */
59 }
60 menu__menustr;
61
62 typedef struct
63 {
64   event_menu_maker m;
65   menu_selectProc sel;
66   menu_helpProc help;
67   void *handle;
68 }
69 menu__handlerstr;
70
71 static menu__handlerstr menu__instant;
72
73 /*---------------------------------------------------------------------------
74
75   The syntax of menu strings is an extension of the system used by the
76   RISC_OSlib menu segment:
77
78     menu_description ::= <flags> <item> <sep> { <flags> <item> <sep> }
79     flags ::= '!' | '~' | '>' | ' ' | '+'
80     sep ::= ',' | '|'
81     item ::= <a bunch of other characters>
82
83   flags have meanings as follows:
84
85     ' ' has no effect
86     '~' shades the item
87     '>' item has a menu dbox attached
88     '!' item is ticked
89     '+' open submenu even if item is shaded
90
91 ---------------------------------------------------------------------------*/
92
93 /*
94  * static BOOL menu__ROparse(menu m,char **p,wimp_menuitem *item)
95  *
96  * Use
97  *  Reads the next menu entry for a RISC_OSlib-style menu definition.
98  *
99  * Parameters
100  *  menu m == the menu structure that this is being added to.
101  *  char **p == pass-by-reference pointer to next item to parse.  This is
102  *    updated to point to the next item after successful termination, to the
103  *    point where an error was discovered, or to zero to indicate successful
104  *    completion of the entire menu.
105  *  wimp_menuitem *item == pointer to structure for the routine to deposit
106  *    its output.
107  *
108  * Returns
109  *  TRUE for successful termination, or FALSE to indicate an error.
110  */
111
112 static BOOL menu__ROparse(menu m,char **p,wimp_menuitem *item)
113 {
114   char *ind;
115   int len=0;
116   int diff;
117   wimp_menuflags f=0;
118   wimp_iconflags i=
119   (
120     (7*wimp_IFORECOL) |
121     wimp_ITEXT |
122     wimp_IVCENTRE |
123     wimp_IFILLED
124   );
125   BOOL done=FALSE;
126   wimp_menuitem *itm;
127   char *eptr;
128   item->submenu=(wimp_menustr *)-1;
129   while (!done)
130   {
131     switch (**p)
132     {
133       case '!':
134         f|=wimp_MTICK;
135         (*p)++;
136         break;
137       case ' ':
138         (*p)++;
139         break;
140       case '>':
141         f|=wimp_MSUBLINKMSG;
142         item->submenu=(wimp_menustr *)1;
143         (*p)++;
144         break;
145       case '~':
146         i|=wimp_INOSELECT;
147         (*p)++;
148         break;
149       case '+':
150         f|=wimp_MOPENSUB;
151         (*p)++;
152         break;
153       default:
154         done=TRUE;
155         break;
156     }
157   }
158   done=FALSE;
159   for (eptr=*p;*eptr!=0 && *eptr!='|' && *eptr!=',';eptr++,len++)
160     /* blank */;
161   if (len>12)
162   {
163     i|=wimp_INDIRECT;
164     if (ind=mem_reAlloc(m->indirect,m->indbytes+len+1),!ind)
165                                /* This will allocate memory if there isn't */
166                                /* any indirected workspace yet             */
167     {
168       werr
169       (
170         FALSE,
171         msgs_lookup("menuNEM:Not enough memory to construct menu.")
172       );
173       return (FALSE);
174     }
175     itm=(wimp_menuitem *)(m->syshandle+1);
176     while (itm!=item)
177     {
178       if (itm->iconflags & wimp_INDIRECT)
179       {
180         diff=itm->data.indirecttext.buffer-m->indirect;
181         if (diff<m->indbytes && diff>=0)
182           itm->data.indirecttext.buffer=ind+diff;
183       }
184       itm++;
185     }
186     m->indirect=ind;
187     ind+=m->indbytes;
188     m->indbytes+=len+1;
189     item->data.indirecttext.buffer=ind;
190     item->data.indirecttext.validstring=(char *)-1;
191     item->data.indirecttext.bufflen=len+1;
192   }
193   else
194     ind=item->data.text;
195   while (**p!=0 && **p!='|' && **p!=',')
196     *(ind++)=*((*p)++);
197   *(ind++)=0;
198   if (**p=='|')
199     f|=wimp_MSEPARATE;
200   if (!**p)
201     *p=0;
202   else
203     (*p)++;
204   item->flags=f;
205   item->iconflags=i;
206   if (len>m->maxlen)
207     m->maxlen=len;
208   m->items+=1;
209   return (TRUE);
210 }
211
212 /*
213  * int menu__ROitemCount(char *string)
214  *
215  * Use
216  *  Counts the number of menu items specified in a RISC_OSlib style menu
217  *  string.
218  *
219  * Parameters
220  *  char *s == pointer to the menu string
221  *
222  * Returns
223  *  The number of items specified
224  */
225
226 static int menu__ROitemCount(char *s)
227 {
228   int count=1;
229   if (*s=='|')
230     s++;
231   while (*s)
232   {
233     switch (*s)
234     {
235       case '|':
236       case ',':
237         count++;
238         break;
239     }
240     s++;
241   }
242   return (count);
243 }
244
245 /*
246  * menu menu_new(char *title,char *items)
247  *
248  * Use
249  *  Creates a menu as per the old RISC_OSlib system.
250  *
251  * Parameters
252  *  char *title == the menu title (truncated to 12 chars if necessary)
253  *  char *items == the menu item text.  Data is indirected where required.
254  *
255  * Returns
256  *  A pointer to the menu if successful, or a NULL pointer if not.
257  */
258
259 menu menu_new(char *title,char *items)
260 {
261   menu m;
262   int i;
263   menu *t;
264   wimp_menuitem *itm;
265   int ino=menu__ROitemCount(items);
266   if (m=mem_alloc(sizeof(menu__menustr)),!m)
267   {
268     werr(FALSE,msgs_lookup("menuNEM:Not enough memory to construct menu."));
269     return (0);
270   }
271   if (t=mem_alloc
272   (
273     sizeof(menu)+sizeof(wimp_menuhdr)+ino*sizeof(wimp_menuitem)+4
274   ),
275   !t)
276   {
277     mem_free(m);
278     werr(FALSE,msgs_lookup("menuNEM:Not enough memory to construct menu."));
279     return (0);
280   }
281   m->syshandle=(wimp_menustr *)(t+1);
282   m->indirect=0;
283   m->indbytes=0;
284   m->submenu=FALSE;
285   m->items=0;
286   m->maxlen=strlen(title);
287   strcpy(m->syshandle->hdr.title,title);
288   m->syshandle->hdr.tit_fcol=7;
289   m->syshandle->hdr.tit_bcol=2;
290   m->syshandle->hdr.work_fcol=7;
291   m->syshandle->hdr.work_bcol=0;
292   m->syshandle->hdr.height=44;
293   m->syshandle->hdr.gap=0;
294   *t=m;
295   itm=((wimp_menuitem *)(m->syshandle+1))-1;
296   for (i=0;i<ino;i++)
297   {
298     itm++;
299     if (!items)
300       werr(TRUE,msgs_lookup("menuICOU:(menu_new): Menu item undercount."));
301     if (!menu__ROparse(m,&items,itm))
302     {
303       if (m->indirect)
304         mem_free(m->indirect);
305       mem_free(m);
306       mem_free(t);
307       return (0);
308     }
309   }
310   if (items)
311     werr(TRUE,msgs_lookup("menuICOO:(menu_new): Menu item overcount."));
312   if (ino!=m->items)
313     werr(TRUE,msgs_lookup("menuIMSC:(menu_new): Menu item count mismatch."));
314   itm->flags|=wimp_MLAST;
315   m->syshandle->hdr.width=16*m->maxlen+16;
316   return (m);
317 }
318
319 /*
320  * wimp_menuitem *menu__itmPointer(menu m,int i)
321  *
322  * Use
323  *  Returns a pointer to the specified menu item.  Items are numbered
324  *  starting from 1.
325  *
326  * Parameters
327  *  menu m == the menu to look up
328  *  int i == the item number to find
329  *
330  * Returns
331  *  A pointer to the specified menu item
332  */
333
334 static wimp_menuitem *menu__itmPointer(menu m,int i)
335 {
336   return ((wimp_menuitem *)(m->syshandle+1)+(i-1));
337 }
338
339 /*
340  * void menu_submenu(menu main,int item,menu submenu)
341  *
342  * Use
343  *  Attaches a menu as a submenu, but it will put in a submenu warning flag,
344  *  so the user-friendly too-many-windows messages come up right!
345  *
346  * Parameters
347  *  menu main == the main menu
348  *  int item == the menu item number
349  *  menu submenu == the submenu
350  */
351
352 void menu_submenu(menu main,int item,menu submenu)
353 {
354   wimp_menuitem *i=menu__itmPointer(main,item);
355   if (submenu)
356   {
357     i->flags|=wimp_MSUBLINKMSG;
358     i->submenu=(wimp_menuptr)(submenu->syshandle);
359     submenu->submenu=TRUE;
360   }
361   else
362   {
363     i->flags&=~wimp_MSUBLINKMSG;
364     i->submenu=(wimp_menuptr)-1;
365   }
366 }
367
368 /*
369  * void menu_dispose(menu *m,BOOL recurse)
370  *
371  * Use
372  *  Kill off a menu or menu tree.  Won't work on submenus.
373  *
374  * Parameters
375  *  menu *m == pointer to menu to Domestos-ise.
376  *  BOOL recurse == do we want to kill its submenus too?
377  */
378
379 void menu_dispose(menu *m,BOOL recurse)
380 {
381   int i;
382   wimp_menuitem *itm=(wimp_menuitem *)((*m)->syshandle+1);
383   menu s;
384   if (recurse)
385   {
386     for (i=0;i<(*m)->items;i++);
387     {
388       if ((int)(itm->submenu)>=0x8000)
389       {
390         s=*((menu *)itm->submenu-1);
391         menu_dispose(&s,TRUE);
392       }
393       itm++;
394     }
395   }
396   if ((*m)->indirect)
397     mem_free((*m)->indirect);
398   mem_free(*m);
399 }
400
401 /*
402  * wimp_menustr *menu_syshandle(menu m)
403  *
404  * Use
405  *  Returns pointer to actual menu structure.
406  *
407  * Parameters
408  *  menu m == menu handle for which structure is required.
409  *
410  * Returns
411  *  A pointer to the WIMP menu structure.
412  */
413
414 wimp_menustr *menu_syshandle(menu m)
415 {
416   return (m->syshandle);
417 }
418
419 /*
420  * void menu_extend(menu m,char *items)
421  *
422  * Use
423  *  Extend the given menu by a bit.
424  *
425  * Parameters
426  *  menu m == the menu to extend
427  *  char *items == the items to tag on the end
428  */
429
430 void menu_extend(menu m,char *items)
431 {
432   wimp_menuitem *itm;
433   wimp_menuitem *start;
434   int i;
435   int ino=menu__ROitemCount(items);
436   menu *t=mem_reAlloc
437   (
438     (menu *)m->syshandle-1,
439     sizeof(menu)+sizeof(wimp_menuhdr)+(ino+m->items)*sizeof(wimp_menuitem)+4
440   );
441   if (!t)
442   {
443     werr(FALSE,msgs_lookup("menuNEME:Not enough memory to extend menu."));
444     return;
445   }
446   m->syshandle=(wimp_menustr *)(t+1);
447   start=itm=menu__itmPointer(m,m->items);
448   if (*items=='|')
449   {
450     itm->flags|=wimp_MSEPARATE;
451     items++;
452   }
453   for (i=0;i<ino;i++)
454   {
455     itm++;
456     if (!menu__ROparse(m,&items,itm))
457       return;
458   }
459   start->flags&=~wimp_MLAST;
460   itm->flags|=wimp_MLAST;
461   m->syshandle->hdr.width=16*m->maxlen+16;
462 }
463
464 /*
465  * void menu_setflags(menu m,int i,BOOL tick,BOOL shade)
466  *
467  * Use
468  *  Changes menu item properties
469  *
470  * Parameters
471  *  menu m == menu to change
472  *  int i == menu item to change
473  *  BOOL tick == tick the item
474  *  BOOL shade == shade the item
475  */
476
477 void menu_setflags(menu m,int i,BOOL tick,BOOL shade)
478 {
479   wimp_menuitem *itm=menu__itmPointer(m,i);
480   if (tick)
481     itm->flags|=wimp_MTICK;
482   else
483     itm->flags&=~wimp_MTICK;
484   if (shade)
485     itm->iconflags|=wimp_INOSELECT;
486   else
487     itm->iconflags&=~wimp_INOSELECT;
488 }
489
490 /*
491  * void menu_make_writeable(menu m,int i,char *buff,int bufflen,char *valid)
492  *
493  * Use
494  *  Makes a menu entry writable (sorry about the spelling - it's Acorn's
495  *  fault)
496  *
497  * Parameters
498  *  menu m == the menu in question
499  *  int i == the item to be handled
500  *  char *buff == where the data is to reside
501  *  int bufflen == max length of data that can be crammed into buff
502  *  char *valid == validation string (0 for none)
503  */
504
505 void menu_make_writeable(menu m,int i,char *buff,int bufflen,char *valid)
506 {
507   wimp_menuitem *itm=menu__itmPointer(m,i);
508   if (!valid)
509     valid=(char *)-1;
510   itm->iconflags|=wimp_INDIRECT;
511   itm->flags|=wimp_MWRITABLE;
512   itm->data.indirecttext.buffer=buff;
513   itm->data.indirecttext.bufflen=bufflen;
514   itm->data.indirecttext.validstring=valid;
515 }
516
517 /*
518  * void menu_make_sprite(menu m,int i,char *name)
519  *
520  * Use
521  *  Turns a menu entry into a sprite.  Utterly useless, but there you go...
522  *
523  * Parameters
524  *  menu m == the menu we're dealing with
525  *  int i == the item to sprite-ise
526  *  char *name == the sprite name
527  */
528
529 void menu_make_sprite(menu m,int i,char *name)
530 {
531   wimp_menuitem *itm=menu__itmPointer(m,i);
532   itm->iconflags|=wimp_INDIRECT|wimp_ISPRITE;
533   itm->iconflags&=~wimp_ITEXT;
534   itm->data.indirectsprite.name=name;
535   itm->data.indirectsprite.spritearea=resspr_area();
536   itm->data.indirectsprite.nameisname=TRUE;
537 }
538
539 /*
540  * void menu_redirectItem(menu m,int i,char *buff,int bufflen,char *valid)
541  *
542  * Use
543  *  Allows you to make a menu item's data point to your workspace, so you
544  *  can change the text of an item at any time.  The buffer length isn't
545  *  important.
546  *
547  * Parameters
548  *  menu m == the menu in question
549  *  int i == the item to be handled
550  *  char *buff == where the data is to reside
551  *  int bufflen == max length of data that can be crammed into buff
552  *  char *valid == validation string (0 for none)
553  */
554
555 void menu_redirectItem(menu m,int i,char *buff,int bufflen,char *valid)
556 {
557   wimp_menuitem *itm=menu__itmPointer(m,i);
558   if (!valid)
559     valid=(char *)-1;
560   itm->iconflags|=wimp_INDIRECT;
561   itm->data.indirecttext.buffer=buff;
562   itm->data.indirecttext.bufflen=bufflen;
563   itm->data.indirecttext.validstring=valid;
564 }
565
566 /*
567  * void menu_minWidth(menu m,int min)
568  *
569  * Use
570  *  Sets the minimum permissable width of a menu (in characters).  Note that
571  *  this call will not make the menu thinner than the width calculated
572  *  during menu_new or menu_extend.
573  *
574  * Parameters
575  *  menu m == the menu to change
576  *  int min == the minumum allowable width fro the menu in characters.  If
577  *    0 is passed, the width reverts to the calculated width.
578  */
579
580 void menu_minWidth(menu m,int min)
581 {
582   if (!min)
583     min=16*m->maxlen+16;
584   else
585   {
586     min=16*min+16;
587     if (min<m->syshandle->hdr.width)
588       return;
589   }
590   m->syshandle->hdr.width=min;
591 }
592
593 /*
594  * void menu_settitle(menu m,char *title)
595  *
596  * Use
597  *  Change a menu title.
598  *
599  * Parameters
600  *  menu m == the menu to change
601  *  char *title == the new title
602  */
603
604 void menu_settitle(menu m,char *title)
605 {
606   strncpy(m->syshandle->hdr.title,title,12);
607 }
608
609 /*
610  * int *menu__processHits(int hit[])
611  *
612  * Use
613  *  Processes a hit string as necessary (translates WIMP menu items to C
614  *  ones etc.)
615  *
616  * Parameters
617  *  int hit[] == PBR array to process, terminated by -1.
618  *
619  * Returns
620  *  The array
621  */
622
623 static int *menu__processHits(int hit[])
624 {
625   int i=0;
626   do
627     hit[i++]++;
628   while (hit[i-1]);
629   return (hit);
630 }
631
632 /*
633  * menu menu__menumaker(void *handle)
634  *
635  * Use
636  *  A surrogate menu maker.
637  *
638  * Parameters
639  *  void *handle == pointer to information about the real maker
640  *
641  * Returns
642  *  The menu to display
643  */
644
645 _dll_static menu menu__menumaker(void *handle)
646 {
647   menu__handlerstr *m=handle;
648   return ((m->m)(m->handle));
649 }
650
651 /*
652  * void menu__menuproc(void *handle,char hit[])
653  *
654  * Use
655  *  General menu handler for menus registered using the menu functions
656  *  rather than the standard event ones.
657  *
658  * Parameters
659  *  void *handle == pointer to the control block for this menu
660  *  char hit[] == an array of menu hits (ignored)
661  */
662
663 _dll_static void menu__menuproc(void *handle,char hit[])
664 {
665   menu__handlerstr *m=handle;
666   int hits[20];
667   int minusone=-1;
668   wimp_eventstr *e=wimpt_last_event();
669   hit=hit;
670   switch (event_whyMenuEvent())
671   {
672     case event_MENUSELECT:
673       if (m->sel)
674         (m->sel)(menu__processHits(e->data.menu),m->handle);
675       break;
676     case event_MENUDELETE:
677       if (m->sel)
678         (m->sel)(menu__processHits(&minusone),m->handle);
679       break;
680     case event_MENUSUBMENU:
681       if (m->sel)
682         (m->sel)(menu__processHits(e->data.msg.data.words+3),m->handle);
683       break;
684     case event_MENUHELP:
685       if (m->help)
686       {
687         wimpt_noerr(wimp_getmenustate(1,
688                                       hits,
689                                       e->data.msg.data.helprequest.m.w,
690                                       e->data.msg.data.helprequest.m.i));
691         (m->help)(menu__processHits(hits),m->handle);
692       }
693       break;
694     default:
695       werr(TRUE,msgs_lookup("menuUKNME:(menu__menuproc): "
696                             "Unknown menu event type."));
697   }
698 }
699
700 /*
701  * void menu_attach
702  * (
703  *   wimp_w w,
704  *   menu m,
705  *   menu_selectProc sel,
706  *   menu_helpProc help,
707  *   void *handle
708  * )
709  *
710  * Use
711  *  Basically equivalent to event_attachmenu, only it handles help requests
712  *  neatly etc.  Source code compatibility *cannot* be assured, because I
713  *  want to be able to expand this routine to cater for later changes.  I
714  *  will try to soften the blow (if any) by supplying macros that will work
715  *  with older programs, but I can't guarantee anything.
716  *
717  * Parameters
718  *  wimp_w w == the window to attach to
719  *  menu m == the menu to attach
720  *  menu_selectProc sel == procedure to handle most selection-type events
721  *  menu_helpProc help == procedure to handle help requests for the menu
722  */
723
724 void menu_attach
725 (
726   wimp_w w,
727   menu m,
728   menu_selectProc sel,
729   menu_helpProc help,
730   void *handle
731 )
732 {
733   menu__handlerstr *h=0;
734   void *old;
735   menu om;
736   event_menu_maker omm;
737   event_menu_proc omp;
738   if (m)
739   {
740     if (h=mem_alloc(sizeof(menu__handlerstr)),!h)
741     {
742       werr
743       (
744         FALSE,
745         msgs_lookup("menuNEM:Not enough memory to construct menu.")
746       );
747       return;
748     }
749     h->sel=sel;
750     h->help=help;
751     h->handle=handle;
752   }
753   event_attachedMenu(w,&om,&omm,&omp,&old);
754   if (omp==menu__menuproc)
755     mem_free(old);
756   event_attachmenu(w,m,_dllEntry(menu__menuproc),h);
757 }
758
759 /*
760  * void menu_attachMaker
761  * (
762  *   wimp_w w,
763  *   event_menu_maker m,
764  *   menu_selectProc sel,
765  *   menu_helpProc help,
766  *   void *handle
767  * )
768  *
769  * Use
770  *  This routine deals with event_attachmenumaker as menu_attach deals with
771  *  event_attachmenu.
772  *
773  * Parameters
774  *  wimp_w w == the window to attach to
775  *  menu m == the menu to attach
776  *  menu_selectProc sel == procedure to handle most selection-type events
777  *  menu_helpProc help == procedure to handle help requests for the menu
778  */
779
780 void menu_attachMaker
781 (
782   wimp_w w,
783   event_menu_maker m,
784   menu_selectProc sel,
785   menu_helpProc help,
786   void *handle
787 )
788 {
789   menu__handlerstr *h=0;
790   void *old;
791   menu om;
792   event_menu_maker omm;
793   event_menu_proc omp;
794   if (m)
795   {
796     if (h=mem_alloc(sizeof(menu__handlerstr)),!h)
797     {
798       werr
799       (
800         FALSE,
801         msgs_lookup("menuNEM:Not enough memory to construct menu.")
802       );
803       return;
804     }
805     h->sel=sel;
806     h->help=help;
807     h->handle=handle;
808     h->m=m;
809   }
810   event_attachedMenu(w,&om,&omm,&omp,&old);
811   if (omp==menu__menuproc)
812     mem_free(old);
813   event_attachmenumaker(w,
814                         _dllEntry(menu__menumaker),
815                         _dllEntry(menu__menuproc),
816                         h);
817 }
818
819 /*
820  * void menu_open(menu m,menu_selectProc sel,menu_helpProc help,void *handle)
821  *
822  * Use
823  *  Analagous to event_openMenu().
824  *
825  * Parameters
826  *  As above.
827  */
828
829 void menu_open(menu m,menu_selectProc sel,menu_helpProc help,void *handle)
830 {
831   menu__instant.sel=sel;
832   menu__instant.help=help;
833   menu__instant.handle=handle;
834   event_openMenu(m,_dllEntry(menu__menuproc),&menu__instant);
835 }
836
837 /*
838  * void menu_make(event_menu_maker m,menu_selectProc sel,menu_helpProc help,void *handle)
839  *
840  * Use
841  *  Analagous to event_makeMenu().
842  *
843  * Parameters
844  *  As above.
845  */
846
847 void menu_make(event_menu_maker m,menu_selectProc sel,menu_helpProc help,void *handle)
848 {
849   menu__instant.sel=sel;
850   menu__instant.help=help;
851   menu__instant.handle=handle;
852   menu__instant.m=m;
853   event_makeMenu(_dllEntry(menu__menumaker),
854                  _dllEntry(menu__menuproc),
855                  &menu__instant);
856 }
857
858 /*
859  * void menu_saveHandler(wimp_w w,menu_handler *h)
860  *
861  * Use
862  *  Saves information about the menu handle for the given window in the
863  *  block specified.  This can be used *only* to restore the handler for a
864  *  window later (although it needn't be the same one).  Note too that the
865  *  window need not have to have menu handlers registered using the menu
866  *  calls, or even have any at all.
867  *
868  * Parameters
869  *  wimp_w w == the window whose menu handlers we are to save
870  *  menu_handler *h == pointer to a chunk of memory to fill in.
871  */
872
873 void menu_saveHandler(wimp_w w,menu_handler *h)
874 {
875   event_menu_maker mk;
876   event_menu_proc p;
877   menu m;
878   void *hand;
879   menu__handlerstr *ha;
880   event_attachedMenu(w,&m,&mk,&p,&hand);
881   if (p==_dllEntry(menu__menuproc))
882   {
883     ha=hand;
884     h->doneByMenu=TRUE;
885     h->info.m.m=m;
886     h->info.m.make=ha->m;
887     h->info.m.sel=ha->sel;
888     h->info.m.help=ha->help;
889     h->info.m.handle=ha->handle;
890   }
891   else
892   {
893     h->doneByMenu=FALSE;
894     h->info.e.m=m;
895     h->info.e.make=mk;
896     h->info.e.proc=p;
897     h->info.e.handle=hand;
898   }
899 }
900
901 /*
902  * void menu_restoreHandler(wimp_w w,menu_handler *h)
903  *
904  * Use
905  *  Restores handlers from a structure filled in by menu_saveHandler.
906  *
907  * Parameters
908  *  wimp_w w == the window whose handlers we are to set up.
909  *  menu_handler *h == pointer to a chunk of memory filled in correctly.
910  */
911
912 void menu_restoreHandler(wimp_w w,menu_handler *h)
913 {
914   if (h->doneByMenu)
915   {
916     if (h->info.m.m)
917     {
918       menu_attach(w,
919                   h->info.m.m,
920                   h->info.m.sel,
921                   h->info.m.help,
922                   h->info.m.handle);
923     }
924     else
925     {
926       menu_attachMaker(w,
927                        h->info.m.make,
928                        h->info.m.sel,
929                        h->info.m.help,
930                        h->info.m.handle);
931     }
932   }
933   else
934   {
935     if (h->info.e.m)
936       event_attachmenu(w,h->info.e.m,h->info.e.proc,h->info.e.handle);
937     else
938     {
939       event_attachmenumaker(w,
940                             h->info.e.make,
941                             h->info.e.proc,
942                             h->info.e.handle);
943     }
944   }
945 }