; ; menu.s ; ; RISC OS menu handling facilities (TMA) ; ; © 1994-1998 Straylight ; ;----- Licensing note ------------------------------------------------------- ; ; This file is part of Straylight's Sapphire library. ; ; Sapphire 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. ; ; Sapphire 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 Sapphire. If not, write to the Free Software Foundation, ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis ;----- External dependencies ------------------------------------------------ GET sapphire:event GET sapphire:libOpts GET sapphire:sapphire GET sapphire:string GET sapphire:fastMove GET sapphire:msgs GET sapphire:help ;----- Useful values -------------------------------------------------------- mFlag_tearoff EQU (1<<1) ;Menu has a tearoff bar mFlag_makeMe EQU (1<<2) ;Recreate menu on adjust mFlag_maxHeight EQU (1<<3) ;Menu has a maximum height mFlag_indirect EQU (1<<0) ;Item text is indirected mFlag_shortcut EQU (1<<1) ;Item has a keyboard shortcut mFlag_iShortcut EQU (1<<2) ;Item has an indirected S/C mFlag_shade EQU (1<<3) ;Item is shadable mFlag_iShade EQU (1<<4) ;Item is inverse shadable mFlag_switch EQU (1<<5) ;Item is a switch mFlag_radio EQU (1<<6) ;Item is a radio item mFlag_sprite EQU (1<<7) ;Menu item has a sprite mFlag_halfSize EQU (1<<8) ;Sprite is halfsize mFlag_subWarn EQU (1<<9) ;Warn client if submenu warn mFlag_subMenu EQU (1<<10) ;Item has menu to be opened mFlag_R12 EQU (1<<16) ;Use R12 for runtime data mFlag_noWarn EQU (1<<17) ;Don't warn on shaded items mFlag_ruleOff EQU (1<<18) ;Put a ruleoff after item mFlag_noTrans EQU (1<<19) ;Don't translate messages mFlag_end EQU (1<<31) ;No more items ; --- Event types --- mEvent_select EQU 0 ;Normal menu selection ; R1 == index of item mEvent_arrow EQU 1 ;Sub menu warning ; R1 == index of item mEvent_deleted EQU 2 ;Menu has been deleted mEvent_help EQU 3 ;Menu help requested ; R1 == ndex of item ; R2 == ptr to packed itmdef ;----- Main code ------------------------------------------------------------ AREA |Sapphire$$Code|,CODE,READONLY ; --- menu__createTitle --- ; ; On entry: R0 == pointer to menu title definition ; R1 == event handler to use (not used here) ; R2 == R10 value to use ; R3 == R12 value to use ; R4 == pointer to menu stack area ; R5 == pointer into stack to build definition ; R9 == workspace pointer ; ; On exit: R0 == points to first word after table ; R1-R4 == preserved ; R5 == next free word in stack after defintion ; ; Use: This call creates the title part of a WIMP menu from ; a menu definition table (assuming that it has a menu ; title definition). menu__createTitle ROUT STMFD R13!,{R1-R4,R6-R8,R14} ;Stack some registers STMDB R4,{R0-R3} ;Store information in stack LDR R7,[R0],#4 ;Get flags MOV R8,R2 ;Use R10 for runtime data TST R7,#mFlag_R12 ;Should we use R12? MOVNE R8,R3 ;Yes, set R8 = R3 then TST R7,#mFlag_indirect ;Is the title indirected LDRNE R6,[R0],#4 ;Yes -- load the offset LDRNE R6,[R6,R8] ;...find new pointer BNE %05 ;...and jump the next bit MOV R6,R0 ;Get the text pointer 00 LDRB R8,[R0],#1 ;Get a byte CMP R8,#0 ;Was it the last character? BNE %00 ;No -- keep looking ADD R0,R0,#3 ;Word align BIC R0,R0,#3 ;Oh yes indeedy 05 MOV R2,R0 ;Remember this pointer MOV R0,R6 ;Point to the message TST R7,#mFlag_noTrans ;Do we translate the string? BLEQ msgs_lookup ;Yes -- lookup the message MOV R6,R0 ;Remember new pointer BL str_len ;Get the length of the text STRGT R0,menu__maxLen ;Yes -- store new length TST R7,#mFlag_makeMe ;Is the menu recreatable? LDMNEIA R2!,{R8} ;Yes -- read it TST R7,#mFlag_maxHeight ;Is there a maximum height LDMNEIA R2!,{R8} ;Yes -- read it ; --- Now enter data into stack --- BL menu__checkOverflow ;Make sure there's room MOV R0,R5 ;Copy to here... MOV R1,R6 ;...this string BL str_cpy ;Do the copy ADD R5,R5,#12 ;Point past 12 bytes MOV R0,R2 ;Get the pointer back LDR R4,=&00070207 ;Colours MOV R6,#40 ;Don't know width yet MOV R7,#44 ;Item height MOV R8,#0 ;Gap between items STMIA R5!,{R4,R6-R8} ;Store information ; --- Return thankfully --- LDMFD R13!,{R1-R4,R6-R8,PC}^ LTORG ; --- menu_create --- ; ; On entry: R0 == pointer to menu definition table ; R1 == event handler to use ; R2 == R10 value for handler ; R3 == R12 value for handler ; ; On exit: -- ; ; Use: Creates a menu from the given menu definition ; table. If this call is called more than once before ; a menu is opened then the menu definiton are concatenated ; into a large menu. Only the first menu title read is ; taken notice of. Notice therefore, that the call doesn't ; actually open a menu. EXPORT menu_create menu_create ROUT STMFD R13!,{R0-R12,R14} ;Stack some registers WSPACE menu__wSpace,R9 ;Find my workspace MOV R12,#0 ;Number of items so far ; --- Do we need to read the menu title? --- LDR R4,menu__flags ;Get the flags word TST R4,#mFlag__creating ;Is this the first menu BEQ %00menu_create ;Yes -- set up things ; --- We are already creating a menu --- LDR R6,menu__begin ;Get beginning of real menu TST R4,#mFlag__tOnly ;Has just the title been done SUBNE R4,R6,#24 ;Yes -- block already there LDR R5,menu__end ;Get the end marker LDMNEIA R13,{R0-R3} ;Reclaim useful values STMNEFD R13!,{R0} ;Pointer to item definitions BNE %05menu_create ;Now read the items SUB R1,R6,#4 ;Copy from here.. ADD R0,R1,#20 ;...to here SUB R2,R5,R1 ;...this much BL fastMove ;Do the copy ADD R5,R5,#20 ;Increment end pointer MOV R4,R1 ;Write header here ADD R6,R6,#20 ;The new beginning STR R6,menu__begin ;Store new value LDMIA R13,{R0-R3} ;Reclaim useful values STMFD R13!,{R0} ;Pointer to item definitions B %05menu_create ;Go through the items ; --- We are creating a new menu --- 00menu_create STR R12,menu__maxLen ;Max length so far TST R4,#mFlag__wasSub ;Was last event a submenu? LDREQ R4,menu__stack ;No -- Get the stack address LDRNE R4,menu__prevMenu ;Yes - start of previous menu LDRNE R5,[R4] ;...get length of menu ADDNE R4,R4,R5 ;Start new menu here STR R4,menu__start ;The menu start (incl. defn) ADD R5,R4,#44 ;Where to start adding defns ADD R4,R4,#20 ;Where to write the header MOV R6,#0 ;The last header marker STR R6,[R5,#-4] ;Store it nicely STR R5,menu__begin ;Store the begin pointer BL menu__createTitle ;Create the title STMFD R13!,{R0} ;Pointer to item definitions ; --- Now go through the menu items --- ; ; At this point, R4 points to the menu header to write to, ; R5 points into the stack at the position that the next ; items should be written to. R0 points to the next menu ; item. 05menu_create LDR R7,[R0],#4 ;Get flags TST R7,#mFlag_end ;Any more items? BNE %40menu_create ;Nope -- branch ahead MOV R8,R2 ;Use R10 for runtime data TST R7,#mFlag_R12 ;Should we use R12? MOVNE R8,R3 ;Yes, set R8 = R3 then TST R7,#mFlag_indirect ;Is the text indirected LDRNE R6,[R0],#4 ;Yes -- load the offset LDRNE R6,[R6,R8] ;...find new pointer BNE %07menu_create ;...and jump the next bit MOV R6,R0 ;Point to the string 06menu_create LDRB R1,[R0],#1 ;Get a byte CMP R1,#0 ;Was it the last character? BNE %06menu_create ;No -- keep looking ADD R0,R0,#3 ;Word align BIC R0,R0,#3 ;Oh yes indeedy ; --- So, we have an item --- 07menu_create MOV R2,R0 ;Remember this pointer MOV R0,R6 ;Point to the message TST R7,#mFlag_noTrans ;Do we translate the string? BLEQ msgs_lookup ;Yes -- lookup the message MOV R6,R0 ;Remember new pointer BL str_len ;Get the length of the text TST R7,#mFlag_sprite ;Is there a sprite too? ADDNE R0,R0,#2 ;Yes -- add 42 to the length LDR R1,menu__maxLen ;Get maximum length so far CMP R0,R1 ;Is new text longer? STRGT R0,menu__maxLen ;Yes -- store new length MOV R0,R2 ;Get the offset back MOV R1,#24 ;The WIMP item flags so far LDR R2,=&7000131 ;The icon flags so far TST R7,#mFlag_shade ;Is it shadable? BEQ %10menu_create ;No -- jump this code BL %99 ;Do a bit specification ORRCS R2,R2,#(1<<22) ;Set the 'shaded' bit 10menu_create TST R7,#mFlag_iShade ;Is it inverse shadable? BEQ %15menu_create ;No -- jump this code BL %99 ;Do a bit specification ORRCC R2,R2,#(1<<22) ;Set the 'shaded' bit 15menu_create TST R7,#mFlag_switch ;Is it a switch? BEQ %20menu_create ;No -- jump this code BL %99 ;Do a bit specification ORRCS R1,R1,#1 ;Set the tick flag 20menu_create TST R7,#mFlag_radio ;Is is a radio type? BEQ %23menu_create ;No -- try next type LDMIA R0!,{R10,R14} ;Get offset and selector ADD R10,R10,R8 ;Get real offset LDR R10,[R10] ;Get the word there CMP R10,R14 ;It is the same as selector? ORREQ R1,R1,#1 ;Yes -- set the tick flag 23menu_create TST R7,#mFlag_sprite ;Does item contain a sprite? BEQ %25menu_create ;No -- try next type LDR R10,[R0],#4 ;Get the sprite name pointer ORR R2,R2,#2 ;Set the 'sprite' bit STR R10,menu__sprite ;Store the sprite pointer 25menu_create TST R7,#mFlag_halfSize ;Make sprite half size? ORRNE R2,R2,#(1<<11) ;Yeap -- set the bit TST R7,#mFlag_noWarn ;Don't open subs if shaded? BICNE R1,R1,#16 ;Clear relevant bit TST R7,#mFlag_ruleOff ;Put a rule off here? ORRNE R1,R1,#2 ;Yes -- set the bit up ; --- Now store this information in the block --- BL menu__checkOverflow ;Make sure there's room MOV R10,R2 ;Put the icon flags in R10 MOV R2,#-1 ;No submenu yet TST R7,#mFlag_subMenu ;Automatic sub menu? LDMNEIA R0!,{R2,R14} ;Yes -- get menu pointer TST R7,#mFlag_subWarn ;Submenu warning required MOVNE R2,#1 ;Yeap -- put non -1 value in STMIA R5!,{R1,R2,R10} ;Splodge! TST R10,#2 ;Is the sprite bit set? MOVEQ R10,#-1 ;No -- no validation LDRNE R10,menu__sprite ;Yes -- point to sprite name MOV R14,#255 ;Buffer length STMIA R5!,{R6,R10,R14} ;Icon data -- splodge! ; --- Now, keep searching for icons --- ADD R12,R12,#1 ;Increment item count ADD R1,R13,#8 ;Point to useful values LDMIA R1,{R1-R3} ;Load back useful values B %05menu_create ;Keep looking ; --- All the icons have been found, tidy up --- 40menu_create LDR R0,menu__flags ;Get my flags word CMP R12,#0 ;Did we create any items? ORREQ R0,R0,#mFlag__tOnly ;Yes -- set the flag BICNE R0,R0,#mFlag__tOnly ;No -- clear the flag ORR R0,R0,#mFlag__creating ;We are creating a menu STR R0,menu__flags ;Store updated flags STR R12,[R4],#4 ;Store number of items STR R5,menu__end ;The end pointer LDR R6,menu__start ;Get the start SUB R7,R5,R6 ;Get the length STR R7,[R6] ;Store in the header LDMFD R13!,{R5} ;First item pointer LDMFD R13!,{R0-R3} ;Load some stuff MOV R14,R0 ;Remember user R0 MOV R0,R5 ;Pointer to first item defn STMIA R4!,{R0-R3} ;Store them in the header MOV R0,R14 ;Preserve R0 ; --- And return to the client --- LDMFD R13!,{R4-R12,PC}^ ;Return to client ; --- Do a bit specification --- 99 STMFD R13!,{R14} ;Save a register LDR R10,[R0],#4 ;Get the next word ADD R14,R8,R10,LSR#5 ;Get the offset LDR R14,[R14] ;And the byte there AND R10,R10,#31 ;Clear bit we don't want ADD R10,R10,#1 ;Correct for shifting MOVS R14,R14,LSR R10 ;Shift that bit into carry LDMFD R13!,{PC} ;And return to caller LTORG ; --- menu__checkOverflow --- ; ; On entry: R5 == offset in menu stack for creating next item ; ; On exit: -- ; ; Use: Checks to see if there's enough room in the menu stack for ; a new menu item or header. menu__checkOverflow ROUT STMFD R13!,{R14} ;Save some registers LDR R14,menu__stackEnd ;Find the end of the stack SUB R14,R14,#64 ;Allow a nice bit of space CMP R14,R5 ;Do we have enough space? LDMGEFD R13!,{PC}^ ;Yes -- return then LDR R14,menu__flags ;Get my flags word AND R14,R14,#mFlag__inited ;Reset all the flags STR R14,menu__flags ;Store updated flags MOV R1,#-1 ;An invalid menu pointer SWI XWimp_CreateMenu ;Close the current menu tree ADR R0,menu__noMem ;Point to error message BL msgs_error ;Translate the error message SWI OS_GenerateError ;And generate the error menu__noMem DCD 1 DCB "mSOVF",0 LTORG ; --- menu__recreate --- ; ; On entry: R1 == pointer to list of menu hits ; R9 == pointer to workspace ; ; On exit: -- ; ; Use: Called to recreate the menu definition after an adjust ; click was made on a menu. menu__recreate ROUT STMFD R13!,{R0-R12,R14} ;Stack some registers MOV R10,R1 ;We need this list LDR R12,menu__stack ;Point to first menu header 00 LDR R0,[R10],#4 ;Load a menu hit (ignore 1st) CMP R0,#-1 ;Is this the end? BEQ %90 ;Yes -- finish LDR R0,[R12,#4] ;Get the title defn pointer LDR R2,[R0],#4 ;Get the flags word MOV R6,R0 ;Point to the text TST R2,#mFlag_R12 ;Do we use R12? LDREQ R7,[R12,#12] ;No -- use R10 value LDRNE R7,[R12,#16] ;Yes -- use R12 value TST R2,#mFlag_indirect ;Is the text indirected? LDRNE R6,[R0] ;Yes -- get the offset LDRNE R6,[R7,R6] ;...load indirected pointer BL menu__skipText ;Skip past the text STMFD R13!,{R10} ;Save this R10 value TST R2,#mFlag_makeMe ;Is it a 'makeme' type BEQ %01 ;No -- skip this bit LDR R5,[R10] ;Get the next hit CMP R5,#-1 ;Are we at the last menu BNE %01 ;No -- ignore this function LDR R5,[R0],#4 ;Get the 'make me' function ; --- Recreate the menu over the top of the old one --- MOV R6,#0 ;Set the new length STR R6,[R12] ;Store this length in field LDR R6,menu__flags ;Get my flags word ORR R6,R6,#mFlag__wasSub ;Fool menu_create STR R6,menu__flags ;Store the modified flags STR R12,menu__prevMenu ;Make this the previous menu MOV R14,PC ;Set up the return address MOV PC,R5 ;Branch to the function ORR R6,R6,#mFlag__recreating ;We're recreating STR R6,menu__flags ;Store the modified flags STR R12,menu__start ;Update this menu MOV R2,R6 ;Put the flags in R2 BL menu__open ;Set the menu up properley BIC R6,R6,#mFlag__creating ;We're not really creating STR R6,menu__flags ;Store the modified flags B %90 ;Tidy up and return 01 MOV R0,R6 ;Point to the text TST R2,#mFlag_noTrans ;Do we translate the string? BLEQ msgs_lookup ;Yes -- lookup the message MOV R6,R0 ;Remember new pointer BL str_len ;Get the length of the text STR R0,menu__maxLen ;Store max length ; --- Go through the items changing the flags --- ; ; We have to use three pointers, one to keep track of ; the real WIMP icon so that we can alter it, one ; to keep track of our position in the users block, and ; one to keep track of which header we are using! ; --- Find the first real item --- MOV R0,R12 ;Point to menu defn BL menu__locateFirst ;Locate the first item MOV R3,R0 ;Use R3 instead STMFD R13!,{R3} ;Stack this pointer SUB R0,R3,#28 ;Point the the menu header MOV R1,R6 ;The text string BL str_cpy ;Copy the title across MOV R14,#7 ;Might have trashed colour STRB R14,[R3,#12-28] ;So store colour back ;-) ; --- Set up the pointer to the first header --- ADD R1,R12,#20 ;Point to first header 05 LDR R8,[R1,#12] ;Get the R10 value LDR R10,[R1,#16] ;Get the R12 value ; --- Finally locate the users definition --- LDR R0,[R1,#4] ;Get the pointer ; --- Now go through the list --- 06 LDR R2,[R0],#4 ;Get the flags word TST R2,#mFlag_end ;No more items? BNE %20 ;No -- try for more LDR R4,[R3,#0] ;Load the item flags MOV R7,R8 ;Use R10 value TST R2,#mFlag_R12 ;Do we use R12? MOVNE R7,R10 ;Yes -- set up R7 MOV R5,R0 ;Pointer to the text TST R2,#mFlag_indirect ;Is the text indirected? LDRNE R0,[R0] ;Yes -- get the offset LDRNE R0,[R7,R0] ;...load indirected pointer TST R2,#mFlag_noTrans ;Do we translate the string? BLEQ msgs_lookup ;Yes -- lookup the message STR R0,[R3,#12] ;Store the new text pointer BL str_len ;Get the length TST R2,#mFlag_sprite ;Is there a sprite? ADDNE R0,R0,#2 ;Yes -- allow for it LDR R6,menu__maxLen ;Get the current max length CMP R0,R6 ;Is new length longer? STRGT R0,menu__maxLen ;Yes, store this MOV R0,R5 ;Get current offset back BL menu__skipText ;Skip the text part 07 TST R2,#mFlag_shade ;Is it shadable? BEQ %10 ;No -- jump this code BL %99 ;Do a bit specification LDR R5,[R3,#8] ;Get the icon flags ORRCS R5,R5,#(1<<22) ;Set the 'shaded' bit BICCC R5,R5,#(1<<22) ;Or maybe clear it STR R5,[R3,#8] ;Put them back 10 TST R2,#mFlag_iShade ;Is it inverse shadable? BEQ %11 ;No -- jump this code BL %99 ;Do a bit specification LDR R5,[R3,#8] ;Get the icon flags ORRCC R5,R5,#(1<<22) ;Set the 'shaded' bit BICCS R5,R5,#(1<<22) ;Or maybe clear it STR R5,[R3,#8] ;Put them back 11 TST R2,#mFlag_switch ;Is it a switch BEQ %12 ;No -- jump this code BL %99 ;Do a bit specification ORRCS R4,R4,#1 ;Set the tick flag BICCC R4,R4,#1 ;Or maybe clear it 12 TST R2,#mFlag_radio ;Is is a radio type? BEQ %13 ;No -- jump this code LDMIA R0!,{R5,R6} ;Get offset and selector ADD R5,R5,R7 ;Get real offset LDR R5,[R5] ;Get the word there CMP R5,R6 ;It is the same as selector? ORREQ R4,R4,#1 ;Yes -- set the tick flag BICNE R4,R4,#1 ;No -- clear it 13 TST R2,#mFlag_sprite ;Is there a sprite? ADDNE R0,R0,#4 ;Yes -- skip the pointer TST R2,#mFlag_subMenu ;Automatic submenu? ADDNE R0,R0,#8 ;Yes -- skip those fields STR R4,[R3,#0] ;Store the item flags back ADD R3,R3,#24 ;Point to next real icon B %06 ;Try another icon ; --- Try more items from next header --- 20 ADD R1,R1,#20 ;Point to next header LDR R0,[R1] ;Get the item count CMP R0,#0 ;Is there one? BNE %05 ;...and do it ; --- Now move onto next menu --- LDMFD R13!,{R3} ;Get the first item pointer LDR R0,menu__maxLen ;Get the menu with ADD R0,R0,#1 ;Add a character width MOV R0,R0,LSL#4 ;Multiply width by 16 STR R0,[R3,#-12] ;Store in menu width field LDR R0,[R12] ;Get the length ADD R12,R12,R0 ;Point to the next menu LDMFD R13!,{R10} ;Load menu hit pointer B %00 ;And keep looking ; --- We have apparently finished now --- 90 LDMFD R13!,{R0-R12,PC}^ ;Return to caller ; --- Calculate a bit specification --- 99 LDR R5,[R0],#4 ;Get the next word ADD R6,R7,R5,LSR#5 ;Get the offset LDR R6,[R6] ;And the byte there AND R5,R5,#31 ;Clear bits we don't want ADD R5,R5,#1 ;Correct for shifting MOVS R6,R6,LSR R5 ;Shift that bit into carry MOV PC,R14 ;Return from subroutine LTORG ; --- menu__locateFirst --- ; ; On entry: R0 == pointer to my menu defn ; ; On exit: R0 == pointer to first item in menu ; R1 == Number of items in menu ; ; Use: Given a pointer to a menu definition (my type, not ; WIMP), it returns a pointer to the first item, and ; a count of the number of items in the menu. menu__locateFirst ROUT STMFD R13!,{R2-R6,R14} ;Stack some registers MOV R1,#0 ;Item count ADD R0,R0,#20 ;Point past length/re-create 00 LDMIA R0!,{R2,R3,R4,R5,R6} ;Load menu data CMP R2,#0 ;Is this the last item? ADDNE R1,R1,R2 ;No -- increment item count BNE %00 ;Keep searching ADD R0,R0,#12 ;Point to the first item LDMFD R13!,{R2-R6,PC}^ ;Return to caller LTORG ; --- menu__height --- ; ; On entry: R1 == pointer to real menu block ; ; On exit: R0 == height of menu ; ; Use: Calculates the height of a WIMP menu, from its data structure menu__height ROUT STMFD R13!,{R1-R2,R14} ;Stack some registers MOV R0,#0 ;The height so far ADD R1,R1,#28 ;Point to the first item 00 ADD R0,R0,#44 ;Increment the height LDR R2,[R1] ;Get the flags word TST R2,#2 ;Is there a dotted line? ADDNE R0,R0,#24 ;Yes -- add 24 to height TST R2,#&80 ;Is this the last item? ADDEQ R1,R1,#24 ;No -- point to next item BEQ %00 ;...and keep counting LDMFD R13!,{R1-R2,PC}^ ;Return to caller LTORG ; --- menu__open --- ; ; On entry: R2 == modules flags word ; R9 == workspace pointer ; ; On exit: -- ; ; Use: Opens the next menu in the right place menu__open ROUT STMFD R13!,{R0-R4,R14} ;Stack some registers LDR R0,menu__start ;Point to current menu BL menu__locateFirst ;Find the first item SUB R1,R1,#1 ;0 index item count MOV R1,R1,LSL#3 ;Multiple no. of items by 8 RSB R1,R1,R1,LSL#2 ;And the by 3 ADD R1,R0,R1 ;Point to last item LDR R3,[R1] ;Load the flags word ORR R3,R3,#&80 ;Set 'Last item' bit BIC R3,R3,#2 ;No ruleoff here STR R3,[R1] ;Store the flags back SUB R1,R0,#28 ;Point to real menu structure LDR R0,menu__maxLen ;Get the longest text length MOV R0,R0,LSL#4 ;Multiply by 16 ADD R0,R0,#16 ;Add one for luck STR R0,[R1,#16] ;Store the width MOV R4,R2 ;Remember flags word TST R4,#mFlag__recreating ;Are we recreating a menu LDMNEFD R13!,{R0-R4,PC}^ ;Yes -- Return ADR R2,menu__coords ;Point to coords block LDMIA R2,{R2,R3} ;Get the x and y coords ; --- Ensure Y position is correct on icon bar --- TST R4,#mFlag__iBar ;Was it on the icon bar? BLNE menu__height ;Calculate the menu height ADDNE R3,R0,#96 ;This is the Y position TST R4,#mFlag__wasSub ;Is it a submenu? BEQ %90 ;No -- create a normal menu LDR R0,menu__prevMenu ;Get menu from which it came ADD R0,R0,#20 ;Point to the first header 50menu__open LDR R14,[R0],#4 ;Get the number of items CMP R14,#0 ;Any more headers? ADDNE R0,R0,#16 ;Yes -- point to next one BNE %50menu__open ;...and keep looking ADD R0,R0,#28 ;Point to first item LDR R14,menu__prevItem ;Get the previous item hit MOV R14,R14,LSL#3 ;Multiply item hit by 8 RSB R14,R14,R14,LSL#2 ;And then by 3 ADD R0,R0,R14 ;Point to the item STR R1,[R0,#4] ;Store pointer in menu field SWI Wimp_CreateSubMenu ;Create sub menu LDMFD R13!,{R0-R4,PC}^ ;Return 90menu__open SUB R2,R2,#64 ;No -- correct X position SWI Wimp_CreateMenu ;...create the menu ORR R4,R4,#mFlag__opened ;Set the opened bit STR R4,menu__flags ;Store the modified flags LDMFD R13!,{R0-R4,PC}^ ;Return LTORG ; --- menu__skipText --- ; ; On entry: R0 == pointer to text field ; R2 == item flags for this item ; ; On exit: R0 == pointer to first data field after the text ; ; Use: Skips text part of an icon definition menu__skipText ROUT STMFD R13!,{R14} ;Stack registers TST R2,#mFlag_indirect ;Is it indirected? ADDNE R0,R0,#4 ;Yes -- just skip a word LDMNEFD R13!,{PC}^ ;And return 00 LDRB R14,[R0],#1 ;Get a character CMP R14,#0 ;Is it a NULL BNE %00 ;No, keep looking ADD R0,R0,#3 ;Word align R0 BIC R0,R0,#3 ;Complete aligning LDMFD R13!,{PC}^ ;And return LTORG ; --- menu__findItem --- ; ; On entry: R0 == pointer to menu definition (my kind) ; R1 == Item number to locate ; ; On exit: R0 == pointer to the item definition ; R1 == pointer to header entry for this item ; R2 == index for this item (to pass to handler) ; ; Use: Locates the given item from a created menu definition, ; and also finds the header entry for it menu__findItem ROUT STMFD R13!,{R3,R4,R14} ;Stack some registers ADD R0,R0,#20 ;Point to first header MOV R2,#0 ;Item so far 00 LDR R3,[R0] ;Number of items header's for MOV R4,R2 ;Number so far ADD R2,R2,R3 ;Increment my count CMP R1,R2 ;Is this the relevant header? ADDGE R0,R0,#20 ;No -- point to next header BGE %00 ;...and keep looking ; --- We have located the relevant header --- SUBS R3,R1,R4 ;Calculate item index STMFD R13!,{R3} ;Store it on the stack MOV R1,R0 ;Point to header for user LDR R0,[R1,#4] ;Find definition pointer 10 LDMEQFD R13!,{R2-R4,PC}^ ;Yes -- return to caller LDR R2,[R0],#4 ;Load the flags BL menu__skipText ;Skip the text TST R2,#mFlag_shade ;Is there a shade field ADDNE R0,R0,#4 ;Yes -- skip it TST R2,#mFlag_iShade ;Is there a ishade field ADDNE R0,R0,#4 ;Yes -- skip it TST R2,#mFlag_switch ;Is there a switch field ADDNE R0,R0,#4 ;Yes -- skip it TST R2,#mFlag_radio ;Are there radio fields ADDNE R0,R0,#8 ;Yes -- skip them TST R2,#mFlag_sprite ;Is there a sprite pointer? ADDNE R0,R0,#4 ;Yes -- skip it TST R2,#mFlag_subMenu ;Automatic submenu? ADDNE R0,R0,#8 ;Yes -- skip fields SUBS R3,R3,#1 ;Decrement the count B %10 ;Keep searching LTORG ; --- menu__dispatch --- ; ; On entry: R0 == event type to send ; R1 == pointer to menu hits ; ; On exit: -- ; ; Use: Called to dispatch a menu hit event menu__dispatch ROUT STMFD R13!,{R0-R3,R10,R12,R14} MOV R3,R0 ;Remember event to send LDR R0,menu__stack ;Point to first menu header 00 LDR R2,[R1,#4]! ;Load a menu hit (ignore 1st) CMP R2,#-1 ;Is this the end? BEQ %10 ;Yes -- branch ahead LDR R2,[R0] ;Get the length ADD R0,R0,R2 ;Point to the next menu B %00 ;And keep looking 10 LDR R1,[R1,#-4] ;Get the item number BL menu__findItem ;Point to item and header MOV R14,R0 ;Keep the item pointer ADD R1,R1,#8 ;Point to the event handler MOV R0,R3 ;Get the event type LDMFD R1,{R3,R10,R12} ;Get the values MOV R1,R2 ;The indexed item number MOV R2,R14 ;Pass item address in R2 CMP R3,#0 ;Sanity check MOV R14,PC ;Set up return address MOVNE PC,R3 ;Jump to the event handler LDMFD R13!,{R0-R3,R10,R12,PC}^ ;Return LTORG ; --- menu__preFilter --- ; ; On entry: R0 == event mask and flags ; R1 == pointer to block to use ; R2 == earliest time to return with NULL event ; R3 == pointer to poll word ; ; On exit: -- ; ; Use: Call as an event pre-filter. Its purpose is to open ; a previously created menu in the right place. menu__preFilter ROUT STMFD R13!,{R0-R2,R9,R14} ;Stack some registers MOV R9,R12 ;Get workspace pointer in R9 LDR R2,menu__flags ;Get my flags word TST R2,#mFlag__creating ;Are we creating a menu? BEQ %90menu__preFilter ;No -- return then TST R2,#mFlag__wasSub ;Is this from a sub menu? BNE %70menu__preFilter ;Yes -- just open the menu ; --- Set up menu coordinates and things --- BL event_last ;Get the last event CMP R0,#6 ;Mouse click? BEQ %50menu__preFilter ;Yes -- deal with it ; --- Open over the mouse pointer then --- MOV R14,R2 ;Preserve flags word SUB R13,R13,#20 ;Get a block for me MOV R1,R13 ;Point to the block SWI Wimp_GetPointerInfo ;Get pointer information LDMIA R1,{R0,R1} ;Get coords out of block ADR R2,menu__coords ;Point to coords block STMIA R2,{R0,R1} ;Store them in the block ADD R13,R13,#20 ;Get stack back MOV R2,R14 ;Get flags back B %70menu__preFilter ;Open the menu ; --- Deal with button click --- 50 MOV R14,R2 ;Preserve flags word LDR R0,[R1,#12] ;Get the window handle CMP R0,#-2 ;Is it the icon bar BNE %60menu__preFilter ;No -- jump ORR R14,R14,#mFlag__iBar ;Set relevent bit STR R14,menu__flags ;Store the flags back 60 ADR R2,menu__coords ;Point to coords block LDMIA R1,{R0,R1} ;Get x and y coords STMIA R2,{R0,R1} ;Store them in the block MOV R2,R14 ;Get flags word back ; --- Open the menu,then --- 70 BL menu__open ;Yes -- open the menu 90 LDR R2,menu__flags ;Get new flags AND R2,R2,#mFlag__inited+mFlag__opened STR R2,menu__flags ;Store the new flags LDMFD R13!,{R0-R2,R9,PC}^ ;Return to caller LTORG ; --- menu__postFilter --- ; ; On entry: R0 == wimp event ; R1 == pointer to block ; ; On exit: -- ; ; Use: Called as an event post filter to catch menu related events menu__postFilter ROUT ; --- Are we at all interested? --- STMFD R13!,{R9,R14} ;Store R9 value MOV R9,R12 ;Get the workspace pointer LDR R14,menu__flags ;Get the menu flags TST R14,#mFlag__opened ;Do we have a menu open? LDMEQFD R13!,{R9,PC}^ ;No -- ignore it then CMP R0,#9 ;Menu click? BEQ %20 ;Yes -- deal with that CMP R0,#17 ;User_Message CMPNE R0,#18 ;User_Message_Recorded BNE %90 ;No -- tidy up a bit ; --- It was a message, are we interested? --- ; ; Note that we are allowed to corrupt R12 STMFD R13!,{R0-R2} ;Stack some registers LDR R0,=&400C0 ;Menu warning LDR R12,[R1,#16] ;Get the message type CMP R0,R12 ;Is it menu warning? BEQ %05 ;Yes -- deal with it LDR R0,=&400C9 ;Menus deleted CMP R0,R12 ;Is that the message? BEQ %03 ;Yes -- deal with it LDR R0,=&502 ;Help request CMP R0,R12 ;Is that the message? LDMNEFD R13!,{R0-R2} ;No -- Get registers BNE %90 ;...and tidy up a bit ; --- There was a help request --- STMFD R13!,{R3,R10,R12} ;We need these SUB R13,R13,#40 ;Get a buffer MOV R0,#1 ;Get state give window/icon ADD R1,R1,#32 ;Point to window/icon handle LDMIA R1,{R2,R3} ;Get them LDR R14,menu__twin ;Get the dbmn address LDR R14,[R14,#twin_trans] ;Get the transient dbox CMP R14,R2 ;Are they the same BEQ %02 ;Yes -- forget it MOV R1,R13 ;Use this buffer SWI XWimp_GetMenuState ;Get the menu state BVS %02 ;If failed, return LDR R0,[R1,#0] ;First first menu index CMP R0,#-1 ;Is it on our menu? BEQ %02 ;No -- skip forward MOV R0,#mEvent_help ;Set the event type BL menu__dispatch ;Dispatch the event ; --- Return to caller --- 02 ADD R13,R13,#40 ;Reclaim my stack LDMFD R13!,{R3,R10,R12} ;Get these values back LDMFD R13!,{R0-R2} ;Get registers B %90 ;...and tidy up a bit ; --- There was a menus deleted message --- 03 STMFD R13!,{R10,R12} ;We need these LDR R0,menu__flags ;Get the flags word BIC R0,R0,#mFlag__opened ;Well,it's closed now STR R0,menu__flags ;Store the flags back LDR R0,menu__stack ;Find the top level menu ADD R0,R0,#8 ;Point to the handler LDMIA R0,{R2,R10,R12} ;Get handler and R10/R12 MOV R0,#mEvent_deleted ;Set the event type CMP R2,#0 ;Sanity check MOV R14,PC ;Set the return address MOVNE PC,R2 ;Call the handler 04 LDMFD R13!,{R10,R12} ;Get these values back LDMFD R13!,{R0-R2} ;Get registers B %90 ;...and tidy up a bit ; --- It was a submenu warning --- 05 LDR R0,menu__flags ;Get the flags ORR R0,R0,#mFlag__wasSub ;It was a menu warning STR R0,menu__flags ;Put the flags back ADR R0,menu__coords ;Point to my coords block ADD R1,R1,#24 ;Point to the (x,y) to use LDMIA R1!,{R2,R12} ;Load x and y STMIA R0,{R2,R12} ;And store them usefully ; --- We now need to find the menu in question --- LDR R0,menu__stack ;Point to first menu header 05 LDR R2,[R1,#4]! ;Load a menu hit (ignore 1st) CMP R2,#-1 ;Is this the end? BEQ %10 ;Yes -- branch ahead LDR R2,[R0] ;Get the length ADD R0,R0,R2 ;Point to the next menu B %05 ;And keep looking 10 STR R0,menu__prevMenu ;Store this pointer LDR R1,[R1,#-4] ;Get the item number STR R1,menu__prevItem ;Store the item number ; --- Now we need to create the submenu if we need to --- BL menu__findItem ;Point to the item definition LDR R2,[R0],#4 ;Get the flags word TST R2,#mFlag_subMenu ;Automatic menu? BNE %15 ;Yes -- deal with it ; --- Here we must just tell the user --- ; ; R0 == pointer to the menu item definition+4 ; R1 == pointer to the menu header for this item ; R2 == item flags MOV R0,#mEvent_arrow ;Set the event type LDR R1,[R13,#4] ;Get the message block ADD R1,R1,#32 ;Point to the menu hit list BL menu__dispatch ;Dispatch the event LDMFD R13!,{R0-R2,R9,PC}^ ;Return to caller ; --- We must automatically open the menu --- 15 BL menu__skipText ;Skip the item text TST R2,#mFlag_switch ;Is there a switch field ADDNE R0,R0,#4 ;Yes -- skip it TST R2,#mFlag_shade ;Is there a shade field ADDNE R0,R0,#4 ;Yes -- skip it TST R2,#mFlag_iShade ;Is there a ishade field ADDNE R0,R0,#4 ;Yes -- skip it TST R2,#mFlag_radio ;Are there radio fields ADDNE R0,R0,#8 ;Yes -- skip them TST R2,#mFlag_sprite ;Is there a sprite? ADDNE R0,R0,#4 ;Yes -- skip pointer ADD R1,R1,#12 ;Point to R10,R12 for item LDMIA R1,{R2,R3} ;Use these values again LDMIA R0,{R0,R1} ;Get menu pointer & handler BL menu_create ;Create this menu LDMFD R13!,{R0-R2,R9,PC}^ ;Return to caller ; --- Deal with menu selection --- 20 STMFD R13!,{R0-R2} ;Stack some registers BIC R14,R14,#mFlag__opened ;Say the menu just closed STR R14,menu__flags ;Save these flags back ; --- Set up the dbmn flag word --- LDR R2,menu__twin ;Locate dbmn area LDR R14,[R2,#twin_flags] ;Get the flags word ORR R14,R14,#twinFlag_recrt ;We are going to recreate it STR R14,[R2,#twin_flags] ;Store the flags back ; --- Dispatch the event --- MOV R0,#mEvent_select ;Set the event type BL menu__dispatch ;Dispatch the event LDR R14,[R2,#twin_flags] ;Get the flags word back TST R14,#twinFlag_recrt ;Do we still need to recreate LDMEQFD R13!,{R0-R2} ;No -- get registers back BEQ %90 ;...and tidy up ; --- Recreate the menu if we need to --- SUB R13,R13,#20 ;Get a block MOV R1,R13 ;Point to it SWI Wimp_GetPointerInfo ;Get pointer information LDR R1,[R1,#8] ;Get the button state TST R1,#1 ;Was Adjust clicked? ADD R13,R13,#20 ;Get the stack back LDMEQFD R13!,{R0-R2} ;No -- get registers back BEQ %90 ;...and tidy up LDMIB R13,{R1} ;Get the menu hit list back BL menu__recreate ;Recreate the menu LDR R1,menu__stack ;Get the stack pointer ADD R1,R1,#20 ;Point to the first header 25 LDR R0,[R1],#4 ;Get the number of items CMP R0,#0 ;Any more headers? ADDNE R1,R1,#16 ;Yes -- point to next one BNE %25 ;...and keep looking SWI Wimp_CreateMenu ;Recreate the menu LDR R14,menu__flags ;Load the menu flags again ORR R14,R14,#mFlag__opened ;The menu is still open STR R14,menu__flags ;Save the flags back LDMFD R13!,{R0-R2} ;Get registers back B %90 ;And tidy up ; --- Tidy up on a non-submenu warning event --- 90 LDR R12,menu__flags ;Get the flags BIC R12,R12,#mFlag__wasSub ;It was not a menu warning STR R12,menu__flags ;Put the flags back LDMFD R13!,{R9,PC}^ ;Return to caller LTORG ; --- menu_help --- ; ; On entry: R0 == pointer to base message tag ; R1 == index of menu item ; ; On exit: -- ; ; Use: Adds a string to the help message found by adding the menu ; item number to the base message tag. EXPORT menu_help menu_help ROUT CMP R1,#0 ;Is the menu item sane? MOVLTS PC,R14 ;No -- don't trust Tim STMFD R13!,{R0-R2,R14} ;Save some registers MOV R1,R0 ;Point to base message tag MOV R0,R11 ;Point to scratchpad BL str_cpy ;Add the string in there MOV R1,R0 ;Point to terminating null MOV R2,#25 ;Should be 25 bytes left over LDR R0,[R13,#4] ;Get his item number SWI OS_ConvertInteger4 ;Tack it on the end MOV R0,R11 ;Point to the message tag BL msgs_lookup ;Translate it nicely BL help_add ;Add it to the help string LDMFD R13!,{R0-R2,PC}^ ;Return to caller LTORG ; --- menu_init --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Initialises the menu system. EXPORT menu_init menu_init ROUT STMFD R13!,{R0-R3,R9,R14} ;Stack some registers WSPACE menu__wSpace,R9 ;Locate my workspace ; --- Are we already initialised? --- LDR R0,menu__flags ;Get my flags TST R0,#mFlag__inited ;Are we initialised? LDMNEFD R13!,{R0-R3,R9,PC}^ ;Yes -- return ORR R0,R0,#mFlag__inited ;Set initialised flag STR R0,menu__flags ;And store them back ; --- Ensure that event is initialised --- BL event_init ;Initialise it ; --- Set up my menu stack --- LDR R0,menu__optName ;Get the option block name BL libOpts_find ;Try to find the block LDRCS R3,[R0,#0] ;If found, load stack size MOVCC R3,#2048 ;Otherwise use default 2K MOV R0,#2 ;Allocate memory BL sapphire_heapAddr ;Find the heap address SWI OS_Heap ;Allocate the stack STR R2,menu__stack ;Store this value ADD R3,R3,R2 ;The end of the menu stack STR R3,menu__stackEnd ;The menu stack end ; --- Set up the filters --- ADR R0,menu__preFilter ;Point to the filter MOV R1,R9 ;Use this workspace BL event_preFilter ;Add the pre filter ADR R0,menu__postFilter ;Point to the filter MOV R1,R9 ;Use this workspace BL event_postFilter ;Add the post filter ; --- Locate the TWIN global area --- LDR R0,menu__TWIN ;Load the global area name MOV R1,#twin_size ;Load the area's size BL sapphire_global ;Find the area's address STR R0,menu__twin ;Store the address away MOVCC R1,#0 ;Clear the global area MOVCC R2,#0 MOVCC R14,#0 STMCCIA R0,{R1,R2,R14} ;Store zeroes all over it ; --- And return peacefully --- LDMFD R13!,{R0-R3,R9,PC}^ ;Return to caller menu__optName DCB "MENU" menu__TWIN DCB "TWIN" LTORG menu__wSpace DCD 0 ;----- Global area layouts -------------------------------------------------- ; --- TWIN global area --- ; ; See transWin for more details ^ 0 twin_flags # 4 ;Various flags for things twin_trans # 4 ;Handle of transient window twin_tmsHook # 4 ;Hook for TMS twin_size # 4 twinFlag_recrt EQU (1<<0) ;Do we want to recreate? ;----- Workspace ------------------------------------------------------------ ^ 0,R9 menu__wStart # 0 menu__flags # 4 ;Flags word mFlag__inited EQU (1<<0) ;We are initialised mFlag__creating EQU (1<<1) ;We are creating a menu mFlag__wasSub EQU (1<<2) ;Last event was sub menu warn mFlag__tOnly EQU (1<<4) ;Only the title has been done mFlag__iBar EQU (1<<5) ;The click was on icon bar mFlag__recreating EQU (1<<6) ;We are recreating a menu mFlag__opened EQU (1<<7) ;I have a menu opened menu__stack # 4 ;Pointer to the menu stack menu__stackEnd # 4 ;The end of the menu stack menu__start # 4 ;Start of current menu defn menu__begin # 4 ;Pointer to real menu menu__end # 4 ;The end of the menu menu__maxLen # 4 ;Maximum length of items menu__sprite # 4 ;Pointer to a sprite name menu__coords # 8 ;(x,y) coords to open menu at menu__prevMenu # 4 ;Menu from which warning came menu__prevItem # 4 ;Menu from which warning came menu__twin # 4 ;Pointer to DBMN global area menu__wSize EQU {VAR}-menu__wStart AREA |Sapphire$$LibData|,CODE,READONLY DCD menu__wSize ;Workspace size DCD menu__wSpace ;Workspace pointer DCD 0 ;Scratchpad size DCD menu_init ;Initialisation ;----- That's all, folks ---------------------------------------------------- END