*
*   Standard Application Shell  - Assembly Version
*   By: Apple II Developer Technical Support
*
*     v3.0    Mensch & Rollin, Deatherage & Luther
*
*    Developer Technical Support Apple II Sample Code
*
*    Copyright (c) Apple Computer, Inc. 1988-1990
*               All Rights Reserved
*
*   ------------------------------------------------------
*
*   This program and its derivatives are licensed only for
*   use on Apple computers.
*
*   Works based on this program must contain and
*   conspicuously display this notice.
*
*   This software is provided for your evaluation and to
*   assist you in developing software for the Apple IIGS
*   computer.
*
*   DISCLAIMER OF WARRANTY
*
*   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT
*   WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
*   WITH RESPECT TO ITS MERCHANTABILITY OR ITS FITNESS
*   FOR ANY PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO
*   THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
*   YOU.  SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU (AND
*   NOT APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE)
*   ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING,
*   REPAIR OR CORRECTION.
*
*   Apple does not warrant that the functions
*   contained in the Software will meet your requirements
*   or that the operation of the Software will be
*   uninterrupted or error free or that defects in the
*   Software will be corrected.
*
*   SOME STATES DO NOT ALLOW THE EXCLUSION
*   OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY
*   NOT APPLY TO YOU.  THIS WARRANTY GIVES YOU SPECIFIC
*   LEGAL RIGHTS AND YOU MAY ALSO HAVE OTHER RIGHTS
*   WHICH VARY FROM STATE TO STATE.
*
                    EJECT

                    mcopy shell.macros
                    copy 2/ainclude/E16.Types
                    copy 2/ainclude/E16.GSOS
                    copy 2/ainclude/E16.Locator
;                   copy 2/ainclude/E16.ADB
                    copy 2/ainclude/E16.IntMath
                    copy 2/ainclude/E16.TextTool
                    copy 2/ainclude/E16.Memory
;                   copy 2/ainclude/E16.SANE
;                   copy 2/ainclude/E16.ACE
;                   copy 2/ainclude/E16.Resources
                    copy 2/ainclude/E16.MiscTool
;                   copy 2/ainclude/E16.Scheduler
;                   copy 2/ainclude/E16.Loader
                    copy 2/ainclude/E16.Quickdraw
;                   copy 2/ainclude/E16.QDAux
                    copy 2/ainclude/E16.Event
;                   copy 2/ainclude/E16.Control
                    copy 2/ainclude/E16.Window
                    copy 2/ainclude/E16.Menu
;                   copy 2/ainclude/E16.LineEdit
;                   copy 2/ainclude/E16.Dialog
;                   copy 2/ainclude/E16.Sound
;                   copy 2/ainclude/E16.NoteSyn
;                   copy 2/ainclude/E16.NoteSeq
;                   copy 2/ainclude/E16.MIDI
;                   copy 2/ainclude/E16.StdFile
;                   copy 2/ainclude/E16.Scrap
                    copy 2/ainclude/E16.Desk
;                   copy 2/ainclude/E16.List
;                   copy 2/ainclude/E16.Font
;                   copy 2/ainclude/E16.Print
;                   copy 2/ainclude/E16.TextEdit
;                   copy 2/ainclude/E16.Video

*******************************************************************************
*
*    Equates used in this program.
*

; Offsets into direct page
Deref               gequ 0              ; for temporary dereferencing

; menu item numbers for standard DA menu items
UndoID              gequ 250
CutID               gequ 251
CopyID              gequ 252
PasteID             gequ 253
ClearID             gequ 254
CloseID             gequ 255

; application menu item numbers
AboutID             gequ $1101           ; 1st item of 1st menu of 1st menu bar
QuitID              gequ $1202           ; 2nd item of 2nd menu of 1st menu bar

; application menu numbers
AppleMenuID         gequ $1100           ; 1st menu of 1st menu bar
FileMenuID          gequ $1200           ; 2nd menu of 1st menu bar
EditMenuID          gequ $1300           ; 3rd menu of 1st menu bar

; resource ID numbers
BaseResID           gequ $00000000       ; start of resource ID numbers
MenuBarOneRID       gequ $00001000       ; resource ID of menu bar

MyTaskMask          gequ $001FFFFF       ; Handle all events possible

                    EJECT
