*******************************************************************************
*
* KeyMenu-Handler.asm  V1.03  02 Mar 1991
*
*
*
*   This program is loaded by 'KeyMenu' and stays resident in the 
*   system until the user runs KeyMenu with the 'QUIT' option.
*   
*
*
* Modification History:
*
*       Date       By                       Modification
*     --------  --------------------    --------------------------------------
*     09/09/89  Ken Lowther             Initial release, V1.01
*
*     09/13/89  Ken Lowther             V1.02 - added qualifier keys to move
*                                       mouse pointer to first/last item.
*
*     09/24/89  Ken Lowther             V1.02 - corrected positioning of mouse
*                                       pointer on menus. The pointer was being
*                                       positioned 1 pixel to the left. This
*                                       really only mattered when the menu is
*                                       1 pixel wide.
*
*     09/30/89  Ken Lowther             V1.02 - Keymenu no longer creates a 
*                                       process for this module. Changed 'main'
*                                       to set the DOS return code and return
*                                       in case this module gets 'execute'ed.
*
*     10/03/89  Ken Lowther             V1.02 - Changed to ignore the 1st 
*                                       occurance of a 'repeated' key. This
*                                       is to fix a problem that occurs when
*                                       there are more than 2 bitplanes in the
*                                       screen.
*
*     03/02/91  Ken Lowther             V1.03 - Modified to use PointerPos 
*                                       events rather than RawMouse events to
*                                       position intuition's pointer. This 
*                                       allows Keymenu to co-exist with most
*                                       mouse accelerators.
*
*     03/02/91  Ken Lowther             V1.03 - Re-installed intuition pointer
*                                       blanking option.
*
*******************************************************************************
            nolist
            include "macros.i"
            include "exec/types.i"
            include "exec/strings.i"
            include "exec/nodes.i"
            include "exec/ports.i"
            include "exec/execbase.i"
            include "exec/ables.i"
            include "exec/interrupts.i"
            include "exec/memory.i"
            include "devices/inputevent.i"
            include "intuition/intuition.i"
            include "libraries/dos.i"
            include "libraries/dosextens.i"
            include "keymenu-handler.i"
            list 
            nomlist

*******************************************************************************
*                                                                             *
*   Displacements into Select routine.                                        *
*                                                                             *
*******************************************************************************
s_top               equ                 si_top-si_jmp-2
s_adjacent_top      equ                 si_adjacent_top-si_jmp-2
s_adjacent_up       equ                 si_adjacent_up-si_jmp-2
s_up                equ                 si_up-si_jmp-2
s_adjacent_right    equ                 si_adjacent_right-si_jmp-2
s_right             equ                 si_right-si_jmp-2
s_leftmost          equ                 si_leftmost-si_jmp-2
s_adjacent_down     equ                 si_adjacent_down-si_jmp-2
s_bottom            equ                 si_bottom-si_jmp-2
s_adjacent_left     equ                 si_adjacent_left-si_jmp-2
s_down              equ                 si_down-si_jmp-2
s_left              equ                 si_left-si_jmp-2
s_rightmost         equ                 si_rightmost-si_jmp-2

gbl         equr    a4                  ; handler global area

            cseg
            near    code
*******************************************************************************
*                                                                             * 
*       main                                                                  *
*                                                                             *
*       This is the main routine of KeyMenu-Handler. It is where execution    *
*       begins. Here we perform the following functions:                      *
*           1) Initialize program. This consists of determining what task we  *
*              are and finding the public message port that the KeyMenu       *
*              program created for us. If the port doesn't exist or if        *
*              another task already owns it, we just exit (this can happen if *
*              the user somehow manages to run this program instead of        *
*              KeyMenu). The global area behind the message port, created by  *
*              Keymenu, is updated with our task address, the signal number   *
*              to use to send messages to us, the address of our input        *
*              handler routine and the version number of this program. We     *
*              then signal KeyMenu that we are initialized.                   *
*           2) Wait for one of two events to occur. Either a message has been *
*              directed to us via the public message port or KeyMenu has      *
*              signalled us to stop. Process the event as appropriate.        *
*           3) Step 2, above, is repeated until our 'active' flag is reset.   *
*              This occurs when, at our request, the input_handler sends a    *
*              message to us indicating it is ready to stop. We then cleanup  *
*              any resources we have allocated and return to AmigaDOS.        *
*                                                                             *
*       Input Registers:                                                      *
*           None.                                                             *
*                                                                             *
*       Output Registers:                                                     *
*           None.                                                             *
*                                                                             *
*******************************************************************************
main        movem.l mainregs,-(sp)          ; save entry registers
*-----------------------------------------------------------------------------*
*           do initialization                                                 *
*-----------------------------------------------------------------------------*
            move.l  AbsExecBase,a6          ; setup base for exec.library
            sub.l   a1,a1                   ; set to find our task
            Call    FindTask                ; get pointer to our task
            move.l  d0,a2                   ; get task address
            clr.l   pr_ConsoleTask(a2)      ; clear console handler pointer
            lea     myport(pc),a1           ; pointer to port name
            Call    FindPort                ; go find our port
            tst.l   d0                      ; does it exist ?
            beq     main060                 ; no, must have it, get out
            move.l  d0,gbl                  ; save pointer to our world
            tst.l   MP_SIGTASK(gbl)         ; is a task already there ?
            bne     main060                 ; yes, branch
*-----------------------------------------------------------------------------*
*           everything seems ok, update our global area and signal KeyMenu to *
*           go ahead and add our input_handler routine to the input.device    *
*-----------------------------------------------------------------------------*
            moveq.l #-1,d0                   
            Call    AllocSignal             ; get a signal for our task
            move.b  d0,MP_SIGBIT(gbl)       ; save the signal number
            move.l  a2,MP_SIGTASK(gbl)      ; make our task the one to signal
            move.l  a2,gb_handtask(gbl)
            lea     input_handler(pc),a1    ; get addr of input handler code
            move.l  a1,gb_handler+IS_CODE(gbl) ; put it in interrupt struct
            jsr     dosignal                ; go signal companion task
            moveq.l #1,d4                   ; set active flag
*-----------------------------------------------------------------------------*
*           if we are still active, wait for something to happen              *
*-----------------------------------------------------------------------------*
main010     tst.w   d4                      ; are we still active ?
            beq     main050                 ; no, go clean things up
            clr.l   d1                      ; clear work register
            move.b  MP_SIGBIT(gbl),d1       ; get signal bit number
            bset.l  d1,d0                   ; create signal mask
            bset.l  #SIGBREAKB_CTRL_C,d0    ; set break bit
            Call    Wait                    ; wait for something to happen
*-----------------------------------------------------------------------------*
*           An event occurred! Determine if KeyMenu signalled 'CTRL C'        *
*           telling us to stop. If not, there must be a message waiting on    *
*           the message port.                                                 *
*-----------------------------------------------------------------------------*
            btst.l  #SIGBREAKB_CTRL_C,d0    ; did user request us to stop ?
            beq     main015                 ; no, branch
            bset.b  #FLAGB_Stop_requested,gb_flags(gbl) ; set stop requested
                                            ; this tells the input handler to 
                                            ; stop the next time it is entered
            bra     main010                 ; go wait for input handler to stop
*-----------------------------------------------------------------------------*
*           get a message from the message port and determine what it is      *
*-----------------------------------------------------------------------------*
main015     move.l  gbl,a0                  ; get address of message port
            Call    GetMsg                  ; get any message placed there
            tst.l   d0                      ; was there a message queued ?
            beq     main010                 ; no, branch
            move.l  d0,a2                   ; save message address
            cmp.w   #req_stopped,LN_NAME(a2) ; stop message ?
            bne     main020                 ; no, branch
