***********************************************************************
* Simple Custom Control procedure for Lister -- Version 3.0
* Copyright (c)
* Apple Computer, Inc.  1988-1990
* All Rights Reserved.
*
* Developer Technical Support Apple II Sample Code
*
* Written by C.K.Haun <TR>
*
* This is the custom control that defines what kinds of border lines
* are drawn on Lister's output.
* Of course, it can	be adapted to be anything you'd like it to be, in it's
* current form it could be a design tool for a warped tic-tac-toe game.  Or
* change things around to make that control you've always wanted.
*
* This infomation is passed back to the	application through	the normal
* _GetControlValue call, with the word value returned being	a series
* of bit flags like	so....
* 0000000000000000
* ||||||||||||||||
* ||||||||||||||| Line across top
* |||||||||||||| 2nd top line
* ||||||||||||| inner bottom line
* |||||||||||| bottom bottom line
* ||||||||||| left side line
* |||||||||| center	line
* ||||||||| right line
* <reserved>	 We like saying <reserved> here at Apple Co.
* In your application activity, when you get a hit in this control you'll look
* at the part code returned and toggle your set-up and the control value, 
* as you would to change the state of a radio button.
*****************************************************************************

	case on

	longi on
	longa on

***********************************************************************

	INCLUDE 'm16.util2'
	INCLUDE 'e16.control'
	INCLUDE 'm16.control'
	INCLUDE 'm16.memory'
	INCLUDE 'm16.quickdraw'
	INCLUDE 'm16.resources'

	INCLUDE 'm16.misctool'

	export boxControl
boxControl	PROC


*  Everything comes	through here, with a standard header.  So 
*  we process the header and jump to the right place, neh?

	phb	; save old program bank
	phk	; get current PB
	plb
	pla
	sta rtlAndDB
	pla
	sta rtlAndDB+2	; save for later

	lda 1,s	; passed control handle
	sta ctlHandle
	lda 3,s
	sta ctlHandle+2


* I'm going copy the control handle for some of the calls 
* to this code area.  
* I'm checking here to see if this call is one I need to copy the params for.
	lda 9,s	; ctlMessage
	ldx #8
@doCopyCheck	cmp copiers,x	; is it something I	care about the
		; record for?
	beq @yesCopy	; yes
	dex
	dex
	bpl @doCopyCheck

	pla	; it wasn't, pull the handle
	pla	; off the stack
	bra @didntCopyHandle ; and go away

@yesCopy		; Note here that the ctlHandle is still on
		; the stack.  It is the last thing on there,
		; so this can act as a parameter for _HdndToPtr.
	pushlong #theControlRecord
	pea 0
	lda recordLength
	pha
	_HandToPtr	; The ctlHandle is not on the stack any longer.


@didntCopyHandle	pla	; parameter passed,occasionally needed
	sta ctlParamPassed
	pla
	sta ctlParamPassed+2
	pla	; the op number, we'll use this to....
	asl a
	tax
	jmp (boxRoutines,x)	; jump to the right	routine

drawBox		; draws the control
	jsr setCtlPort
	jsr drawRects
	jsr restorePort
	jmp exitNull

calcBox	jmp exitNull	; use if the rectangle you want the CM to
		; drag a different
		; rectangle than the standard one
		; use the standard rectangle 

testBox		; Hit test our control

	lda #6	; Loop until we find which part this is	in...
@rectTestLoop	pha
	pea 0
	ldy #ctlParamPassed>>16 ; mouse click, in local coords.
	phy
	ldy #ctlParamPassed
	phy
	asl a
	tax
	lda #boxInnerRects>>16
	pha
	lda boxInnerRects,x
	pha
	_PtInRect
	pla
	bne @foundit
	pla
	dea
	bpl @rectTestLoop
	pha	; acc = $FF, so when incremented, it
		; will be 0, which indicates no part hit.
@foundit	lda #0
	sta 3,s
	pla
	ina	; pass back 1+part, so the $FF case
	sta 1,s	; becomes the no-part event.

	jmp exitCustom


initBox
* This is the place	where you set up all your internal parts, additional
* parameters, and load any other data or resources you'll need for this
* control.
* First check to see if there is anything other than standard for us
* like an inital value or color table
	pushlong $0
	lda ctlParamPassed+2 ; In sys 5.0 and later this parameter 
	sta $2	; contains a pointer to the control
		; template, if
	lda ctlParamPassed	; _NewControl2 was used to create
	sta $0	; the control.
	lda [$0]
	cmp #6	; minimum param count
	beq @nonextra	; no additional parameters
	tax
	ldy #26	; passed init value, store it in the 
	lda [$0],y	; record
	sta boxValue
	cpx #8	; was there a color	table?
	bne @nonextra
	iny	; There was a color	table. Put the 
	iny	; reference to it in the control record
	lda [$0],y
	sta boxColors
	iny
	iny
	lda [$0],y
	sta boxColors+2