*******************************************************************************
*
main                START
*
* Description:      This is the main routine. It calls procedures to startup
*                   the tools, initialize application specific data, run the
*                   main eventLoop, close the application, and shutdown the
*                   tools.  Then it calls the GS/OS Quit command.
*
* Inputs:           NONE
* Outputs:          NONE
* External Refs:
*                   import initTools
*                   import initApp
*                   import eventLoop
*                   import closeApp
*                   import closeTools
*                   import QuitParms
* Entry Points:     NONE
*
                    using globals

                    jsr initTools       ; Initialize tools.
                    jsr initApp         ; Initialize application specific
;                                         stuff.

                    jsr eventLoop       ; Do application stuff until user 
;                                         wants to do something else!
                    
                    jsr closeApp        ; ShutDown application specific things.
                    jsr closeTools      ; ShutDown the tools.

                    _QuitGS QuitParms   ; and Quit
                    END

                    EJECT
*******************************************************************************
*
Globals             DATA
*
* Description:      Holder of all of our data.
*
* Inputs:           N/A
* Outputs:          N/A
* External Refs:    NONE
* Entry Points:
*                   export QuitParms    ; used by main
*

; Standard global variables here
MyMemoryID          ds  2               ; Application's memory ID
Done                ds  2               ; flag to show when to quit application
ToolRecRef          ds  4               ; StartStopRec from StartUpTools
WindowKind          ds  2               ; type of top window from GetWKind call
MenuHeight          ds  2               ; Stored height of menu bar
QuitParms           dc  i2'$00'         ; pCount = 0

; The following is the record that is used by TaskMaster to return
; events. It is similar to a regular event record, except that there are
; additional fields at the end. The first additional field is used to convey
; some TaskMaster specific data back to the application. The second additional
; field is called the TaskMask and is used to tell TaskMaster what situations
; to handle.  In this shell, we tell TaskMaster to handle everything by
; setting all currently defined bits to 1 (MyTaskMask) in the initApp
; routine.

MyEvent             anop
EventWhat           ds  2
EventMessage        ds  4
EventWhen           ds  4
EventWhere          ds  4
EventModifiers      ds  2
TaskData            ds  4
TaskMask            dc  i4'MyTaskMask'
LastClickTick       ds  4
ClickCount          ds  2
TaskData2           ds  4
TaskData3           ds  4
TaskData4           ds  4
LastClickPtY        ds  2
LastClickPtX        ds  2

                    END

                    EJECT
*******************************************************************************
*
initTools           START
*
* Description:      Load and startup the tools needed. Errors are detected
*                   and fatalError is called if any occur.
*
* Inputs:           NONE
* Outputs:          NONE
* External Refs:
*                   import fatalError
* Entry Points:     NONE
*
                    using Globals

                    phk                 ; save program bank register and
                    plb                 ; load it as the data bank register

                    _TLStartup          ; start up Tool Locator
                    bcc initMM
                    ldx #$1             ; if error, then set the error
;                                         reference number
                    jmp fatalError      ; and jump to fatal routine

initMM              pha                 ; space for result
                    _MMStartup          ; start up memory manager
                    bcc getMMID
                    ldx #$2             ; if error, then set the error
;                                         reference number
                    jmp fatalError      ; and jump to fatal routine

getMMID             PullWord MyMemoryID ; Get the Memory ID returned

                    PushLong #$0        ; space for the Record returned
                    PushWord MyMemoryID ; our User ID
                    PushWord #refIsResource ; the StartStopRec is a resource
                    PushLong #BaseResID+1 ; the actual resource ID
                    _StartUpTools
                    bcc getToolRecRef
                    ldx #$3             ; if error, then set the error
;                                         reference number
                    jmp fatalError      ; and jump to fatal routine

getToolRecRef       PullLong ToolRecRef ; Store the reference returned

                    rts
                    END

                    EJECT