*-----------------------------------------------------------------------------*
*           A 'stop' message was received from the input handler. Clear our   *
*           'active' flag.                                                    *
*-----------------------------------------------------------------------------*
            clr.l   d4                      ; yes, clear active flag
main020     cmp.w   #req_clearpointer,LN_NAME(a2) ; clear pointer request ?
            bne     main030                 ; no, branch
*-----------------------------------------------------------------------------*
*           A 'clearpointer' request was received from the input handler.     *
*           This causes us to reset the intuition pointer to its previous     *
*           condition prior to our blanking it.                               *
*-----------------------------------------------------------------------------*
            tst.l   gb_Pointer(gbl)         ; is any pointer info saved ?
            beq     main025                 ; no, branch
            move.l  gb_window(gbl),a0       ; yes, setup to do 'SetPointer'
            move.l  gb_Pointer(gbl),a1      ; this will restore the window's
            move.b  gb_PtrHeight(gbl),d0    ; custom pointer
            ext.w   d0
            ext.l   d0
            move.b  gb_PtrWidth(gbl),d1
            ext.w   d1
            ext.l   d1
            move.b  gb_XOffset(gbl),d2
            ext.w   d2
            ext.l   d2
            move.b  gb_YOffset(gbl),d3
            ext.w   d3
            ext.l   d3
            Call    SetPointer,gb_IBase(gbl) ; restore the custom pointer
            clr.l   gb_Pointer(gbl)         ; clear pointer info
            bra     main030
main025     move.l  gb_window(gbl),a0
            Call    ClearPointer,gb_IBase(gbl) ; reset back to intuition's
                                               ; pointer
main030     cmp.w   #req_setpointer,LN_NAME(a2) ; set pointer request ?
            bne     main040                 ; no, branch
*-----------------------------------------------------------------------------*
*           A 'setpointer' request was received from the input handler. This  *
*           causes us to save info about the current custom pointer, if any,  *
*           and call 'SetPointer' using our own 'blank' pointer. This removes *
*           the pointer from the user's view during menu operations.          *
*-----------------------------------------------------------------------------*
            move.l  gb_window(gbl),a0        
            tst.l   wd_Pointer(a0)          ; is there a custom pointer ?
            beq     main035                 ; no, branch
            move.l  wd_Pointer(a0),gb_Pointer(gbl) ; save custom pointer info
            move.b  wd_PtrHeight(a0),gb_PtrHeight(gbl)
            move.b  wd_PtrWidth(a0),gb_PtrWidth(gbl)
            move.b  wd_XOffset(a0),gb_XOffset(gbl)
            move.b  wd_YOffset(a0),gb_YOffset(gbl)
main035     move.l  gb_blank_ptr(gbl),a1    ; setup to call 'SetPointer'
            moveq.l #-1,d0
            moveq.l #-1,d1
            clr.l   d2
            clr.l   d3
            Call    SetPointer,gb_IBase(gbl)
*-----------------------------------------------------------------------------*
*           free the message we received and loop to see if there is more     * 
*-----------------------------------------------------------------------------*
main040     clr.l   d0
            move.l  a2,a1                   ; setup addr of block to free
            move.w  MN_LENGTH(a1),d0        ; setup length of block to free
            Call    FreeMem                 ; go free it
            bra     main015                 ; loop for next message

myport      portname
            
*-----------------------------------------------------------------------------*
*           We are no longer active, cleanup after ourselves and get out      *
*-----------------------------------------------------------------------------*
main050     move.l  gbl,a0                  ; get address of message port
            Call    GetMsg                  ; get any message placed there
            tst.l   d0                      ; was there a message queued ?
            beq     main055                 ; no, branch
            move.l  d0,a1                   ; setup to free the block
            clr.l   d0
            move.w  MN_LENGTH(a1),d0        ; setup length of block to free
            Call    FreeMem                 ; go free it
            bra     main050                 ; loop for next message
main055     clr.l   d0
            move.b  MP_SIGBIT(gbl),d0       ; get signal number
            Call    FreeSignal              ; go free it
            Forbid
            jsr     dosignal                ; go signal companion task
main060     movem.l (sp)+,mainregs          ; restore entry registers
            rts                             ; return to AmigaDOS
mainregs    reg     d1-d7/a0-a6


*******************************************************************************
*                                                                             * 
*       dosignal                                                              *
*                                                                             *
*       Send a signal to our companion task (should be KeyMenu).              *
*                                                                             *
*       Input Registers:                                                      *
*           a4 - global work area                                             *
*                                                                             *
*       Output Registers:                                                     *
*           None.                                                             *
*                                                                             *
*******************************************************************************
dosignal    clr.l   d1                      ; clear work register
            move.b  gb_tasksignum(gbl),d1   ; get keymenu task signal number
            bset.l  d1,d0                   ; create signal mask
            move.l  gb_task(gbl),a1         ; get task to signal
            Call    Signal                  ; signal the task that we are ready
            rts

******************************************************************************
*                                                                             * 
*       input_handler                                                         *
*                                                                             *
*       This routine is added to the input.device's list of handlers by the   *
*       KeyMenu program. It is called whenever an input event, such as a      *
*       keyboard press or mouse movement, occurs. In general, the input event *
*       chain is scanned to see if the user has pressed any one of the        *
*       various keys that we care about. If so, the input event representing  *
*       that keypress is replaced with a mouse event(s) to accomplish         *
*       KeyMenu's purpose. Note that the memory associated with event(s) that *
*       are removed from the list actually belongs to the creator of that     *
*       event. It is the event creator's responsibility to free this memory.  *
*       See the RKM for more info about input handlers and the input.device   *
*                                                                             *
*       Input Registers:                                                      *
*           a0 - input event chain, a forward reference linked list with the  *
*                last event in the chain containing a 'NULL' reference.       *
*           a1 - global work area                                             *
*                                                                             *
*       Output Registers:                                                     *
*           d0 - new input event chain                                        *
*                                                                             *
*******************************************************************************
input_handler

laste       equr    a1                      ; last event handled in chain
ev          equr    a2                      ; original event passed to us
ep          equr    a3                      ; current event pointer

            movem.l ihregs,-(sp)            ; save registers
            move.l  a1,gbl                  ; setup pointer to our world
            move.l  a0,ev                   ; save pointer to input event chain
*-----------------------------------------------------------------------------*
*           If 'stop' has been posted, check to see if there is currently a   *
*           menu active. If so, wait for the user to get out of it otherwise  *
*           send a 'stop' message (actually means 'ready to stop') to the     *
*           KeyMenu-Handler process.                                          *
*-----------------------------------------------------------------------------*
            btst.b  #FLAGB_Stop_requested,gb_flags(gbl) ; stop requested ?
            beq     ih010                   ; no, branch
            btst.b  #FLAGB_MActive,gb_flags(gbl) ; is a menu currently active ?
            bne     ih010                        ; yes, branch
            btst.b  #FLAGB_Stopped,gb_flags(gbl) ; stop already been posted ?
            bne     ih010                        ; yes, branch
            moveq.l #req_stopped,d0         ; set type fo request to send
            jsr     sendrequest             ; go send it
            bset.b  #FLAGB_Stopped,gb_flags(gbl) ; indicate stop has been posted
*-----------------------------------------------------------------------------*
*           Begin stepping through the input event chain                      *
*-----------------------------------------------------------------------------*
ih010       FORBID                          ; don't let anyone mess with events
            sub.l   laste,laste             ; clear last event register
            move.l  ev,ep                   ; setup event work register
            bclr.b  #FLAGB_Events_inserted,gb_flags(gbl) ; clear inserted flag
            bra     ih040                   ; go see if an event exists
