supermario/base/SuperMarioProj.1994-02-09/Toolbox/ListMgr/ListMgrPACK.a
2019-06-29 23:17:50 +08:00

3047 lines
103 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;
; File: ListMgrPACK.a
;
; Contains: the List Manager
;
; Written by: Steve Capps and Ernie Beernik
;
; Copyright: © 1984-1993 by Apple Computer, Inc. All rights reserved.
;
; This file is used in these builds: Bigbang Sys606
;
; Change History (most recent first):
;
; <SM7> 9/9/93 SAM Changed ShiftDown to not assume the PtInRect will leave
; thePoint.v in D1 (good lord!).
; <SM6> 8/16/93 fau Moved <LW3,4,5> to happen in the GetTheListProc routine, instead
; of where they were. This is where the check should be
; performed. This was exposed by a PageMaker bug, after Ludwig
; shipped.
; <SM5> 6/14/93 kc Roll in Ludwig.
; <LW5> 5/3/93 chp Flush the caches for LDEF handles smaller than 32 bytes. It used
; to be 16 bytes, but a 22-byte, unflushed, fake defproc has been
; discovered in Software Ventures Microphone II and Microphone
; Pro. (RADAR #1082386)
; <LW4> 3/8/93 fau Made the compare in previous checkin be 16 bytes, instead of 12,
; and unsigned.
; <LW3> 3/6/93 fau Fixed bug 1070072. Essentially, a slimy developer was using a
; fake LDEF to stuff their own code and NOT flushing the cache
; afterwards. They relied on the HLOCK in the initlist routine to
; flush it for them. We "fixed" them by checking the size of the
; LDEF handle to see if it's less than 12 bytes. If so, we will
; flush the cache for them. This penalizes developers who use
; this method and do flush the cache. All for compatibility.
; <SM4> 1/27/93 PN Add emStartTicks extended memory global to use in ListMgrPack to
; replace the 'startTicks'.
; <3> 11/11/92 GMA Set RomMapInsert to mapTrue before getting LDEF resources. fixes
; bug # 1052658.
; <SM2> 11/5/92 SWC Changed PackMacs.a->Packages.a.
; <13> 3/4/91 dba dty: get rid of SysVers conditionals
; <12> 1/14/91 dba (stb) Restore the use of hilite value of 255 when there is no
; scrolling possible.
; <11> 1/14/91 dba (vl) Use the hilite value of 254 (feature added to the scroll
; bar CDEF) for inactive scroll bars.
; <10> 10/24/90 ngk <KSM>Fix AddColumn/AddRow to not trash heap if growing handle fails
; <9> 9/17/90 BG Removed <6>. 040s are behaving more reliably now.
; <8> 8/17/90 ngk Fix bug in last bug fix. changed BHS to BHI.
; <7> 7/29/90 ngk Fix bug in LClick where is was testing the is_selected bit of a
; cell out of range when clicking past bottom of item list.
; <6> 7/19/90 BG Added EclipseNOPs for flakey 040s.
; <5> 4/16/90 BBH fixed parameter passing to the ldefproc in LNew (was danger of
; passing an odd address in the parameter designed lRect which
; caused Pascal and C ldefproc's to crash occasionally on 68000
; machines)
; <4> 3/21/90 dba get rid of hiliting while auto-scrolling; fix bug in check for
; double-click; assume that ScrollDelay is implemented on 7.0
; systems; fix bug where horizontal auto-scrolling is tied to
; vertical auto-scrolling
; <3> 12/28/89 dba changed from PROC to MAIN to get dead code stripping
; <2> 12/20/89 dba fix bug in JustRect routine that scrolls the list wrong; get
; package handle from low memory to lock instead of using
; RecoverHandle
; <1.6> 12/10/89 EMT NEEDED FOR 6.0.5: Check for UserDelay trap before using
; ScrollDelay.
; <1.5> 12/7/89 dba added interface to SetHilite for use by Standard File who is
; intimate with the List Manager; also fix some bugs from the last
; version, including PinRect
; <1.4> 12/7/89 dba got rid of MyRectInRgn and MyPinRect; merged the scroll bar
; code; created the MyShowWindow and MyValidRect routines
; <1.3> 11/21/89 EMT NEEDED FOR 6.0.5: Added humane scrolling.
; <1.2> 11/17/89 dba fixed flashing arrow while scrolling; frame rectangle and call
; ValidRect when hiding; the scroll bar to deactivate it (this
; fixes a bug that was worked around in the Chooser and Control
; Panel); get rid of code to handle 64K ROMs; fixed one or two
; bugs with handles and registers
; <1.1> 10/11/89 ngk bug fix to ListGetSelect, it used to not return that the last
; item was selected
; <1.0> 11/16/88 CCH Added to EASE.
; <S412> 2/29/88 GP and EMT test for an empty handle (purged LDEF)
; <S3> 12/21/86 JTC 32-bit cleaning, with care to get the conditionals correct
; for old ROMs.
; <S2> 12/17/86 EHB Fixed lock state problem in StdExit.
; 12/2/86 GP Changed handling of LVBadFit in SetVisible due to conflicting
; assignment of lDoHAutoscroll
; 8/14/86 DLD Converted source to MPW.
; 5/30/86 SAB Fixed HUnLock bug in UnlockLProc which was trying to test for
; NIL LDEF Handle, but actually relied on the condition code left
; by the LDEF
; 12/16/85 EHB Bad exit in ListHilite (special StdExit for internal routines)
; 12/11/85 EHB Added checking for defproc not found (just skips call to
; defproc); Fixed DelRow/Col update bug when all rows/columns
; deleted; Do update after justRect,moveRect in DelExit; Dont
; inval cells that arent visible (intersect with rview); SetHilite
; was calling DrawControl when control invisible; Intensive
; scramble testing done; Removed bogus size checking code in
; setCell/addtocell; changed all #lock/#unlock to _Lock/_Unlock
; 12/11/85 EHB Added flags that disable autoscrolling. Default is disabled if
; there is not a scroll bar on that axis.
; 11/18/85 EHB Fixed ListSize bugs (of unknown origin)
; 11/15/85 EHB Fixed up RAM entry and RecoverHandle
; 8/15/85 EHB ROMMapInsert for ROM version only (no longer used since not in
; ROM)
; 8/5/85 EHB Left some error checking hanging...
; 8/5/85 EHB Fixed SectRect bug in DoDraw
; 7/16/85 EHB Lock down RAM version of List Manager
; 7/11/85 EHB Squished the dispatcher for small ROMs Optimized for routines
; fixed in ROM (RectInRgn, PinRect)
; 7/9/85 EHB added setup of ROMMapInsert for ROM resources
; 5/22/85 EHB added slop to double clicking, disabled double clicks in scroll
; bars moved noDraw testing to low level=> NO drawing if doDraw
; FALSE!!
;
print push,off
load 'StandardEqu.d'
include 'Packages.a'
include 'ListMgrPriv.a'
INCLUDE 'SysEqu.a' ; <LW3> fau
MACHINE MC68020 ; needed for cache flush <LW3> fau
print pop
ListMgrPackage MAIN EXPORT
ListMgrHandle equ AppPacks + listMgr * 4 ; handle to List Manager package
IntUtilHandle equ AppPacks + intUtil * 4 ; handle to International Utilities package
BRA.S @skipHeader
DC.W 0 ; flags
DC.L ('PACK') ; resource type
DC.W 0 ; id = 0
DC.W 7 ; version 7
@skipHeader
MOVE.L (SP)+,a1 ; get return address
move.l ListMgrHandle,A0 ; get our handle <2>
_HGetState ; D0 := current lock, etc. state <S3>
MOVE.B D0,D1 ; D1 := ditto <S3>
_HLock ; and lock ourselves down
MOVE.W (SP)+,D0 ; get the selector
ASR.W #1,D0 ; table of offsets, not jumps
LEA ListStart,A0 ; point to dispatch table
ADD.W 0(A0,D0.W),A0 ; add offset to address
jsr (A0) ; dispatch the call
move.l ListMgrHandle,A0 ; get our handle <2>
MOVE.B D1,D0 ; D0 := saved lock state <S3>
_HSetState ; restore to handle <S3>
jmp (a1) ; return to our caller
DC.W SetHilite-ListStart ; for internal use by Standard File only
ListStart
DC.W ListActivate-ListStart
DC.W ListAddColumn-ListStart
DC.W ListAddRow-ListStart
DC.W ListAddToCell-ListStart
DC.W ListAutoScroll-ListStart
DC.W ListCellSize-ListStart
DC.W ListClick-ListStart
DC.W ListClrCell-ListStart
DC.W ListDelColumn-ListStart
DC.W ListDelRow-ListStart
DC.W ListDispose-ListStart
DC.W ListDoDraw-ListStart
DC.W ListDraw-ListStart
DC.W ListFind-ListStart
DC.W ListGetCell-ListStart
DC.W ListGetSelect-ListStart
DC.W ListLastClick-ListStart
DC.W ListNew-ListStart
DC.W ListNextCell-ListStart
DC.W ListRect-ListStart
DC.W ListScroll-ListStart
DC.W ListSearch-ListStart
DC.W ListSetCell-ListStart
DC.W ListSetSelect-ListStart
DC.W ListSize-ListStart
DC.W ListUpdate-ListStart
; All procs have the form of ...h: ListHandle ) or ...c: Point; h: ListHandle )
; StdEntry takes advantage of this consistency and loads up some registers in a standard way.
; StdEntry locks down the handle, and saves it A4. The dereferenced handle is saved
; in A3. It then puts 2nd param in D7 (which is usually a cell). Watch out for strange
; references to D7 in routines that dont have c passed to them. Finally it saves the
; current port and gets into the lists port.
; Stack Frame used by almost all routines. SavePort is set up by StdEntry. GoodCell thru
; index are set up by MyFind. R is a temporary rect. VisBounds is set by the routine
; GetVisBounds, and it is the equivalent of dataBounds, but it is adjusted if cells do not
; fit exactly into the visBounds. Below the stack frame are the saved registers and then <S3>
; the saved state of the input handle. This is unraveled in the exit procedure. <S3>
; This entry and exit preserves D1 and A1, because the package wrapper puts the real return
; address in A1 and the handle state in D1.
goodCell EQU -4 ; state of next-to-last parameter
offset EQU goodCell-4 ; offset of cell
len EQU offset-4 ; length of cell
index EQU len-4 ; index to current cell
r EQU index-8 ; temp rect
visBounds EQU r-8 ; bounds for scrolling
stdFrame EQU visBounds ; size of std frame
ListMgrSavedRegisters REG A1-A4/D1-D7
StdEntry
MOVE.L (SP)+,A0 ; get local RTS
LINK A6,#stdFrame ; std frame for every routine
MOVEM.L ListMgrSavedRegisters,-(SP)
MOVE.L 8(A6),A4 ; save list handle in A4
MOVE.L (A4),A3 ; save list pointer in A3
MOVE.L 12(A6),D7 ; get next to last parameter in D7 (often cell)
move.l a0,a2 ; keep return address in A2 for a moment
StdEntryMidstream
MOVEA.L A4,A0 ; input handle <S3>
_HGetState ; D0 := current lock, etc. state<S3>
MOVE.B D0,-(SP) ; save across entire LM call <S3>
_HLock ; lock handle across call <S3>
subq #4,sp ; make room for saved port
move.l sp,-(sp) ; save the port
_GetPort
MOVE.L port(A3),-(SP) ; set port to our port
_SetPort
jmp (a2) ; return to our caller inside the List Mgr.
ListNewEntry ; entry point used by ListNew
MOVE.L (SP)+,a2 ; get local RTS
BRA.S StdEntryMidstream
StdExit
MOVE.W D0,D3 ; amount of stack to kill <S3>
_SetPort ; restore port
MOVE.B (SP)+,D0 ; saved handle state <S3>
MOVEA.L A4,A0 ; extant handle (could be NIL)
_HSetState ; restore handle state (works for NIL)
@noHandle
MOVE.W D3,D0 ; D0 := amount of stack to kill <S3>
MOVEM.L (SP)+,ListMgrSavedRegisters
UNLK A6
MOVE.L (SP)+,A0 ; get return address
ADD.W D0,SP ; strip params
JMP (a0) ; return
;------------
; GetVisBounds is sort of a standard entry routine. It is called by all
; routines that need to do scrolling and positioning of scroll bars. The upper left
; visible boundary is always the same as the upper left data boundary. The lower right
; must be set according to whether or not cells fit exactly into the window.
; The flags LHBadFit and LVBadFit are set up by ListCellSize when the size of the cells
; are initially set.
GetVisBounds
MOVE.L dataBounds(A3),visBounds(A6) ; assume that the visible
MOVE.L dataBounds+4(A3),visBounds+4(A6) ; bounds is the same as data
BTST #LHBadFit,ListFlags(A3) ; if cells dont fit exactly
BEQ.S @1 ; then add one
ADD.W #1,visBounds+right(A6) ; to right
@1
BTST #LVBadFit,ListFlags(A3) ; if cells dont fit exactly
BEQ.S @2 ; then add one
ADD.W #1,visBounds+bottom(A6) ; to bottom
@2
RTS
;----------------------------------------------------------------------
;
; FUNCTION ListLastClick (h: ListHandle): Cell;
;
; Returns the cell number of the last cell clicked.
;
;----------------------------------------------------------------------
ListLastClick
MOVE.L 4(SP),A0 ; get list handle
MOVE.L (A0),A0 ; get list pointer
MOVE.L lastClick(A0),8(SP) ; return result
MOVE.L (SP)+,(SP) ; strip parameter
RTS ; and return to restore handle lock state
;----------------------------------------------------------------------
;
; PROCEDURE SetHilite(h: ListHandle );
;
; SetHilite is an internal routine used to update the scroll bars.
;
; There are two basic things that need to be done to scroll bars.
; When the amount of data in the list changes, or the size of the
; list changes, or the size of the cells change, or if the list is
; scrolled, then the control values must be updated. When the list
; is activated or deactivated, the hilite state of the list must be
; adjusted.
;
;----------------------------------------------------------------------
SetHilite ; called when data or window changes
BSR.S StdEntry ; set things up
BTST #noDraw,ListFlags(A3) ; is drawing on?
BNE.S @noDraw ; => nope, just exit
BSR.S GetVisBounds ; set scrolling bounds
MOVE.L hScroll(A3),A0 ; get horizontal scroll handle
MOVE.W #h,D4 ; use left and right
BSR.S SetAScroll ; set min, max, activate
MOVE.L vScroll(A3),A0 ; get vertical scroll handle
MOVE.W #v,D4 ; use top and bottom
BSR.S SetAScroll ; set min, max, activate
@noDraw
BRA Std4Exit ; and use routine exit
;----------------------------
; SetAScroll is a code saving utility.
; It is called with a control handle in A0, and
; and offset (top or left) in D4.
SetAScroll
MOVE.L A0,D7 ; test handle
BEQ.S @done ; cant get a handle on it
moveq #0,d6
MOVE.W visBounds(A6,D4.W),D6 ; get min for later
moveq #0,d5
MOVE.W visBounds+botRight(A6,D4.W),D5 ; stuff and save max
SUB.W visible+botRight(A3,D4.W),D5
moveq #0,d3
MOVE.W visible(A3,D4.W),D3 ; get visible
ADD.W D3,D5 ; and use to set max
; figure out the right hilite for the control
; 0 for normal circumstances
; 255 if there is no scrolling to do
; 254 if the list is not active (overrides 255)
moveq #0,d0 ; set up high byte of D0 <11>
cmp.w d6,d5 ; is max > min? <12>
sle.b d0 ; no, unhilite control (if not already so) <12>
tst.b LActive(a3) ; are we active? <11>
bne.s @active ; => yes, dont use 254 hiliting <11>
move.b #254,d0 ; => no, use 254 hiliting <11>
@active
; set all of the values in the control
move.l (a0),a0 ; dereference the control handle
cmp.w contrlMin(a0),d6 ; is the minimum different?
bne.s @doSetMinimum
moveq #-1,d6 ; dont set minimum
@doSetMinimum
cmp.w contrlMax(a0),d5 ; is the maximum different?
bne.s @doSetMaximum
moveq #-1,d5 ; dont set minimum
@doSetMaximum
cmp.w contrlValue(a0),d3 ; is the value different?
bne.s @doSetValue
moveq #-1,d3 ; dont set value
@doSetValue
move.b contrlHilite(a0),d1 ; get the current hilite value
bz.s @hilite ; if there is nothing hilited, we can do the hilite
cmp.b #254,d1 ; is it a 254 or 255? <11>
blo.s @noHilite ; no, dont do hilite while scrolling
@hilite
cmp.b d0,d1 ; any change to hilite?
beq.s @noHilite
MOVE.L D7,-(SP) ; push handle
MOVE.W D0,-(SP) ; make inactive with the magic 254, or make active with 0 <11>
_HiliteControl ; and stuff hilite value
@noHilite
tst.l d6 ; any change to min?
bmi.s @noMin
MOVE.L D7,-(SP) ; push handle
MOVE.W D6,-(SP) ; push min
_SetMinCtl
@noMin
tst.l d5 ; any change to max?
bmi.s @noMax
MOVE.L D7,-(SP) ; push scroll handle
MOVE.W D5,-(SP) ; and max value
_SetMaxCtl
@noMax
tst.l d3 ; any change to value?
bmi.s @noValue
MOVE.L D7,-(SP) ; push control handle
MOVE.W D3,-(SP) ; and current value
_SetCtlValue
@noValue
MOVE.L D7,-(SP) ; show it just in case drawing
bsr.s MyShowControl ; was just turned on
@done
RTS
;----------------------------------------------------------------------
;
; PROCEDURE ListActivate( act: BOOLEAN; h: ListHandle );
;
;----------------------------------------------------------------------
ListActivate
BSR StdEntry ; ho hum
BTST #noDraw,ListFlags(A3) ; is drawing on?
BNE.S @done ; => nope, just exit
MOVE.B 12(A6),LActive(A3) ; record active state
LEA DrawASel,A2 ; get routines that draws selections
BSR DoSelLoop ; and call once for each selection
BSR.S DrawScrollBars
@done
Std6Exit
MOVEQ #6,D0 ; adios
BRA StdExit
;-----------------------
; DrawScrollBars draws both the horizontal and vertical scroll bars.
;
; In: A4 list handle
DrawScrollBars
move.l a4,-(sp) ; set up the hiliting
bsr.s SetHilite
MOVE.L hScroll(A3),D0 ; get handle to control
BSR.S DrawScrollBar ; do horizontal
MOVE.L vScroll(A3),D0 ; get handle to control
DrawScrollBar
beq.s @done ; no handle, all done
MOVE.L d0,a2 ; get handle to control
move.l (a2),a0 ; get pointer to control
CLR.B contrlVis(a0) ; mark it invisible to force redraw
MOVE.L D0,-(SP) ; push control handle
bsr.s MyShowControl ; make the control visible
@done
RTS
;-----------------------
; MyShowControl is the same as ShowControl, except that it also validates the controls
; rectangle. It should only be called for rectangular controls (here used only for scroll bars).
; A more advanced version could be written that uses the controls region.
MyShowControl
move.l 4(sp),a1 ; get the control handle
move.l (a1),a0 ; dereference and point to the control record
lea contrlRect+8(a0),a0 ; point after the controls rectangle
move.l -(a0),-(sp) ; push the rectangle on the stack
move.l -(a0),-(sp)
move.l a1,-(sp) ; push control handle for ShowControl
_ShowControl
move.l sp,-(sp) ; push pointer to rectangle for ValidRect
bsr.s MyValidRect
addq.l #8,sp ; remove the rectangle from the stack
move.l (sp)+,(sp) ; remove the control handle from the stack
rts
;-----------------------
; MyValidRect is the same as ValidRect, except that it intersects the validate region with the
; visRgn and clipRgn. This is useful so that we only validate the area that we just drew, without
; marking areas that are still not drawn as valid.
MyValidRect
move.l d3,-(sp) ; save register for region handle
subq.l #4,sp ; make room for region handle
_NewRgn
move.l (sp),d3 ; keep region handle around
move.l 4+4+4(sp),-(sp) ; get rectangle from lower part of stack
_RectRgn ; make the region
subq.l #4,sp ; make room for the port pointer
move.l sp,-(sp) ; point where to put the port
_GetPort
move.l (sp)+,a0 ; get the port
move.l d3,-(sp)
move.l clipRgn(a0),-(sp)
move.l d3,-(sp)
move.l d3,-(sp)
move.l visRgn(a0),-(sp)
move.l d3,-(sp)
_SectRgn ; intersect with both regions
_SectRgn
move.l d3,-(sp)
_ValidRgn ; do the invalidate
move.l d3,-(sp)
_DisposRgn ; get rid of the region now
move.l (sp)+,d3 ; restore register
move.l (sp)+,(sp) ; remove the rectangle pointer from the stack
rts
;----------------------------------------------------------------------
;
; PROCEDURE ListAutoScroll( h: ListHandle );
; 8
; Scroll the topmost selection to the top left of the screen iff it isnt
; visible. If no selections, or that selection is visible, do nothing.
;
;----------------------------------------------------------------------
ListAutoScroll
BSR StdEntry ; hoe humb
; get first selection
SUBQ #2,SP ; make room for result
MOVE.B #1,-(SP) ; get the next selection
PEA r(A6) ; temp storage for c
MOVE.L dataBounds+topLeft(A3),r(A6) ; start search at first cell
MOVE.L A4,-(SP) ; pass the handle
_LGetSelect ; get the next selection
MOVE.B (SP)+,D0 ; test the result
BEQ.S @done ; => sorry, no selections
MOVE.L r(A6),D7 ; get the returned cell
; is it visible?
BSR.S IsCellVisible ; is cell visible? <10Dec85>
BNE.S @done ; => yes, dont scroll
; else scroll the selected cell to the upper left of the visible
SUB.W visible+left(A3),D7 ; get horizontal delta
SWAP D7
SUB.W visible+top(A3),D7 ; get vertical delta
SWAP D7
MOVE.L D7,-(SP) ; push dh,dv
MOVE.L A4,-(SP) ; and list handle
_LScroll ; and update the screen
@done
Std4Exit
MOVEQ #4,D0
BRA StdExit ; and scurry back home
;------------------------------
;
; IsCellVisible tests to see if the cell in D7 is visible.
; If so, it returns NE.
IsCellVisible
SUBQ #2,SP ; make room for result
MOVE.L D7,-(SP) ; pass point
PEA visible(A3) ; and the visible rect
_PtInRect ; is it visible
TST.B (SP)+
RTS ; NE if visible
;----------------------------------------------------------------------
;
; FUNCTION ListClick( pt: Point; modifiers: INTEGER; h: ListHandle ):BOOLEAN;
; 14 12 8 18
; Call this routine when there is a mouse down in the list area. Pt is the
; mouse coordinates (local). Modifiers is the integer returned in the event record.
; This routine handles all scrolling (clicks within the scroll bars
; and autoscrolling) and selection of items in the list.
;
;----------------------------------------------------------------------
ListClick
BSR StdEntry ; ho hum
BSR GetVisBounds ; get visible boundaries
CLR.B 18(A6) ; assume no double click
BSET #7,12(A6) ; flag first time through
MOVE.L #$80008000,D7 ; set up bogus last point
SUBQ #8,SP ; save space for a rect
MOVE.L 14(A6),D5 ; get click point into D5
MOVE.B selFlags(A3),14(A6) ; save current selFlags in Frame
; code:=FindControl( pt, port, ctl );
SUBQ #2,SP ; result of find control
MOVE.L D5,-(SP) ; pass point
MOVE.L port(A3),-(SP)
PEA r(A6) ; get control handle into r(A6)
_FindControl
MOVE (SP)+,D6 ; get code into D6
BNE clickCont ; if <> 0 then hit control
ClickArea
; Immediately discard if the initial point isnt in the view rect
; IF PtInRect( pt, rView ) THEN BEGIN
SUBQ #2,SP
MOVE.L D5,-(SP) ; pass point
PEA rView(A3)
_PtInRect
TST.B (SP)+
BEQ ClickExit ; escape if not inside
clickWait
; Register conventions in this loop are: D7 used for last click
; D6 used for anchor for shift-selection
; D5 used for new point
; D3 used for new cell
; A4,A3 used for list handle,pointer
; A2 used for routine to call repeatedly
; call the clikProc if there is one (current mouse point is in mouseLoc)
MOVE.L lClikLoop(A3),D0 ; get the click proc
BEQ.S @noClickProc ; => there isnt one
MOVE.L D0,A0 ; get the proc
JSR (A0) ; and call it
BEQ clickExit ; => user said to abort!
@noClickProc
; now do any autoscrolling that is required
MOVE.L D5,D0 ; get working copy of point (h coordinate first)
MOVEQ #0,D2 ; horizontal delta := 0
BTST #lDoHAutoscroll,listFlags(A3) ; horizontal auto-scrolling?
BEQ @noHAutoScroll ; no, dont do any horizontal auto-scrolling
CMP.W rView+left(A3),D0 ; is pt.h <= rView.left
BGT.S @4 ; and
MOVE.W visBounds+left(A6),D3 ; visible.left > databounds.left
CMP.W visible+left(A3),D3
BGE.S @4
MOVEQ #-1,D2 ; delta fields:=-1
@4
ADDQ #1,D0
CMP.W rView+right(A3),D0 ; is pt.h >= rView.right
BLT.S @5 ; and
MOVE.W visBounds+right(A6),D3 ; visible.right < databounds.right
CMP.W visible+right(A3),D3
BLE.S @5
MOVEQ #1,D2 ; delta fields:=1
@5
@noHAutoScroll
SWAP D0 ; get v coordinate now
MOVEQ #0,D1 ; vertical delta := 0
BTST #lDoVAutoscroll,listFlags(A3) ; vertical auto-scrolling?
BEQ @noVAutoScroll ; no, dont do any vertical auto-scrolling
CMP.W rView+top(A3),D0 ; is pt.v <= rView.top
BGT.S @6 ; and
MOVE.W visBounds+top(A6),D3 ; visible.top > databounds.top
CMP.W visible+top(A3),D3
BGE.S @6
MOVEQ #-1,D1 ; delta fields:=-1
@6
ADDQ #1,D0
CMP rView+bottom(A3),D0 ; is pt.v >= rView.bottom
BLT.S @7 ; and
MOVE visBounds+bottom(A6),D3 ; visible.bottom < databounds.bottom
CMP visible+bottom(A3),D3
BLE.S @7
MOVEQ #1,D1 ; delta fields:=1
@7
@noVAutoScroll
MOVE.B D1,D3 ; is any auto-scrolling desired?
OR.B D2,D3
BEQ.S @noAutoScroll ; no, get out of here
LEA -10(SP), SP ; Reserve space for call to ScrollDelay
; calculate the number of items to pass to ScrollDelay
MOVE.L visBounds+botRight(A6), D0
TST.W D2 ; Scrolling horizontally?
BEQ.S @tryV ; Skip if not.
SUB.W visBounds+left(A6), D0 ; Number of horizontal items visible
@tryV
TST.W D1 ; Scrolling vertically?
BEQ.S @notV ; Skip if not.
SWAP D0 ; Get visible.bottom
SUB.W visBounds+top(A6), D0 ; Number of vertical items visible
@notV
MOVE.W D0, -(SP) ; itemsVisible
MOVE D2,-(SP) ; dh
MOVE D1,-(SP) ; dv push params for ListScroll
MOVE.L A4,-(SP) ; list handle
SUBQ.L #4, SP
_TickCount ; Get a time stamp.
MOVE.L (SP)+, D1 ; Copy it into D1.
MOVE.L D1, 10(SP) ; And put it on the stack for ScrollDelay.
move.l ExpandMem,A0 ; A0 -> ExpandedMem <SM4>
move.l ExpandMemRec.emStartTicks(A0),D0 ; See if we need to set this. <SM4>
BNE.S @startTicksOK ; Skip if not.
MOVE.L D1, D0 ; Use TickCount value.
MOVE.L D0,ExpandMemRec.emStartTicks(A0) ; Put it in startTicks. <SM4>
@startTicksOK
MOVE.L D0,14(SP) ; copy startTicks to stack for ScrollDelay.
_LScroll
_ScrollDelay
ADDQ.L #2, SP ; Toss the result
@noSD
BRA.S @doneAutoScroll
@noAutoScroll
move.l ExpandMem,A0 ; A0 -> ExpandedMem <SM4>
move.l #0,ExpandMemRec.emStartTicks(A0) ; Clear out startTicks we're not scrolling anymore <SM4>
@doneAutoScroll
; pin point to the visible rect and then do auto-scroll
SUBQ #4,SP ; pin point to view rect
PEA rView(A3)
MOVE.L D5,-(SP) ; pass point
_PinRect
MOVE.L (SP)+,D5 ; pinned point
; Turn mouse point into a cell coordinate
; i.v:=visible.top + (pt.v-rView.top) DIV cellSize.v;
; i.h:=visible.left + (pt.h-rView.left) DIV cellSize.h;
MOVE.L D5,-(SP) ; subract from this point
MOVE.L rView+topLeft(A3),-(SP) ; subtract this point
PEA 4(SP) ; act on point
_SubPt
MOVEQ #0,D3
MOVE (SP)+,D3 ; get v
DIVU cellSize+v(A3),D3
ADD visible+top(A3),D3
SWAP D3 ; put v in lower half of D3
MOVEQ #0,D0
MOVE (SP)+,D0 ; get h
DIVU cellSize+h(A3),D0
ADD visible+left(A3),D0
MOVE.W D0,D3 ; put h in lower half of D3
; If the current click in same location, skip the selection
CMP.L D3,D7
BEQ clickScan
; if first time through, then set up our state. Check for shift, command, or
; nothing in modifier byte. Initialize according to which it is
ChkKeys
BCLR #7,12(A6) ; test and say its not the first time
BEQ CallRtn ; => doesnt feel like the first time
CMP.L lastClick(A3),D3 ; are we in the same cell as before?
SEQ 18(A6) ; assume double click if so
MOVE.L D3,lastClick(A3) ; save new click
; set low modifier byte to $FF if cell not selected, $00 if selected. This is later
; used if whatever were doing is affected by state of first cell.
MOVEM.L D3/D7,-(SP) ; save new and old cells
MOVE.L D3,D7 ; get new cell in D7
BSR CellToIndex ; and index in D3
st d4 ; default d4 to $FF <ngk 29July90>
cmp.w maxIndex(a3),d3 ; is d3 out of range (clicked on nothing below last item)? <ngk 29July90>
bhi.s @1 ; if so, then clicked "cell" was not selected <ngk 29July90> <ngk 17Aug90>
TST.B cellArray(A3,D3) ; if selected, turn cells off
SPL D4 ; get into D4
@1 MOVE.B D4,13(A6) ; and save in low modifier byte (not used)
MOVEM.L (SP)+,D3/D7 ; restore new and old cells
; test for double click. If the mouse is in the same cell as the last click, the elapsed interval
; is within doubleTime and the mouse is within a few pixels of clikLoc, then return true.
; If an app doesnt want to get two double clicks from a triple click, then it must tweek clikTime
; upon receiving a double click.
SUBQ #4,SP ; make room for result
_TickCount ; get ticks into D0
; *** this should use the time of the event instead!!!
MOVE.L (SP),D0 ; and leave on stack
SUB.L clikTime(A3),D0 ; see if double click
MOVE.L (SP)+,clikTime(A3) ; but first save new time
CMP.L doubleTime,D0 ; compare with lowMem doubleTime
SLE D1 ; set a flag
NOT.B D4 ; make D4=1 if cell selected
AND.B D4,D1 ; dclick if cell selected
AND.B D1,18(A6) ; doubleTime
MOVE.L clikLoc(A3),-(SP) ; make a slop rect
MOVE.L (SP),-(SP) ; out of the last click location
MOVE.L SP,-(SP) ; push a pointer to the rect
MOVE.L #$FFFDFFFD,-(SP) ; make the slop -3,-3
_InsetRect ; make last point a rect
SUBQ #2,SP ; room for boolean result
MOVE.L D5,-(SP) ; push current mouse point
PEA 6(SP) ; push pointer to rect
_PtInRect ; is the point in the slop?
MOVE.B (SP)+,D1 ; get result
AND.B D1,18(A6) ; and it into our mondo result
ADDQ #8,SP ; strip off the rect
MOVE.L D5,clikLoc(A3) ; update click location
; This horrendous bit of code figures out how selections will be done based on the
; selection flags and the modifier keys.
DoSelect
BTST #LOnlyOne,14(A6) ; Are multiple selections allowed?
BNE OneClick ; => no, allow only one at a time
BTST #CmdKey,12(A6) ; is it command?
BEQ.S TryShft ; => nope, try for shift
CmdClick
BSET #LUseSense,14(A6) ; use sense flag we just set up
Cmd2Click
BSET #LNoExtend,14(A6) ; dont extend current selection
BSET #LNoRect,14(A6) ; dont drag out a rect
MOVE.L D3,D6 ; set anchor to new cell
BRA TryDisJoint ; => use ShiftDown, check for deselect all
; If shift, use the upper left selection and the lower right selection as the old selection
; rect. If no cells are selected, the new cell is used instead. Future changes to the
; selection will be based on this rect, which is saved on the stack. The anchor is the
; point from which the rectangle will grow. If you click above the current rect, then the
; botright of the rect becomes the anchor, if below, the topleft becomes anchor.
TryShft
BTST #ShiftKey,12(A6) ; is it shift?
BEQ.S GotNoMods ; => no modifiers to speak of
ShiftClick
MOVE.L D3,-(SP) ; save current click
MOVE.L #$7FFF7FFF,D0 ; choose largest possible topleft
MOVE.L D0,D1 ; get D1 = #$80008000
NOT.L D1 ; and smallest possible botRight
LEA GetBounds,A2 ; get upper left and lower right of selections
BSR DoSelLoop ; into D0 and D1
MOVE.L (SP)+,D3 ; restore cell
CMP.L #$80008000,D1 ; any selections?
BEQ.S GetTopLeft ; => no, use cell
BTST #LNoExtend,14(A6) ; do we need to anchor?
BEQ.S GetAnchor ; => yes, use bounds
GetTopLeft
MOVE.L D3,D7 ; where cellToIndex can get it
MOVE.L D3,D0 ; use as default topleft
MOVE.L D3,D1 ; and default botright
GetAnchor
MOVE.L D0,(SP) ; save topLeft of rect
MOVE.L D1,4(SP) ; save botRight of rect
; anchor is topleft unless click.v<top or click.h<left or click=topleft
MOVE.L D3,D2 ; save new click in D2
MOVE.L D0,D6 ; assume anchor is topleft
CMP.L D0,D3 ; special case if click on topleft
BEQ.S BRAnchor ; => anchor is botRight
CMP.W D0,D3 ; check left first
BLT.S BRAnchor ; => click.h<left
SWAP D3
SWAP D0
CMP.W D0,D3 ; check top
BGE.S TLAnchor ; => click.v<top
BRAnchor
MOVE.L D1,D6 ; else anchor is botright
TLAnchor
MOVE.L D2,D3 ; restore new click
BRA.S TryDisJoint ; =>check for deselect all
; It goes here if no modifier keys are held down...
; If LExtendDrag is set, then we want to select cells as we drag over them
GotNoMods
BTST #LExtendDrag,14(A6) ; should we select as we go?
BNE.S DoExtend ; => yes, extend selections
OneClick
LEA NoMods,A2 ; a simple click on a selected cell does nothing
TST.B 13(A6) ; was it already selected?
BEQ.S CallRtn ; => yes, its a no-op for double clicking
MOVE.L A2,-(SP) ; use NoMods as routine
BRA.S ClearAll ; => and deselect everything
DoExtend
BSET #LNoExtend,14(A6) ; dont want to extend current rect
BTST #LNoRect,14(A6) ; should we drag out a rect?
BEQ.S ShiftClick ; => yes, use shift code
BCLR #LUseSense,14(A6) ; else use command code
BSET #LNoDisjoint,14(A6) ; but without disjoint selections
BRA.S Cmd2Click ; and without sense
TryDisJoint
LEA ShiftDown,A2 ; use shiftDown routine
MOVE.L A2,-(SP) ; save routine
BTST #LNoDisJoint,14(A6) ; should we deselect?
BEQ.S UseStkRtn ; => no, disjoint ok
ClearAll
LEA clrSel,A2 ; else no keys down
BSR DoSelLoop ; clear all selections
UseStkRtn
MOVE.L (SP)+,A2 ; get the routine to use
CallRtn
JSR (A2) ; call the proper routine
clickScan
SUBQ #4,SP ; get current mouse
MOVE.L SP,-(SP)
_GetMouse
MOVE.L (SP)+,D5 ; and save in var
MOVE.L D5,mouseLoc(A3) ; save for defProc
SUBQ #2,SP ; is mouse still down
_StillDown
TST.B (SP)+
BEQ clickExit ; escape on mouseUp
BRA clickWait
; It hit in a control, look for our two
clickCont
MOVE.L r(A6),D7 ; get control handle
CMP.L hScroll(A3),D7 ; skip if not ours
BEQ.S @0
CMP.L vScroll(A3),D7
BNE ClickArea ; used to be "bne.s clickExit" <10>
@0
CMP.W #128,D6 ; indicators are 129-253
BLE.S @2
CMP.W #254,D6
BGE.S @2
; its an indicator!
SUBQ #2,SP
MOVE.L D7,-(SP)
_GetCtlValue ; leave on stack
SUBQ #2,SP
MOVE.L D7,-(SP) ; pass ctl Handle
MOVE.L D5,-(SP) ; pass point
MOVE.L minusOne,-(SP)
_TrackControl ; leave junk on stack
MOVE.L D7,-(SP) ; pass point
_GetCtlValue
MOVEQ #0,D0 ; zero other dimension
MOVE (SP)+,D0 ; get new value
SUB (SP)+,D0 ; subtract old value
BEQ.S clickExit
CMP.L hScroll(A3),D7 ; is this horizontal scroll?
BEQ.S @1
SWAP D0
@1
MOVE.L D0,-(SP) ; push both deltas
MOVE.L A4,-(SP)
_LScroll
BRA.S clickExit
; For all other parts, just track incrementally with proc
@2
MOVE.L D7,-(SP) ; pass ctl
MOVE.L A4,-(SP) ; handle is refcon-to-be
_SetCRefCon
SUBQ.L #4, SP
_TickCount ; Get a time stamp.
move.l ExpandMem,A0 ; A0 -> ExpandedMem <SM4>
move.l (SP)+,ExpandMemRec.emStartTicks(A0) ; Put it in startTicks. <SM4>
SUBQ #2,SP
MOVE.L D7,-(SP) ; pass ctl Handle
MOVE.L D5,-(SP) ; pass point
PEA ClickTrack ; track proc
_TrackControl
ADDQ #2,SP
clickExit
move.l ExpandMem,A0 ; A0 -> ExpandedMem <SM4>
move.l #0,ExpandMemRec.emStartTicks(A0) ; Clear out startTicks <SM4>
ADDQ #8,SP ; strip off the rect
Std10Exit
MOVEQ #10,D0
BRA StdExit
;------------
; NoMods
; This routine is called repeatedly if neither shift nor command is held down
; The last click is in D7, the new one is in D3. First time through, last click
; is invalid, but ListSetSelect just ignores it.
NoMods
MOVEQ #0,D6 ; deselect old click
BSR.S CmdD1 ; (in D7)
MOVEQ #-1,D6 ; select new click
MOVE.L D3,D7
CmdD1
MOVE.B D6,-(SP) ; on or off?
MOVE.L D7,-(SP) ; push cell
MOVE.L A4,-(SP) ; and list handle
_LSetSelect ; select/deselect it
RTS
;------------
; GetBounds
; This routine is called repeatedly by DoSelLoop. It calculates the
; smallest rect that contains all the selections, and puts the topleft
; cell in D0 and the botRight cell in D1
GetBounds
BSR IndexToCell ; index in D3 -> cell in D7
MOVEQ #1,D2 ; go through this code twice
@1
CMP.W D0,D7 ; check top/left
BGE.S @2 ; => not less
MOVE.W D7,D0 ; else get new top/left
@2
CMP.W D1,D7 ; check bottom/right
BLE.S @3 ; => not greater
MOVE.W D7,D1 ; else get new bot/right
@3
SWAP D7
SWAP D0
SWAP D1
DBRA D2,@1 ; loop once
RTS
;------------
; ShiftDown
; This routine is called repeatedly while the shift key is held down.
; Its goal is to form a selection rectangle on the screen. The last
; rect selected is on the stack. Select everything in the current rect
; and deselect things in the old rect but not in the new one.
; The new cell position is in D3. The "anchored position" is in D6.
; Note: D3 (new click) saved, and D7 (old click) restored.
; if LExtendDrag then anchor was set to newclick (D6=D3)
; if LUseSense then use 13(A6) to determine select on/off
; if LNoRect then set new anchor point each time through the loop
; and we dont want to deselect behind us, so ignore old rect
ShiftDown
MOVEM.L D3/A2,-(SP) ; self preservation
MOVE.L D6,-(SP) ; pass anchor
MOVE.L D3,-(SP) ; and new position
PEA r(A6) ; save new rect
_Pt2Rect ; make it a rect
ADDQ #1,r+bottom(A6)
ADDQ #1,r+right(A6)
LEA 12(SP),A2 ; assume we want to use old rect
BTST #LNoRect,14(A6) ; do we?
BEQ.S @0 ; => yes
MOVE.L D3,D6 ; else next anchor is current point
LEA r(A6),A2 ; and use new rect instead of old rect
@0
MOVE.L A2,-(SP) ; get the union of the
PEA r(A6) ; last rect and the new rect
MOVE.L A2,-(SP) ; and save as union (jack)
_UnionRect
MOVE.W (A2)+,D5 ; get top into D5
MOVE.L (A2)+,D2 ; get bottom into D2
MOVE.W (A2),D4 ; get right into D4
MOVE.L -(A2),D3 ; point at left for D3
@1
MOVE.W (A2),D3 ; need to load left each time through
@2
SUBQ #2,SP ; make room for boolean result
MOVE.W D3,-(SP) ; pass the cell h=low word
MOVE.W D5,-(SP) ; v=high word
PEA r(A6) ; if the cell in the rect
_PtInRect ; then select, else deselect
MOVE.B (SP)+,D0 ; get result
BNE.S @11 ; => always do points in rect
BTST #LNoRect,14(A6) ; if this bit set, dont off outsiders
BNE.S @12 ; => skip the selection
BTST #LNoExtend,14(A6) ; like it says above
BNE.S @12
@11
BTST #LUseSense,14(A6) ; use sense?
BEQ.S @3 ; => not today
AND.B 13(A6),D0 ; get selection sense
@3
MOVE.B D0,-(SP) ; only on if in rect and turn on
MOVE.W D3,-(SP) ; push the cell again
MOVE.W D5,-(SP) ; push point.v (dont assume PtInRect will leave it in D1! SHeesh!) <SM7> SAM
MOVE.L A4,-(SP) ; and the handle
_LSetSelect ; and select/deselect the cell
@12
ADDQ #1,D3 ; check next horizontal
CMP.W D3,D4 ; hit right yet?
BGE.S @2 ; => nope, go that way --->
ADDQ #1,D5 ; check next vertical
CMP.W D5,D2 ; hit bottom yet?
BGE.S @1 ; => no spanks to you!
MOVE.L r(A6),12(SP) ; save new rect as old rect
MOVE.L r+4(A6),16(SP)
MOVEM.L (SP)+,D7/A2 ; get old click, our address
RTS
;-------------------------------------------------------------------------------
; PROCEDURE ClickTrack( ctl: ControlHandle; part: INTEGER );
; 10 8
ClickTrack
LINK A6,#0 ; hum de dum...
MOVE.L D3,-(SP) ; save this one
MOVE.L 10(A6),A0 ; get control handle
MOVE.L A0,D3 ; save in D3
MOVE.L (A0),A0
MOVE.L contrlRfCon(A0),A0 ; get list handle
MOVE.L A0,D2 ; save list Handle in D2
MOVE.L (A0),A0 ; and dereference
LEA visible(A0),A1 ; point to rect
CMP.L hScroll(A0),D3 ; hScroll
BEQ.S @3
SUBQ #2,A1 ; point to high order words
@3
MOVEQ #1,D1 ; assume change
MOVEQ #-20,D0 ; offset constant
ADD.W 8(A6),D0 ; get the part (zero based)
BMI.S @noScroll ; => part too small
SUB.W #2,D0
BMI.S @0 ; line at a time
CMP #1,D0
BGT.S @noScroll ; => part too big
MOVE.L (A1)+,D1 ; get negative delta from visible
SUB.L (A1)+,D1 ; in low order
NOT.W D1 ; get delta -1
BNE.S @0 ; => special case for one cell wide
ADDQ #1,D1
@0
BTST #0,D0 ; see if odd therefore down
BNE.S @1
NEG D1 ; flip sense
@1
SWAP D1 ; put in high order
CLR D1 ; low order = o
; Calculate the number of visible items for ScrollDelay. Assume vertical unless told otherwise.
MOVE.W visible+bottom(A0), D0
SUB.W visible+top(A0), D0
CMP.L hScroll(A0),D3 ; hScroll
BNE.S @2
SWAP D1
; It was horizontal - correct our assumption.
MOVE.W visible+right(A0), D0
SUB.W visible+left(A0), D0
@2
SUBQ.L #2, SP ; Make room for ScrollDelay return
move.l ExpandMem,A0 ; A0 -> ExpandedMem <SM4>
move.l ExpandMemRec.emStartTicks(A0),-(SP) ; startTicks for ScrollDelay <SM4>
SUBQ.L #4, SP ; Reserve space for actionTicks
MOVE.W D0, -(SP) ; itemsVisible for ScrollDelay
MOVE.L D1,-(SP) ; pass deltas
MOVE.L D2,-(SP) ; pass list handle
SUBQ.L #4, SP
_TickCount
MOVE.L (SP)+, 10(SP)
_LScroll
_ScrollDelay
ADDQ.L #2, SP ; Toss the result
@noSD
@noScroll
MOVE.L (SP)+,D3
MOVE.W #6,D0
NonStd3
UNLK A6
MOVE.L (SP)+,A0 ; get RTS
ADD.W D0,SP ; strip params
JMP (A0) ; and return
;------------
; Routine ListHilite is called by ListSetSelect instead of ListDraw
; This is because the routine is only called if the cell is already
; drawn and the sense needs to be reversed. Draw procs can simply
; call draw when they get the hilite command, or they can do something
; faster. Only do if list is active.
ListHilite
BSR StdEntry ; set things up
TST.B LActive(A3) ; are we active?
BEQ.S @notActive ; => no, skip it <16Dec85>
BSR.S DoHilite ; invert the cell
@notActive
bra.s Std8Exit ; strip 8 bytes of params
DoHilite
MOVE.L D6,-(SP) ; save D6
MOVE.W #LHiliteMsg,D6 ; and set with hilite command
BRA.S DDraw1
;----------------------------------------------------------------------
;
; PROCEDURE ListDraw( c: Point; h: ListHandle );
; 12 8
; Draws the given cell from the given list
; DoDraw is split off to be used internally
;
;----------------------------------------------------------------------
ListDraw
BSR StdEntry ; ho hum again
BSR.S DoDraw ; do the actual drawing
Std8Exit
MOVEQ #8,D0
BRA StdExit
;------------
; DoDraw -- This is the drawing stuff that must be done for
; each cell drawn. It is called by ListDraw and ListUpdate.
; It assumes that StdEntry has been called (cell in D7 etc.).
DoDraw
MOVE.L D6,-(SP) ; save D6
MOVE.W #LDrawMsg,D6 ; code = Draw
DDraw1
BTST #noDraw,ListFlags(A3) ; is drawing on?
BNE.S DoDrExit ; => nope, just exit
BSR MyFind ; validate the cell
BEQ.S DoDrExit ; skip if bad cell
SUBQ #4,SP ; save the current clipn regn
_NewRgn ; get a new region
MOVE.L (SP),-(SP) ; and save it on the stack
_GetClip ; set it to the current clip
PEA r(A6) ; get the cells rect into r
MOVE.L D7,-(SP) ; pass the cell
MOVE.L A4,-(SP)
_LRect ; get the cells rect
SUBQ #2,SP ; room for result
PEA rView(A3) ; get intersection of rView
PEA r(A6) ; and current cell
MOVE.L (SP),-(SP) ; and save into r
_SectRect
TST.B (SP)+ ; is cell visible?
BEQ.S DoneDraw ; => no, get clip off stack & exit
PEA r(A6) ; push rect for our view
_ClipRect ; and set clip to it
MOVE.L (SP),-(SP) ; get original clip
MOVE.L port(A3),A2
MOVE.L clipRgn(A2),-(SP) ; intersect with our new rect
MOVE.L (SP),-(SP) ; and save in clip rgn
_SectRgn
; call the defprocs routine to draw/hilite the cell
BSR.S GetTheListProc ; what it says, but into A0
BEQ.S DoneDraw ; => oh no! no defproc
MOVE.W D6,-(SP) ; push call
MOVE.W Index(A6),D0 ; get an index for this cell
TST.B cellArray(A3,D0.W) ; and check it
SMI D0 ; set D5 if selected
AND.B LActive(A3),D0 ; and in active byte
MOVE.B D0,-(SP) ; pass select boolean
PEA r(A6) ; and here is the cells rect
MOVE.L D7,-(SP) ; here is the cell
MOVE.W offset(A6),-(SP) ; this is the offset into the data
MOVE.W len(A6),-(SP) ; and this is how long the data is
MOVE.L A4,-(SP) ; and a handle to our record
JSR (A0) ; call the defproc
BSR.S UnlockLProc ; now unlock that poor defproc...
DoneDraw
MOVE.L (SP),-(SP) ; dup rgn on stack
_SetClip ; restore it
_DisposRgn ; and erase last traces
DoDrExit
MOVE.L (SP)+,D6 ; restore D6
RTS
;------------
; GetTheListProc
; This routine makes sure that the listproc stored in the list record
; as ListDefHandle is loaded into memory and locked down.
GetTheListProc
MOVE.L ListDefHandle(A3),D0 ; get the handle
BEQ.S @done ; => didnt get one
; Some programmers are pretty slimy and load an LDEF that is empty. They then <LW3> fau
; stuff some code into it. However, since HLOCK does not flush the cache anymore, <LW3> fau
; the code that they stuff into it might not get written back to memory. To solve this, <LW3> fau
; we check here whether the LDEF resource size is less than, say, 32 bytes. If so, we <LW5> chp
; assume that they have already loaded the LDEF and modified it, so we flush the cache <LW3> fau
; for them.
MOVE.L ListDefHandle(A3),A0 ; get the handle <LW3> fau
_GetHandleSize ; How big is our LDEF Handle <LW3> fau
cmp.l #32,D0 ; Is it "small" <LW5> chp
bhi.s @RealLDEF ; no, don't flush the cache <LW3> fau
jsr ([jCacheFlush]) ; else, flush the caches. <LW3> fau
@RealLDEF ; <LW3> fau
MOVE.L ListDefHandle(A3),A0 ; get the handle <LW3> fau
MOVE.L A0,-(SP) ; and load the defProc
_LoadResource
MOVE.L (A0),D0 ; Is the handle still empty? <S412 29Feb88>
BEQ.S @done ; Yes, skip <S412 29Feb88>
_HLock ; lock it down
MOVE.L (A0),A0 ; and get its address
MOVEQ #-1,D0 ; make sure NE
@done
RTS
;------------
; UnlockLProc
; This routine unlocks the List defProc and leaves it floating around.
UnlockLProc
MOVE.L ListDefHandle(A3),A0 ; get the handle
_HUnlock ; and unlock it
RTS
;-----------------------------------------------------------------------------
;
; Procedure ListCellSize (cSize: Point; h: ListHandle );
; 12 8
; ListCellSize sets the cellSize field in the specified list and updates the
; visible rect accordingly.
;
;-----------------------------------------------------------------------------
ListCellSize
BSR StdEntry ; hum ho
MOVE.L D7,cellSize(A3) ; set cellsize
BSR.S SetVisible ; set visible and fit flags
LCS1
MOVE.L A4,-(SP) ; this is the handle
BSR SetHilite ; check databounds and position thumb
BRA Std8Exit ; use standard exit
;------------
; SetVisible is a utility used to set the visible rect based on the
; cellsize and the lists window (rView). It also sets flags that
; indicate whether the cells fit exactly into the window.
; A trick is used to index into ListFlags using D3. The bit positions
; of the flags used to indicate exact fit (LHBadFit and LVBadFit) were
; chosen to be equal to h and v.
;
; **** Note, we now clobber D4 in this routine
; The trick above did not work out so well. lDoHAutoscroll was assigned the same position in the
; flags byte as LVBadFit. the gp 12/2/86 fix was done to solve that problem as the lDoHAutoscroll
; EQU had already been published and LVBadFit has not been published.
SetVisible
MOVEQ #v,D3 ; do vertical half first
MOVEQ #LVBadFit,D4 ; will need this to set/clear the right bit - gp 12/2/86
BSR.S CellCommon
MOVEQ #h,D3 ; then do horizontal
MOVEQ #LHBadFit,D4 ; will need this to set/clear the right bit - gp 12/2/86
CellCommon
MOVEQ #0,D0 ; division needs a long
ADD.W rView+bottom(A3,D3),D0 ; add bottom/left
SUB.W rView+top(A3,D3),D0 ; subtract top/right
ADD.W cellsize(A3,D3),D0 ; add cellsize.v/h
SUBQ #1,D0 ; and subtract 1
DIVU cellsize(A3,D3),D0 ; divide by cellsize.v/h
ADD.W visible+top(A3,D3),D0 ; add visible top/left
MOVE.W D0,visible+bottom(A3,D3) ; and update visible bottom/right
BCLR D4,ListFlags(A3) ; assume cells fit exactly - gp 12/2/86 {was using D3}
SWAP D0 ; get remainder
ADD.W #1,D0 ; compensate for rounding
CMP.W cellSize(A3,D3),D0 ; hit a cell boundary?
BEQ.S @1 ; => Hey boss, they do fit!
BSET D4,ListFlags(A3) ; else need we say more room - gp 12/2/86 {was using D3}
@1
RTS
;-----------------------------------------------------------------------------
;
; Procedure ListSize (w,h:Integer; h: ListHandle );
; 14,12 8
; ListSize is called by the user to resize his list. It should be called
; immediately after the call to sizeWindow. w and h are the width and height
; of the list, not including the scroll bars.
;
; ListSize sets the viewRect to the specified rect, and sets flags that
; determine whether or not cells fit exactly into the view. If not, then
; a flag is set that indicates that scrolling must be extended by one cell
; in the direction(s) that doesnt fit. It also updates the scroll bars
; to fit within their new confines.
;
;-----------------------------------------------------------------------------
ListSize
BSR StdEntry ; hum ho (get h,w into D7)
BSR.S InvalStuff ; invalidate scroll bars + grow
@12
; save original RView so we can validate the part that was scrollRected
MOVE.L rView+4(A3),-(SP) ; except for the part scrolled
MOVE.L rView(A3),-(SP) ; push rect
MOVE.L rView(A3),D0 ; get current topLeft
ADD.W D7,D0 ; add width to left
SWAP D0
SWAP D7
ADD.W D7,D0 ; add height to right
SWAP D0 ; get botRight
MOVE.L D0,rView+botRight(A3) ; and update viewRect
BSR.S SetVisible ; set visible and flags
MOVE.L hScroll(A3),D4 ; horizontal scroll bar?
MOVEQ #top,D0 ; indicate which scroller
BSR SetScRect ; set scroll rect
MOVE.L vScroll(A3),D4 ; vertical scroll bar?
MOVEQ #left,D0 ; indicate which bar
BSR.S SetScRect
MOVE.L visible(A3),r(A6) ; make a copy of visible
MOVE.L visible+4(A3),r+4(A6)
BSR JustRect ; align rect for optimal display
BNE.S @3 ; => no change, no update
PEA rView(A3) ; else invalidate entire view
_InvalRect
; now slide original rView to its JustRected position for validation
BSR GetOffset ; get offset between r and visible
MOVE.L SP,-(SP) ; push pointer to old rView (saved on stack)
MOVE.L D0,-(SP) ; push offset returned by GetOffset
_OffsetRect ; and get new rect
SUBQ #2,SP ; make room for result
PEA rView(A3) ; get intersection between view
PEA 6(SP) ; and new rect
MOVE.L (SP),-(SP) ; and save in new rect
_SectRect ; get intersection
TST.B (SP)+ ; ignore result
MOVE.L SP,-(SP) ; point to rect
bsr.s MyValidRect ; and validate it
CLR.W D0 ; move, but no update
BSR MoveRect ; now go move the thing
@3
ADDQ #8,SP ; strip rView off stack
BRA LCS1 ; recalc scroll settings, redraw
;------------
; InvalStuff is by ListSize to save a little code
; D0 indexes to the side of the rect that needs to grow to include
; the grow box. The scrollHandle is in D1.
InvalStuff
BTST #LHasGrow,ListFlags(A3) ; is there one?
BEQ.S @2 ; => no, nothing to do
MOVE.L SP,A2 ; and point to it
MOVEQ #top,D0 ; do vertical bar first
BSR.S @1 ; invalidate it
MOVEQ #left,D0 ; fall into routine for horizontal
@1
MOVE.L D0,D4 ; save index here
MOVE.L rView+4(A3),-(SP) ; default rect is the view
MOVE.L rView(A3),-(SP) ; get temp copy on stack
MOVE.L SP,A2 ; and point to it with A2
BSR GetScSize ; get scroll bar size in A2
NEG.W D4 ; need to index the other way...
ADD.W #16,6(A2,D4.W) ; stretch scroll bar for grow box
MOVE.L A2,-(SP) ; push for invalidate
_EraseRect
MOVE.L A2,-(SP) ; and for erase
_InvalRect
ADDQ #8,SP ; strip the rect
@2
RTS
;------------
; SetScRect is used by ListSize to move and resize the scroll bars when the
; window is grown. D0 flags which scroll bar is being set.
SetScRect
MOVE.L rView+4(A3),-(SP) ; push botRight of r
MOVE.L rView(A3),-(SP) ; and topLeft too
MOVE.L SP,A2 ; pass pointer to rect in A2
BSR GetScSize ; get scrolls rect (uses D0) into A2
TST.L D4 ; is control valid?
BNE.S @haveControl ; => yes, move the control
; the controls not valid. Erase the rect if there is a grow box
@noControl
BTST #LHasGrow,ListFlags(A3) ; is there a grow box?
BEQ.S @done ; => no, just exit
MOVE.L A2,-(SP) ; else erase the rect
_EraseRect
BRA.S @done ; and return
@haveControl
MOVE.L D4,-(SP) ; hide the control
_HideControl
MOVE.L D4,-(SP) ; push control handle
MOVE.L topLeft(A2),-(SP) ; push topLeft of control
_MoveControl ; and move it to new spot
MOVE.L D4,-(SP) ; now make it the new size
MOVE.L botRight(A2),D0 ; get width
SUB.W left(A2),D0
MOVE.W D0,-(SP) ; and push it
SWAP D0 ; get height
SUB.W top(A2),D0
MOVE.W D0,-(SP) ; and push it
_SizeControl
MOVE.L D4,-(SP) ; now show the control again
bsr.s MyShowControl
@done
MOVE.L A2,-(SP) ; validate the rect
bsr.s MyValidRect ; so it doesnt get drawn again
ADDQ #8,SP ; and strip the rect
RTS
;-----------------------------------------------------------------------------
;
; Procedure ListDoDraw (drawIt: BOOLEAN; h: ListHandle);
;
; Sets the flag that indicates whether or not drawing occurs as a
; result of List Add/Del Row/column calls and set/addto/clr cell calls.
;
;-----------------------------------------------------------------------------
ListDoDraw
BSR StdEntry
BSET #noDraw,ListFlags(A3) ; assume noDraw = TRUE
TST.B 12(A6) ; check the boolean
BEQ.S @1 ; => noDraw = TRUE
BCLR #noDraw,ListFlags(A3) ; noDraw = FALSE
MOVE.L A4,-(SP) ; push the list handle
BSR SetHilite ; and set the control values
@1
BRA Std6Exit
;-----------------------------------------------------------------------------
;
; Procedure ListNew (view,dBounds: Rect; CSize: cell; theProc:INTEGER; theWindow: WindowPtr;
; drawIt,hasGrow,scrollHoriz,scrollVert: BOOLEAN): ListHandle;
;
; Create a new list.
;
;-----------------------------------------------------------------------------
NLsVert EQU 8
NLsHoriz EQU NLsVert+2
NLhasGrow EQU NLsHoriz+2
NLdrawIt EQU NLhasGrow+2
NLWindow EQU NLdrawIt+2
NLProc EQU NLWindow+4
NLcSize EQU NLProc+2
NLBounds EQU NLcSize+4
NLview EQU NLBounds+4
NLResult EQU NLview+4
ListNew ; dont use StdEntry because no handles yet
LINK A6,#stdFrame ; standard frame
MOVEM.L ListMgrSavedRegisters,-(SP)
CLR.L NLResult(A6) ; assume some error
; First allocate memory for the data structure + the cellArray
; the cellArray is initialized to 0s, indicating no data yet.
MOVE.L NLBounds(A6),A2 ; get pointer to bounds rect
MOVEQ #0,D7
MOVEQ #0,D1
MOVE.W right(A2),D7 ; l := (right-left)*(bottom-top)*3
SUB.W left(A2),D7
MOVE.W bottom(A2),D1
SUB.W top(A2),D1
MULU D1,D7 ; get number of cells
LSL #1,D7 ; 2 bytes per cell
moveq #sizeList+2,D0 ; we need this mush room (said Alice)
add.l d7,d0
_NewHandle clear ; get handle in A0 (ignore result *** )
MOVE.L A0,A4 ; handle in A4 is our custom
MOVE.L (A4),A3 ; and get list pointer in A3
MOVE.L NLWindow(A6),port(A3) ; make specified window our port
BSR ListNewEntry ; get into our port, lock list
MOVE.L NLBounds(A6),a2 ; get pointer to bounds rect
MOVE.L (a2)+,dataBounds+topLeft(A3) ; dataBounds:=bounds
MOVE.L (a2),dataBounds+botRight(A3)
MOVE.W D7,maxIndex(A3) ; set max index for cellArray
; set up the default list fields
; the user can replace any of these settings after doing the ListNew, or by
; letting the defProcs Init function do it.
; Set viewRect, in global coordinates, to the values passed in r.
LEA rView(A3),A2 ; use A2 to index through record
MOVE.L NLview(A6),A1 ; get our view rect
MOVE.L (A1)+,(A2)+ ; set our viewRect to r
MOVE.L (A1),(A2)+
MOVEQ #0,D7 ; save cellSize.v in D7
MOVEQ #0,D6 ; save cellSize.h in D6
MOVE.W NLcSize+v(A6),D7 ; set specified cellSize
MOVE.W NLcSize+h(A6),D6
MOVE.L #0,indent(A3) ; default indent = 0
; visible := (right-left)/cellsize.h,(bottom-top)/cellsize.v)
; if cellsize := 0, then assign a size that is wide enough to fit all columns
; on the screen and tall enough for text in the current font.
SUBQ #8,SP ; make room for GetFontInfo record
MOVE.L SP,-(SP) ; point to it
_GetFontInfo ; and go get info
MOVE.L NLBounds(A6),A1 ; get boundsrect into A1
TST.W D7 ; cellSize.v = 0 ?
BNE.S @1 ; => no
MOVE.W (SP),D7 ; else cellSize.v := ascent+descent+leading
ADD.W 2(SP),D7 ; add descent
ADD.W 6(SP),D7 ; add leading
@1
TST.W D6 ; cellSize.h = 0 ?
BNE.S @2 ; => no
MOVE.L NLview(A6),A0 ; get r
MOVE.W right(A0),D6 ; else cellSize := r.right-r.left
SUB.W left(A0),D6
MOVE.W right(A1),D0 ; if bounds.right-bounds.left <> 0
SUB.W left(A1),D0
BEQ.S @2
DIVU D0,D6 ; then divide size.h by # of columns
@2
ADDQ #8,SP ; all done with font info
MOVE.L topLeft(A1),visible(A3) ; visible.topLeft := bounds.topLeft
MOVE.W D6,-(SP) ; push cellSize.h
MOVE.W D7,-(SP) ; push cellSize.v
MOVE.L A4,-(SP) ; push list handle
_LCellSize ; and set visible bottom, right
MOVE.B #1,LActive(A3) ; active = 1
MOVE.B NLdrawIt(A6),-(SP) ; push drawIt flag
MOVE.L A4,-(SP) ; and our handle
_LDoDraw ; and set our internal flag
; Set up the flag that says whether or not there is a grow box
TST.B NLhasGrow(A6) ; is there a grow box?
BEQ.S @0 ; => no
BSET #LHasGrow,ListFlags(A3) ; else say there is one
@0
TST.B NLsHoriz(A6) ; scrollHoriz?
BEQ.S @4 ; => no thanks, not today
MOVEQ #top,D0 ; use top index
SUBQ #4,SP ; make room for result
BSR ScrollNew ; get new control (trashes A2)
MOVE.L (SP)+,hScroll(A3) ; save control handle
BSET #lDoHAutoScroll,listFlags(A3) ; and allow autoscrolling
@4
TST.B NLsVert(A6) ; scrollVert?
BEQ.S @5 ; => thanks, but no
MOVEQ #left,D0 ; use left index
SUBQ #4,SP ; make room for result
BSR ScrollNew
MOVE.L (SP)+,vScroll(A3) ; save control handle
BSET #lDoVAutoScroll,listFlags(A3) ; and allow autoscrolling
@5
MOVE.L A4,-(SP) ; set control min, max
BSR SetHilite ; if necessary
NOT.L lastClick(A3) ; set invalid click of all ones
; set up a handle for the data
MOVEQ #0,D0 ; get a handle for data
_NewHandle clear ; make sure theyre 0 length
MOVE.L A0,D0 ; test result
BEQ.S NewExit ; => couldnt get memory
MOVE.L A0,cells(A3) ; save handle
; initialize the list defProc handle
MOVE.W #MapTrue, RomMapInsert ; insert ROM map <SM3>
MOVE.W NLProc(A6),D3 ; get the defProc ID
SUBQ #4,SP ; make room for function result
MOVE.L #'LDEF',-(SP) ; push LDEF resource type
MOVE.W D3,-(SP) ; push defProc ID1
_GetResource ; get it
MOVE.L (SP)+,ListDefHandle(A3) ; save list def handle
; Now call the defProc to let it do Init if it so desires.
; Do it last so it can muck with all our other Init defaults.
BSR GetTheListProc ; get listproc and lock down
BEQ.S @noLDEF ; => oops, we didnt get it
MOVE.W #LInitMsg,-(SP) ; do init call to defproc
MOVE.L #0,-(SP) ; this results in an EVEN address
MOVE.L #0,-(SP) ; for the lRect ALWAYS
MOVE.L #0,-(SP)
MOVE.W #0,-(SP) ; CLR's are twice as slow
MOVE.L A4,-(SP) ; push handle
JSR (A0) ; init it
BSR UnlockLProc ; then unlock it
@noLDEF
MOVE.L A4,NLResult(A6) ; return result
NewExit
MOVE.W #26,D0 ; strip 26 bytes of parameters
BRA StdExit ; unlock list, restore port, strip params...
;-------------------------
; ScrollNew -- gets a new scroll bar; returns the handle on the stack
;
; A2 points to the temp rect used here. D0 indexes to top or left. The result of this
; routine should be placed on the stack (below the RTS). A3 points to window list record.
ScrollNew
MOVE.L NLview(A6),A2 ; get new view
MOVE.L 4(A2),-(SP) ; push low word of r
MOVE.L (A2),-(SP) ; and high word too
MOVE.L SP,A2 ; pass pointer to rect in A2
BSR.S GetScSize ; calculate the size
SUBQ #4,SP ; room for horizontal handle
MOVE.L port(A3),-(SP) ; push port address
MOVE.L A2,-(SP) ; point to rect
CLR.W len(A6) ; use len as a null string
PEA len(A6) ; point to null string
MOVE.B NLdrawIt(A6),-(SP) ; push visible flag
CLR.L -(SP) ; 0,0: int
CLR.W -(SP) ; 0: int
MOVE.W #scrollBarProc,-(SP) ; proc ID: integer
CLR.L -(SP) ; 0
_NewControl
MOVE.L (SP)+,12(SP) ; put result below rect and RTS
ADDQ #8,SP ; strip off rect
RTS
;------------
; GetScSize is a utility that is used
; by ScrollNew and ListSize to calculate the size of the scroll bar
; based on the lists rect.
; A pointer to a temp rect containing the current rView is in A2.
; The index D0 is used to access either vertical (D0=0) or
; horizontal elements (D0=2).
GetScSize
MOVE.W 4(A2,D0),D1 ; top/left := bottom/right + 1
ADD.W #1,D1
MOVE.W D1,topLeft(A2,D0.W)
ADD.W #14,D1 ; bottom/right := bottom/right + 15
MOVE.W D1,botRight(A2,D0.W)
MOVE.L A2,-(SP) ; point to rect
MOVE.L MinusOne,-(SP) ; -1,-1
_InsetRect ; make it a bit bigger
RTS
;----------------------------------------------------------------------
;
; PROCEDURE ListDispose( h: ListHandle );
;
; Disposes of the given list.
;
;----------------------------------------------------------------------
ListDispose
BSR StdEntry ; ho hum again
BSR GetTheListProc ; get listproc and lock down
BEQ.S @noDefProc ; => no defproc found
MOVE.W #LcloseMsg,-(SP) ; do close call to defproc
CLR.L -(SP) ; no select, rect or data
CLR.L -(SP)
CLR.L -(SP)
CLR.W -(SP)
MOVE.L A4,-(SP) ; push handle
JSR (A0) ; init it
BSR UnlockLProc ; then unlock it
@noDefProc
MOVE.L cells(A3),A0 ; dump the data
_DisposHandle
MOVE.L hScroll(A3),D0 ; is there a horizontal guy?
BEQ.S @noHScroll ; => nope
MOVE.L D0,-(SP) ; else push the control handle
_DisposControl ; and down the dumper
@noHScroll
MOVE.L vScroll(A3),D0 ; is there a vertical guy?
BEQ.S @noVScroll ; => nope
MOVE.L D0,-(SP) ; else push the control handle
_DisposControl ; and down the dumper
@noVScroll
MOVE.L A4,A0 ; crumple up the list
_DisposHandle ; and file it away
move.l #0,a4 ; put a NIL in the handle so we dont crash on unlock
BRA Std4Exit
;--------------------------------------------------------------------------------------
;
; Function ListAddColumn ( count, colNum: INTEGER; h: ListHandle ): INTEGER;
; 14 12 8 16
; ListAddColumn inserts count columns at colNum. If colNum is not within dataBounds, it
; adds new last columns. The new cells added are all empty (have 0 length). The column
; number of the first column added is returned. DataBounds is updated. If there are no cells
; (because dataBounds.top=dataBounds.bottom), no cells are added, but dataBounds is still
; extended.
;
; A new cell is added by inserting a new offset into the cellArray. This is accomplished
; by shifting the array, from the insertion point to the end, two bytes to the right. This
; leaves the offset of the new cell the same as the offset of the next cell, hence its
; length is 0.
;
; A4 is the list handle. A3 is the list pointer. D7 is used for the cell.
; D6 is used to make this routine work for ListAddRow too. This routine is
; written to work to add columns (D6=D5=0). To make it work with rows, we must
; add 2 to vertical references (D6=2) and subtract 2 from horizontal references
; (D5 = -2). Got that?
;
; For listAddRow, interchange references to c.h and c.v, rows and columns,
; right and bottom, and left and top.
;
;--------------------------------------------------------------------------------------
ListAddRow
BSR StdEntry ; set up standard stuff
MOVEQ #h,D6 ; do rows
BRA.S LAC1 ; => use common code
ListAddColumn
BSR StdEntry ; set up standard stuff
MOVEQ #v,D6 ; do columns
LAC1
MOVE.L D6,D5 ; use a negative copy for
NEG.W D5 ; vertical references
; if colNum is out of bounds, set it to dataBounds.right
SWAP D7 ; get c.h into low word
MOVE.W dataBounds+right(A3,D5),D0 ; get right for below
CMP.W dataBounds+left(A3,D5),D7 ; is it too small?
BLT.S @1 ; => yes, add new right column
CMP.W D0,D7 ; is it too big?
BLE.S @2 ; => no, use specified column
@1
MOVE.W D0,D7 ; else return new column
@2
MOVE.W D7,16(A6) ; return final column
SWAP D7 ; set up c.v
MOVE.W dataBounds+bottom(A3,D6),D4 ; set final index
MOVE.W dataBounds+top(A3,D6),D7 ; for c.v:= top to bottom-1
; allocate enough extra memory for bottom-top new cells
MOVE.W D4,D3 ; get number of cells to add in D3
SUB.W D7,D3 ; bottom-top := new cells
EXT.L D3 ; make it a long
MULU 14(A6),D3 ; multiply by number of columns
BSR.S GrowRecord ; make the record bigger
tst.w d0 ; did grow fail? <10>
bne Std8Exit ; if so bail out, note: there is no way to tell the caller we failed <10>
; increase our bounds in the proper direction
MOVE.W 14(A6),D0 ; get number of columns
ADD.W D0,dataBounds+right(A3,D5) ; bump right by count
BRA.S @7 ; check bounds and go
@4
TST.W D6 ; if columns, then get c.h
BNE.S @5 ; => its rows, have c.h
SWAP D7
@5
BSR CellToIndex ; cell in D7 -> index in D3
LEA cellArray(A3,D3),A0 ; get source address
MOVE.L A0,A1 ; and destination address
MOVE.W 14(A6),D1 ; get count
EXT.L D1 ; as a long
ASL.L #1,D1 ; 2*count is amount of shift
ADD.L D1,A1 ; form destination address
MOVE.W maxIndex(A3),D0 ; # of bytes to move =
ADD.W #2,D0 ; maxindex + 2 - D3
SUB.W D3,D0
EXT.L D0 ; make it long
ADD.W D1,maxIndex(A3) ; update maxIndex by amount of shift
_BlockMove ; and slide the bytes over
; set the new offsets to the first pre-existing offset to the right (-> 0 length)
MOVE.L A3,A0 ; make sure new cells arent selected
EXT.L D3
ADD.L D3,A0 ; get pointer to first new cell
MOVE.W 14(A6),D0 ; get count
ASL.W #1,D0 ; *2
MOVE.W cellArray(A0,D0),D1 ; get index for next pre-existing cell
BCLR #15,D1 ; de-select it
BRA.S @8 ; need to subtract 2 for 0 based
@3
MOVE.W D1,cellArray(A0,D0) ; stuff the new value
@8
SUBQ #2,D0 ; do next cell
BPL.S @3
TST.W D6 ; if columns, then get c.v
BNE.S @6 ; => its rows, use c.h
SWAP D7 ; do next c.v
@6
ADD.W #1,D7
@7
CMP.W D4,D7 ; hit bottom yet
BLT.S @4 ; => no, keep looping
BRA DelExit ; redraw scroll bars and exit
;------------
; GrowRecord -- extend the record by the number
; of cells in D3. Trashes A0, updates A3, D0 = error code <10>
GrowRecord
MOVE.L A4,A0 ; first get current handle
_HUnlock ; unlock the handle
_GetHandleSize ; get size into D0
ADD.W D3,D0 ; convert cells to bytes
ADD.W D3,D0 ; get new size
EXT.L D0 ; and make it long
_SetHandleSize ; resize it
move.w d0,-(sp) ; save SetHandleSize error <10>
_HLock ; lock the handle
move.w (sp)+,d0 ; restore SetHandleSize error <10>
MOVE.L (A4),A3 ; get the pointer
RTS ; and return
;------------
; HomeVisible -- move the visible rect to the upper left of the
; bounds rect.
HomeVisible
MOVE.L dataBounds(A3),D0 ; get the databounds
SUB.W visible+left(A3),D0 ; get databounds.left - visible.left
SWAP D0
SUB.W visible+top(A3),D0 ; get databounds.top - visible.top
SWAP D0 ; get offset
PEA visible(A3) ; adjust visible rectangle
MOVE.L D0,-(SP) ; push the offset
_OffsetRect
RTS
;--------------------------------------------------------------------------------------
;
; Function ListDelColumn ( count, colNum: INTEGER; h: ListHandle );
; 14 12 8
; ListDelColumn deletes count columns, starting at colNum. If colNum is not within
; dataBounds, it does nothing. If there arent the specified number of columns to the
; right of colnum, then only existing columns are deleted. DataBounds is updated.
; See comments for ListAddColumn
;
; A4 is the list handle. A3 is the list pointer. D7 is used for the cell. D6 for the colNum
;
;--------------------------------------------------------------------------------------
ListDelRow
BSR StdEntry ; save standard stuff
MOVEQ #h,D6 ; say were doing rows
BRA.S LDC1 ; and use (un)common code
ListDelColumn
BSR StdEntry ; save standard stuff
MOVEQ #v,D6
LDC1
MOVE.L D6,D5
NEG D5 ; get negative for horizontal refs
MOVE.W D7,D0 ; get count into D0
BMI Del2Exit ; negative counts are verboten!
BNE.S @0 ; => not special case of 0
MOVEQ #0,D7 ; set colNum to 0
BRA.S DelAll ; and flush out the record
; If (colNum = dataBounds.left AND count >= # of columns) then flushRecord
@0 SWAP D7 ; c.h := colNum
MOVE.W dataBounds+right(A3,D5),D1 ; get right
MOVE.W dataBounds+left(A3,D5),D2 ; get left
CMP.W D7,D2 ; colNum = databounds.left?
BNE.S DelSome ; => no, do the usual
MOVE.W D1,D3 ; if right - left <= count
SUB.W D2,D3 ; then clear everything out
CMP.W D3,D0
BLT.S DelSome ; => count < width
DelAll BSR FlushRecord ; clear out data and bounds
BSR.S HomeVisible ; move visible to topLeft of bounds
SWAP D7 ; fix up D7
BRA DelExit ; => use common code to redraw
DelSome CMP.W D2,D7 ; if (colNum >= left) AND (colNum < right)
BLT Del2Exit ; => oops, colNum < left
CMP.W D1,D7
BGE Del2Exit ; => oops, colNum >= right
; now make sure that the requested number of columns can be deleted. If not, get the
; maximum number that can be deleted.
MOVE.W D7,D3 ; get first column to delete
ADD.W D0,D3 ; get proposed right column
CMP.W D1,D3 ; how does it compare to databounds.right?
BLE.S @0 ; its just fine
SUB.W D7,D1 ; else calculate number of columns to our right
MOVE.W D1,14(A6) ; and save new value in frame
; save state of doDraw, and turn drawing off while clearing cells
@0 MOVE.B ListFlags(A3),-(SP) ; save flags
BSET #noDraw,ListFlags(A3) ; turn drawing off
SWAP D7 ; set up c.v
MOVE.W dataBounds+bottom(A3,D6),D7 ; for i := bottom-1 downto top do
MOVE.W dataBounds+top(A3,D6),D4
BRA.S EndClrEm ; pre-decrement i
ClrLoop
; repeat count times (next cell to be deleted will slide back into current position)
MOVE.W 14(A6),A2 ; get number of cells in row
TST.W D6 ; is it columns
BNE.S DelNext ; => no, got c.h
SWAP D7 ; get c.h into low word
; first get rid of the cells data
DelNext MOVE.L D7,-(SP) ; this is the cell
MOVE.L A4,-(SP) ; and this is the list
_LClrCell ; go zero out the data
; now get rid of the cell itself
BSR CellToIndex ; cell in D7 -> index in D3
LEA cellArray(A3,D3),A1 ; get destination address
MOVE.L A1,A0 ; source address is two beyond it
ADDQ.L #2,A0 ; to delete current cell
MOVE.W maxIndex(A3),D0 ; get maxIndex for the array
SUB.W #2,maxIndex(A3) ; and shrink for next time
SUB.W D3,D0 ; get number of bytes to move
EXT.L D0 ; make it long
_BlockMove ; and slide the bytes over
SUBA #1,A2 ; do next count
MOVE.W A2,D0 ; test count
BNE.S DelNext ; => delete "same cell" again
TST.W D6 ; is it columns?
BNE.S EndClrEm ; => no, use c.h
SWAP D7 ; do next c.v
EndClrEm SUB.W #1,D7
CMP.W D4,D7 ; hit top yet?
BGE.S ClrLoop ; => no, keep looping
MOVE.B (SP)+,ListFlags(A3) ; restore flags
MOVE.W 14(A6),D0 ; get count
SUB.W D0,dataBounds+right(A3,D5) ; make right smaller by count
; release the extra memory that we just threw away
MOVE.W D4,D3 ; get number of cells to subtract in D3
SUB.W dataBounds+bottom(A3,D6),D3 ; top-bottom := new cells (negative)
EXT.L D3 ; make it long and negative
MULS D0,D3 ; multiply by count
BSR.S GrowRecord ; make the record smaller
DelExit
; This hunk of code is shared by add/delete row/column. On entry, the databounds have
; been adjusted to account for the change in structure. This routine first calls JustRect
; to adjust (and redraw) the visible rect if necessary. Then it calls ListUpdate to
; redraw any portions of the array that have changed. Finally it calls SetHilite to
; update the scroll bars.
; If the flag noDraw is set then dont do this stuff
BTST #noDraw,ListFlags(A3) ; should we do update?
BNE.S Del2Exit ; => no
LEA r(A6),A2 ; use our temp rect
MOVE.L visible(A3),(A2) ; get the visible rect
MOVE.L visible+4(A3),4(A2)
BSR JustRect ; and adjust it
BNE.S @1 ; => no change do normal update
ST D0 ; we want an update
BSR MoveRect ; move and update if necessary
; The rect we want to update is basically the visible rect, except with our
; row/column replacing its left/top. Get the local coordinates of the upper left
; cell to be updated, and update down to lower right of rView.
; At this point our row/column is in the high word of D7, and its index is in D5
@1
MOVE.L visible(A3),(A2) ; get the visible topLeft
SWAP D7 ; and the top/Left imposed by new row, column
CMP.W left(A2,D5),D7 ; use whichever is greater
BLE.S @2 ; ours is less or equal to visible, use visible
MOVE.W D7,left(A2,D5) ; new row/column
@2
MOVE.L A2,-(SP) ; get coordinates into r
MOVE.L (A2),-(SP) ; and cell to update
MOVE.L A4,-(SP) ; and list handle
_LRect ; get local coordinates of topleft (no bounds check)
MOVE.L rView+botRight(A3),botRight(A2) ; and of botRight into r
; ListUpdate needs a region. Create one equal to the rect in r.
SUBQ #4,SP ; make room for handle
_NewRgn ; get handle for DisposRgn
MOVE.L (SP),-(SP) ; make copy for ListUpdate
MOVE.L (SP),-(SP) ; and one for EraseRgn
MOVE.L (SP),-(SP) ; and one for RectRgn
MOVE.L A2,-(SP) ; push the rect
_RectRgn ; make the rect a region (leave on stack)
; If there arent enough rows/columns to fill the screen, then we need to erase the
; rightmost/bottom row/column. It is more general to erase update region within ListUpdate,
; but then erases get done twice for everything (eraseRect done in drawProc too).
_EraseRgn
MOVE.L A4,-(SP) ; pass the list handle
_LUpdate ; go update the whole mess
_DisposRgn ; and dispose the region
skipUpdate
MOVE.L A4,-(SP) ; push handle
BSR SetHilite ; update scroll bars, hiliting
Del2Exit
BRA Std8Exit ; do the standard ausgang spiel
;------------
; FlushRecord is a little utility that throws out all the current data, and
; resets everything relevant to 0. The list record is locked, but its not growing.
FlushRecord
MOVE.L A4,A0 ; get handle to list
MOVEQ #sizeList+2,D0 ; list record plus 2 for maxindex
_SetHandleSize ; re-size the list record (which is locked)
MOVE.W dataBounds+left(A3,D5),D0 ; say 0 columns by setting right=left
MOVE.W D0,dataBounds+right(A3,D5) ; (this keeps users left the same)
MOVE.L cells(A3),A0 ; get handle to cell data
MOVEQ #0,D0 ; set size to 0
_SetHandleSize ; no more data!
CLR.L maxIndex(A3) ; clear maxIndex and first cell of array
RTS
;-------------------------------------------------------------------------------------
;
; Procedure ListRect ( VAR r: Rect; c: Cell; h:ListHandle );
; 16 12 8
; ListRect returns in r the local coordinates of the cell c in the window. If c.h <0
; the coordinates of the entire row identified by c.v are returned.
;
; StdEntry puts c in D7; list pointer in A3; list handle in A4
; NOTE: dont bounds check c because delExit, above, wants coordinates of out of bounds cells.
;-------------------------------------------------------------------------------------
ListRect
BSR StdEntry ; *** what if c out of bounds??
MOVE.L 16(A6),A1 ; get r into A1
MOVE.L rView(A3),(A1) ; get topLeft of lists rect
SWAP D7 ; get c.v in low word
MOVE.W D7,D0 ; r.top := r.top+(c.v-visible.top)*cellsize.v
SUB.W visible+top(A3),D0
MULU cellsize+v(A3),D0
ADD.W D0,top(A1)
MOVE.W top(A1),D0
ADD.W cellSize+v(A3),D0 ; r.bottom := r.top+cellSize.v
MOVE.W D0,bottom(A1)
SWAP D7 ; get c.h in low word
MOVE.W D7,D0 ; if c.h < 0 then
BLT.S @wholeRow ; return whole row
SUB.W visible+left(A3),D0 ; r.left := r.left+(c.h-visible.left)*cellsize.h
MULU cellSize+h(A3),D0
ADD.W D0,left(A1)
MOVE.W left(A1),D0
ADD.W cellSize+h(A3),D0 ; r.right := r.left+cellSize.v
MOVE.W D0,right(A1)
@wholeRow
Std12Exit
MOVEQ #12,D0 ; strip 12 bytes of parameters
BRA StdExit ; and exit
;-------------------------------------------------------------------------------
;
; PROCEDURE ListScroll ( dCols, dRows: INTEGER; h: ListHandle );
; 14 12 8
; ListScroll scrolls the list dCols horizontally and dRows vertically, bounded
; by the visBounds. Does all necessary updating of the screen.
;
;-------------------------------------------------------------------------------
ListScroll
BSR StdEntry ; ho humm
MOVE.L visible(A3),r(A6) ; make a copy of visible rect
MOVE.L visible+4(A3),r+4(A6)
; first offset the visible rect by the requested amount
PEA r(A6) ; OffsetRect(rNew,dCols,dRows)
MOVE.L D7,-(SP) ; push rows, cols
_OffsetRect
BSR.S JustRect ; align the rect for optimal display
BNE.S @3 ; => no movement. Dont flicker
ST D0 ; we want an update
BSR.S MoveRect ; else update display
MOVE.L A4,-(SP) ; and update the scroll bars
BSR SetHilite
@3
BRA Std8Exit ; strip 8 bytes of parameters
;------------
; JustRect
; This routine is used by ListScroll and Add/Delete Row/Column. Given a rect in r,
; (the proposed visible rect), it makes sure that the bottom right of the rect are
; within the current visBounds. If not, it tries to move r up and to the left
; so that all visible cells contain something. It returns BNE if no visible change.
JustRect
BSR GetVisBounds ; get visible boundaries
moveq #v,d2 ; do the vertical sliding
bsr.s JustifyOneDimension
moveq #h,d2 ; do the horizontal sliding
bsr.s JustifyOneDimension
SUBQ #2,SP ; if rNew <> visible then
PEA r(A6)
PEA visible(A3)
_EqualRect
TST.B (SP)+
RTS ; BNE if no change
;------------
; JustifyOneDimension slides one dimension (horizontal or vertical) of the rectangle, r,
; into view. The parameters are:
;
; r(a6) rectangle to slide
; visBounds(a6) area defining what is visible
; d2.w h or v to define which dimension to slide
;
; uses d0 and d1
JustifyOneDimension
move.w r+botRight(a6,d2.w),d0
sub.w visBounds+botRight(a6,d2.w),d0
ble.s @tryTopLeft ; if bottom/right needs to be slid into place, do it
bsr.s @slide
@tryTopLeft
move.w r+topLeft(a6,d2.w),d0
sub.w visBounds+topLeft(a6,d2.w),d0
bge.s @noSlide ; if top/left needs to be slid into place, do it
@slide
sub.w d0,r+topLeft(a6,d2.w) ; slide so that it is visible
sub.w d0,r+botRight(a6,d2.w)
@noSlide
rts
;------------
; MoveRect scrolls and updates the list so that the rect in r becomes the visible rect.
MoveRect
movem.l d7/a2,-(sp) ; save registers
move.b d0,d7 ; save update flag
SUBQ #4,SP ; go get a new region
_NewRgn
move.l (sp),a2 ; get region handle (keep around for dispose)
BTST #noDraw,ListFlags(A3) ; is drawing on?
BNE.S @noScroll ; => nope, just exit
PEA rView(A3) ; scroll by our x and y deltas
BSR.S GetOffset ; get offset into D0
MOVE.L D0,-(SP)
MOVE.L A2,-(SP) ; pass the region handle
_ScrollRect
@noScroll
MOVE.L r(A6),visible(A3) ; visible := rNew
MOVE.L r+4(A6),visible+4(A3)
TST.B d7 ; update?
BEQ.S @noUpdate ; => no
MOVE.L A2,-(SP) ; pass the region handle
MOVE.L A4,-(SP) ; and the handle to our list
_LUpdate ; and update region
@noUpdate
_DisposRgn ; dispose of region
movem.l (sp)+,d7/a2 ; restore registers
rts
GetOffset
MOVEQ #0,D0 ; prepare for long multiply
MOVE.W visible+left(A3),D0 ; dx = (visible.left-r.left)*cellsize.h
SUB.W r+left(A6),D0
MULU cellSize+h(A3),D0
MOVE.W D0,-(SP)
MOVEQ #0,D0 ; prepare for long multiply
MOVE.W visible+top(A3),D0 ; dy = (visible.top-r.top)*cellSize.v
SUB.W r+top(A6),D0
MULU cellSize+v(A3),D0
MOVE.W D0,-(SP)
MOVE.L (SP)+,D0
RTS
;-------------------------------------------------------------------------------
;
; PROCEDURE ListUpdate ( r: RgnHandle; h: ListHandle );
; 12 8
; ListUpdate redraws any of the visible cells which intersect with r. Also
; draws the scroll bar controls if they intersect with r.
;
;-------------------------------------------------------------------------------
ListUpdate
BSR StdEntry ; ho hum
BTST #noDraw,ListFlags(A3) ; is drawing on?
BNE.S @done ; => nope, just exit
MOVE.W visible+top(A3),D6 ; D6 := visible.top
MOVE.W visible+bottom(A3),D4 ; D4 := visible.bottom
MOVE.W visible+right(A3),D5 ; D5 := visible.right
BRA.S @5 ; for i := top to bottom-1
@1
MOVE.W visible+left(A3),D3 ; for j := left to right-1
BRA.S @4
@2
MOVE.W D6,D7 ; get current cell into D7 for DoDraw
SWAP D7 ; c.v into high word
MOVE.W D3,D7 ; and c.h into low word
PEA r(A6) ; ListRect (r,c,h)
MOVE.L D7,-(SP) ; push c
MOVE.L A4,-(SP) ; push h
_LRect ; find rect containing c
; is the cell in the update region?
SUBQ #2,SP ; make room for boolean result
PEA r(A6) ; push our rect
MOVE.L 12(A6),-(SP) ; and update Region
_RectInRgn ; is the rect in the region?
TST.B (SP)+ ; check el resulto
BEQ.S @3 ; => no, dont draw
BSR DoDraw ; go draw the cell
@3
ADDQ #1,D3 ; j:=j+1
@4
CMP.W D5,D3 ; until left = right
BLT.S @2
ADDQ #1,D6 ; i:=i+1
@5
CMP.W D4,D6 ; until top = bottom
BLT.S @1
BSR DrawScrollBars
@done
BRA Std8Exit ; and exit using commoners code
;-----------------------------------------------------------------------------
; Selection handling routines
; Register conventions: A4 = List handle
; A3 = List pointer
; D3 = used as cell index
; D4 = set to end of selection array
; D6 = actual cell, if needed
;-----------------------------------------------------------------------------
;------------
; DoSelLoop
; This routine calls the routine in A2 once for each selected cell.
DoSelLoop
MOVEM.L D3-D4/D7,-(SP) ; save work registers
MOVEQ #0,D3 ; get starting index
MOVE.W maxIndex(A3),D4 ; get maximum index
BRA.S @3 ; do initial check
@1
TST.B cellArray(A3,D3) ; test next cell
BPL.S @2
JSR (A2) ; call for each selection
@2
ADDQ #2,D3 ; go to next element
@3
CMP.W D4,D3 ; done yet?
BLE.S @1 ; => noop, lope on
MOVEM.L (SP)+,D3-D4/D7
RTS
;------------
; DrawASel -- draws a selected cell called by listactivate (must draw)
;
; This routine is passed to DoSelLoop, and called once for each selected cell.
DrawASel
BSR.S DrawIndex ; get cell, draw it
PEA r(A6) ; get rect into r
MOVE.L D7,-(SP) ; get rect we just drew
MOVE.L A4,-(SP)
_LRect ; get cells rect into r
SUBQ #2,SP ; room for result
PEA rView(A3) ; get intersection of rView
PEA r(A6) ; and current cell
MOVE.L (SP),-(SP) ; and save into r
_SectRect
TST.B (SP)+ ; is cell in view
BEQ.S @nothingDrawn ; => no, nothing to validate
PEA r(A6) ; get rect into r
bsr.s MyValidRect ; validate rectangle so we dont draw twice
@nothingDrawn
RTS ; and return
;------------
; DrawSel -- draws the cell in D7
DrawIndex
BSR IndexToCell ; index in D3 -> cell in D7
DrawSel
MOVE.L D7,-(SP) ; pass selected cell
MOVE.L A4,-(SP) ; and handle
_LDraw ; draw the cell
RTS ; and return
;------------
; ClrSel
; This utility deselects the cell whose index is in D3 and redraws it.
; On entry, the list pointer is in A3.
ClrSel
BCLR #7,cellArray(A3,D3) ; test and clear selection
HiliteIndex
BSR IndexToCell ; index in D3 -> cell in D7
HiliteSel
MOVE.L D7,-(SP) ; pass selected cell
MOVE.L A4,-(SP) ; and handle
BSR ListHilite ; draw the cell
RTS ; and return
;-------------------------------------------------------------------------------
;
; PROCEDURE ListSetSelect ( SetIt: BOOLEAN; c: Cell; h: ListHandle );
; 16 12 8
; If SetIt is true, then select the cell and redraw if necessary.
; If SetIt is false,then deselect the cell and redraw if necessary.
; If the noDraw bit in ListFlags is set, then dont draw.
;
;-------------------------------------------------------------------------------
ListSetSelect
BSR StdEntry ; ha ha
BSR MyFind ; validate cell
BEQ.S @3 ; bogus cell, exit post haste
BTST #LNoNilHilite,selFlags(A3) ; should we select empty cells?
BEQ.S @0 ; => yes, select anything
TST.W len(A6) ; is it empty?
BEQ.S @3 ; => yes, dont select
@0
BSR CellToIndex ; get index into D3 (cell in D7)
TST.B 16(A6) ; select or deselect cell?
BNE.S @1 ; => select the cell
BCLR #7,cellArray(A3,D3) ; test and deselect
BEQ.S @3 ; => dont need to draw
BRA.S @2 ; => else redraw the cell
@1
BSET #7,cellArray(A3,D3) ; test and select
BNE.S @3 ; => dont need to redraw
@2
BTST #noDraw,ListFlags(A3) ; is drawing on?
BNE.S @3 ; => no, dont invert
BSR.S HiliteSel ; else hilite the cell
@3
BRA Std10Exit ; strip off 10 bytes and go
;-------------------------------------------------------------------------------
;
; ListGetSelect ( getNext:boolean; VAR c: cell; h: ListHandle): boolean
; 16 12 8 18
; If getNext is FALSE, returns whether the cell c is selected.
; If getNext is TRUE, returns in c the next selected cell (including c).
; If there is no next cell, FALSE is returned.
;
;-------------------------------------------------------------------------------
ListGetSelect
BSR StdEntry ; hee hee
CLR.W 18(A6) ; assume result false
MOVE.L D7,A2 ; get cell pointer
MOVE.L (A2),D7 ; get cell
BSR MyFind ; validate cell (A2 preserved)
BEQ.S LGSExit ; => exit if bad
MOVE.W index(A6),D3 ; get index to cell
TST.B 16(A6) ; get the next one?
BNE.S @1 ; => yes
; simply set the result according to the selection bit of the indicated cell
TST.B cellArray(A3,D3) ; test selection bit
BPL.S LGSExit ; if plus, return false
@0
MOVE.W #$0100,18(A6) ; else return true
BRA.S LGSExit
; starting at the indicated cell, find the next selected cell.
@1
MOVE.W maxIndex(A3),D4 ; get last one for quick checking
@2
TST.B cellArray(A3,D3) ; test selection bit
BPL.S @3 ; => not selected, keep looking
BSR IndexToCell
MOVE.L D7,(A2) ; set new cell
BRA.S @0 ; => and return true
@3
ADDQ #2,D3 ; point to next cell
CMP.W D3,D4 ; done yet?
BGE.S @2 ; <NGK 11-OCT-89> changed from BNE.S, missed last one
LGSExit
BRA Std10Exit
;---------------------------------------------------------------------------------
;
; FUNCTION ListNextCell (hNext,vNext: BOOLEAN; VAR c: Cell,h: ListHandle): BOOLEAN
; 18 16 12 8 20
; If HNext and VNext, then advance c to the next cell in the list.
; If just hNext or just vNext, then advance c within the row or column.
; If no next cell, return FALSE.
ListNextCell
BSR StdEntry ; set up standard stuff
MOVE.L D7,A2 ; get VAR pointer
MOVE.L (A2),D7 ; get c in D7
BSR MyFind ; check bounds on c
BEQ.S @0 ; => out of bounds, return false
TST.B 18(A6) ; hNext?
BEQ.S @2 ; => no, just increment column
ADDQ #1,D7 ; c.h = c.h + 1
CMP.W databounds+right(A3),D7 ; still within dataBounds?
BLT.S @1 ; => yes, return true
MOVE.W databounds+left(A3),D7 ; else move to left of next row
@2
TST.B 16(A6) ; vNext?
BEQ.S @0 ; => no, return false (note, c.h may be 0)
SWAP D7 ; get c.v
ADDQ #1,D7 ; go to next row
CMP.W databounds+bottom(A3),D7 ; still within databounds?
BLT.S @4 ; => yes
SWAP D7 ; get D7 where it belongs
@0
CLR.W 20(A6) ; return false
BRA @5 ; and current D7 as c
@4
SWAP D7
@1
MOVE.W #$0100,20(A6) ; return TRUE
@5
MOVE.L D7,(A2) ; return c
BRA Std12Exit
;------------------------------ Data Structure Routines --------------------------
; These are the routines that directly manipulate the data.
; A few comments and conventions:
; ListFind, called by every relevant routine, places the index into cellArray of
; the current cell into index(A6). The data structure contains a value maxIndex
; that is the index to the last element of cellArray, for convenient bounds checking.
; This last element does not indicate a data element, it is used to calculate the
; length of the last element as well as to detect the end of the array.
; For each element in the list, there is a 2 byte field in the cellArray. The high bit
; of the field indicates whether or not the cell is selected. The remaining 15 bits are
; the offset into the cell data structure of the beginning of that element. The length
; of an element is calculated by subtracting the offset to the next element in the array
; from the offset of the current element.
;----------------------------------------------------------------------
;
; PROCEDURE ListFind( VAR offset, len: Integer; c: Cell; h: ListHandle );
; 20 16 12 8
; Given a cell coordinate, returns the offset to the data part of that cell (just
; past the length for that cell), and the length for that cell. If the cell c is
; not within bounds, offset and len are set to -1.
ListFind
BSR StdEntry ; set up standard stuff
BSR.S LFind2 ; call shared listFind code
TST.B D4 ; was the cell valid?
BNE.S @1 ; => yes, return our data
MOVEQ #-1,D6 ; else len = -1
MOVEQ #-1,D5 ; and offset = -1
@1
MOVE.L 16(A6),A1 ; set VAR len
MOVE.W D6,(A1)
MOVE.L 20(A6),A1 ; set VAR offset
MOVE.W D5,(A1)
Std16Exit
MOVEQ #16,D0
BRA StdExit ; restore regs, unlink, strip params...
;------------
; MyFind -- This routine is used internally to validate the cell that is in D7.
; It assumes that StdEntry has already been called to set things up.
; GoodCell indicates the cells validity; len, offset and index are set.
MyFind
MOVEM.L D1-D7/A1-A4,-(SP) ; save em all
BSR LFind2 ; call ListFind
MOVE.W D6,len(A6) ; return length of chosen cell
MOVE.W D5,offset(A6) ; return offset to chosen cell
MOVE.W D3,index(A6) ; set index of cell
MOVE.B D4,goodCell(A6) ; set ccs
MOVEM.L (SP)+,D1-D7/A1-A4 ; and fix em up
RTS
;------------
; LFind2 is used by ListFind and MyFind
LFind2
; Len is left in D6 and offset in D5. D4 is 0 if the cell was invalid.
MOVEQ #0,D5 ; save offset in D5
MOVEQ #0,D6 ; save len in D6
; set D4 true if the cell is a valid, otherwise false
SUBQ #2,SP ; check if c param ok
MOVE.L D7,-(SP)
PEA dataBounds(A3)
_PtInRect
MOVE.B (SP)+,D4 ; remember result in D4
BEQ.S findExit
; find the correct cell
BSR.S CellToIndex ; cell in D7 -> index in D3
MiniFind ; used by search
LEA cellArray(A3,D3),A0 ; get pointer to offset
MOVE.W (A0)+,D5 ; get offset to current cell
AND.W #$7FFF,D5 ; minus the select bit
MOVE.W (A0),D6 ; get offset to next cell
AND.W #$7FFF,D6 ; minus the select bit
SUB.W D5,D6 ; get the length
findExit ; index is in D3
RTS
;------------
; CellToIndex
; Given a cell number in D7, this routine returns in D0 the offset
; into cellArray of that cell. Leaves D7 intact.
CellToIndex
SWAP D7 ; c.v in low word
MOVE.W dataBounds+right(A3),D3 ; index := c.v * rowLen + c.h
SUB.W dataBounds+left(A3),D3
MULU D7,D3
SWAP D7
ADD.W D7,D3 ; got cell number
LSL.W #1,D3 ; get index into D3
RTS
;------------
; IndexToCell
; Given an offset into the cellArray in D3, this routine returns in D7
; the cell that has that index. Leaves D3 intact.
IndexToCell
MOVE.L D1,-(SP) ; save D1
MOVE.W D3,D7
EXT.L D7 ; make it a long
BEQ.S @1 ; offset is 0 => cell is 0
LSR.L #1,D7 ; make it a simple index
MOVE.W dataBounds+right(A3),D1 ; get right
SUB.W dataBounds+left(A3),D1 ; get row width
DIVU D1,D7 ; get number of columns
SWAP D7 ; high, remainder is rows
@1
MOVE.L (SP)+,D1 ; restore D1
RTS
;----------------------------------------------------------------------
;
; PROCEDURE ListClrCell( c: Cell; h: ListHandle );
;
; Deletes the given cell from the given list
; Sets up stack to make a ListSetCell call with NIL string.
;
;----------------------------------------------------------------------
ListClrCell
; pull off return address, our params, push on p and l for SetCell, put ours back
; note: lock state of pack is in D1, dont use D1
MOVEM.L (SP)+,D0/D2/A0 ; get RTS, handle, cell
CLR.L -(SP) ; push p = NIL
CLR.W -(SP) ; push l = 0
MOVEM.L A0/D2/D0,-(SP) ; restore cell, handle, RTS
; and fall into a more macho routine...
;-------------------------------------------------------------------------------
;
; PROCEDURE ListSetCell ( p: Ptr; l: INTEGER; c: Cell; h: ListHandle );
; 18 16 12 8
; ListSetCell Replaces the current contents of the cell c with the l bytes
; of data pointed to by p. If p is NIL and l is 0, this is the same as
; ListClrCell.
;
;-------------------------------------------------------------------------------
ListSetCell
BSR StdEntry ; ho hum again
BSR MyFind ; validate cell, set things up
BEQ std14Exit ; skip if bad cell
MOVE.W len(A6),D6 ; get the current length
MOVE.W 16(A6),D5 ; get the new length
; *** need to verify that data will fit?
SUBQ #4,SP ; munge out the field
MOVE.L cells(A3),-(SP) ; heres the data handle
MOVE.W offset(A6),-(SP) ; pass offset
CLR.W -(SP) ; which must be long
CLR.L -(SP)
MOVE.W D6,-(SP) ; pass length
CLR.W -(SP) ; which must be long
MOVE.L 18(A6),-(SP) ; pass in this string
MOVE.W D5,-(SP) ; with this length
CLR.W -(SP) ; which must be long
_Munger
; *** test result of Munger here (in D0.W)
ADDQ #4,SP ; dump result
SUB.W D6,D5 ; get delta for subsequent elements
BRA.S CellShare ; share common exit
;----------------------------------------------------------------------
;
; PROCEDURE ListAddtoCell( p: Ptr; l: INTEGER; c: Point; h: ListHandle );
; 18 16 12 8
; Appends l bytes of data starting at p to the cell c in list h.
;----------------------------------------------------------------------
ListAddtoCell
BSR StdEntry ; ho hum
BSR MyFind ; validate the cell
BEQ.S std14Exit ; skip if bad cell
MOVE.W 16(A6),D5 ; get new length into D5
BEQ.S std14Exit ; => nothing to add
; *** need to verify that data will fit?
SUBQ #4,SP ; go munge it in
MOVE.L cells(A3),-(SP) ; pass the data handle
MOVEQ #0,D0 ; Munger needs a long offset
MOVE.W offset(A6),D0 ; add offset + length
ADD.W len(A6),D0
MOVE.L D0,-(SP) ; insert at the end
CLR.L -(SP) ; NIL for p1
CLR.L -(SP) ; zero length1
MOVE.L 18(A6),-(SP) ; pass pointer
MOVE.W D5,-(SP) ; low length word
CLR.W -(SP) ; high length word
_Munger
ADDQ #4,SP ; ignore reply
cellShare
MOVE.W D5,D0 ; get length into D0
BSR.S FixOffsets ; and adjust other cells
BSR DoDraw ; and draw cell in D7
Std14Exit
MOVEQ #14,D0
BRA StdExit
RTS ; and exit
;------------
; FixOffsets
; This is a register based routine that takes a word length offset in D0.
; D0 is added to each element in the cellArray from index+1 to the end.
; Trashes D1, A0, and A1
FixOffsets
TST.W D0 ; if 0, nothing to do
BEQ.S @2 ; => yippee! saved us some work
MOVE.W maxIndex(A3),D1 ; get pointer past end of array in A1
LEA cellArray(A3,D1),A1 ; stop after updating this one
MOVE.W index(A6),D1 ; get index into D2
LEA cellArray+2(A3,D1),A0 ; index+1 is the first one to do
@1
ADD.W D0,(A0)+ ; add offset to it
CMP.L A1,A0 ; done yet?
BLE.S @1
@2
RTS
;-----------------------------------------------------------------------------
;
; PROCEDURE ListGetCell ( p: Ptr; VAR l: INTEGER; c: Cell; h: ListHandle );
; 20 16 12 8
; ListGetCell returns in p the data associated with the cell c. If the data
; is longer than l, only l bytes are returned. If the data is shorter than l,
; l is set to the new length.
;
;-----------------------------------------------------------------------------
ListGetCell
BSR StdEntry ; oh muh!
BSR MyFind ; validate cell
BEQ.S @2 ; => cell no good, just return
MOVE.L 16(A6),A0 ; get pointer to length
MOVE.W len(A6),D0
CMP.W (A0),D0 ; is len < l?
BGE.S @1
MOVE.W D0,(A0) ; yes, return new length
@1
MOVE.W (A0),D0 ; get count in D0
BEQ.S @2 ; => no data to move
SUBQ #2,SP ; make room for result
EXT.L D0 ; make it long
MOVE.L cells(A3),A0 ; get data handle
MOVE.L (A0),A0 ; get data pointer
ADD.W offset(A6),A0 ; bump it to our cell
MOVE.L 20(A6),A1 ; get destination pointer
_BlockMove
ADDQ #2,SP ; ignore result ***
@2
BRA Std16Exit
;-------------------------------------------------------------------------------
;
; FUNCTION ListSearch ( p: Ptr; l: INTEGER; MyProc: Ptr; VAR c: Cell; h: ListHandle): BOOLEAN;
; 22 20 16 12 8 26
; ListSearch searches through the data, one cell at a time, beginning with cell c, for a
; cell that contains data that matches the data in p with length l (according to the
; definition of matching kindly provided by IUMagIDString). If a matching cell is found,
; TRUE is returned, and c is set to that cell. If no matching cell is found, or the
; supplied starting cell is out of bounds, the result is set to FALSE, and the returned
; cell is invalid.
;
; If MyProc is non-zero, it is called in place of IUMagIDString, with the same parameters:
; MyProc(aPtr,bPtr: Pointer; aLen,bLen: INTEGER): INTEGER;
; We depend on the user to preserve registers D3-D7 and A2-A7!!.
;-------------------------------------------------------------------------------
ListSearch
BSR StdEntry
MOVE.W #$0100,26(A6) ; assume cell is found
MOVE.L D7,A2 ; get address of cell
MOVE.L (A2),D7 ; get cell, save address in A2
MOVE.L IntUtilHandle,A0 ; get the handle into A0
_HGetState ; D0 := current lock, etc. state<S3>
MOVE.B D0,-(SP) ; save status on stack <S3>
_HLock ; lock down the package
MOVE.L cells(A3),A0 ; get handle to data
_HGetState ; D0 := current lock, etc. state<S3>
MOVE.B D0,-(SP) ; save status on stack <S3>
_HLock ; and lock down the data
MOVE.W maxIndex(A3),D4 ; get terminator in D4
BSR CellToIndex ; cell in D7 -> index in D3
BSR MyFind ; bounds-check cell in D7
BEQ.S NoMatch ; => cell not valid, pattern not found
LookAtNext
BSR MiniFind ; index in D3 -> D5 = offset, D6 = len
MOVE.L D3,-(SP) ; because Jerome doesnt want to save it
SUBQ #2,SP ; make room for integer result
MOVE.L cells(A3),A0 ; get handle to data
MOVE.L (A0),A0 ; get pointer to data
ADD.W D5,A0 ; get pointer to current cell
MOVE.L A0,-(SP) ; push pointer to cell data
MOVE.L 22(A6),-(SP) ; this is the data to find
MOVE.W D6,-(SP) ; and cell length
MOVE.W 20(A6),-(SP) ; and pass the data length
MOVE.L 16(A6),D2 ; get routine and test
BEQ.S @1 ; => no routine
MOVE.L D2,A0 ; call users routine
JSR (A0) ; with same params as IUMagIDString
; NOTE: We count on the users preserving the standard registers (D3-D7,A2-A7) !
BRA.S @2
@1
_IUMagIDString
@2
MOVE.W (SP)+,D0 ; get result
MOVE.L (SP)+,D3 ; harrumph
TST.W D0
BEQ.S GotAMatch ; => we found it
ADDQ.W #2,D3 ; spin that odometer
CMP.W D4,D3 ; done scanning yet?
BLT.S LookAtNext ; => nope, do next string
NoMatch
CLR.W 26(A6) ; no match -> return false
GotAMatch
; get cell to return. If no match, cell is invalid
BSR IndexToCell ; index in D3 -> cell in D7
MOVE.L D7,(A2) ; return "new" cell
MOVE.L cells(A3),A0 ; get handle to data
MOVE.B (SP)+,D0 ; saved lock state <S3>
_HSetState ; restore to handle <S3>
MOVE.L IntUtilHandle,A0 ; get handle to package
MOVE.B (SP)+,D0 ; saved lock state <S3>
_HSetState ; restore to handle <S3>
MOVEQ #18,D0
BRA StdExit ; strip of 18 bytes of parameters
ENDPROC
END