*******************************************************************************
*
* BoxCtl CDEFProc -- Version 3.0
*
* (C)  Copyright Apple Computer, Inc. 1988-1990
* All rights reserved.
*
* Developer Technical Support Apple II Sample Code
*
* by Keith Rollin
*
* This is a Custom Control for the Apple IIGS. It is similar in appearance
* and action to the 'resize' box you get when clicking on an object in GS Draw
* or MacDraw.
*
* The Control is essentially a frame with 4 or 8 grow knobs on the corners
* and/or edges. Dragging on any of these knobs will grow the control. Dragging
* on the frame will move the entire control (like moving a window). A flag can
* be set so that the control will be dragged if the user clicks in the interior
* of the control as well. Part codes for the knobs and frame are listed below.
*
* Snapping the rectangle's coordinates to a grid is also supported. The
* rectangle is snapped to the nearest point on the grid. This effectively means
* that I perform rounding, and not truncating.
*
* The frame of the control is the rectangle passed to NewControl. The size of
* the knobs and the size of the grid are passed in ctlValue. The 'interior drag
* flag' is passed in ctlFlags. See below for details.
*
* Color is supported. Currently, only two colors are used: one for the frame,
* and one for the knobs. The default colors are black.
*
*******************************************************************************
*
*   Part Code returned:
*
*           $A0 for all parts of the control (all parts are 'indicators')
*
*
*   CtlValue:   Bits  8-15: width of grow knobs
*               Bits  0- 7: height of grow knobs
*
*   CtlFlag bits:
*               7 = 1 - Control is invisible
*                 = 0 - Control is visible
*               2 = 1 - Has edge knobs
*                 = 0 - Doesn't
*               1 = 1 - Has corner knobs
*                 = 0 - Doesn't
*               0 = 1 - Clicking in interior will drag control
*                 = 0 - Clicking in interior does nothing
*
*   Data:   Contains a pointer to the following data block. This data
*           block holds the following information, which is copied to
*           the end of the control record:
*
*               WORD: Min Y when growing
*               WORD: Min X when growing
*               WORD: Max Y when growing
*               WORD: Max X when growing
*               WORD: Spacing for Y segment of grid
*               WORD: Spacing for X segment of grid
*
*   Color Table:
*       WORD: Bits  12-15:  <reserved>
*                    8-11:  Knob Color
*                    4- 7:  Inactive Frame/Knob Color
*                    0- 3:  Frame Color
*
*******************************************************************************
*
*   How to use the Control:
*
*       Initialization and tracking of the control work just like any other;
*       set it up with NewControl. When GetNextEvent returns inContent, call
*       FindControl to see if you hit the control. If you did, call
*       TrackControl.
*
*   Initialization:
*
*               pha                     ; space for result
*               pha
*               PushLong theWindow
*               PushLong #theRect       ; pointer to bounding rectangle
*               PushLong #0             ; no title
*               PushWord #%00000111     ; vis, int will drag, corners & edges
*               PushWord #$0503         ; Width = 5/Height = 3
*               PushLong #CtrlData      ; Pointer to additional data
*               PushLong #BoxProc       ; pointer to DefProc
*               PushLong #0             ; refcon
*               PushLong #0             ; use std color table
*               _NewControl
*               PullLong theControl
*
*   theRect     dc i2''   50,140,110,300
*   CtrlData    dc i2''   10      ; min Y
*               dc i2''   10      ; min X
*               dc i2''   100     ; max Y
*               dc i2''   200     ; max X
*               dc i2''   16      ; grid Y
*               dc i2''   32      ; grid X
*
*******************************************************************************
**********************************************************************
*                                                                    *
*             Apple IIGS Source Code Sampler, Volume I               *
*                                                                    *
*             Copyright (c) Apple Computer, Inc. 1988                *
*                       All Rights Reserved                          *
*                                                                    *
*            Written by Apple II Developer Tech Support              *
*                                                                    *
*                                                                    *
*                                                                    *
*  ----------------------------------------------------------------  *
*                                                                    *
*     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
;-------------------------------------------
;
; --- Constants
;
; --- Stack Frame/Local Direct Page usage
;

OrigD               gequ 1               ; caller's saved direct page register
OrigB               gequ OrigD+2         ; caller's saved data bank register
work                gequ OrigB+1         ; general use work space
CtlPtr              gequ work+4          ; pointer to control record
RtnAddr             gequ CtlPtr+4        ; RTL address back to Control Manager
theCtlHandle        gequ RtnAddr+3       ; handle to control record
CtlParam            gequ theCtlHandle+4  ; add'l parameter passed to DefProc
CtlCode             gequ CtlParam+4      ; operation to perform
ReturnValue         gequ CtlCode+2       ; space for return value to Ctl Mgr.