ih015       cmp.b   #IECLASS_RAWKEY,ie_Class(ep) ; Raw key event ?
            bne     ih025                   ; no, branch
*-----------------------------------------------------------------------------*
*           This is a RAWKEY event (keypress/keyrelease).                     *
*-----------------------------------------------------------------------------*
            jsr     Check_RawKey_Event      ; go look at this one
            beq     ih030                   ; do we keep it ? yes, branch
            move.l  laste,d0                ; was there a previous event ?
            bne     ih020                   ; yes, branch
            move.l  ie_NextEvent(ep),ev     ; drop the event
            bra     ih035                     
ih020       move.l  ie_NextEvent(ep),ie_NextEvent(laste)
            bra     ih035                   ; loop
*-----------------------------------------------------------------------------*
*           Here we look for a 'MENUDOWN' event. If this occurs while we have *
*           a menu active, it indicates that the user pressed the right mouse *
*           button intending to take over menu selection with the mouse.      *
*-----------------------------------------------------------------------------*
ih025       cmp.b   #IECLASS_RAWMOUSE,ie_Class(ep) ; is this a raw mouse event ?
            bne     ih030                   ; no, branch
            cmp.w   #MENUDOWN,ie_Code(ep)   ; is it a menu down event ?
            bne     ih030                   ; no, branch
            btst.b  #FLAGB_MActive,gb_flags(gbl) ; is a menu in progress ?
            beq     ih030                   ; no, branch
            bclr.b  #FLAGB_MActive,gb_flags(gbl) ; give control of menu to mouse
            moveq.l #req_clearpointer,d0
            jsr     sendrequest             ; go reset intuition pointer
*-----------------------------------------------------------------------------*
*           Now we look to see if we inserted any events in the chain. If so, *
*           we must get out. This is because we only have a fixed number of   *
*           input event areas allocated (3) and they can be used only once    *
*           per invocation of the handler. This could be a potential problem  *
*           in that we could miss events that we care about (since we don't   *
*           look at the remaining events in the event chain). However, this   *
*           is nothing that would cause harm to the system. It would just     *
*           appear to the user as though a keypress intended to cause a menu  *
*           operation didn't take. This is better than incuring the overhead  *
*           and buffer management problems associated with dynamically        *
*           allocating input events.                                          *
*-----------------------------------------------------------------------------*
ih030       move.l  ep,laste                ; save this event
ih035       btst.b  #FLAGB_Events_inserted,gb_flags(gbl) ; were events inserted?
            bne     ih045                   ; yes, branch
*-----------------------------------------------------------------------------*
*           Setup to process the next event if one exists, otherwise get out  *
*-----------------------------------------------------------------------------*
            move.l  ie_NextEvent(ep),ep     ; get next event
ih040       move.l  ep,d0                   ; set cc
            bne     ih015                   ; go check it
ih045       PERMIT
            move.l  ev,d0                   ; pass event on
            movem.l (sp)+,ihregs            ; restore registers
            rts                             ; return to input device
ihregs      reg     a0/a1/a2/a3/a4/a6

*******************************************************************************
*                                                                             * 
*       sendrequest                                                           *
*                                                                             *
*       This routine is called by the input handler when it wishes to send a  *
*       request message to the KeyMenu-Handler process. There are currently   *
*       three kinds of messages that may be sent:                             *
*           1)  req_setpointer, to blank the intuition pointer during menu    *
*               operations.                                                   *
*           2)  req_clearpointer, to restore the intuition pointer when menu  *
*               operations are finished.                                      *
*           3)  req_stopped, to signal that the handler is ready to stop.     *
*       Sendrequest is always called regardless of the setting of the hide    *
*       intuition pointer option. This is where we determine whether or not   *
*       to actually send the request.                                         *
*                                                                             *
*       Input Registers:                                                      *
*           d0 - type of request to be sent                                   *
*           a4 - global work area                                             *
*                                                                             *
*       Output Registers:                                                     *
*           none.                                                             *
*                                                                             *
*******************************************************************************
sendrequest movem.l sendreqregs,-(sp)       ; save entry registers
            btst.b  #FLAGB_Blank_pointer,gb_flags(gbl) ; blank pointer option on ?
            bne     sendreq010              ; yes, branch
            cmp.w   #req_stopped,d0         ; is this a 'stopped' request ?
            bne     sendreq020              ; no, branch
sendreq010  move.l  d0,d2                   ; save request type
            move.l  #MEMF_CLEAR+MEMF_PUBLIC,d1 ; set attributes
            move.l  #MN_SIZE,d0             ; set size of block
            Call    AllocMem                ; go allocate a block
            tst.l   d0                      ; was a block given to us ?
            beq     sendreq020              ; no, branch
            move.l  d0,a1                   ; yes, save pointer to block
            move.w  d2,LN_NAME(a1)          ; format message
            clr.l   MN_REPLYPORT(a1)
            move.w  #MN_SIZE,MN_LENGTH(a1)
            move.l  gbl,a0
            Call    PutMsg                  ; write it to the port
sendreq020  movem.l (sp)+,sendreqregs       ; restore registers
            rts                             ; return to caller
sendreqregs reg     a0/a1/d0-d2

*******************************************************************************
*                                                                             * 
*       Check_RawKey_Event                                                    *
*                                                                             *
*       This routine checks each RAWKEY event (keypress/keyrelease) to see if *
*       it is one that we are interested in. If it is, the event that is      *
*       passed to us is replaced with events to do the appropriate KeyMenu    *
*       function. Events that have no meaning for us are passed back to the   *
*       caller indicating that they should remain in the input event chain.   *
*                                                                             *
*       Input Registers:                                                      *
*           a3 - rawkey event to be checked                                   *
*           a4 - global work area                                             *
*                                                                             *
*       Output Registers:                                                     *
*           d0 - 0=discard the event, 1=keep the event                        *
*                                                                             *
*******************************************************************************
Check_RawKey_Event
            movem.l creregs,-(sp)           ; save registers
            clr.l   d0
            btst.b  #IEQUALIFIER_REPEAT_R,ie_Qualifier(a3)  ; is it a repeat ?
            beq     cre003                  ; no, branch
            btst.b  #FLAGB_Repeat_skipped,gb_flags(gbl) ; have we skipped one ?
            bne     cre004                  ; yes, branch
            bset.b  #FLAGB_Repeat_skipped,gb_flags(gbl) ; indicate repeat skipped
            bra     cre_remove              ; remove it from the input chain
cre003      bclr.b  #FLAGB_Repeat_skipped,gb_flags(gbl) ; clear repeat skipped
cre004      btst.b  #FLAGB_MActive,gb_flags(gbl) ; is a menu currently active ?
            bne     cre035                  ; yes, branch
*-----------------------------------------------------------------------------*
*           A menu is not currently active, check to see if one is being      *
*           activated (keypress of the configured activation key).            *
*-----------------------------------------------------------------------------*
            move.b  gb_AKey(gbl),d0         ; get configured activation key code
            cmp.w   ie_Code(a3),d0          ; Menu Activate Key ?
            bne     cre010                  ; no, branch
*-----------------------------------------------------------------------------*
*           The user pressed the menu activation key, set a flag indicating   *
*           that fact and wait to see if the key is released without pressing *
*           any other keys.                                                   *
*-----------------------------------------------------------------------------*
            bset.b  #FLAGB_AKey_down,gb_flags(gbl) ; indicate Menu activate key
                                                   ; down
cre005      bra     cre_remove              ; remove this event
cre010      bset.l  #IECODE_UP_PREFIX_R,d0  ; form keyrelease keycode
            cmp.w   ie_Code(a3),d0          ; is it activate menu keyrelease ?
            beq     cre014                  ; yes, branch