@nonextra	pulllong $0	; Initialize all our rectangles
	lda masterRect+4	; First see if the User specified end 
	ora masterRect+6	; coords
	bne @userInited	; we have to do it,	use
	lda masterRect	; standard x2,y2 values
	clc
	adc defaultRect
	sta masterRect+4
	lda masterRect+2
	clc
	adc defaultRect+2
	sta masterRect+6
@userInited
* Now we set up our	inner rects

	lda masterRect
	sta iRect6
	sta iRect5
	sta iRect7
	sta iRect1
	clc
	adc #3
	sta iRect1+4
	clc
	adc #2
	sta iRect2
	clc
	adc #3
	sta iRect2+4

	lda masterRect+2
	sta iRect5+2
	clc
	adc #5
	sta iRect1+2
	sta iRect2+2
	sta iRect3+2
	sta iRect4+2
	sta iRect5+6

	lda masterRect+4
	sta iRect6+4
	sta iRect5+4
	sta iRect4+4
	sta iRect7+4
	sec
	sbc #3
	sta iRect4
	sbc #2
	sta iRect3+4
	sbc #3
	sta iRect3
	sta iRect7+2

	lda masterRect+6
	sta iRect7+6
	sec
	sbc #5
	sta iRect1+6
	sta iRect2+6
	sta iRect3+6
	sta iRect4+6
	sta iRect7+2

	lda masterRect+6	; center the middle	bit
	sec
	sbc masterRect+2
	lsr a
	clc
	adc masterRect+2
	dea
	dea
	sta iRect6+2
	adc #5
	sta iRect6+6

	pushlong #theControlRecord  ; Pass these rects
	pushlong ctlHandle	        ; back to the control record ..
	pea 0	        ; please
	lda recordLength
	pha
	_PtrToHand
* Putting all the parameters we just initialized back in the control record 
* is _very_ important. 
* First this makes sure that the inital	tracking the CM does is
* correct and we don't have to calculate our rectangles every time we
* enter our routine.
* Second, you may want a few of these custom controls.  The	control record 
* keeps everything for each control in it's own special place.
* Third, keep in mind that your control proc can be a resource.  If it's
* a resource, then it could get purged and re-loaded.  If that happens, and
* you're expecting data to hang around inside the proc then you're in 
* big trouble.


	jmp exitNull



disposeBox	jmp exitNull	; we didn't allocate anything we need 
		; to clean up
		; If you had loaded	a picture or icon, 
		; or allocated any
		; additional memory, you'd clean it up .
		; here

positionBox	jmp exitCustom	; If you have an indicator, you'd set
		; it here

thumbBox	jmp exitNull	; this routine sets	up the _DragRect 
		; parameters for the
		; thumb of your control, if you have 
		; one. The CM will take
		; the param block you create here and
		; pass it to _DragRect
		; no thumb 

dragBox	jmp exitNull	; Put your custom dragging routine here
		; if you have one
		; I'm telling the CM to use the  
		; standard dragging action
		; with a rect the size of the complete 
		; control.

trackBox	jmp exitNull

newboxValue	lda #0	; I do nothing extra with the new val
	sta ctlParamPassed
	jmp drawBox	; but we should redraw if the value 
		; changed

newAdditionalParams jmp exitNull	; we do nothing with these, the CM 
		; has already put them in the
		; control record.  Things like scroll 
		; bars use these.


moveBox	lda masterRect	; re-calculate the inner rects after
	sec	; the control has been
	sbc ctlParamPassed	; moved by _MoveControl
	sta masterRect
	lda masterRect+2
	sec
	sbc ctlParamPassed+2
	sta masterRect+2
	lda masterRect+4
	sec
	sbc ctlParamPassed
	sta masterRect+4
	lda masterRect+6
	sec
	sbc ctlParamPassed+2
	sta masterRect+6
	jmp initBox	; let our original routine
		; re-calc the other rects


sendRecSize		; this tells the control manager what 
		; size handle to
	lda recordLength	; allocate for our control record
	sta 1,s
	lda #0
	sta 3,s
	jmp exitCustom


* All of the following fall through to null exit
* They are new calls for extended controls, and deal with controls that can
* act as targets ( like LineEdit controls ) Window position	aware controls
* ( like TextEdit controls ) Multi-part	controls ( LineEdit, PopUp, List, etc. )

boxHandleEvent
boxStatusToggle
boxRectChanged
boxWindowChanged
boxTabbedTo
boxMultiPart
boxWindStateChanged
exitNull		; this is the exit point for custom 
	lda #0	; routines that don't use a specific  
	sta 1,s	; function. You should fill the return 
	sta 3,s	; parameters with 0's if you don't 
		; support a function. Not _maniditory_,	
		; as some of the routines return parms  
		; are specified as undefined, but it 
		; doesn't hurt to be safe.
