antoine-source/scsi2/AppleScanGS/BoxCtrl..oc.aii
Antoine Vignau fae5db3afd scsi-2 powa!
2024-08-15 21:27:35 +02:00

1 line
53 KiB
Plaintext

case obj
case on
PRINT PUSH,OFF
INCLUDE 'BoxCtrl.DefProc.mac'
PRINT POP
*******************************************************************************
* BoxCtl CDEFProc
*
* (C) Copyright Apple Computer, Inc. 1988
* All rights reserved.
*
* by Keith Rollin
* September 1, 1988
*
* 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.
*
*
*
*
* Modification History:
*
* v1.0a1 02-Apr-88 kaar New Today
* v1.0a2 04-Apr-88 kaar Changed part codes. Defined CtlData to point
* to a data block. Added edge knobs. Grid can
* now be rectangular. Knob rectangles no longer
* part of control (created on the fly). Added
* new flags to ctlFlag. CtlValue now contains
* only the size of the knobs.
* v1.0a3 07-Apr-88 kaar Removed dependance on Direct Page from the
* application (now uses the stack). Responds to
* Hilite(255).
* V1.0a4 08-Apr-88 kaar Added support for when Param1/2 are null.
* V1.0B1 23-Apr-88 kaar Fixed some bugs. Checked for gridsizes of 0
* and 1. Called InvalRect in DragCtl.
* V2.0B2 08-Jun-88 kaar Cleaned up the code some in response to a
* code review. Version number bumped to 2.0 to
* reflect existance of simpler 1.0 version for
* a technote. Added support for fCtlTie in the
* owner window's frame flags. Improved perfor-
* mance and legibility of Snap2Grid routine.
* V2.0B3 08-Jul-88 kaar Part codes changed again. It seems that only
* one indicator per control is allowed. Turned
* the source code upside down and put it into
* Steve Glass format. Implemented some of Dan's
* programming style suggestions.
* V2.0 01-Sep-88 kaar First Release
*
* V2.01 23-May-90 Greg Branche
*
* Added minHeight and minWidth parameters to allow the
* application to define the minimum width and height of
* the control. This was formerly handled by the minX
* and minY parameters. These have been redefined so
* that they specify the leftmost and uppermost limits
* of the control. The maxX and maxY parameters specify
* the right- and bottom-most limits.
*
* Bug fixes:
*
* 1) In myDrawCtl, the code was never working
* right that checked the current state of the
* control to determine whether to draw the control
* as active or inactive. This has been modified
* to use a flag passed in the high word of ctlParam
* as the state indicator. $0000 = control is active,
* $FFFF = control is inactive.
*
* Version 1.0 kaar
*
* Used in Custom Control technote
*
*
* Version 2.0 kaar
*
* Release for Source Code Demo Disk #1
*
*
* Version 2.01 Greg Branche
*
* Used for AppleScan.GS
*
*******************************************************************************
*
* 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 (top limit)
* WORD: Min X when growing (left limit)
* WORD: Max Y when growing (bottom limit)
* WORD: Max X when growing (right limit)
* WORD: Min Height
* WORD: Min Width
* 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.w 50,140,110,300
* CtrlData dc.w 10 ; min Y
* dc.w 10 ; min X
* dc.w 100 ; max Y
* dc.w 200 ; max X
* dc.w 10 ; min height
* dc.w 16 ; min width
* dc.w 16 ; grid Y
* dc.w 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 equ 1 ; caller's saved direct page register
OrigB equ OrigD+2 ; caller's saved data bank register
work equ OrigB+1 ; general use work space
CtlPtr equ work+4 ; pointer to control record
RtnAddr equ CtlPtr+4 ; RTL address back to Control Manager
theCtlHandle equ RtnAddr+3 ; handle to control record
CtlParam equ theCtlHandle+4 ; add'l parameter passed to DefProc
CtlCode equ CtlParam+4 ; operation to perform
ReturnValue equ CtlCode+2 ; space for return value to Ctl Mgr.
;
; --- Offsets for my control record
;
oCtlMinY equ octlColor+4
oCtlMinX equ oCtlMinY+2
oCtlMaxY equ oCtlMinX+2
oCtlMaxX equ oCtlMaxY+2
oCtlMinHeight equ oCtlMaxX+2
oCtlMinWidth equ oCtlMinHeight+2
oCtlGridY equ oCtlMinWidth+2
oCtlGridX equ oCtlGridY+2
oCtlSize equ oCtlGridX+2
;
; --- Part codes for my control
;
BoxCtlPart equ $A0 ; This is returned to the app.
FramePart equ $01 ; These are used internally to tell
InteriorPart equ $02 ; 'myDragCtl' what part of the
ULPart equ $03 ; control we are dragging.
TopPart equ $04
URPart equ $05
RightPart equ $06
LLPart equ $07
BottomPart equ $08
LRPart equ $09
LeftPart equ $0A
;
; --- Masks for ctlFlags
;
dragIntMask equ %00000001 ; if set, let user drag on interior
knobCornerMask equ %00000010 ; if set, draw knobs on the corners
knobEdgeMask equ %00000100 ; if set, draw knobs on the edges
visMask equ ctlInVis
EJECT
*******************************************************************************
*
CtlData RECORD
*
* Description: Storage for CDEF
*
*
* External Refs: NONE
*
* Entry Points: NONE
*
*******************************************************************************
deltaX ds.b 2 ; used when growing the control
deltaY ds.b 2
ColorPtr ds.b 4 ; pointer to actual color table to use
theParam ds.b 4 ; copy of CtlParam passed on stack
dragPart ds.b 2 ; internal part of control hit
myCtlRect ds.b 8 ; copy of CtlRect in control record
myCtlGridY ds.b 2 ; copy of CtlGridY
myCtlGridX ds.b 2 ; copy of CtlGridX
FrameHeight ds.b 2
FrameWidth ds.b 2
knobUL ds.b 8 ; This space is used to store the
knobTop ds.b 8 ; rectangles that define the grow
knobUR ds.b 8 ; knobs on the control. They are
knobRight ds.b 8 ; created as needed, and are not
knobLR ds.b 8 ; stored in the control record.
knobBottom ds.b 8
knobLL ds.b 8
knobLeft ds.b 8
StdColorTable dc.b $C0 ; grey inact frame, blk normal frame
dc.b $00 ; black knobs
StdCtlData dc.w 10,10,200,640,0,0 ; min 10,10; max 200,640; no grid
oldPenState ds.b $32 ; storage for old pen state.
GreyPattern dc.b $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
dc.b $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
dc.b $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
dc.b $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
ENDR
EJECT
*******************************************************************************
*
BoxProc PROC Export
*
* 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
*
*******************************************************************************
with 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
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
dc.w myDrawCtl ; 0 drawCtl
dc.w Null ; 1 calcCRect
dc.w myTestCtl ; 2 testCtl
dc.w myInitCtl ; 3 initCtl
dc.w Null ; 4 dispCtl
dc.w Null ; 5 posCtl
dc.w Null ; 6 thumbCtl
dc.w myDragCtl ; 7 dragCtl
dc.w Null ; 8 autoTrack
dc.w myNewValue ; 9 newValue
dc.w mySetParams ;10 setParams
dc.w myInitCtl ;11 moveCtl
dc.w myRecSize ;12 recSize
ENDP
EJECT
*******************************************************************************
*
myDrawCtl PROC
*
* Description: Draw control command.
*
*
* Inputs: NONE
*
* Outputs: NONE
*
* External Refs:
import CalcCorners
import SetMyPen
*
* Entry Points: NONE
*
*******************************************************************************
with 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.
lda CtlParam+2 ; get the flag passed by the Control Manager
bmi DrawInactive ; $FFFF = inactive
DrawActive lda #1
sta ActivFlag
bra SetColorTable
DrawInactive stz ActivFlag
; Put the color table pointer into a Direct page location.
SetColorTable
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
lda [<work] ; get the color for the frame from
and #$000f ; the color table and use it.
pha
_SetSolidPenPat
DrawFrame
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
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
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
PushLong #oldPenState ; clean up after ourselves.
_SetPenState
lda #0
tax
rts
ActivFlag ds.b 2
ENDP
EJECT
*******************************************************************************
*
myTestCtl PROC
*
* 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
*
*******************************************************************************
with CtlData
jsr TestFrame
cmp #noPart ; hit anything?
beq exit ; no - return nothing
lda #BoxCtlPart ; yes - return our partcode
exit
ldx #0 ; high byte of return value always $00
rts
ENDP
EJECT
*******************************************************************************
*
myInitCtl PROC
*
* Description: Called to initialize my custom fields of the control
* record. 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
*
*******************************************************************************
with 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
ENDP
EJECT
*******************************************************************************
*
myDragCtl PROC
*
* Description: Drag command. We hit something. Determine what it was and
* drag it around the screen. If we hit the frame, then drag
* the whole control. If we hit a knob, then we grow the
* control (just like growing a window).
*
*
* Inputs: NONE
*
* Outputs: NONE
*
* External Refs:
import SetMyPen
import Snap2Grid
import myDrawCtl
import FindPart
import SetCtlRect
*
* Entry Points: NONE
*
*******************************************************************************
with CtlData
PushLong #oldPenState ; save drawing state, as we change it
_GetPenState
ldx #6 ; copy original control rect to drag_rect
@loop
lda myCtlRect,x
sta drag_rect,x
dex
dex
bpl @loop
ldy #oCtlMaxX ; get local copies of the maximum
ldx #6 ; and minimum dimensions.
loop0010 lda [<CtlPtr],y
sta limitRect,x
dey
dey
dex
dex
bpl loop0010
ldy #oCtlMinHeight
lda [<CtlPtr],y
sta MinHeight
iny
iny
lda [<CtlPtr],y
sta MinWidth
; Get the portRect of the window that owns this control. This rectangle
; will be used to control the limits of dragging.
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 #oportRect+6 ; copy the PortRect of the window
ldx #6 ; out of its GrafPort into slopRect.
loop400 lda [<work],y
sta slopRect,x
dey
dey
dex
dex
bpl loop400
; Now we adjust slopRect to account for the width of the knobs
ldy #octlValue ; get the height of the knobs
lda [<CtlPtr],y
and #$00ff
pha
clc
adc slopRect
sta slopRect
lda slopRect+4
sec
sbc 1,s
sta slopRect+4
pla ; remove from stack
lda [<CtlPtr],y ; get the width of the knobs
xba
and #$00ff
pha
clc
adc slopRect+2
sta slopRect+2
lda slopRect+6
sec
sbc 1,s
sta slopRect+6
; ; leave on stack as space for result of GetNextEvent
; pha ; has the mouse been lifted up?
PushWord #mUpMask ; ask for any such events
PushLong #TrackEvent
_GetNextEvent
pla
lda TrackEvent+owhat
cmp #mouseUpEvt ; was there a mouse up event?
bne mDown ; no - so start tracking
brl done ; yes - so leave
mDown
PushLong #TrackEvent+owhere
_GlobalToLocal
lda TrackEvent+owhere
sta OldMouse ; FindPart likes the mouse Position
sta theParam ; in 'theParam'
lda TrackEvent+owhere+2
sta OldMouse+2
sta theParam+2
jsr FindPart ; what did we hit? (returns internal
sta dragPart ; partcode number).
cmp #InteriorPart+1 ; what part did we hit?
bge GrowFrame ; some knob - grow the control
brl DragFrame ; interior or frame - move the control
;
; This part of the program is called when we detect that we have clicked on
; a grow knob. It tracks the motions of the mouse, and grows/draws the
; control appropriately.
;
GrowFrame
PushWord #2 ; set us to XOR mode. This is for the
_SetPenMode ; rubber-banding effect of growing.
jsr SetMyPen ; set my pen's width
PushLong #GreyPattern ; grow with a grey pattern
_SetPenPat
; validate the min and max values. the min values must be at least $4.
; if not, they are set there. the max values must be at least as large
; as the min values. if not, they are set to $FFFF
lda #4
cmp MinWidth
blt ckMinY
sta MinWidth
ckMinY cmp MinHeight
blt ckMaxX
sta MinHeight
ckMaxX lda limitRect+6
sec
sbc MinWidth
cmp limitRect+2
bge ckMaxY
ChgX lda #$FFFF
sta limitRect+6
ckMaxY lda limitRect+4
sec
sbc MinHeight
cmp limitRect
bge doneCk
ChgY lda #$FFFF
sta limitRect+4
doneCk
lda myCtlRect+4 ; get lower boundary
sec
sbc MinHeight ; calculate theoretical upper boundary
sta minRect ; store it as upper limit
lda myCtlRect ; get upper boundary
clc
adc MinHeight ; calculate
sta minRect+4 ; store as lower limit
lda myCtlRect+6 ; get right boundary
sec
sbc MinWidth ; calculate
sta minRect+2 ; store as left limit
lda myCtlRect+2 ; get left boundary
clc
adc MinWidth ; calculate
sta minRect+6 ; store as right limit
;
; We grow the control with the following algorithm. First, setup by:
;
; 1. saving the current mouse postion in 'NewMouse',
; 2. aligning 'NewMouse' to the grid, and
; 3. making a working copy of the control's rectangle in 'drag_rect'.
;
; We then enter the main loop:
;
; 4. Draw a copy of the rubber-banding rectangle.
; 5. Check the mouse button. If it is up, goto #12
; 6. Put the position of the mouse into 'NewerMouse'. Align it to grid.
; 7. Check it against 'NewMouse'. If it hasn't changed, goto #5.
; 8. Erase the old rubber-Band.
; 9. Calculate the new rectangle and put it into 'drag_rect'
; 10. Move 'NewerMouse' into 'NewMouse'
; 11. Goto #4
;
; The growing is all done. Clean up and leave.
;
; 12. Erase the rubber band.
; 13. Erase the control.
; 14. Invalidate the area under the control
; 15. Make drag_rect the new boundary of the control
; 16. Say goodnite, Gracie.
;
lda OldMouse
sta NewMouse
lda OldMouse+2
sta NewMouse+2
ldx #^NewMouse ; make sure we are on the grid!!!
lda #NewMouse
jsr Snap2Grid
DrawLoop
PushLong #drag_rect ; draw the rubber-band
_FrameRect
WaitLoop
pha ; has the mouse been lifted up?
PushWord #mUpMask ; ask for any such events
PushLong #TrackEvent
_GetNextEvent
pla
lda TrackEvent+owhat
cmp #mouseUpEvt
beq done ; mouse up occured - so leave.
lda TrackEvent+owhere ; mouse still down. copy it to
sta NewerMouse ; NewerMouse
lda TrackEvent+owhere+2
sta NewerMouse+2
PushLong #NewerMouse ; convert it to window coordinates.
_GlobalToLocal
ldx #^NewerMouse ; make sure the new position is on
lda #NewerMouse ; the grid.
jsr Snap2Grid
lda NewerMouse ; See if the mouse position moved. We
cmp NewMouse ; only redraw the rubber-band if it did.
bne ReDraw ; It did - redraw the rubber-band
lda NewerMouse+2
cmp NewMouse+2
beq WaitLoop ; No movement - check button state
ReDraw
PushLong #drag_rect ; erase the old rubber band
_FrameRect
lda NewerMouse ; copy NewerMouse for next loop
sta NewMouse
sec ; and update deltaXY for AdjFrameRect
sbc OldMouse
sta deltaY
lda NewerMouse+2
sta NewMouse+2
sec
sbc OldMouse+2
sta deltaX
jsr AdjFrameRect ; adjust for drawing the new rect
brl DrawLoop ; redraw the rubber-band
done
PushLong #drag_rect ; erase the rubber band
_FrameRect
PushLong #myCtlRect ; erase the control
_EraseRect
PushLong #myCtlRect ; invalidate the stuff under it
_InvalRect
brl Exit
;
; This part of the program is called when we have clicked on either the
; frame or the interior. It calls dragrect to move an outline of the
; control around.
;
DragFrame
;
; Use dragRect to drag an outline of the control around the
; screen for me.
;
pha ; space for result
pha
PushLong #DragDraw ; no action proc
PushLong #GreyPattern ; drag pattern
PushLong OldMouse ; starting location of mouse
PushLong #drag_rect ; outline to drag
PushLong #limitRect ; rect defining limits
PushLong #slopRect ; slopRect
PushWord #%00101000 ; custom drag flag, ReturnRect flag
_DragRect
pla ; pull off and discard the deltas
pla
PushLong #myCtlRect ; erase the control in its old pos'n
_EraseRect
PushLong #myCtlRect ; invalidate the stuff under it
_InvalRect
ldx #^drag_rect ; align the new rectangle to the grid.
lda #drag_rect
jsr Snap2Grid
Exit
ldy #6 ; copy over the new control frame
loop200 lda drag_rect,y
sta myCtlRect,y
dey
dey
bpl loop200
PushLong #myCtlRect ; Invalidate the new area under the
_InvalRect ; control so it will be redrawn.
PushLong #oldPenState
_SetPenState ; clean up QuickDraw after ourselves.
jsr SetCtlRect ; copy new rect into control record
lda #-1 ; Say that we handled it
tax
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Called by DragRect to draw the rectangle of the control. I draw the outline
; myself so that I can align it to my own grid. The normal aligning of the
; rectangle to the grid only aligns to powers of 2. I align to anything.
;
; When this routine is called, the stack looks like this:
;
; PUSH:WORD - delta X
; PUSH:WORD - delta Y
; PUSH:BYTE[3] - return address
;
; However, since I set the ReturnRect flag, I can ignore the deltas passed to
; me, and use drag_rect directly. I make a copy of it, make sure that it is
; aligned to the grid, draw it, remove the parameters, and return to the
; control manager.
;
DragDraw
RTLAddr equ 1
DY equ RTLAddr+3
DX equ DY+2
ldx #6 ; make a copy of the rectangle that
loop500 lda drag_rect,x ; _DragRect is passing to us. We are
sta draw_rect,x ; going to modify it so that it is
dex ; aligned to the grid.
dex
bpl loop500
ldx #^draw_rect ; align the new rectangle to the grid.
lda #draw_rect
jsr Snap2Grid
DrawOutline
PushLong #draw_rect
_FrameRect
ExitDragDraw
lda 1,s ; move the RTL address back up
sta 5,s
lda 2,s
sta 6,s
pla ; remove the parameters (DX, DY)
pla
rtl ; back to dragRect!!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Called by the routine that grows the control with a rubber-band
; sort of thing. It uses the part code we hit as an index into
; a table. This table contains a list of offsets into the
; 4 words of drag_rect. It reads in a pair of these offsets and
; uses them to modify the appropriate X/Y coordinates with
; deltaY and deltaX. DeltaY and deltaX are set up by the
; rubber-banding routine.
AdjFrameRect
ldx #6
loop380 lda drag_rect,x
sta new_rect,x
dex
dex
bpl loop380
lda dragPart ; get the part code we are dragging
sec
sbc #ULPart
asl a ; turn it into an index.
asl a
tax
lda PtToAdj,x ; get Y coordinate to adjust
tay
lda PtToAdj+2,x ; get X coordinate to adjust
tax
; if the entry that was fetched from the table was -1, then that means
; that we are making no adjustments in that direction. For instance, it
; we are dragging on the top edge knob, we want to adjust just the top
; side of the rectangle, but want to leave either of the sides alone.
cpy #-1 ; modify the vertical coordinate
beq NoChg1 ; of choice if necessary.
clc
lda deltaY
adc myCtlRect,y
sta new_rect,y
NoChg1 cpx #-1 ; modify the horizontal coordinate
beq NoChg2 ; of choice if necessary.
clc
lda deltaX
adc myCtlRect,x
sta new_rect,x
NoChg2
ldx #^new_rect ; align the rectangle to the grid.
lda #new_rect
jsr Snap2Grid
; now that we have a new rectangle, we have to make sure that it is within
; the maximum and minimum sizes allowed.
; First, we check to make sure the new rect is within the slopRect. If not
; we revert back to the original rect position. This mimics the action of
; DragRect.
pha ; space for result
pushlong #NewMouse ; put point on stack
pushlong #slopRect ; put slopRect on stack
_PtInRect
pla ; retrieve result
bne inRect ; yes, the new point is within slopRect
copyOriginal
ldx #6 ; copy original control rect to drag_rect
@loop
lda myCtlRect,x
sta drag_rect,x
dex
dex
bpl @loop
rts
inRect
; Now we can check to make sure the new rect is within the bounds of the
; limit rect. If it isn't, we peg it at the limit, again mimicking
; DragRect.
lda limitRect ; outside the top limit?
cmp new_rect
bcc @1 ; no...
sta new_rect ; else peg it there
@1
lda limitRect+2 ; how 'bout the left limit?
cmp new_rect+2
bcc @2
sta new_rect+2
@2
lda limitRect+4
cmp new_rect+4
bcs @3
sta new_rect+4
@3
lda limitRect+6
cmp new_rect+6
bcs @4
sta new_rect+6
@4
lda minRect ; now check the width and height for minimums
cmp new_rect
bcs @5
sta new_rect
@5
lda minRect+2
cmp new_rect+2
bcs @6
sta new_rect+2
@6
lda minRect+4
cmp new_rect+4
bcc @7
sta new_rect+4
@7
lda minRect+6
cmp new_rect+6
bcc @8
sta new_rect+6
@8
ldx #6 ; now we can copy the new rect to drag_rect
@loop2
lda new_rect,x
sta drag_rect,x
dex
dex
bpl @loop2
rts
PtToAdj dc.w 0,2,0,-1,0,6,-1,6,4,6,4,-1,4,2,-1,2
TrackEvent ds.b $10
OldMouse ds.b 4
NewMouse ds.b 8
NewerMouse ds.b 8
drag_rect ds.b 8
draw_rect ds.b 8
old_drag ds.b 8
new_rect ds.b 8
limitRect ds.b 8
slopRect ds.b 8
minRect ds.b 8
MinHeight ds.b 2
MinWidth ds.b 2
ENDP
EJECT
*******************************************************************************
*
myNewValue PROC
*
* Description: Called when the value of the control is to be changed.
* The area under the control is invalidated so that it will
* be redrawn.
*
*
* Inputs: NONE
*
* Outputs: NONE
*
* External Refs:
import Snap2Grid
import myDrawCtl
import SetCtlRect
*
* Entry Points: NONE
*
*******************************************************************************
with CtlData
PushLong #myCtlRect ; erase old control
_EraseRect
PushLong #myCtlRect ; invalidate area erased
_InvalRect
ldx #^myCtlRect ; make sure control is on the grid
lda #myCtlRect
jsr Snap2Grid
jsr SetCtlRect
lda #0 ; return no value
tax
rts
ENDP
EJECT
*******************************************************************************
*
mySetParams PROC
*
* Description: Set new parameters command. Don't set if either of them
* are negative.
*
*
* Inputs: NONE
*
* Outputs: NONE
*
* External Refs: NONE
*
* Entry Points: NONE
*
*******************************************************************************
with CtlData
lda theParam ; these have gotta go on in reverse
sta <work+2
lda theParam+2
sta <work
ora <work+2 ; is this a NULL pointer?
bne CopyIt ; no - so use it
lda #^StdCtlData ; yes - so set pointer to default data
sta <work+2
lda #StdCtlData
sta <work
CopyIt
ldy #oCtlSize-2 ; copy the data into control record.
ldx #oCtlSize-oCtlMinY-2
loop phy
txy
lda [<work],y
ply
sta [<CtlPtr],y
dey
dey
dex
dex
bpl loop
lda #0 ; return no value
tax
rts
ENDP
EJECT
*******************************************************************************
*
myRecSize PROC
*
* Description: Return record size command.
*
*
* Inputs: NONE
*
* Outputs: A = Size of the control record to allocate.
*
* External Refs: NONE
*
* Entry Points: NONE
*
*******************************************************************************
with CtlData
lda #oCtlSize ; low word of record size
ldx #0 ; high word
rts
ENDP
EJECT
*******************************************************************************
*
Null PROC
*
* Description: Null routine. Does nothing. Returns no error.
*
*
* Inputs: NONE
*
* Outputs: NONE
*
* External Refs: NONE
*
* Entry Points: NONE
*
*******************************************************************************
lda #0
tax
rts
ENDP
EJECT
*******************************************************************************
*
InitCtlData PROC
*
* Description: Initialize some data that will needs to be handy: Get a
* pointer to the control record (we are supplied only with
* a handle), make a local copy of the control rectangle, set
* up a pointer to a color table, and move CtlParam from a
* Direct Page location into an absolute location.
*
*
* Inputs: NONE
*
* Outputs: NONE
*
* External Refs: NONE
*
* Entry Points: NONE
*
*******************************************************************************
with CtlData
;
; Check to see if the Control Manager is sending us a 'RecSize' call. If it
; is, then we cannot rely on the validity of the Control handle, as it has not
; yet been allocated and is essentially garbage. Since we do not know what it
; points to, we don't want to read from it, as it may actually accidentally
; point to something dangerous, like the IWM I/O locations!!! So if we detect
; a RecSize code, skip over this routine.
;
lda <CtlCode ; is this a RecSize call?
cmp #recSize
beq done ; yes - skip this whole routine
;
; Copy the value in CtlParam into a local location -- off of the direct page.
; We do this for convenience. We may need to pass a pointer to CtlParam to
; a toolbox routine, but you can't do that for a direct page location. The
; number assiciated with a direct page location is really an offset off of the
; base specified by the Direct Page register; it is NOT an absolute memory
; location. So that we can conveniently use 'PushLong #location' if we need
; to, I copy CtlParam to a non-direct page location.
;
lda <CtlParam
sta theParam
lda <CtlParam+2
sta theParam+2
ldy #2 ; get a handy pointer to the CtlRec
lda [<theCtlHandle],y
sta <CtlPtr+2
lda [<theCtlHandle]
sta <CtlPtr
ldy #octlRect+6 ; copy the control rect over to the
ldx #6 ; local variable.
loop lda [<CtlPtr],y
sta myCtlRect,x
dey
dey
dex
dex
bpl loop
;
; The following values are used by Snap2Grid. However, Snap2Grid can be
; called with an invalid Direct Page, so we copy them out the control
; record (which is pointed to by a pointer in our Direct Page) and into
; some absolute locations that we can get to at all times.
;
ldy #oCtlGridX ; get the X and Y spacings out of the
lda [<CtlPtr],y ; control record and into some local
sta myCtlGridX ; storage for easy handling.
ldy #oCtlGridY
lda [<CtlPtr],y
sta myCtlGridY
; Check the color table pointer. If it is non-zero, install it as the
; pointer to the color table we'll be using. If not, then install a
; pointer to a default color table.
ldy #octlColor ; Get the pointer to the color
lda [<CtlPtr],y ; table in the control record.
sta ColorPtr ; Save it to a local pointer.
iny
iny
lda [<CtlPtr],y
sta ColorPtr+2
ora ColorPtr ; is it a NULL pointer?
bne done ; no, so use it.
lda #StdColorTable ; yes, install my own color table.
sta ColorPtr
lda #^StdColorTable
sta ColorPtr+2
done
rts
ENDP
EJECT
*******************************************************************************
*
SetCtlRect PROC
*
* Description: This routine is called when the control's bounding
* rectangle has been changed and needs to be written back
* out to the control record.
*
*
* Inputs: NONE
*
* Outputs: NONE
*
* External Refs: NONE
*
* Entry Points: NONE
*
*******************************************************************************
with CtlData
ldy #octlRect+6 ; get an offset into the control rec.
ldx #6 ; index to my local record.
loop
lda myCtlRect,x
sta [<CtlPtr],y
dey
dey
dex
dex
bpl loop
rts
ENDP
EJECT
*******************************************************************************
*
SetMyPen PROC
*
* Description: We want our control to look good in both 320 and 640 mode.
* Because vertical lines are thinner in 640 than 320, its a
* good idea to draw them doubly thick just so they can be
* seen. This routine will determine what mode we are in and
* adjust the pen accordingly.
*
*
* Inputs: NONE
*
* Outputs: NONE
*
* External Refs: NONE
*
* Entry Points: NONE
*
*******************************************************************************
with CtlData
pha ; we base pen width on the MasterSCB
_GetMasterSCB
pla
and #$0080 ; check the mode bit
beq pen320 ; we are in 320 mode - go push a 1
PushWord #2 ; in 640 mode - push on a 2 width
bra setpn
pen320 PushWord #1 ; in 320 mode - push on a 1 width
setpn ; width is 1 or 2
PushWord #1 ; Height is always 1
_SetPenSize
rts
ENDP
EJECT
*******************************************************************************
*
CalcCorners PROC
*
* Description: This routine calculates the rectangles for all of the
* little knobs that are used to grow the control. They are
* stored locally - not with the control record - and so need
* to be calculated every time we need to check for hits or
* to draw the control.
*
*
* Inputs: NONE
*
* Outputs: knobXXX, FrameHeight, FrameWidth variables are initialized.
*
* External Refs: NONE
*
* Entry Points: NONE
*
*******************************************************************************
with CtlData
top equ 0 ; alias these to something a little
left equ 2 ; more descriptive.
bottom equ 4
right equ 6
; Get the size of the knobs. The height and width are stored in the CtlValue
; field in a packed format. This unpacks them and stores them locally for
; ease of access.
ldy #octlValue
lda [<CtlPtr],y
and #$00FF
sta FrameHeight
lda [<CtlPtr],y
xba
and #$00FF
sta FrameWidth
;
; We "assembly line" the initialization of the knob rectangles. Instead
; of calculating just one knob at a time, we do a few of them all at the
; same time. Here we initialize the tops and bottoms of the 3 top knobs.
lda myCtlRect+top
sta knobUL+top
sta knobTop+top
sta knobUR+top
clc
adc FrameHeight
sta knobUL+bottom
sta knobTop+bottom
sta knobUR+bottom
;
; Initialize the lefts and rights of the three left knobs.
;
lda myCtlRect+left
sta knobUL+left
sta knobLeft+left
sta knobLL+left
clc
adc FrameWidth
sta knobUL+right
sta knobLeft+right
sta knobLL+right
;
; Initialize the tops and bottoms of the three bottom knobs.
;
lda myCtlRect+bottom
sta knobLL+bottom
sta knobBottom+bottom
sta knobLR+bottom
sec
sbc FrameHeight
sta knobLL+top
sta knobBottom+top
sta knobLR+top
;
; Initialize the lefts and rights of the three right knobs.
;
lda myCtlRect+right
sta knobUR+right
sta knobRight+right
sta knobLR+right
sec
sbc FrameWidth
sta knobUR+left
sta knobRight+left
sta knobLR+left
;
; At this point, the 4 corner knobs have been completely initialized.
; If we are going to be using the edge knobs, then we'll have to do
; some extra math. However, if we aren't, then we'll skip over the
; math.
ldy #octlFlag
lda [<CtlPtr],y
and #knobEdgeMask
beq done
; Do extra setup for edge knobs
;
; First, get top and bottom coordinates for the side edges:
;
; top = (myCtlRect.top + myCtlRect.bottom - FrameHeight)/2
; bot = (myCtlRect.top + myCtlRect.bottom + FrameHeight)/2
clc
lda myCtlRect+top
adc myCtlRect+bottom
pha
adc FrameHeight
lsr a
sta knobLeft+bottom
sta knobRight+bottom
pla
sec
sbc FrameHeight
lsr a
sta knobLeft+top
sta knobRight+top
;
; Now perform similar calculations for the left and right sides
; of the top and bottom edge knobs.
;
clc
lda myCtlRect+left
adc myCtlRect+right
pha
adc FrameWidth
lsr a
sta knobTop+right
sta knobBottom+right
pla
sec
sbc FrameWidth
lsr a
sta knobTop+left
sta knobBottom+left
done
rts
ENDP
EJECT
*******************************************************************************
*
Snap2Grid PROC
*
* Description: This control defproc lets the application optionally create
* an invisible grid based on local coordinates. When this is
* done, the 4 corners of the control are always aligned to
* the coordinates of this grid. This occurs when we drag or
* resize the control, or call MoveControl. Snap2Grip is the
* routine that ensures we are on the grid.
*
*
* Inputs: A = low word of pointer to rectangle to fix
* X = high word
*
* Outputs: rectangle is modified in place.
*
* External Refs: NONE
*
* Entry Points: NONE
*
*******************************************************************************
with CtlData
oldD equ 1
oldB equ oldD+2
rectPtr equ oldB+1
;
; We are going to use a local direct page here. This is because Snap2Grid is
; called by my routine that draws the control's recangle as it is being
; dragged. When that happens, we are not using our normal direct page, and
; hence can't use any of the values or work locations there. So we set up
; another one with it's own workspace.
;
phx ; save the pointer to the rectangle
pha
phb ; save the caller's data bank register
phd ; save the caller'd direct page reg.
phk ; switch data bank to program bank
plb
tsc ; switch direct page to stack pointer
tcd
;
; WhchGridYX is used to help generalize this routine. We support different
; spacings in the X and Y directions of the grid. As we loop through all 4
; coordinates of the rectangle, we want to make sure that we are using the
; correct spacing. 'whichGridYX' holds flags that tell us when to use X and
; when to use Y spacing (0 = use Y spacing; 1 = use X spacing).
;
lda #%0101 ; Set up some indices into GridYX
sta whichGridYX
;
; Fix the coordinates by using the following method:
;
; 1. Get the difference between the coordinate we are examining and
; the next lowest grid location:
;
; z = coordinate mod GridSize
;
; 2. Z tells how far away from the next lowest grid line
; the coordinate we are looking at is, so subtracting that
; value will put the coordinate on the grid.
;
; coordinate := coordinate - z
;
; 3. However, this is simply truncating, and I don't like the
; way that feels when resizing the control. So lets add an
; instruction so that it ROUNDS instead.
;
; if z > GridSize/2 then
; coordinate := coordinate + GridSize
;
ldy #6 ; init our loop index
Loop
sty YSave
lda whichGridYX ; do we now use X or Y grid spacing?
and #%0001
beq UseY
lda myCtlGridX
bra UseIt
UseY
lda myCtlGridY
; we now have the gridsize in the Acc
UseIt
cmp #2 ; Is it 0 or 1? If so, then skip over
blt ShortCircuit ; dividing routine.
sta GridSize ; save this for the divide later.
pha ; push space on stack for the result
pha ; of the divide we are going to do.
lda [<rectPtr],y ; push on the coordinate we are
pha ; adjusting as the Dividend
PushWord GridSize ; push this on as the divisor
_UDivide ; unsigned divide
pla ; quotient - we don't want it.
PullWord Remainder ; remainder is 'z' - Keep it!
ldy YSave ; get my coordinate index back.
lda [<rectPtr],y ; get the coordinate to change
sec
sbc Remainder ; change it
sta [<rectPtr],y ; save it back out
lda GridSize ; Make a GridSize/2
lsr a
cmp Remainder ; is remainder < GridSize/2?
bge ShortCircuit ; yes - so we're done
lda [<rectPtr],y ; no - so bump the coordinate
clc
adc GridSize
sta [<rectPtr],y
ShortCircuit lsr whichGridYX ; move the next index into place
dey ; bump down to the next coordinate
dey ; of the rectangle.
bpl Loop
Exit
pld ; restore caller's Direct Page reg.
plb ; restore data bank register
pla ; remove rect pointer
pla
rts ; all done - go bye-bye
whichGridYX ds.b 2
YSave ds.b 2
Remainder ds.b 2
GridSize ds.b 2
ENDP
EJECT
*******************************************************************************
*
FindPart PROC
*
* Description: This routine is called to find out exactly which part of
* the control we clicked in. It first checks to see if we
* clicked in any of the grow knobs. If so, then it returns
* an 'internal partcode' telling the calling routine which
* knob was clicked in. If a knob wasn't clicked in, a check
* is made to see if we clicked on the frame. If we are
* allowed to click in the interior, a check is made to see
* if we clicked there, too.
*
*
* Inputs: NONE
*
* Outputs: A = internal partcode of part hit (1-10)
*
* External Refs:
import CalcCorners
*
* Entry Points:
entry TestFrame ; called by myTestCtl
*
*******************************************************************************
with CtlData
jsr CalcCorners
;
; We first check to see if the mouse was clicked in any of the knobs. We do
; this by assuming a knob was clicked in, and then checking to see if that was
; true. If so, we leave here with the partcode we assumed. If not, we loop
; through and check all of the other knobs. If none of the knobs was clicked
; in, we see if the frame was clicked on.
ldx #LeftPart ; get last knob's part code
ldy #7*8 ; point to last knob's rect
loop
phx ; save partcode we're checking for...
phy ; and the rect's pointer
pha ; space for _PtInRect result
PushLong #theParam ; push on the point to check
lda #^knobUL ; create a pointer to the rect
pha
clc ; Bumping just the low byte is OK
tya ; unless the GS suddenly lets
adc #knobUL ; code segments cross bank
pha ; boundaries
_PtInRect
pla ; get result of PtInRect
ply ; retrieve rectangle index
plx ; retrieve part code
cmp #0 ; did we score a hit?
bne InKnob ; yes, return with the part code
dex ; move down to next partcode
sec ; move down to next rectangle
tya
sbc #8
tay
bpl loop ; keep going until rect index < 0
bra TestFrame ; not in knobs - try frame
;
; We just scored a hit on one of the knobs, and X holds the knob number.
; However, the application may not have corner or edge knobs active. So
; if we clicked on an inactive knob, have to check for that and return
; 'inFrame' instead of a knob partcode.
;
InKnob
ldy #octlFlag ; are corner knobs active?
lda [<CtlPtr],y
and #knobCornerMask
bne ckEdge ; yes - so pass this code on.
txa ; Corners not allowed. Did we hit one?
lsr a
bcs RetFramePart ; yes, so return click on Frame instead
ckEdge
ldy #octlFlag ; are edge knobs active?
lda [<CtlPtr],y
and #knobEdgeMask
bne done ; yes - so return this code
txa ; Edges not allowed. Did we hit one?
lsr a
bcs TestFrame ; no, so pass this code on.
RetFramePart
ldx #FramePart ; yes, so return click on Frame instead
done
txa ; put the part code in the Acc
rts
TestFrame
; First test to see if it is within the Control's rectangle. It should
; be, but let's check anyway...
pha
PushLong #theParam
PushLong #myCtlRect
_PtInRect
pla
beq NotInFrame ; no in the frame. Signal noPart
; Now see if we differentiate between the frame and the interior
ldy #octlFlag
lda [<CtlPtr],y
and #dragIntMask
bne RetFramePart ; nope - everything drags
; Yes we do. Now see if we hit the frame fair and square. Inset the
; control's bounding rectangle by the size of the knobs that are on
; it to create an inner rectangle. If we fall between those two
; rectangles, then I say that you have landed on the frame. If we
; got this far, then we know that we are within the outer rectangle.
; Now see if we are outside of the inner rectangle.
ldx #6 ; make a copy of the control rect
CopyLoop lda myCtlRect,x ; so that we can reduce it.
sta innerRect,x
dex
dex
bpl CopyLoop
PushLong #innerRect ; push on pointer to the rect to inset
PushWord FrameWidth ; push on the amounts to inset it by
PushWord FrameHeight ; (these vals inited by CalcCorners)
_InsetRect
pha ; see if we are outside of the inner
PushLong #theParam ; rectangle
PushLong #innerRect
_PtInRect
pla ; check the result
beq RetFramePart ; we were between rects!
NotInFrame lda #noPart ; Not *exactly* on the frame.
rts
innerRect ds.b 8
ENDP
End