cre012      bclr.b  #FLAGB_AKey_down,gb_flags(gbl) ; clear activate key down
            bra     cre_keep                ; keep this event
cre014      btst.b  #FLAGB_AKey_down,gb_flags(gbl) ; is activate key down still
                                                   ; posted ?
            beq     cre012                  ; no, branch
*-----------------------------------------------------------------------------*
*           The user pressed and released the menu activation key without     *
*           hitting any other keys, find the current window and see if it has *
*           a menu.                                                           *
*-----------------------------------------------------------------------------*
            move.l  gb_IBase(gbl),a5        ; get intuitionbase
            move.l  ib_ActiveWindow(a5),d0  ; get current window
            beq     cre005                  ; if none, branch
            move.l  d0,a1                   ; save pointer to current window
            move.l  wd_MenuStrip(a1),d0     ; get window's menu
            beq     cre005                  ; if none, branch
            move.l  d0,a0                   ; save menu for use later
            move.l  wd_Flags(a1),d0         ; get window's flags
            btst.l  #MENUSTATE_B,d0         ; is a menu already active ?
            bne     cre005                  ; yes, branch
*-----------------------------------------------------------------------------*
*           The current window has a menu, check the RMBTRAP flag. If this is *
*           set and the user didn't specify that we can clear it, then we     *
*           can't use this menu.                                              *
*-----------------------------------------------------------------------------*
            btst.l  #RMBTRAP_B,d0           ; is RMBTRAP set in window ?
            beq     cre015                  ; no, branch
            btst.b  #FLAGB_Clear_rmbtrap,gb_flags(gbl) ; clear rmbtrap option 
                                                       ; set ?
            beq     cre005                  ; no, ignore the menu
            bclr.l  #RMBTRAP_B,d0           ; clear RMBTRAP bit
            move.l  d0,wd_Flags(a1)         ; update window's flags
*-----------------------------------------------------------------------------*
*           Finally, a menu we can use. Update our globals.                   *
*-----------------------------------------------------------------------------*
cre015      move.l  a0,gb_menu(gbl)         ; update our Menu pointer
            move.l  a1,gb_window(gbl)       ; update our window pointer
*-----------------------------------------------------------------------------*
*           Now look to see if the menu and menu item saved in our globals is *
*           still valid for this menu. If so, we will position the mouse on   *
*           them otherwise we'll put the mouse on the topmost item in the     *
*           leftmost menu.                                                    *
*-----------------------------------------------------------------------------*
            move.l  gb_currentmenu(gbl),a1  ; get saved menu
            jsr     Verify_Item             ; go verify currentmenu
            bne     cre020                  ; does it exist ? yes, use it
            move.w  #s_leftmost,d0          ; no, setup to find leftmost menu
            jsr     Select_Item             ; go select a Menu
            move.l  d0,gb_currentmenu(gbl)  ; update our globals with new menu
cre020      move.l  gb_currentmenu(gbl),a0  ; get current menu
            move.l  mu_FirstItem(a0),a0     ; point to first menu item
            move.l  gb_currentitem(gbl),a1  ; get current menu item
            jsr     Verify_Item             ; go verify current item
            bne     cre025                  ; does it exist ? yes, branch
            move.w  #s_top,d0               ; no, setup to find top menuitem
            jsr     Select_Item             ; go select menu item
            move.l  d0,gb_currentitem(gbl)  ; update our globals
*-----------------------------------------------------------------------------*
*           Save the current mouse position so we can put it back where we    *
*           found it when we're done. set/clear various flags reflecting our  *
*           current state. Three events are inserted in the event chain.      *
*           A rawmouse menudown event that causes intuition to activate the   *
*           menu followed by two pointerpos events to position the pointer    *
*           first on the desired menu then on the menu item within that menu. *
*-----------------------------------------------------------------------------*
cre025      move.w  ib_MouseX(a5),gb_old_MouseX(gbl) ; save current X
            move.w  ib_MouseY(a5),gb_old_MouseY(gbl) ; save current Y
            clr.l   gb_currentsubitem(gbl)  ; no subitem at present
            move.w  #MENUDOWN,d0            ; setup to insert a menudown event
            move.l  a3,a1                   ; event to attach new event to
            jsr     Insert_RawMouse_Event   ; go insert the rawmouse event
            moveq.l #2,d0                   ; indicate 2 events to be built
            move.l  a0,a1                   ; event to attach new events to
            jsr     db010                   ; go build the necessary events
            bclr.b  #FLAGB_AKey_down,gb_flags(gbl) ; no longer waiting for
                                                   ; activate key down event
            move.l  #req_setpointer,d0      ; setup to send request
cre027      bchg.b  #FLAGB_MActive,gb_flags(gbl) ; invert menu active bit
            jsr     sendrequest             ; send pointer req to our task
            bra     cre_remove              ; go to common return
*-----------------------------------------------------------------------------*
*           A menu is currently active, check for the various keys that we    *
*           care about.                                                       *
*-----------------------------------------------------------------------------*
cre035      move.b  gb_DKey(gbl),d0
            cmp.w   ie_Code(a3),d0          ; Deactivate key ?
            bne     cre040                  ; no, branch
*-----------------------------------------------------------------------------*
*           The menu 'deactivate' key was pressed. Three events are inserted  *
*           in the event chain. They consist of a pointerpos event to         *
*           position the pointer in the menu strip followed by a rawmouse     *
*           menuup event that causes intuition to deactivate the menu without *
*           selecting anything followed by another pointerpos event to put    *
*           the pointer back where we found it when the menu was activated.   *
*-----------------------------------------------------------------------------*
            moveq.l #3,d0                   ; build 2 events
            jsr     dobuild                 ; go build mouse event(s)
            move.w  #MENUUP,d0              ; setup for a menu up rawmouse event
            move.l  a0,a1                   ; event to attach new event to
            jsr     Insert_RawMouse_Event   ; go insert the a rawmouse event
            move.l  #req_clearpointer,d0    ; setup to restore pointer
            bra     cre027                  ; get out

cre040      move.b  gb_SKey(gbl),d0
            cmp.w   ie_Code(a3),d0          ; Select Key ?
            bne     cre055                  ; yes, branch
*-----------------------------------------------------------------------------*
*           The menu 'select' key was pressed. Two events are inserted in the *
*           event chain. They consist of a menuup rawmouse event to select    *
*           the current menu item, followed by a pointer event to put the     *
*           pointer back where we found it when the menu was activated.       *
*-----------------------------------------------------------------------------*
            move.l  a3,a1                   ; event to attach new ones to
            move.w  #MENUUP,d0              ; set type of rawmouse event
            jsr     Insert_RawMouse_Event   ; go insert a menuup event
            move.l  gb_IBase(gbl),a5        ; get intuitionbase address
            move.w  gb_old_MouseX(gbl),d0   ; get saved mouse x coordinate
            move.w  gb_old_MouseY(gbl),d1   ; get saved mouse y cooridnate
            move.l  a0,a1                   ; event to attach new event to
            lea     gb_ppos1(gbl),a0        ; new event to be inserted
            jsr     Insert_PointerPos_Event   
            move.l  #req_clearpointer,d0    ; setup to restore pointer
            bra     cre027                  ; get out

cre055      move.b  gb_RightKey(gbl),d0
            cmp.w   ie_Code(a3),d0          ; Move Right ?
            bne     cre060                  ; no, branch
            move.w  #s_adjacent_right,d1    ; 1st selection choice
            move.w  #s_right,d2             ; 2nd selection choice
            move.w  #s_leftmost,d3          ; 3rd selection choice
            bra     cre065