exitCustom	lda rtlAndDB+2
	pha
	lda rtlAndDB
	pha
	plb
	rtl



setCtlPort		; sets the current port to the control 
	pea 0	; owning port specified in the control 
	pea 0	; record
	_GetPort	; save current port
	pla	; We do this because your control  
	sta lastPort	; drawing procedure can get called 
	pla	; while the window your control is
	sta lastPort+2 	; in is not the front window.
	lda owningWindow+2
	pha
	lda owningWindow
	pha
	_SetPort
	rts
restorePort		; reset the port to	what it was on 
	lda lastPort+2	; entry
	pha
	lda lastPort
	pha
	_SetPort
	rts


drawRects
* draws the interior rectangles 
* first we need the	ctlValue for this one so we know
* how to draw the rectangles
* First, see if there is a custom color	table
	lda boxColors	; are boxColors non-zero?
	ora boxColors+2
	bne @hascustom
	brl @nocustoms	; they're zero, use defaults
@hascustom	lda boxMoreFlags	; read flags to see	what kind of 
	and #$0C	; reference this color table is
	beq @dopointer
	cmp #$04
	beq @dohandle
	pea 0	; it's a resource.  Load and Go
	pea 0
	pea $800D	; control color table resource type
	lda boxColors+2
	pha
	lda boxColors
	pha
	_LoadResource
* if there was an error, go back to standards.  
* You can also use _CMLoadResource here, since, as a ControlProc, 
* you're considered part of the control manager.
* But, if the load fails _CMLoadResource calls Sysdeath.I use _LoadResource
* here because the color table is a non-critical part of my	control and 
* if it's missing I want to continue without death.

	bcc @keepitup
	stz boxColors	; couldn't load the resource, 
	stz boxColors+2	; change reference to zippo
	pla
	pla	; yank the bad handle
	bra drawRects	; go back to the beginning
@dohandle	lda boxColors+2
	pha
	lda boxColors
	pha
	bra @loadHandle
@keepitup	lda #-1
	sta loadedResource  ; flag indicating that the resource was loaded
@loadHandle	pushlong #mycolorbuffer
	pea 0	; Size, constant for this to avoid  
	pea 6	; trouble if the resource or handle
	_HandToPtr	; has been weirdly altered and not the right size anymore
	lda loadedResource	; was this from a resource?
	beq @dopats	; no, continue
	pea 3	; purge level
	pea $800D	; control color table resource
	lda boxColors+2	; the color table ID
	pha
	lda boxColors
	pha
	_ReleaseResource	; release it, we're done with it for now.
	stz loadedResource	; clear the flag
	bra @dopats	
@dopointer
	pushlong #mycolorbuffer
	lda boxColors+2
	pha
	lda boxColors
	pha
	pea 0
	pea 6
	_BlockMove
	bra @dopats
@nocustoms
	ldx #4
@movedefs	lda defaultColors,x	; move the default colors over to the  
	sta mycolorbuffer,x ; pat area.  
	dex
	dex
	bpl @movedefs
@dopats
	ldx #30
@patfill
	lda mycolorbuffer
	sta hiPat,x
	lda mycolorbuffer+2
	sta dimPat,x
	lda mycolorbuffer+4
	sta outlinePat,x
	dex
	dex
	bpl @patfill

	pushlong #patBuffer	; save the current GrafPort pen pat
	_GetPenPat
	lda ctlParamPassed
	beq @drawAll	; No parts to worry about -- draw the whole thing.

	dea
	asl a
	tax	; Index for part code.

	lda boxHilite 	; Check hiliting here.
	and #$FF
	beq @drawAll	; Put hiliting back to normal.

	cmp #$FF	; Is the control de-hilited?
	beq @drawAll	; Control is inactive.

@hilitePart	lda #boxInnerRects>>16
	pha
	lda boxInnerRects,x
	pha

	pushlong #outlinePat
	_SetPenPat
	pushlong #oldPen	; save current pen size
	_GetPenSize
	pea 2
	pea 1
	_SetPenSize
	_FrameRect
	pushlong oldPen
	_SetPenSize
	pushlong #patBuffer	; restore the current GP pen pat
	_SetPenPat
	lda #0
	sta lastPartHit
	rts


@drawAll	pha	; Make work region.
	pha
	_NewRgn
	pla
	sta workRgn
	pla
	sta workRgn+2

	pha	; Get clip region.
	pha
	_NewRgn
	lda 3,s
	pha
	lda 3,s
	pha
	_GetClip
	pla
	sta clipRgn
	pla
	sta clipRgn+2

	pha
	pha
	_GetClipHandle
	pea masterRect>>16
	pea masterRect
	_RectRgn

	lda #6	; number of rects
