mac-rom/Toolbox/ListMgr/ListMgrPACK.a

3047 lines
103 KiB
Plaintext
Raw Normal View History

;
; File: ListMgrPACK.a
;
; Contains: the List Manager
;
; Written by: Steve Capps and Ernie Beernik
;
; Copyright: <09> 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; Don<6F>t
; inval cells that aren<65>t 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 don<6F>t have c passed to them. Finally it saves the
; current port and gets into the list<73>s 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 don<6F>t fit exactly
BEQ.S @1 ; then add one
ADD.W #1,visBounds+right(A6) ; to right
@1
BTST #LVBadFit,ListFlags(A3) ; if cells don<6F>t 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 ; can<61>t 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, don<6F>t 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 ; don<6F>t set minimum
@doSetMinimum
cmp.w contrlMax(a0),d5 ; is the maximum different?
bne.s @doSetMaximum
moveq #-1,d5 ; don<6F>t set minimum
@doSetMaximum
cmp.w contrlValue(a0),d3 ; is the value different?
bne.s @doSetValue
moveq #-1,d3 ; don<6F>t 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, don<6F>t 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 control<6F>s
; 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 control<6F>s 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 control<6F>s 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 isn<73>t
; 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, don<6F>t 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 isn<73>t 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 isn<73>t 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, don<6F>t 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, don<6F>t 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 it<69>s not the first time
BEQ CallRtn ; => doesn<73>t 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 we<77>re 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 doesn<73>t 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) ; don<6F>t extend current selection
BSET #LNoRect,14(A6) ; don<6F>t 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, it<69>s 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) ; don<6F>t 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
; it<69>s 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 don<6F>t 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, don<6F>t 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 clip<69>n reg<65>n
_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 cell<6C>s rect into r
MOVE.L D7,-(SP) ; pass the cell
MOVE.L A4,-(SP)
_LRect ; get the cell<6C>s 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 defproc<6F>s 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 cell<6C>s 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 ; => didn<64>t 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 list<73>s 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 doesn<73>t 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 it<69>s 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 scroll<6C>s rect (uses D0) into A2
TST.L D4 ; is control valid?
BNE.S @haveControl ; => yes, move the control
; the control<6F>s 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 doesn<73>t 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 ; don<6F>t 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 defProc<6F>s 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 they<65>re 0 length
MOVE.L A0,D0 ; test result
BEQ.S NewExit ; => couldn<64>t 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 didn<64>t 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 list<73>s 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 don<6F>t 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 ; => it<69>s 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 aren<65>t 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 ; => it<69>s 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 aren<65>t 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 we<77>re 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 ; it<69>s 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 cell<6C>s 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 don<6F>t 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 aren<65>t 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: don<6F>t 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 list<73>s 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. Don<6F>t 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, don<6F>t 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 commoner<65>s 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 cell<6C>s 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 don<6F>t 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 don<6F>t 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, don<6F>t 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 ; => don<6F>t need to draw
BRA.S @2 ; => else redraw the cell
@1
BSET #7,cellArray(A3,D3) ; test and select
BNE.S @3 ; => don<6F>t need to redraw
@2
BTST #noDraw,ListFlags(A3) ; is drawing on?
BNE.S @3 ; => no, don<6F>t 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 cell<6C>s 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 cc<63>s
MOVEM.L (SP)+,D1-D7/A1-A4 ; and fix <20>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, don<6F>t 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) ; here<72>s 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 doesn<73>t 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 user<65>s routine
JSR (A0) ; with same params as IUMagIDString
; NOTE: We count on the user<65>s 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