cre060      move.b  gb_LeftKey(gbl),d0
            cmp.w   ie_Code(a3),d0          ; Move Left ?
            bne     cre125                  ; no, branch
            move.w  #s_adjacent_left,d1     ; 1st selection choice
            move.w  #s_left,d2              ; 2nd selection choice
            move.w  #s_rightmost,d3         ; 3rd selection choice
cre065      move.b  ie_Qualifier+1(a3),d0   ; get key qualifier
            and.b   gb_Qual(gbl),d0         ; was qualifier key present ?
            beq     cre067                  ; no,branch
*-----------------------------------------------------------------------------*
*           The menu 'right' or 'left' key was pressed. See if a qualifier    *
*           key was also present. If so, we move to either rightmost or       *
*           leftmost menu.                                                    *
*-----------------------------------------------------------------------------*
            move.w  #s_rightmost,d0         ; select rightmost
            cmp.w   d0,d3                   ; same selection ?
            bne     cre107                  ; no, branch
            move.w  #s_leftmost,d0          ; select leftmost
            bra     cre107                  ; go choose the menu
*-----------------------------------------------------------------------------*
*           The menu 'right' or 'left' key was pressed. Now we must see if    *
*           a subitem list exists for the current menu item.                  *
*-----------------------------------------------------------------------------*
cre067      move.l  gb_currentitem(gbl),a0  ; get current menu item ptr
            move.l  mi_SubItem(a0),a1       ; get its menu subitem ptr, if any
            move.l  a1,d0                   ; set cc
            beq     cre100                  ; no subitems on this menu, branch
*-----------------------------------------------------------------------------*
*           There are subitems attached to this menu item. Some explanation   *
*           is in order here. With Intuition, menu subitems can be rendered   *
*           anywhere on the menu display as long as at least one pixel of the *
*           menu subitem overlaps a pixel of its associated menu item.        *
*           Generally, subitems are designed to appear either to the right of *
*           its associated menu item or to the left. This affects our         *
*           interpretation of what the user wants to do when they press       *
*           either the menu right or menu left key.  For example, if the menu *
*           subitems are positioned to the right of the menu item and the     *
*           menu right key is pressed, this means that the user intends to    *
*           step into the menu subitem list. If the menu subitems are         *
*           positioned on the left and the menu right key is pressed, the     *
*           user wants to step to the next list of menu items on the right    *
*           (or wrap to the first set of menu items if we are positioned on   *
*           the rightmost menu item). Here we check the leftedge of the       * 
*           subitem against the leftedge of the menu item to determine if the *
*           subitem's leftedge is less than (or to the left of) the menu      *
*           item's leftedge. The result of this comparison is saved in d4 and *
*           is used later in determining what the user's intentions are.      *
*-----------------------------------------------------------------------------*
            move.w  mi_LeftEdge(a1),d0      ; get subitem's leftedge
            cmp.w   mi_LeftEdge(a0),d0      ; compare to menuitem's leftedge
            slt     d4                      ; save comparison result
            tst.l   gb_currentsubitem(gbl)  ; are we processing subitems ?
            bne     cre080                  ; yes, branch
*-----------------------------------------------------------------------------*
*           We are positioned on a menu item that has subitems attached and   *
*           the user has pressed the menu right or menu left key. Determine   *
*           what their intention is. Either they want to step into the        *
*           subitem list or they want to select the next menu item.           *
*-----------------------------------------------------------------------------*
            clr.l   d0
            move.b  gb_RightKey(gbl),d0     ; setup for compare
            cmp.w   ie_Code(a3),d0          ; right key ?
            bne     cre070                  ; no, branch
            tst.b   d4                      ; are subitems on menu items left ?
            beq     cre075                  ; no, step into the subitem list
            bra     cre100                  ; yes, step to next menu item list
cre070      tst.b   d4                      ; are subitems on menu items left ?
            beq     cre100                  ; no, step to next menu item list
                                            ; yes, step into the subitem list
*-----------------------------------------------------------------------------*
*           The user wants to step into the subitem list. Select the topmost  *
*           adjacent subitem in the list. (Note: there can be subitems        *
*           positioned horizontally as well as vertically)                    * 
*-----------------------------------------------------------------------------*
cre075      move.w  #s_adjacent_top,d0      ; set to select the topmost subitem
            move.l  a1,a0                   ; list of subitems to choose from
            jsr     Select_Item             ; go find the subitem
            move.l  d0,gb_currentsubitem(gbl) ; save it in our globals
            bra     cre085                  ; go generate an event
*-----------------------------------------------------------------------------*
*           We are currently positioned on a menu subitem and the user has    *
*           pressed either the menu right key or menu left key. Determine     *
*           what their intention is. If there is another subitem positioned   *
*           adjacent to the current subitem in the direction of the key that  *
*           was pressed, that subitem will be selected. Otherwise, the user   *
*           wants to step out of the subitem list.                            *
*-----------------------------------------------------------------------------*
cre080      move.l  d1,d0                   ; get 1st selection choice
            move.l  a1,a0                   ; list of subitems to choose from
            move.l  gb_currentsubitem(gbl),a1 ; supply current subitem
            jsr     Select_Item             ; go select next subitem
            cmp.l   d0,a1                   ; same item selected ?
            beq     cre090                  ; yes, get out of the subitem list
            move.l  d0,gb_currentsubitem(gbl) ; no, save new subitem
cre085      moveq.l #1,d0                   ; set to build only one event
            bra     cre175                  ; go generate an event
*-----------------------------------------------------------------------------*
*           The user wants to step out of the subitem list. Determine whether *
*           they want to go back to the menu item associated with the subitem *
*           list or to the next menu item.                                    *
*-----------------------------------------------------------------------------*
cre090      clr.l   gb_currentsubitem(gbl)  ; clear current subitem pointer
            clr.l   d0
            move.b  gb_RightKey(gbl),d0     ; setup for compare
            cmp.w   ie_Code(a3),d0          ; right key ?
            bne     cre095                  ; no, branch
            tst.b   d4                      ; are subitems on menu items left ?
            bne     cre085                  ; yes, go back to subitems menu item
            bra     cre100                  ; no, go select next menu item list
cre095      tst.b   d4                      ; are subitems on menu items left ?
            beq     cre085                  ; no, go back to subitems menu item
*-----------------------------------------------------------------------------*
*           Select a menu item. (Note: there can be menu items positioned     *
*           horizontally as well as vertically)                               *
*-----------------------------------------------------------------------------*
cre100      move.l  d1,d0                   ; get 1st selection choice
            move.l  gb_currentmenu(gbl),a0  ; get current menu
            move.l  mu_FirstItem(a0),a0     ; list of items to choose from
            move.l  gb_currentitem(gbl),a1  ; get current menu item
            jsr     Select_Item             ; go select an item
            cmp.l   d0,a1                   ; same item selected ?
            bne     cre115                  ; no, use the new item, branch
*-----------------------------------------------------------------------------*
*           There are no menu items positioned in the horizontal direction of *
*           the key that was pressed, so we must step to the next menu.       *
*-----------------------------------------------------------------------------*
            move.l  d2,d0                   ; get 2nd selection choice
            move.l  gb_menu(gbl),a0         ; list of menus to choose from
            move.l  gb_currentmenu(gbl),a1  ; get current menu
            jsr     Select_Item             ; go select a menu
            cmp.l   d0,a1                   ; same menu selected ?
            beq     cre105                  ; yes, branch
            move.l  d0,gb_currentmenu(gbl)  ; no, use the new menu
            bra     cre110                  ; go select an item in the menu