;
; --- Offsets for my control record
;

oCtlMinY            gequ octlColor+4
oCtlMinX            gequ oCtlMinY+2
oCtlMaxY            gequ oCtlMinX+2
oCtlMaxX            gequ oCtlMaxY+2
oCtlGridY           gequ oCtlMaxX+2
oCtlGridX           gequ oCtlGridY+2
oCtlSize            gequ oCtlGridX+2

;
; --- Part codes for my control
;

BoxCtlPart          gequ $A0             ; This is returned to the app.

FramePart           gequ $01             ; These are used internally to tell
InteriorPart        gequ $02             ; 'myDragCtl' what part of the
ULPart              gequ $03             ; control we are dragging.
TopPart             gequ $04
URPart              gequ $05
RightPart           gequ $06
LLPart              gequ $07
BottomPart          gequ $08
LRPart              gequ $09
LeftPart            gequ $0A


;
; --- Masks for ctlFlags
;

dragIntMask         gequ %00000001       ; if set, let user drag on interior
knobCornerMask      gequ %00000010       ; if set, draw knobs on the corners
knobEdgeMask        gequ %00000100       ; if set, draw knobs on the edges
visMask             gequ ctlInVis

                    EJECT
*******************************************************************************
*
CtlData             data
*
* Description:      Storage for CDEF
*
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************

deltaX              ds 2                ; used when growing the control
deltaY              ds 2
ColorPtr            ds 4                ; pointer to actual color table to use
theParam            ds 4                ; copy of CtlParam passed on stack
dragPart            ds 2                ; internal part of control hit

myCtlRect           ds 8                ; copy of CtlRect in control record
myCtlGridY          ds 2                ; copy of CtlGridY
myCtlGridX          ds 2                ; copy of CtlGridX

FrameHeight         ds 2
FrameWidth          ds 2

knobUL              ds 8                ; This space is used to store the
knobTop             ds 8                ; rectangles that define the grow
knobUR              ds 8                ; knobs on the control. They are
knobRight           ds 8                ; created as needed, and are not
knobLR              ds 8                ; stored in the control record.
knobBottom          ds 8
knobLL              ds 8
knobLeft            ds 8

StdColorTable       dc i1'$C0'          ; grey inact frame, blk normal frame
                    dc i1'$00'          ; black knobs
StdCtlData          dc i2'10,10,200,640,0,0' ; min 10,10; max 200,640; no grid
oldPenState         ds $32              ; storage for old pen state.

GreyPattern         dc i1'$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC'
                    dc i1'$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC'
                    dc i1'$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC'
                    dc i1'$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC'

                    end

                    EJECT
*******************************************************************************
*
BoxProc             start
*
* Description:      Main Routine for Custom Control.
*
*
* Inputs:           On entry, the parameters are passed to us on the stack.
*
*                   |                   | Previous Contents
*                   |-------------------|
*                   |    ReturnValue    | LONG - Space for return value
*                   |-------------------|
*                   |    CtlCode        | WORD - operation to perform
*                   |-------------------|
*                   |    CtlParam       | LONG - add'l parameter
*                   |-------------------|
*                   |    theCtlHandle   | LONG - Handle to control record
*                   |-------------------|
*                   |    RtnAddr        | 3 BYTES - RTL address
*                   |-------------------|
*                   |                   | <-- Stack pointer
*
* Outputs:          Put something into ReturnValue, pull off the parameters,
*                   and return to the Control Manager.
*
*                   |                   | Previous Contents
*                   |-------------------|
*                   |    ReturnValue    | LONG - Space for return value
*                   |-------------------|
*                   |    RtnAddr        | 3 BYTES - RTL address
*                   |-------------------|
*                   |                   | <-- Stack pointer
*
* External Refs:
*                   Import InitCtlData
*                   Import myDrawCtl
*                   Import myTestCtl
*                   Import myInitCtl
*                   Import myDragCtl
*                   Import myNewValue
*                   Import mySetParams
*                   Import myRecSize
*                   Import Null
*
* Entry Points:     NONE
*
*******************************************************************************
                    using CtlData