@rectDrawing	pha
	asl a
	tax

	lda workRgn+2
	pha
	lda workRgn
	pha

	lda #boxInnerRects>>16
	ldy boxInnerRects,x
	pha
	phy
	pha
	phy

	lda valueBits,x
	and boxValue	; see if we're drawing this selected or 
	bne @drawSet	; unselected
	pushlong #dimPat
	bra @setit
@drawSet	pushlong #hiPat
@setit
	_SetPenPat
	_PaintRect
	_RectRgn

	pha
	pha
	_GetClipHandle

	lda workRgn+2
	pha
	lda workRgn
	pha

	pha
	pha
	_GetClipHandle

	_DiffRgn

	pla
	dea
	bmi @zzz
	brl @rectDrawing

@zzz	ldx clipRgn+2
	ldy clipRgn
	phx
	phy
	phx
	phy
	_SetClip
	_DisposeRgn

	lda workRgn+2
	pha
	lda workRgn
	pha
	_DisposeRgn

	pushlong #patBuffer	; restore the current GP pen pat
	_SetPenPat
	rts

workRgn	dc.l 0
clipRgn	dc.l 0

boxRoutines	dc.w drawBox
	dc.w calcBox
	dc.w testBox
	dc.w initBox
	dc.w disposeBox
	dc.w positionBox
	dc.w thumbBox
	dc.w dragBox
	dc.w trackBox
	dc.w newboxValue
	dc.w newAdditionalParams
	dc.w moveBox
	dc.w sendRecSize
	dc.w boxHandleEvent
	dc.w boxStatusToggle
	dc.w boxRectChanged
	dc.w boxWindowChanged
	dc.w boxTabbedTo
	dc.w boxMultiPart
	dc.w boxWindStateChanged


rtlAndDB	ds.b 4
ctlHandle	ds.b 4
ctlParamPassed	ds.b 4
lastPort	ds.b 4


lastPartHit	dc.w 0


theControlRecord		; The first parameters ( up to boxVers ) 
nextCtl	ds.b 4	; are standard for an extended control
owningWindow	ds.b 4	;  record, and _must_ be present or
masterRect	ds.w 4	; the control manager cannot handle 
boxFLags	ds.b 1	; your custom control
boxHilite	ds.b 1	; correctly for many common actions.
boxValue	ds.b 2
boxProc	ds.b 4
boxAction	dc.l -1	; for a TrackRoutine, if we have one
boxData	ds.b 4
boxRefCon	ds.b 4
boxColors	ds.b 4
	ds.b 16	; reserved
boxID	ds.b 4
boxMoreFlags	ds.b 2
boxVers	ds.b 2
* Hit test and drawing rectangles
* These will be passed into the control	handle 
* after creation of	the control.
* And passed back during action times
iRect1	ds.w 4
iRect2	ds.w 4
iRect3	ds.w 4
iRect4	ds.w 4
iRect5	ds.w 4
iRect6	ds.w 4
iRect7	ds.w 4
endControlRecord
recordLength	dc.w endControlRecord-theControlRecord

* and pointers to those rects....
	export boxInnerRects
boxInnerRects
	dc.w iRect1
	dc.w iRect2
	dc.w iRect3
	dc.w iRect4
	dc.w iRect5
	dc.w iRect6
	dc.w iRect7

* Here are the defaults for our control	type, if the user 
* doesn't select a color table or a bounding rect.

defaultRect	dc.w 30,100
mycolorbuffer	ds.w 3
defaultColors	dc.w 0,$CCCC,$4444	; black,grey,red
* A small table of routine numbers that	need the handle copied to this
* proc area before taking action
copiers	dc.w 0,2,3,9,11
* Pattern creation area
hiPat	ds.b 32
dimPat	ds.b 32
outlinePat	ds.b 32

* The next two data	stashes are for GrafPort variables in 
* the port our control is currently in.
* Be nice, save and	restore anything you're changing in your ControlProc.  
* If you change pen patterns, masks, sizes, or anything else be 
* sure to reset them, or something else will get
* drawn in funny ways.
oldPen	ds.b 4	; stash for old pen	width
patBuffer	ds.b 32	; stash for current	GrafPort pattern

* Masks for how to draw the different parts of the control
valueBits
	dc.w %1	; top 
	dc.w %10	; 2nd top 
	dc.w %100	; inner bottom
	dc.w %1000	; bottom bottom
	dc.w %10000	; left side
	dc.w %100000	; center
	dc.w %1000000	; right
loadedResource ds.b 2	; flag for color table resource load
	ENDP
	end