*-----------------------------------------------------------------------------*
*           There are no menus in the horizontal direction of the key that    *
*           was pressed. We will interpret this to mean that the user wants   *
*           to wrap to the opposite end of the menu strip. For example, if we *
*           are currently on the rightmost menu and the user presses the menu *
*           right key, we will wrap around to the leftmost menu.              *
*-----------------------------------------------------------------------------*
cre105      move.l  d3,d0                   ; set 3rd selection choice
cre107      move.l  gb_menu(gbl),a0         ; list of menus to choose from
            jsr     Select_Item             ; go select again
            move.l  d0,gb_currentmenu(gbl)  ; save new menu
cre110      move.w  #s_top,d0               ; set to select topmost menu item
            move.l  gb_currentmenu(gbl),a0  ; get current menu
            move.l  mu_FirstItem(a0),a0     ; list of items to choose from
            jsr     Select_Item             ; go select a menu item
            move.l  d0,gb_currentitem(gbl)  ; save new item
*-----------------------------------------------------------------------------*
*           Normally, we will generate only one event. A pointerpos event to  *
*           position the pointer on the desired menu item or subitem. Here we *
*           have determined that a new menu is to be selected. This requires  *
*           us to generate 2 events. The first will be a pointerpos event to  *
*           position the pointer in the menu strip over the menu that we want *
*           to select. This causes Intuition to remove the rendering of the   *
*           current menu from the display and render the menu that we are     *
*           selecting. The second event will be a pointerpos event to         *
*           position the pointer on the menu item that we have chosen.        *
*-----------------------------------------------------------------------------*
            moveq.l #2,d0                   ; set number of events to build
            bra     cre120
cre115      move.l  d0,gb_currentitem(gbl)  ; save new item
            moveq.l #1,d0                   ; set number of events to build
cre120      clr.l   gb_currentsubitem(gbl)  ; clear current subitem pointer
            bra     cre175                  ; go generate event(s)

cre125      move.b  gb_DownKey(gbl),d0      ; setup for compare
            cmp.w   ie_Code(a3),d0          ; Down key ?
            bne     cre130                  ; no, branch
            move.w  #s_adjacent_up,d1       ; 1st selection choice
            move.w  #s_up,d2                ; 2nd selection choice
            move.w  #s_top,d3               ; 3rd selection choice
            bra     cre135                  ; go handle keypress
cre130      move.b  gb_UpKey(gbl),d0        ; setup for compare
            cmp.w   ie_Code(a3),d0          ; Up key ?
            bne     cre_keep                ; no, pass this event on
            move.w  #s_adjacent_down,d1     ; 1st selection choice
            move.w  #s_down,d2              ; 2nd selection choice
            move.w  #s_bottom,d3            ; 3rd selection choice
*-----------------------------------------------------------------------------*
*           The menu 'up' or 'down' key was pressed. If we are currently      *
*           positioned in a subitem menu, setup to select the appropriate     *
*           subitem otherwise setup to select the appropriate menu item.      *
*-----------------------------------------------------------------------------*
cre135      moveq.l #1,d4                   ; set default # events to be created
            tst.l   gb_currentsubitem(gbl)  ; are we processing subitems ?
            beq     cre140                  ; no, branch
            move.l  gb_currentsubitem(gbl),a1 ; supply parameters to process
            move.l  gb_currentitem(gbl),a0    ; menu subitems
            move.l  mi_SubItem(a0),a0
            bra     cre145
cre140      move.l  gb_currentitem(gbl),a1  ; supply parameters to process menu
            move.l  gb_currentmenu(gbl),a0  ; items
            move.l  mu_FirstItem(a0),a0
*-----------------------------------------------------------------------------*
*           Here we check to see if there are subitems attached to the        *
*           current menu item. If so, we will generate 2 events. The first    *
*           will be a pointerpos event to position the pointer on the menu    *
*           strip. The second will be a pointerpos event to position the      *
*           pointer on the desired menu item. This is done to cause Intuition *
*           to remove the subitem's rendering from the display in case it     *
*           overlaps onto the menu item being selected. Without this, we      *
*           would tell Intuition to position the mouse on the new menu item   *
*           but the overlapping subitems from the current menu item would     *
*           still be displayed causing us to select a menu subitem instead of *
*           the menu item that we intended to select.                         *
*-----------------------------------------------------------------------------*
            tst.l   mi_SubItem(a1)          ; are there subitems attached to
                                            ; this menu item ?
            beq     cre145                  ; no, branch
            moveq.l #2,d4                   ; yes, use 2 events to select item
cre145      move.b  ie_Qualifier+1(a3),d0   ; get key qualifier
            and.b   gb_Qual(gbl),d0         ; was qualifier key present ?
            beq     cre147                  ; no, branch
*-----------------------------------------------------------------------------*
*           See if a qualifier key was also present. If so, we move to either *
*           topmost or bottommost menu or submenu item.                       *
*-----------------------------------------------------------------------------*
            move.w  #s_bottom,d0            ; select bottom
            cmp.w   d0,d3                   ; same selection ?
            bne     cre153                  ; no, branch
            move.w  #s_top,d0               ; select top
            bra     cre153
cre147      move.l  d1,d0                   ; get 1st selection choice
            jsr     Select_Item             ; go select a menuitem or subitem
            cmp.l   d0,a1                   ; same item selected ?
            bne     cre155                  ; no, use the new one
            move.l  d2,d0                   ; get 2nd selection choice
            jsr     Select_Item             ; select again
cre150      cmp.l   d0,a1                   ; same item selected ?
            bne     cre155                  ; no, use new item
            move.l  d3,d0                   ; get 3rd selection choice
cre153      jsr     Select_Item             ; go select again
cre155      move.l  d0,a1                   ; save new item
cre160      tst.l   gb_currentsubitem(gbl)  ; are we processing subitems ?
            beq     cre165                  ; no, branch
            move.l  a1,gb_currentsubitem(gbl) ; yes, save new subitem
            bra     cre170
cre165      move.l  a1,gb_currentitem(gbl)  ; save new item
cre170      move.l  d4,d0                   ; get # of events to be generated
cre175      jsr     dobuild                 ; go build our events
*-----------------------------------------------------------------------------*
*           Indicate that the original input event passed to this routine is  *
*           to be removed from the input event chain and return.              *
*-----------------------------------------------------------------------------*
cre_remove  moveq.l #1,d0                   ; indicate old event to be removed
            bra     cre_exit                ; go to common return
*-----------------------------------------------------------------------------*
*           Indicate that the original input event passed to this routine is  *
*           to be kept in the input event chain and return.                   *
*-----------------------------------------------------------------------------*
cre_keep    clr.l   d0                      ; indicate old event to be kept
cre_exit    movem.l (sp)+,creregs           ; restore registers
            rts                             ; return to caller
creregs     reg     a1-a3/a5/d2-d4

*******************************************************************************
*                                                                             * 
*       dobuild                                                               *
*                                                                             *
*       This routine is called by Check_RawKey_Event to perform a general     *
*       call to the Build_PointerPos_Event routine.                           *
*                                                                             *
*       Input Registers:                                                      *
*           a3 - input event to attach new events to                          *
*           a4 - global work area                                             *
*                                                                             *
*       Output Registers:                                                     *
*           a0 - new input event                                              *
*           a1 - original input event                                         *
*                                                                             *
*******************************************************************************
dobuild     move.l  a3,a1                   ; pass original input event
db010       lea     gb_ppos2(gbl),a0        ; point to our input event area
            jsr     Build_PointerPos_Event  ; go create our input events
            rts                             ; return to caller