;
; We are going to be using the stack as our direct page for the
; custom control. The equates listed at the beginning of the custom
; control (right after the pageful of comments that explain it)
; describe the layout of the stack frame.
;
; The way we set up the stack frame as our direct page is by first
; saving the old contents of the DP register, transfering the stack
; pointer to the accumulator, and then transferring that to the DP
; register. We also save the contents of the Data Bank Register and
; reset it to the Program Bank Register so that we don't have to
; use 3-byte long addressing anywhere. We will restore both of these
; registers when we leave the custom control.
;

                    pha                 ; push on some room for 'CtlPtr'
                    pha
                    pha                 ; push on some room for 'work'
                    pha

                    phb                 ; save the Data Bank register
                    phd                 ; save the Direct Page register

                    phk                 ; switch data bank to program bank
                    plb
                    tsc                 ; switch Direct Page into stack
                    tcd

                    jsr InitCtlData     ; init some handy data

                    lda <CtlCode        ; get routine # to call
                    cmp #recSize+1      ; we only know of 12 CtlCodes
                    blt ShiftIt
                    lda #6              ; force unknown codes to null events
ShiftIt             ANOP
                    asl a
                    tax
                    jsr (CtlTable,x)

                    sta <ReturnValue    ; save the return value
                    stx <ReturnValue+2

;
; The Return Value has been stored on the stack, and it is time for us to
; return back to the Control Manager. Before we do so, however, we must
; remove the parameters that were passed to us on the stack. We do this
; by moving the RTL address up just below the Return Value, getting the
; stack pointer, and adding an amount to it so that we point to where
; the RTL address has been moved to. We can then simply RTL back to the
; Control Manager, and it will pick up the Return Value right off of the
; stack!
;
                    lda <RtnAddr        ; move the return address up
                    sta <ReturnValue-3
                    lda <RtnAddr+1
                    sta <ReturnValue-2

                    tsc                 ; Get the stack pointer

                    pld                 ; restore caller's Data Bank and
                    plb                 ; Direct Page registers

                    clc                 ; Adjust the stack pointer to point to
                    adc #ReturnValue-4  ; the new location of the RTL address.
                    tcs                 ; Put the stack pointer back.

                    rtl                 ; back to the caller


; This is a table of pointers to routines to be called for specific
; CtlCodes passed to us. The 'null' entries are pointers to a
; routine that does nothing except return 'no error'.

CtlTable            ANOP
                    dc i2'myDrawCtl'    ; 0 drawCtl
                    dc i2'Null'         ; 1 calcCRect
                    dc i2'myTestCtl'    ; 2 testCtl
                    dc i2'myInitCtl'    ; 3 initCtl
                    dc i2'Null'         ; 4 dispCtl
                    dc i2'Null'         ; 5 posCtl
                    dc i2'Null'         ; 6 thumbCtl
                    dc i2'myDragCtl'    ; 7 dragCtl
                    dc i2'Null'         ; 8 autoTrack
                    dc i2'myNewValue'   ; 9 newValue
                    dc i2'mySetParams'  ;10 setParams
                    dc i2'myInitCtl'    ;11 moveCtl
                    dc i2'myRecSize'    ;12 recSize

                    end

                    EJECT
*******************************************************************************
*
myDrawCtl           start
*
* Description:      Draw control command.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:
*                   Import CalcCorners
*                   Import SetMyPen
*
* Entry Points:     NONE
*
*******************************************************************************
                    using CtlData

; figure out the coordinates of the grow knobs.

                    jsr CalcCorners

; Save the pen state, as we'll be changing it a lot.

                    PushLong #oldPenState
                    _GetPenState

; Set the correct width of the pen for the mode we are in.

                    jsr SetMyPen

;
; Set the local variable 'ActivFlag' to indicate how to draw the
; control. We draw it inactive if a) the Hilite value is 255, or b)
; the owner window is inactive and fCtrlTie is set.
;
                    ldy #octlHilite     ; are we inactive?
                    lda [<CtlPtr],y
                    and #$00FF
                    cmp #$00FF
                    beq DrawInactive    ; yes, so clear ActivFlag

; At this point, the control can potentially be drawn as an active
; control. But this can only happen if the window is active, or is
; inactive but with the fCtlTie flag clear.

                    ldy #octlOwner      ; get the pointer to the control's
                    lda [<CtlPtr],y     ; owning window into 'work'
                    sta <work
                    iny
                    iny
                    lda [<CtlPtr],y
                    sta <work+2

                    ldy #owFrame-4      ; get the frame bits
                    lda [<work],y
                    and #fHilited       ; check the Hilite bit
                    bne DrawActive      ; window is active -> control active
                    lda [<work],y       ; window is inactive
                    and #fCtlTie        ; does CtlTie bit affect control?
                    bne DrawInactive    ; yes, so draw it inactive too