*******************************************************************************
*
fatalError          START
*
* Description:      Routine that is called whenever an error is detected
*                   that can not be recovered from. This routine prints an
*                   error message, waits for a keypress, then quits back
*                   to whoever started this application up!
*
* Inputs:           A = Error number
*                   X = the reference number that tells you where in
*                   initTools the error happened.
* Outputs:          NONE (program exits)
* External Refs:
*                   import closeTools
* Entry Points:     NONE
*
                    using Globals

                    phx                 ; save the "where" reference number
                    
                    pha                 ; Push the error code
                    PushLong #errNumStr ; Address of storage area
                    PushWord #$4        ; Length of string
                    _Int2Hex

                    pla                 ; get the "where" reference number
                    pha                 ; and push it
                    PushLong #errWhereStr ; Address of storage area
                    PushWord #$4        ; Length of string
                    _Int2Hex
                    
                    _GrafOff            ; If QD Started, shut off graphics

                    PushLong #ErrStr    ; Write error message to the screen
                    _WriteCString
                    
                    _SysBeep            ; ring the bell

                    pha                 ; Space for result
                    PushWord #noEcho    ; No echo
                    _ReadChar           ; Wait for a key to be pressed
                    pla                 ; Discard the key

                    jsr closeTools      ; Shut down all the tools in case
;                                         any got started up

                    _QuitGS QuitParms   ; Quit back to where we came from.

; Quit can only fail with GS/OS busy, so we never come back from the quit call.
                    
ErrStr              dc  c'Fatal error $'
ErrNumStr           dc  c'xxxx has occured at '
ErrWhereStr         dc  c'xxxx. Press any key to exit:',i1'0'

                    END

                    EJECT
*******************************************************************************
*
initApp             START
*
* Description:      Perform any application specific initialization. For this
*                   app, we initialize the Done to false, set WindowKind to
*                   an application window kind, and initialize all of the
*                   menus.
*
*                   You might use this procedure to create windows, initialize
*                   variables and allocate memory needed for the entire
*                   program.
*
* Inputs:           NONE
* Outputs:          NONE
* External Refs:    NONE
* Entry Points:     NONE
*
                    using Globals

                    stz Done            ; we aren't done yet
                    stz WindowKind      ; window kind = application

;
; create default system menu bar from a resource and make it the
; current menu bar
;
                    pha
                    pha                 ; space for result
                    PushWord #refIsResource ; reference is resource
                    PushLong #MenuBarOneRID ; menu bar resource ID
                    PushLong #$0        ; system menu bar
                    _NewMenuBar2
                                        
                    _SetSysBar          ; and this is the system bar
                    
                    PushLong #$0        ; zero means use the system menu bar
                    _SetMenuBar
                    
                    PushLong #$0        ; refresh the whole desktop
                    _RefreshDeskTop

                    _InitCursor         ; normal arrow cursor

                    PushWord #AppleMenuID ; Add NDA's
                    _FixAppleMenu

                    pha                 ; Call FixMenuBar to cause the menu
                    _FixMenuBar         ; Compute standard menu sizes
                    PullWord MenuHeight ; and store the menu height for later

                    _DrawMenuBar        ; Finally, draw it.

                    rts
                    END

                    EJECT
*******************************************************************************
*
eventLoop           START
*
* Description:      The Event Loop. Handle things until user selects Quit.
*
* Inputs:           NONE
* Outputs:          NONE
* External Refs:
*                   import doMenu
*                   import testTopWindow
* Entry Points:     NONE
*
                    using Globals

                    jsr testTopWindow   ; test to see if top window is a DA
                    
                    pha                 ; Push on space for TaskMaster result
                    PushWord #EveryEvent ; GetNextEvent mask
                    PushLong #MyEvent   ; Pointer to Event Record
                    _TaskMaster
                    pla                 ; Get TaskMaster result
                    
; Now we have the task type

                    ldx #endTaskTable-taskTable-4
checkNextItem       cmp taskTable,x     ; are we handling this event?
                    beq doRoutine       ; yes, then call a routine
                    dex                 ; no, then keep checking
                    dex
                    dex
                    dex
                    bpl checkNextItem   ; keep scanning table

                    bra eventLoop       ; event not handled, so get another

doRoutine           jsr (taskTable+2,x) ; call the routine

                    lda Done            ; Quit selected?
                    beq eventLoop       ; no - keep looping

                    rts                 ; yes- leave the program

