/* * menu * * A working menu system * * © 1993-1998 Straylight */ /*----- Licensing note ----------------------------------------------------* * * This file is part of Straylight's Steel library. * * Steel is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * Steel is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Steel. If not, write to the Free Software Foundation, * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "menu.h" #include "mem.h" #include "wimp.h" #include "werr.h" #include "msgs.h" #include "resspr.h" #include "event.h" #include "wimpt.h" #include "os.h" #include "swis.h" #include #include #include #include "dll.h" #ifndef _dll_NODLL extern void _dllEntry(menu__menuproc)(void *handle,char hits[]); extern menu _dllEntry(menu__menumaker)(void *handle); #endif typedef struct menu__menustr { int items; int maxlen; BOOL submenu; char *indirect; int indbytes; wimp_menustr *syshandle; /* This points to the actual structure, */ /* and 1 word ahead of a back-pointer to */ /* this structure */ } menu__menustr; typedef struct { event_menu_maker m; menu_selectProc sel; menu_helpProc help; void *handle; } menu__handlerstr; static menu__handlerstr menu__instant; /*--------------------------------------------------------------------------- The syntax of menu strings is an extension of the system used by the RISC_OSlib menu segment: menu_description ::= { } flags ::= '!' | '~' | '>' | ' ' | '+' sep ::= ',' | '|' item ::= flags have meanings as follows: ' ' has no effect '~' shades the item '>' item has a menu dbox attached '!' item is ticked '+' open submenu even if item is shaded ---------------------------------------------------------------------------*/ /* * static BOOL menu__ROparse(menu m,char **p,wimp_menuitem *item) * * Use * Reads the next menu entry for a RISC_OSlib-style menu definition. * * Parameters * menu m == the menu structure that this is being added to. * char **p == pass-by-reference pointer to next item to parse. This is * updated to point to the next item after successful termination, to the * point where an error was discovered, or to zero to indicate successful * completion of the entire menu. * wimp_menuitem *item == pointer to structure for the routine to deposit * its output. * * Returns * TRUE for successful termination, or FALSE to indicate an error. */ static BOOL menu__ROparse(menu m,char **p,wimp_menuitem *item) { char *ind; int len=0; int diff; wimp_menuflags f=0; wimp_iconflags i= ( (7*wimp_IFORECOL) | wimp_ITEXT | wimp_IVCENTRE | wimp_IFILLED ); BOOL done=FALSE; wimp_menuitem *itm; char *eptr; item->submenu=(wimp_menustr *)-1; while (!done) { switch (**p) { case '!': f|=wimp_MTICK; (*p)++; break; case ' ': (*p)++; break; case '>': f|=wimp_MSUBLINKMSG; item->submenu=(wimp_menustr *)1; (*p)++; break; case '~': i|=wimp_INOSELECT; (*p)++; break; case '+': f|=wimp_MOPENSUB; (*p)++; break; default: done=TRUE; break; } } done=FALSE; for (eptr=*p;*eptr!=0 && *eptr!='|' && *eptr!=',';eptr++,len++) /* blank */; if (len>12) { i|=wimp_INDIRECT; if (ind=mem_reAlloc(m->indirect,m->indbytes+len+1),!ind) /* This will allocate memory if there isn't */ /* any indirected workspace yet */ { werr ( FALSE, msgs_lookup("menuNEM:Not enough memory to construct menu.") ); return (FALSE); } itm=(wimp_menuitem *)(m->syshandle+1); while (itm!=item) { if (itm->iconflags & wimp_INDIRECT) { diff=itm->data.indirecttext.buffer-m->indirect; if (diffindbytes && diff>=0) itm->data.indirecttext.buffer=ind+diff; } itm++; } m->indirect=ind; ind+=m->indbytes; m->indbytes+=len+1; item->data.indirecttext.buffer=ind; item->data.indirecttext.validstring=(char *)-1; item->data.indirecttext.bufflen=len+1; } else ind=item->data.text; while (**p!=0 && **p!='|' && **p!=',') *(ind++)=*((*p)++); *(ind++)=0; if (**p=='|') f|=wimp_MSEPARATE; if (!**p) *p=0; else (*p)++; item->flags=f; item->iconflags=i; if (len>m->maxlen) m->maxlen=len; m->items+=1; return (TRUE); } /* * int menu__ROitemCount(char *string) * * Use * Counts the number of menu items specified in a RISC_OSlib style menu * string. * * Parameters * char *s == pointer to the menu string * * Returns * The number of items specified */ static int menu__ROitemCount(char *s) { int count=1; if (*s=='|') s++; while (*s) { switch (*s) { case '|': case ',': count++; break; } s++; } return (count); } /* * menu menu_new(char *title,char *items) * * Use * Creates a menu as per the old RISC_OSlib system. * * Parameters * char *title == the menu title (truncated to 12 chars if necessary) * char *items == the menu item text. Data is indirected where required. * * Returns * A pointer to the menu if successful, or a NULL pointer if not. */ menu menu_new(char *title,char *items) { menu m; int i; menu *t; wimp_menuitem *itm; int ino=menu__ROitemCount(items); if (m=mem_alloc(sizeof(menu__menustr)),!m) { werr(FALSE,msgs_lookup("menuNEM:Not enough memory to construct menu.")); return (0); } if (t=mem_alloc ( sizeof(menu)+sizeof(wimp_menuhdr)+ino*sizeof(wimp_menuitem)+4 ), !t) { mem_free(m); werr(FALSE,msgs_lookup("menuNEM:Not enough memory to construct menu.")); return (0); } m->syshandle=(wimp_menustr *)(t+1); m->indirect=0; m->indbytes=0; m->submenu=FALSE; m->items=0; m->maxlen=strlen(title); strcpy(m->syshandle->hdr.title,title); m->syshandle->hdr.tit_fcol=7; m->syshandle->hdr.tit_bcol=2; m->syshandle->hdr.work_fcol=7; m->syshandle->hdr.work_bcol=0; m->syshandle->hdr.height=44; m->syshandle->hdr.gap=0; *t=m; itm=((wimp_menuitem *)(m->syshandle+1))-1; for (i=0;iindirect) mem_free(m->indirect); mem_free(m); mem_free(t); return (0); } } if (items) werr(TRUE,msgs_lookup("menuICOO:(menu_new): Menu item overcount.")); if (ino!=m->items) werr(TRUE,msgs_lookup("menuIMSC:(menu_new): Menu item count mismatch.")); itm->flags|=wimp_MLAST; m->syshandle->hdr.width=16*m->maxlen+16; return (m); } /* * wimp_menuitem *menu__itmPointer(menu m,int i) * * Use * Returns a pointer to the specified menu item. Items are numbered * starting from 1. * * Parameters * menu m == the menu to look up * int i == the item number to find * * Returns * A pointer to the specified menu item */ static wimp_menuitem *menu__itmPointer(menu m,int i) { return ((wimp_menuitem *)(m->syshandle+1)+(i-1)); } /* * void menu_submenu(menu main,int item,menu submenu) * * Use * Attaches a menu as a submenu, but it will put in a submenu warning flag, * so the user-friendly too-many-windows messages come up right! * * Parameters * menu main == the main menu * int item == the menu item number * menu submenu == the submenu */ void menu_submenu(menu main,int item,menu submenu) { wimp_menuitem *i=menu__itmPointer(main,item); if (submenu) { i->flags|=wimp_MSUBLINKMSG; i->submenu=(wimp_menuptr)(submenu->syshandle); submenu->submenu=TRUE; } else { i->flags&=~wimp_MSUBLINKMSG; i->submenu=(wimp_menuptr)-1; } } /* * void menu_dispose(menu *m,BOOL recurse) * * Use * Kill off a menu or menu tree. Won't work on submenus. * * Parameters * menu *m == pointer to menu to Domestos-ise. * BOOL recurse == do we want to kill its submenus too? */ void menu_dispose(menu *m,BOOL recurse) { int i; wimp_menuitem *itm=(wimp_menuitem *)((*m)->syshandle+1); menu s; if (recurse) { for (i=0;i<(*m)->items;i++); { if ((int)(itm->submenu)>=0x8000) { s=*((menu *)itm->submenu-1); menu_dispose(&s,TRUE); } itm++; } } if ((*m)->indirect) mem_free((*m)->indirect); mem_free(*m); } /* * wimp_menustr *menu_syshandle(menu m) * * Use * Returns pointer to actual menu structure. * * Parameters * menu m == menu handle for which structure is required. * * Returns * A pointer to the WIMP menu structure. */ wimp_menustr *menu_syshandle(menu m) { return (m->syshandle); } /* * void menu_extend(menu m,char *items) * * Use * Extend the given menu by a bit. * * Parameters * menu m == the menu to extend * char *items == the items to tag on the end */ void menu_extend(menu m,char *items) { wimp_menuitem *itm; wimp_menuitem *start; int i; int ino=menu__ROitemCount(items); menu *t=mem_reAlloc ( (menu *)m->syshandle-1, sizeof(menu)+sizeof(wimp_menuhdr)+(ino+m->items)*sizeof(wimp_menuitem)+4 ); if (!t) { werr(FALSE,msgs_lookup("menuNEME:Not enough memory to extend menu.")); return; } m->syshandle=(wimp_menustr *)(t+1); start=itm=menu__itmPointer(m,m->items); if (*items=='|') { itm->flags|=wimp_MSEPARATE; items++; } for (i=0;iflags&=~wimp_MLAST; itm->flags|=wimp_MLAST; m->syshandle->hdr.width=16*m->maxlen+16; } /* * void menu_setflags(menu m,int i,BOOL tick,BOOL shade) * * Use * Changes menu item properties * * Parameters * menu m == menu to change * int i == menu item to change * BOOL tick == tick the item * BOOL shade == shade the item */ void menu_setflags(menu m,int i,BOOL tick,BOOL shade) { wimp_menuitem *itm=menu__itmPointer(m,i); if (tick) itm->flags|=wimp_MTICK; else itm->flags&=~wimp_MTICK; if (shade) itm->iconflags|=wimp_INOSELECT; else itm->iconflags&=~wimp_INOSELECT; } /* * void menu_make_writeable(menu m,int i,char *buff,int bufflen,char *valid) * * Use * Makes a menu entry writable (sorry about the spelling - it's Acorn's * fault) * * Parameters * menu m == the menu in question * int i == the item to be handled * char *buff == where the data is to reside * int bufflen == max length of data that can be crammed into buff * char *valid == validation string (0 for none) */ void menu_make_writeable(menu m,int i,char *buff,int bufflen,char *valid) { wimp_menuitem *itm=menu__itmPointer(m,i); if (!valid) valid=(char *)-1; itm->iconflags|=wimp_INDIRECT; itm->flags|=wimp_MWRITABLE; itm->data.indirecttext.buffer=buff; itm->data.indirecttext.bufflen=bufflen; itm->data.indirecttext.validstring=valid; } /* * void menu_make_sprite(menu m,int i,char *name) * * Use * Turns a menu entry into a sprite. Utterly useless, but there you go... * * Parameters * menu m == the menu we're dealing with * int i == the item to sprite-ise * char *name == the sprite name */ void menu_make_sprite(menu m,int i,char *name) { wimp_menuitem *itm=menu__itmPointer(m,i); itm->iconflags|=wimp_INDIRECT|wimp_ISPRITE; itm->iconflags&=~wimp_ITEXT; itm->data.indirectsprite.name=name; itm->data.indirectsprite.spritearea=resspr_area(); itm->data.indirectsprite.nameisname=TRUE; } /* * void menu_redirectItem(menu m,int i,char *buff,int bufflen,char *valid) * * Use * Allows you to make a menu item's data point to your workspace, so you * can change the text of an item at any time. The buffer length isn't * important. * * Parameters * menu m == the menu in question * int i == the item to be handled * char *buff == where the data is to reside * int bufflen == max length of data that can be crammed into buff * char *valid == validation string (0 for none) */ void menu_redirectItem(menu m,int i,char *buff,int bufflen,char *valid) { wimp_menuitem *itm=menu__itmPointer(m,i); if (!valid) valid=(char *)-1; itm->iconflags|=wimp_INDIRECT; itm->data.indirecttext.buffer=buff; itm->data.indirecttext.bufflen=bufflen; itm->data.indirecttext.validstring=valid; } /* * void menu_minWidth(menu m,int min) * * Use * Sets the minimum permissable width of a menu (in characters). Note that * this call will not make the menu thinner than the width calculated * during menu_new or menu_extend. * * Parameters * menu m == the menu to change * int min == the minumum allowable width fro the menu in characters. If * 0 is passed, the width reverts to the calculated width. */ void menu_minWidth(menu m,int min) { if (!min) min=16*m->maxlen+16; else { min=16*min+16; if (minsyshandle->hdr.width) return; } m->syshandle->hdr.width=min; } /* * void menu_settitle(menu m,char *title) * * Use * Change a menu title. * * Parameters * menu m == the menu to change * char *title == the new title */ void menu_settitle(menu m,char *title) { strncpy(m->syshandle->hdr.title,title,12); } /* * int *menu__processHits(int hit[]) * * Use * Processes a hit string as necessary (translates WIMP menu items to C * ones etc.) * * Parameters * int hit[] == PBR array to process, terminated by -1. * * Returns * The array */ static int *menu__processHits(int hit[]) { int i=0; do hit[i++]++; while (hit[i-1]); return (hit); } /* * menu menu__menumaker(void *handle) * * Use * A surrogate menu maker. * * Parameters * void *handle == pointer to information about the real maker * * Returns * The menu to display */ _dll_static menu menu__menumaker(void *handle) { menu__handlerstr *m=handle; return ((m->m)(m->handle)); } /* * void menu__menuproc(void *handle,char hit[]) * * Use * General menu handler for menus registered using the menu functions * rather than the standard event ones. * * Parameters * void *handle == pointer to the control block for this menu * char hit[] == an array of menu hits (ignored) */ _dll_static void menu__menuproc(void *handle,char hit[]) { menu__handlerstr *m=handle; int hits[20]; int minusone=-1; wimp_eventstr *e=wimpt_last_event(); hit=hit; switch (event_whyMenuEvent()) { case event_MENUSELECT: if (m->sel) (m->sel)(menu__processHits(e->data.menu),m->handle); break; case event_MENUDELETE: if (m->sel) (m->sel)(menu__processHits(&minusone),m->handle); break; case event_MENUSUBMENU: if (m->sel) (m->sel)(menu__processHits(e->data.msg.data.words+3),m->handle); break; case event_MENUHELP: if (m->help) { wimpt_noerr(wimp_getmenustate(1, hits, e->data.msg.data.helprequest.m.w, e->data.msg.data.helprequest.m.i)); (m->help)(menu__processHits(hits),m->handle); } break; default: werr(TRUE,msgs_lookup("menuUKNME:(menu__menuproc): " "Unknown menu event type.")); } } /* * void menu_attach * ( * wimp_w w, * menu m, * menu_selectProc sel, * menu_helpProc help, * void *handle * ) * * Use * Basically equivalent to event_attachmenu, only it handles help requests * neatly etc. Source code compatibility *cannot* be assured, because I * want to be able to expand this routine to cater for later changes. I * will try to soften the blow (if any) by supplying macros that will work * with older programs, but I can't guarantee anything. * * Parameters * wimp_w w == the window to attach to * menu m == the menu to attach * menu_selectProc sel == procedure to handle most selection-type events * menu_helpProc help == procedure to handle help requests for the menu */ void menu_attach ( wimp_w w, menu m, menu_selectProc sel, menu_helpProc help, void *handle ) { menu__handlerstr *h=0; void *old; menu om; event_menu_maker omm; event_menu_proc omp; if (m) { if (h=mem_alloc(sizeof(menu__handlerstr)),!h) { werr ( FALSE, msgs_lookup("menuNEM:Not enough memory to construct menu.") ); return; } h->sel=sel; h->help=help; h->handle=handle; } event_attachedMenu(w,&om,&omm,&omp,&old); if (omp==menu__menuproc) mem_free(old); event_attachmenu(w,m,_dllEntry(menu__menuproc),h); } /* * void menu_attachMaker * ( * wimp_w w, * event_menu_maker m, * menu_selectProc sel, * menu_helpProc help, * void *handle * ) * * Use * This routine deals with event_attachmenumaker as menu_attach deals with * event_attachmenu. * * Parameters * wimp_w w == the window to attach to * menu m == the menu to attach * menu_selectProc sel == procedure to handle most selection-type events * menu_helpProc help == procedure to handle help requests for the menu */ void menu_attachMaker ( wimp_w w, event_menu_maker m, menu_selectProc sel, menu_helpProc help, void *handle ) { menu__handlerstr *h=0; void *old; menu om; event_menu_maker omm; event_menu_proc omp; if (m) { if (h=mem_alloc(sizeof(menu__handlerstr)),!h) { werr ( FALSE, msgs_lookup("menuNEM:Not enough memory to construct menu.") ); return; } h->sel=sel; h->help=help; h->handle=handle; h->m=m; } event_attachedMenu(w,&om,&omm,&omp,&old); if (omp==menu__menuproc) mem_free(old); event_attachmenumaker(w, _dllEntry(menu__menumaker), _dllEntry(menu__menuproc), h); } /* * void menu_open(menu m,menu_selectProc sel,menu_helpProc help,void *handle) * * Use * Analagous to event_openMenu(). * * Parameters * As above. */ void menu_open(menu m,menu_selectProc sel,menu_helpProc help,void *handle) { menu__instant.sel=sel; menu__instant.help=help; menu__instant.handle=handle; event_openMenu(m,_dllEntry(menu__menuproc),&menu__instant); } /* * void menu_make(event_menu_maker m,menu_selectProc sel,menu_helpProc help,void *handle) * * Use * Analagous to event_makeMenu(). * * Parameters * As above. */ void menu_make(event_menu_maker m,menu_selectProc sel,menu_helpProc help,void *handle) { menu__instant.sel=sel; menu__instant.help=help; menu__instant.handle=handle; menu__instant.m=m; event_makeMenu(_dllEntry(menu__menumaker), _dllEntry(menu__menuproc), &menu__instant); } /* * void menu_saveHandler(wimp_w w,menu_handler *h) * * Use * Saves information about the menu handle for the given window in the * block specified. This can be used *only* to restore the handler for a * window later (although it needn't be the same one). Note too that the * window need not have to have menu handlers registered using the menu * calls, or even have any at all. * * Parameters * wimp_w w == the window whose menu handlers we are to save * menu_handler *h == pointer to a chunk of memory to fill in. */ void menu_saveHandler(wimp_w w,menu_handler *h) { event_menu_maker mk; event_menu_proc p; menu m; void *hand; menu__handlerstr *ha; event_attachedMenu(w,&m,&mk,&p,&hand); if (p==_dllEntry(menu__menuproc)) { ha=hand; h->doneByMenu=TRUE; h->info.m.m=m; h->info.m.make=ha->m; h->info.m.sel=ha->sel; h->info.m.help=ha->help; h->info.m.handle=ha->handle; } else { h->doneByMenu=FALSE; h->info.e.m=m; h->info.e.make=mk; h->info.e.proc=p; h->info.e.handle=hand; } } /* * void menu_restoreHandler(wimp_w w,menu_handler *h) * * Use * Restores handlers from a structure filled in by menu_saveHandler. * * Parameters * wimp_w w == the window whose handlers we are to set up. * menu_handler *h == pointer to a chunk of memory filled in correctly. */ void menu_restoreHandler(wimp_w w,menu_handler *h) { if (h->doneByMenu) { if (h->info.m.m) { menu_attach(w, h->info.m.m, h->info.m.sel, h->info.m.help, h->info.m.handle); } else { menu_attachMaker(w, h->info.m.make, h->info.m.sel, h->info.m.help, h->info.m.handle); } } else { if (h->info.e.m) event_attachmenu(w,h->info.e.m,h->info.e.proc,h->info.e.handle); else { event_attachmenumaker(w, h->info.e.make, h->info.e.proc, h->info.e.handle); } } }