DrawActive          lda #1
                    sta ActivFlag
                    bra SetColorTable

DrawInactive        stz ActivFlag

; Put the color table pointer into a  Direct page location.

SetColorTable       ANOP
                    lda ColorPtr
                    sta <work
                    lda ColorPtr+2
                    sta <work+2

;
; See if the control is active or not, and set the color
; accordingly.
;

                    lda ActivFlag
                    bne SetFrameColor

                    lda [<work]         ; YES - so we are. Use the inactive
                    lsr a               ; pattern for this.
                    lsr a
                    lsr a
                    lsr a
                    and #$000f
                    pha
                    _SetSolidPenPat

                    bra DrawFrame

SetFrameColor       ANOP
                    lda [<work]         ; get the color for the frame from
                    and #$000f          ; the color table and use it.
                    pha
                    _SetSolidPenPat

DrawFrame           ANOP
                    PushLong #myCtlRect ; draw the frame of the control
                    _FrameRect

;
; See if the control is active or not, and set the color accordingly. If
; we so chose we could even decide not to draw knobs on an inactive sizer.
; This is similar to the scrollbar defproc no drawing the thumb on an
; inactive scrollbar.
;

                    lda ActivFlag
                    beq DrawKnobs       ; it's inactive; keep that pattern

                    lda [<work]         ; get the color of the knobs from
                    xba                 ; the color table and set it.
                    and #$000f
                    pha
                    _SetSolidPenPat

DrawKnobs           ANOP
                    ldy #octlFlag       ; do we need corner knobs?
                    lda [<CtlPtr],y
                    and #knobCornerMask
                    beq ckEdges         ; no, so check for edge knobs.

                    PushLong #knobUL    ; yes, so draw them
                    _PaintRect

                    PushLong #knobUR
                    _PaintRect

                    PushLong #knobLL
                    _PaintRect

                    PushLong #knobLR
                    _PaintRect

ckEdges             ANOP
                    ldy #octlFlag       ; do we need edge knobs?
                    lda [<CtlPtr],y
                    and #knobEdgeMask
                    beq done            ; no, so leave

                    PushLong #knobTop   ; yes, so draw them.
                    _PaintRect

                    PushLong #knobRight
                    _PaintRect

                    PushLong #knobBottom
                    _PaintRect

                    PushLong #knobLeft
                    _PaintRect

done                ANOP
                    PushLong #oldPenState ; clean up after ourselves.
                    _SetPenState

                    lda #0
                    tax

                    rts

ActivFlag           ds 2

                    end

                    EJECT
*******************************************************************************
*
myTestCtl           start
*
* Description:      Hit test command. This routine checks to see if we have
*                   chosen the ability to drag the control when we click in
*                   the interior or not. If so, then we will always return
*                   our partcode (BoxCtlPart = $A0). If not, then we only
*                   return our partcode if we click on the frame or a knob.
*
*
* Inputs:           NONE
*
* Outputs:          A = Part code hit
*
* External Refs:
*                   Import TestFrame
*
* Entry Points:     NONE
*
*******************************************************************************
                    using CtlData

                    jsr TestFrame
                    cmp #noPart         ; hit anything?
                    beq exit            ; no - return nothing
                    lda #BoxCtlPart     ; yes - return our partcode
exit                ANOP
                    ldx #0              ; high byte of return value always $00
                    rts

                    end

                    EJECT
*******************************************************************************
*
myInitCtl           start
*
* Description:      Called to initialize my custom fields of the control
*                   data. It calles mySetParams to handle the data that
*                   is pointed to by the Data/Params field, and makes sure
*                   that the corners of the rectangle lie on the grid.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:
*                   Import mySetParams
*                   Import Snap2Grid
*                   Import SetCtlRect
*                   Import InitCtlData
*
* Entry Points:     NONE
*
*******************************************************************************
                    using CtlData

; First, set up some fields based on the value of Param. This is
; a pointer to some additional data, so what SetParams does is
; copy that into our control record.

                    jsr mySetParams
                    jsr InitCtlData

; Next, make sure the edges of the control are aligned to a grid.

                    ldx #^myCtlRect
                    lda #myCtlRect
                    jsr Snap2Grid

                    jsr SetCtlRect

                    lda #0              ; return nothing
                    txa

                    rts
                    end