*******************************************************************************
*                                                                             * 
*       Verify_Item                                                           *
*                                                                             *
*       This routine is called by Check_RawKey_Event to verify if a menu or   *
*       menu item is a member of a provided list of menu/menu items.          *
*                                                                             *
*       Input Registers:                                                      *
*           a0 - menu/menu item list                                          *
*           a1 - item to be verified.                                         *
*                                                                             *
*       Output Registers:                                                     *
*           d0 - 0=no match, 1=match                                          *
*                                                                             *
*******************************************************************************
Verify_Item movem.l viregs,-(sp)
vi010       cmp.l   a0,a1                   ; does this item match ?
            beq     vi015                   ; yes, branch
            move.l  im_NextItem(a0),d0      ; get next item's address
            beq     vi020                   ; entire list scanned, branch
            move.l  d0,a0                   ; use next item's address
            bra     vi010                   ; loop to check next item
vi015       moveq.l #1,d0                   ; indicate that the item exists
vi020       movem.l (sp)+,viregs
            rts
viregs      reg     a0/a1

*******************************************************************************
*                                                                             * 
*       Select_Item                                                           *
*                                                                             *
*       This routine is called to select a menu, menu item or menu subitem.   *
*       Given a list of items to choose from, the attribute of the item       *
*       desired and the current item; this routine attempts to find a new     *
*       item that matches the desired attribute. For example, if d0 contains  *
*       's_up' the list of items provided is scanned to find the closest item *
*       that is physically above the current item on the display.             *
*                                                                             *
*       Input Registers:                                                      *
*           a0 - menu/menu item/menu subitem list                             *
*           a1 - current item                                                 *
*           d0 - type of item desired in relation to current item             *
*                                                                             *
*       Output Registers:                                                     *
*           d0 - selected item (or current item if no item was selected)      *
*                                                                             *
*******************************************************************************
Select_Item

select_type equr    d0                      ; select type & return value
item        equr    a0                      ; item list
currentitem equr    a1                      ; currentitem
returnitem  equr    a2                      ; return item
work        equr    d1                      ; work register

*-----------------------------------------------------------------------------*
*           'returnitem' contains the address of the item that is being       *
*           considered for return to the calling routine. Here we create a    *
*           dummy item on the stack that contains extreme values for topedge  *
*           and leftedge. The address of this entry is loaded into            *
*           'returnitem' to give us an initial entry to compare against.      *
*           For example, if 's_leftmost' is the attribute of the item that we *
*           are searching for, the leftedge field in the dummy entry would    *
*           contain 32768. In an effort to find the 'leftmost' item in the    *
*           list, the 'si_leftmost' portion of this routine will compare the  *
*           leftedge of each item in the list with the leftedge of            *
*           'returnitem'. When an item is found with a leftedge that is less  *
*           than returnitem's, its address is placed in returnitem. When the  *
*           end of the list is reached, the item with the lowest leftedge     *
*           should be contained in 'returnitem'                               *
*-----------------------------------------------------------------------------*
            link    a5,#-im_size
            movem.l siregs,-(sp)
            move.w  #32767,work
            cmp.w   #s_leftmost,select_type
            ble     si010
            move.w  #-32768,work
si010       lea     -im_size(a5),returnitem          
            move.w  work,im_TopEdge(returnitem) ; setup dummy item
            move.w  work,im_LeftEdge(returnitem)
            bra     si_jmp                  ; go to computed jump
si_adjacent_up
            move.w  im_LeftEdge(item),work
            cmp.w   im_LeftEdge(currentitem),work
            bne     si_check_end
si_up       move.w  im_TopEdge(item),work
            cmp.w   im_TopEdge(currentitem),work
            ble     si_check_end
si_top
si_adjacent_top
            move.w  im_TopEdge(item),work
            cmp.w   im_TopEdge(returnitem),work
            bge     si015
            move.l  item,returnitem
si015       cmp.w   #s_adjacent_top,select_type
            bne     si_check_end
            move.w  im_TopEdge(item),work
            cmp.w   im_TopEdge(returnitem),work
            bne     si_check_end
            move.w  im_LeftEdge(item),work
            cmp.w   im_LeftEdge(returnitem),work
            bge     si_check_end
            move.l  item,returnitem
            bra     si_check_end
si_adjacent_right
            move.w  im_TopEdge(item),work
            cmp.w   im_TopEdge(currentitem),work
            bne     si_check_end
si_right    move.w  im_LeftEdge(item),work
            cmp.w   im_LeftEdge(currentitem),work
            ble     si_check_end
si_leftmost move.w  im_LeftEdge(item),work
            cmp.w   im_LeftEdge(returnitem),work
            bge     si_check_end
            move.l  item,returnitem
            bra     si_check_end
si_adjacent_down
            move.w  im_LeftEdge(item),work
            cmp.w   im_LeftEdge(currentitem),work
            bne     si_check_end
si_down     move.w  im_TopEdge(item),work
            cmp.w   im_TopEdge(currentitem),work
            bge     si_check_end
si_bottom   move.w  im_TopEdge(item),work
            cmp.w   im_TopEdge(returnitem),work
            ble     si_check_end
            move.l  item,returnitem
            bra     si_check_end
si_adjacent_left
            move.w  im_TopEdge(item),work
            cmp.w   im_TopEdge(currentitem),work
            bne     si_check_end
si_left     move.w  im_LeftEdge(item),work
            cmp.w   im_LeftEdge(currentitem),work
            bge     si_check_end
si_rightmost
            move.w  im_LeftEdge(item),work
            cmp.w   im_LeftEdge(returnitem),work
            ble     si_check_end
            move.l  item,returnitem
si_check_end
            move.l  im_NextItem(item),item
            move.l  item,work               ; set cc
            beq     si_end
si_jmp      jmp     (pc,d0.w)               ; jump to proper routine
si_end      lea     -im_size(a5),item
            cmp.l   item,returnitem         ; still pointing to dummy item ?
            bne     si_return               ; no, branch
            move.l  currentitem,returnitem  ; yes, pass currentitem back
si_return   move.l  returnitem,d0           ; set return 
            movem.l (sp)+,siregs            ; restore regs
            unlk    a5                      ; remove area from stack
            rts                             ; return to caller
siregs      reg     d1/a0-a2

*******************************************************************************
*                                                                             * 
*       Insert_PointerPos_Event                                               *
*                                                                             *
*       This routine creates a pointerpos event and attaches it to the input  *
*       event provided.                                                       *
*                                                                             *
*       Input Registers:                                                      *
*           a0 - new event to be inserted                                     *
*           a1 - input event to attach new event to                           *
*           d0 - Mouse X coordinate to place in new event                     *
*           d1 - Mouse Y coordinate to place in new event                     *
*                                                                             *
*       Output Registers:                                                     *
*           none.                                                             *
*                                                                             *
*******************************************************************************
Insert_PointerPos_Event
            move.b  #IECLASS_POINTERPOS,ie_Class(a0) ; build mousemove event
            move.w  #IECODE_NOBUTTON,ie_Code(a0)
            move.w  d0,ie_X(a0)             ; set coordinates
            move.w  d1,ie_Y(a0)
            bra     ire010

*******************************************************************************
*                                                                             * 
*       Insert_RawMouse_Event                                                 *
*                                                                             *
*       This routine creates a rawmouse event and attaches it to the input    *
*       event provided.                                                       *
*                                                                             *
*       Input Registers:                                                      *
*           a1 - input event to attach new event to                           *
*           d0 - type of rawmouse event                                       *
*                                                                             *
*       Output Registers:                                                     *
*           none.                                                             *
*                                                                             *
*******************************************************************************
Insert_RawMouse_Event
            lea     gb_rawm(gbl),a0
            move.b  #IECLASS_RAWMOUSE,ie_Class(a0) ; build rawmouse event
            move.w  d0,ie_Code(a0)
            clr.w   ie_X(a0)
            clr.w   ie_Y(a0)
ire010      clr.b   ie_SubClass(a0)
            clr.w   ie_Qualifier(a0)
            move.l  ie_NextEvent(a1),ie_NextEvent(a0) ; chain to original event
            move.l  a0,ie_NextEvent(a1)
            rts                             ; return to caller

