; ; File: ListMgrPACK.a ; ; Contains: the List Manager ; ; Written by: Steve Capps and Ernie Beernik ; ; Copyright: © 1984-1993 by Apple Computer, Inc. All rights reserved. ; ; This file is used in these builds: Bigbang Sys606 ; ; Change History (most recent first): ; ; 9/9/93 SAM Changed ShiftDown to not assume the PtInRect will leave ; thePoint.v in D1 (good lord!). ; 8/16/93 fau Moved 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. ; 6/14/93 kc Roll in Ludwig. ; 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) ; 3/8/93 fau Made the compare in previous checkin be 16 bytes, instead of 12, ; and unsigned. ; 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. ; 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. ; 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 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. ; 2/29/88 GP and EMT test for an empty handle (purged LDEF) ; 12/21/86 JTC 32-bit cleaning, with care to get the conditionals correct ; for old ROMs. ; 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Õt ; inval cells that arenÕ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' ; fau MACHINE MC68020 ; needed for cache flush 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 MOVE.B D0,D1 ; D1 := ditto _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 _HSetState ; restore to handle 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Õt have c passed to them. Finally it saves the ; current port and gets into the listÕ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 ; the saved state of the input handle. This is unraveled in the exit procedure. ; 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 _HGetState ; D0 := current lock, etc. state MOVE.B D0,-(SP) ; save across entire LM call _HLock ; lock handle across call 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 _SetPort ; restore port MOVE.B (SP)+,D0 ; saved handle state 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 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Õ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Õ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Õ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Õ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Õt set minimum @doSetMinimum cmp.w contrlMax(a0),d5 ; is the maximum different? bne.s @doSetMaximum moveq #-1,d5 ; donÕt set minimum @doSetMaximum cmp.w contrlValue(a0),d3 ; is the value different? bne.s @doSetValue moveq #-1,d3 ; donÕ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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 move.l ExpandMemRec.emStartTicks(A0),D0 ; See if we need to set this. BNE.S @startTicksOK ; Skip if not. MOVE.L D1, D0 ; Use TickCount value. MOVE.L D0,ExpandMemRec.emStartTicks(A0) ; Put it in startTicks. @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 move.l #0,ExpandMemRec.emStartTicks(A0) ; Clear out startTicks we're not scrolling anymore @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Õs not the first time BEQ CallRtn ; => doesnÕ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Õ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 cmp.w maxIndex(a3),d3 ; is d3 out of range (clicked on nothing below last item)? bhi.s @1 ; if so, then clicked "cell" was not selected 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Õ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Õt extend current selection BSET #LNoRect,14(A6) ; donÕ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 anchor is botRight CMP.W D0,D3 ; check left first BLT.S BRAnchor ; => click.h click.vcheck 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Õ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Õ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Õ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 move.l (SP)+,ExpandMemRec.emStartTicks(A0) ; Put it in startTicks. 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 move.l #0,ExpandMemRec.emStartTicks(A0) ; Clear out startTicks 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Õ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Õ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!) 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 move.l ExpandMemRec.emStartTicks(A0),-(SP) ; startTicks for ScrollDelay 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Õn regÕ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Õs rect into r MOVE.L D7,-(SP) ; pass the cell MOVE.L A4,-(SP) _LRect ; get the cellÕ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Õ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Õ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Õt get one ; Some programmers are pretty slimy and load an LDEF that is empty. They then fau ; stuff some code into it. However, since HLOCK does not flush the cache anymore, fau ; the code that they stuff into it might not get written back to memory. To solve this, fau ; we check here whether the LDEF resource size is less than, say, 32 bytes. If so, we chp ; assume that they have already loaded the LDEF and modified it, so we flush the cache fau ; for them. MOVE.L ListDefHandle(A3),A0 ; get the handle fau _GetHandleSize ; How big is our LDEF Handle fau cmp.l #32,D0 ; Is it "small" chp bhi.s @RealLDEF ; no, don't flush the cache fau jsr ([jCacheFlush]) ; else, flush the caches. fau @RealLDEF ; fau MOVE.L ListDefHandle(A3),A0 ; get the handle fau MOVE.L A0,-(SP) ; and load the defProc _LoadResource MOVE.L (A0),D0 ; Is the handle still empty? BEQ.S @done ; Yes, skip _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Õ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Õ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Õ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Õs rect (uses D0) into A2 TST.L D4 ; is control valid? BNE.S @haveControl ; => yes, move the control ; the controlÕ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Õ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Õ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Õ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Õre 0 length MOVE.L A0,D0 ; test result BEQ.S NewExit ; => couldnÕt get memory MOVE.L A0,cells(A3) ; save handle ; initialize the list defProc handle MOVE.W #MapTrue, RomMapInsert ; insert ROM map 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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õ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Õt need to draw BRA.S @2 ; => else redraw the cell @1 BSET #7,cellArray(A3,D3) ; test and select BNE.S @3 ; => donÕt need to redraw @2 BTST #noDraw,ListFlags(A3) ; is drawing on? BNE.S @3 ; => no, donÕ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 ; 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Õ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Õs MOVEM.L (SP)+,D1-D7/A1-A4 ; and fix Õem up RTS ;------------ ; LFind2 is used by ListFind and MyFind LFind2 ; Len is left in D6 and offset in D5. D4 is 0 if the cell was invalid. MOVEQ #0,D5 ; save offset in D5 MOVEQ #0,D6 ; save len in D6 ; set D4 true if the cell is a valid, otherwise false SUBQ #2,SP ; check if c param ok MOVE.L D7,-(SP) PEA dataBounds(A3) _PtInRect MOVE.B (SP)+,D4 ; remember result in D4 BEQ.S findExit ; find the correct cell BSR.S CellToIndex ; cell in D7 -> index in D3 MiniFind ; used by search LEA cellArray(A3,D3),A0 ; get pointer to offset MOVE.W (A0)+,D5 ; get offset to current cell AND.W #$7FFF,D5 ; minus the select bit MOVE.W (A0),D6 ; get offset to next cell AND.W #$7FFF,D6 ; minus the select bit SUB.W D5,D6 ; get the length findExit ; index is in D3 RTS ;------------ ; CellToIndex ; Given a cell number in D7, this routine returns in D0 the offset ; into cellArray of that cell. Leaves D7 intact. CellToIndex SWAP D7 ; c.v in low word MOVE.W dataBounds+right(A3),D3 ; index := c.v * rowLen + c.h SUB.W dataBounds+left(A3),D3 MULU D7,D3 SWAP D7 ADD.W D7,D3 ; got cell number LSL.W #1,D3 ; get index into D3 RTS ;------------ ; IndexToCell ; Given an offset into the cellArray in D3, this routine returns in D7 ; the cell that has that index. Leaves D3 intact. IndexToCell MOVE.L D1,-(SP) ; save D1 MOVE.W D3,D7 EXT.L D7 ; make it a long BEQ.S @1 ; offset is 0 => cell is 0 LSR.L #1,D7 ; make it a simple index MOVE.W dataBounds+right(A3),D1 ; get right SUB.W dataBounds+left(A3),D1 ; get row width DIVU D1,D7 ; get number of columns SWAP D7 ; high, remainder is rows @1 MOVE.L (SP)+,D1 ; restore D1 RTS ;---------------------------------------------------------------------- ; ; PROCEDURE ListClrCell( c: Cell; h: ListHandle ); ; ; Deletes the given cell from the given list ; Sets up stack to make a ListSetCell call with NIL string. ; ;---------------------------------------------------------------------- ListClrCell ; pull off return address, our params, push on p and l for SetCell, put ours back ; note: lock state of pack is in D1, donÕ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Õ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 MOVE.B D0,-(SP) ; save status on stack _HLock ; lock down the package MOVE.L cells(A3),A0 ; get handle to data _HGetState ; D0 := current lock, etc. state MOVE.B D0,-(SP) ; save status on stack _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Õ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Õs routine JSR (A0) ; with same params as IUMagIDString ; NOTE: We count on the userÕ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 _HSetState ; restore to handle MOVE.L IntUtilHandle,A0 ; get handle to package MOVE.B (SP)+,D0 ; saved lock state _HSetState ; restore to handle MOVEQ #18,D0 BRA StdExit ; strip of 18 bytes of parameters ENDPROC END