; With most of these events, we do nothing (in fact, most applications will
; never see some of these events). You should cut or comment out the lines
; for events your application does not use out of this table (as we've done).
; Any of these events your application does use should call a routine to
; handle the event.

taskTable           anop
;                   dc  i2'nullEvt,doNullEvt'
;                   dc  i2'mouseDownEvt,doMouseDownEvt'
;                   dc  i2'mouseUpEvt,doMouseUpEvt'
;                   dc  i2'keyDownEvt,doKeyDownEvt'
;                   dc  i2'autoKeyEvt,doAutoKeyEvt'
;                   dc  i2'updateEvt,doUpdateEvt'
;                   dc  i2'activateEvt,doActivateEvt'
;                   dc  i2'switchEvt,doSwitchEvt'
;                   dc  i2'deskAccEvt,doDeskAccEvt'
;                   dc  i2'driverEvt,doDriverEvt'
;                   dc  i2'app1Evt,doApp1Evt'
;                   dc  i2'app2Evt,doApp2Evt'
;                   dc  i2'app3Evt,doApp3Evt'
;                   dc  i2'app4Evt,doApp4Evt'
;                   dc  i2'wInDesk,doWInDesk'
                    dc  i2'wInMenuBar,doMenu'
;                   dc  i2'wClickCalled,doWClickCalled'
;                   dc  i2'wInContent,doWInContent'
;                   dc  i2'wInDrag,doWInDrag'
;                   dc  i2'wInGrow,doWInGrow'
;                   dc  i2'wInGoAway,doWInGoAway'
;                   dc  i2'wInZoom,doWInZoom'
;                   dc  i2'wInInfo,doWInInfo'
                    dc  i2'wInSpecial,doMenu'
;                   dc  i2'wInDeskItem,doWInDeskItem'
;                   dc  i2'wInFrame,doWInFrame'
;                   dc  i2'wInactMenu,doWInactMenu'
;                   dc  i2'wClosedNDA,doWClosedNDA'
;                   dc  i2'wCalledSysEdit,doWCalledSysEdit'
;                   dc  i2'wTrackZoom,doWTrackZoom'
;                   dc  i2'wHitFrame,doWHitFrame'
;                   dc  i2'wInControl,doWInControl'
;                   dc  i2'wInControlMenu,doWInControlMenu'
endTaskTable        anop
                    END

                    EJECT
*******************************************************************************
*
closeApp            START
*
* Description:      Close down things. This disposes of all items and memory
*                   that we allocated. Usually undoes what was done in
*                   initApp.  We don't close our window since _WindShutDown
*                   does it for us.
*
* Inputs:           NONE
* Outputs:          NONE
* External Refs:    NONE
* Entry Points:     NONE
*
                    using Globals
                    
; do nothing in this shell

                    rts
                    END

                    EJECT
*******************************************************************************
*
closeTools          START
*
* Description:      Shut down the tools I started.
*
* Inputs:           NONE
* Outputs:          NONE
* External Refs:    NONE
* Entry Points:     NONE
*
                    using Globals

                    PushWord #refIsHandle ; A handle (since StartUp returned it)
                    PushLong ToolRecRef ; The value returned by StartUpTools
                    _ShutDownTools
                    
                    PushWord MyMemoryID
                    _MMShutDown
                    _TLShutDown

                    rts
                    END

                    EJECT
*******************************************************************************
*
testTopWindow       START
*
* Description:      This routine is called on every NULL event. It checks the
*                   type of the top window, if it has changed from App window
*                   to system window or back, this routine will call
*                   doSysChange.
*
* Inputs:           NONE
* Outputs:          NONE
* External Refs:
*                   import doSysChange
* Entry Points:     NONE
*
                    using Globals
                    
                    PushLong #$0        ; first get a pointer to the frontmost
                    _FrontWindow        ; window
                    PullLong Deref      ; store it in deref temporarily
                    
                    lda Deref           ; first test for a NIL pointer. If its
                    ora Deref+2         ; NIL then we do not have to do the 
                    beq checkKind       ; getWKind call
                    
                    PushWord #$0        ; if not a NIL pointer then get the
                    PushLong Deref      ; Kind of window
                    _getWKind
                    pla
                    
checkKind           cmp WindowKind      ; Now test the window kind to see if it
                    beq noChange        ; has changed, if not then ignore it
                    
                    sta WindowKind      ; if the window kind has changed, then
                    jsr doSysChange     ; save the new value and change menus

noChange            rts
                    End

                    EJECT
*******************************************************************************
*
doSysChange         START
*
* Description:      Called by eventLoop when the upper most window
*                   has changed to or from a system window.
*
* Inputs:           <A> Bit 15 of WindowKind is 0 if top window is an
*                   application window, 1 if top window is a system window.
* Outputs:          NONE
* External Refs:    NONE
* Entry Points:     NONE
*
                    using Globals

                    and #%1000000000000000
                    bpl SysDeact        ; if high bit clear then App Window

; This routine enables the edit menu when a system window becomes active

SysActivate         PushWord #UndoID    ; its an activate event so go thru and
                    _EnableMItem        ; enable all the edit items and the
                    
                    PushWord #CutID     ; close Item
                    _EnableMItem
                    
                    PushWord #CopyID
                    _EnableMItem
                    
                    PushWord #PasteID
                    _EnableMItem
                    
                    PushWord #ClearID
                    _EnableMItem
                    
                    PushWord #CloseID
                    _EnableMItem
                    
; If your edit menu has items that are selectable when a DA is not front-most,
; remove the next six lines.
                    PushWord #enableMenu
                    PushWord #EditMenuID
                    _SetMenuFlag
                    pea $0000           ; FALSE
                    PushWord #EditMenuID
                    _HiliteMenu
                    
                    rts

; This routine disables the edit menu when an system window is not active

SysDeact            PushWord #UndoID    ; its an deactivate event so go thru 
                    _DisableMItem       ; and disable all the edit items and 
                    
                    PushWord #CutID     
                    _DisableMItem
                    
                    PushWord #CopyID
                    _DisableMItem
                    
                    PushWord #PasteID
                    _DisableMItem
                    
                    PushWord #ClearID
                    _DisableMItem

                    PushWord #CloseID   ; the close Item
                    _DisableMItem

; If your edit menu has items that are selectable when a DA is not front-most,
; remove the next six lines.
                    PushWord #disableMenu
                    PushWord #EditMenuID
                    _SetMenuFlag
                    pea $0000           ; FALSE
                    PushWord #EditMenuID
                    _HiliteMenu
                    
                    rts
                    END

                    EJECT
*******************************************************************************
*
doMenu              START
*
* Description:      This routine is called when TaskMaster returns a menu
*                   event. It takes the menu item that was hit and calls the
*                   proper routine, and then unhilites the menu when it is
*                   done. 
*
* Inputs:           TaskData holds menu item selected.
* Outputs:          NONE
* External Refs:
*                   import doAbout
*                   import doQuit
* Entry Points:     NONE
*
                    using Globals
                    
                    lda TaskData        ; get ID of selected menu item

                    ldx #endMenuTable-menuTable-4
checkNextItem       cmp menuTable,x
                    beq doRoutine
                    dex
                    dex
                    dex
                    dex
                    bpl checkNextItem

                    pha                 ; space for result
                    PushWord #$4        ; reference is resource
                    PushLong #$0        ; no substitution strings currently
                    PushLong #BaseResID+2 ; alertString resource ID
                    _AlertWindow
                    pla                 ; get the button number and dispose
                    bra finishUp

doRoutine           jsr (menuTable+2,x) ; and call the routine

finishUp            PushWord #$0        ; Routine done - unhilite the menubar.
                    PushWord taskData+2 ; the menu number
                    _HiLiteMenu

                    rts

; Menu items that are ignored in this shell are included, but commented out.

menuTable           anop
;                   dc  i2'UndoID,doUndo'
;                   dc  i2'CutID,doCut'
;                   dc  i2'CopyID,doCopy'
;                   dc  i2'PasteID,doPaste'
;                   dc  i2'ClearID,doClear'
;                   dc  i2'CloseID,doClose' ; close handled by TaskMaster
                    dc  i2'AboutID,doAbout' ; show about dialog
                    dc  i2'QuitID,doQuit'   ; set Done flag
endMenuTable        anop

                    END

                    EJECT
*******************************************************************************
*
doAbout             START
*
* Description:      Bring up an Alert Dialog box with our about message in it.
*
* Inputs:           NONE
* Outputs:          NONE
* External Refs:    NONE
* Entry Points:     NONE
*
                    using Globals

                    pha                 ; space for result
                    PushWord #$4        ; reference is resource
                    PushLong #$0        ; no substitution strings currently
                    PushLong #BaseResID+1 ; alertString resource ID
                    _AlertWindow
                    pla                 ; get the button number and dispose

                    rts
                    END

                    EJECT
*******************************************************************************
*
doQuit              START
*
* Description:      Set the Done flag to true. This tells the Event loop to
*                   exit.
*
* Inputs:           NONE
* Outputs:          Done set to $FFFF (true)
* External Refs:    NONE
* Entry Points:     NONE
*
                    using Globals

                    lda #$FFFF          ; load any non-zero value
                    sta Done            ; and save it in the Done
                    rts
                    END