*******************************************************************************
*                                                                             * 
*       Build_PointerPos_Event                                                *
*                                                                             *
*       This routine creates pointer position event(s) and attaches them to   *
*       the input event provided. The resolution of the current screen is     *
*       taken into account when generating the events. The coordinates of the *
*       pointer position events are alway represented in the highest          *
*       resolution i.e. 640x400. The coordinate fields of menus, menu items,  *
*       etc. are represented in the resolution of the screen to which the     *
*       menu is attached. When we position the pointer on anything but a      *
*       hires interlace screen, we must adjust the coordinates that are taken *
*       from the menus to allow for this.                                     *
*                                                                             *
*       Input Registers:                                                      *
*           a0 - address of input event to be built                           *
*           a1 - original input event                                         *
*           d0 - number of input events to generate.                          *
*                                                                             *
*       Output Registers:                                                     *
*           none.                                                             *
*                                                                             *
*******************************************************************************
Build_PointerPos_Event

iheight     equr    d1
iwidth      equr    d1
itopedge    equr    d2
ileftedge   equr    d2
mheight     equr    d3
mwidth      equr    d3
mtopedge    equr    d4
mleftedge   equr    d4
offsetx     equr    d5
offsety     equr    d6

            movem.l breregs,-(sp)           ; save registers
            jsr     Insert_PointerPos_Event   ; go insert new event
            move.l  gb_window(gbl),a2       ; get current window
            move.l  wd_WScreen(a2),a2       ; get window's screen
            move.l  gb_currentmenu(gbl),a3  ; get current menu
            move.w  mu_TopEdge(a3),mtopedge ; get menu's topedge
            add.w   sc_ViewPort+vp_DyOffset(a2),mtopedge ; add screen's offset
            swap    mtopedge
            move.w  mu_LeftEdge(a3),mleftedge ; get menu's leftedge
            add.w   sc_ViewPort+vp_DxOffset(a2),mleftedge ; add screen's offset
            clr.l   mheight
            move.b  sc_BarHeight(a2),mheight ; get menu's height
            swap    mheight
            move.w  mu_Width(a3),mwidth     ; get menu's width
            move.l  gb_currentitem(gbl),a3  ; get current menuitem
            move.w  mi_TopEdge(a3),itopedge ; get menuitem's topedge
            swap    itopedge
            move.w  mi_LeftEdge(a3),ileftedge ; get menuitem's leftedge
            move.w  mi_Height(a3),iheight   ; get menuitem's height
            swap    iheight
            move.w  mi_Width(a3),iwidth     ; get menuitem's width
            move.l  gb_currentsubitem(gbl),a5 ; is there a subitem ?
            move.l  a5,d5                   ; set cc
            beq     bre010                   ; no, branch
            add.w   mi_LeftEdge(a5),ileftedge ; yes, add subitem's leftedge
            move.w  mi_Width(a5),iwidth     ; use subitem's width
            swap    ileftedge
            add.w   mi_TopEdge(a5),itopedge ; add subitem's topedge
            swap    itopedge
            swap    iwidth
            move.w   mi_Height(a5),iheight  ; use subitem's height
            swap    iheight
            bra     bre025
bre010      move.l  mi_SubItem(a3),a5       ; is there a subitem for this item ?
            move.l  a5,d5                   ; set cc
            beq     bre025                  ; no, branch
            move.w  mi_LeftEdge(a5),d5      ; yes, get subitem's leftedge
            cmp.w   mi_LeftEdge(a3),d5      ; is it's leftedge less than item's?
            bge     bre020                  ; no, branch
            add.w   d5,ileftedge            ; yes, add subitems leftedge
            add.w   mi_Width(a5),ileftedge  ; add subitem's width
            move.w  mi_LeftEdge(a3),iwidth  ; get items leftedge
            add.w   mi_Width(a3),iwidth     ; add its width
            move.l  ileftedge,d5            ; get item's leftedge
            bge     bre015                  ; if its positive, branch
            neg     d5                      ; make it positive
bre015      sub.w   d5,iwidth
            bra     bre025
bre020      movem.l d0/a0,-(sp)             ; save our registers
            move.l  a5,a0                   ; pass subitem pointer
            move.l  #s_adjacent_top,d0      ; set select type
            jsr     Select_Item
            move.l  d0,a5                   ; get selected item
            movem.l (sp)+,d0/a0             ; restore our registers
            move.w  mi_LeftEdge(a5),iwidth
            sub.w   #1,iwidth
bre025      move.w  sc_ViewPort+vp_Modes(a2),d5 ; get screen modes
            btst.l  #V_HIRES_B,d5           ; is this a hires screen ?
            bne     bre030                  ; yes, branch
            asl.w   #1,iwidth               ; no, adjust coordinates for hires
            asl.w   #1,ileftedge
            asl.w   #1,mwidth
            asl.w   #1,mleftedge
bre030      btst.l  #V_LACE_B,d5            ; is this an interlace screen ?
            bne     bre035                  ; yes, branch
            swap    iwidth                  ; no, adjust coordinates for interlace
            swap    ileftedge
            swap    mwidth
            swap    mleftedge
            asl.w   #1,iheight
            asl.w   #1,itopedge
            asl.w   #1,mheight
            asl.w   #1,mtopedge
            swap    iheight
            swap    itopedge
            swap    mheight
            swap    mtopedge
bre035      move.l  gb_IBase(gbl),a5        ; get intuition base
            addq.w  #1,mleftedge
            move.w  mleftedge,offsetx       ; get menu leftedge
            move.w  offsetx,ie_X(a0)        ; update event's X coordinate
            swap    mleftedge
            addq.w  #1,mtopedge
            move.w  mtopedge,offsety        ; get menu topedge
            move.w  offsety,ie_Y(a0)        ; update event's Y coordinate
            asr.w   #1,mwidth               ; divide menu width by 2
            move.w  ileftedge,offsetx       ; get item leftedge
            asr.w   #1,iwidth               ; divide item width by 2
            add.w   iwidth,offsetx          ; add it to offsetx
            swap    mwidth
            move.w  mheight,offsety         ; get menu height
            asr.w   #1,mheight              ; divide menu height by 2
            swap    mheight
            swap    ileftedge
            add.w   itopedge,offsety        ; add item topedge
            swap    iwidth
            asr     #1,iheight              ; divide item height by 2
            add.w   iheight,offsety         ; add to offsety
            cmp.w   #1,d0                   ; one event to be generated ?
            bne     bre040                  ; no, branch
            add.w   offsetx,ie_X(a0)        ; adjust mouse x coordinate
            add.w   offsety,ie_Y(a0)        ; adjust mouse y coordinate
            bra     bre045
bre040      cmp.w   #3,d0                   ; go to saved mouse position ?
            bne     bre042                  ; no, branch
            move.w  gb_old_MouseX(gbl),offsetx
            move.w  gb_old_MouseY(gbl),offsety
            bra     bre044
bre042      add.w   ie_X(a0),offsetx
            add.w   ie_Y(a0),offsety
bre044      add.w   mwidth,ie_X(a0)         ; add menu width to current item
            swap    mwidth
            add.w   mheight,ie_Y(a0)        ; add menu height to current item
            move.l  a0,a1                   ; event to attach new event to
            lea     gb_ppos1(gbl),a0        ; new event area
            move.w  offsetx,d0
            move.w  offsety,d1
            jsr     Insert_PointerPos_Event   ; go add the event to the chain
bre045      bset.b  #FLAGB_Events_inserted,gb_flags(gbl) ; indicate events added
            movem.l (sp)+,breregs
            rts                             ; return to caller
breregs     reg     a0-a5/d1-d6

            end
