;
; listbox.s
;
; Nice listbox handling routines (TMA)
;
;  1994-1998 Straylight
;

;----- Licensing note -------------------------------------------------------
;
; This file is part of Straylight's Sapphire library.
;
; Sapphire is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2, or (at your option)
; any later version.
;
; Sapphire is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with Sapphire.  If not, write to the Free Software Foundation,
; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

;----- Standard header ------------------------------------------------------

		GET	libs:header
		GET	libs:swis

;----- External dependencies ------------------------------------------------

		GET	sapphire:alloc
		GET	sapphire:fastMove
		GET	sapphire:idle
		GET	sapphire:divide
		GET	sapphire:pane
		GET	sapphire:resspr
		GET	sapphire:sapphire
		GET	sapphire:screen
		GET	sapphire:tspr
		GET	sapphire:win

;----- Main code ------------------------------------------------------------

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- lb_create ---
;
; On entry:	R0 == pointer to list manager description block
;		R1 == pointer to the list
;		R2 == pointer to a width function
;		R3 == The height of each item
;		R4 == parent window handle or
;		      pointer to window block if R5 == -1
;		R5 == parent icon handle or -1 if not a pane
;
; On exit:	R0 == listbox handle
;		R1 == window handle of list box
;		May return an error (R1 corrupted)

		EXPORT	lb_create
lb_create	ROUT

		STMFD	R13!,{R2,R3,R10,R14}	;Stack some registers

		; --- First, create the block ---

		MOV	R10,R0			;Preserve R0
		MOV	R0,#lb__size		;The structure size
		BL	alloc			;Get the block
		BLCS	alloc_error		;Get a message if no memory
		BCS	%99lb_create		;Return on error

		MOV	R14,R10			;Do a swap type thing
		MOV	R10,R0			;Point to the block
		MOV	R0,R14			;Confused yet?
		STMIA	R10,{R0-R5}		;Store information in block
		MOV	R0,#0			;No flags set yet
		STR	R0,[R10,#lb__flags]	;Store this word
		STR	R0,[R10,#lb__handler]	;No handler either
		BL	lb__getMaxWidth		;Find length of longest item

		; --- Calculate the minimum width and height of the box ---

		CMP	R5,#-1			;Is this a pane listbox?
		BNE	%10lb_create		;No -- use icon then

		MOV	R14,#176		;A minimum height
		STR	R14,[R10,#lb__height]	;Store the width
		LDR	R0,[R4,#0]		;Load the minimum x position
		LDR	R14,[R4,#8]		;Load the maximum x position
		SUB	R0,R14,R0		;Get the initial width
		STR	R0,[R10,#lb__width]	;Store the width
		B	%20lb_create		;And jump ahead a bit

10lb_create	SUB	R13,R13,#40		;Get another block
		STR	R4,[R13,#0]		;Store parent window handle
		STR	R5,[R13,#4]		;And icon handle
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetIconState	;Get the icon's state

		LDR	R2,[R1,#16]		;Get the x1 coord
		LDR	R14,[R1,#8]		;And x0 coord
		SUB	R2,R2,R14		;Calculate the icon width
		STR	R2,[R10,#lb__width]	;Store the width
		LDR	R2,[R1,#20]		;Get the y1 coord
		LDR	R14,[R1,#12]		;And y0 coord
		SUB	R2,R2,R14		;Calculate the icon height
		STR	R2,[R10,#lb__height]	;Store the height
		ADD	R13,R13,#40		;Get the block back

		; --- Now create the window ---

20lb_create	SUB	R13,R13,#88		;Get a block
		MOV	R0,R13			;Point to it
		CMP	R5,#-1			;Is this a pane listbox?
		ADRNE	R1,lb__windowDef	;No -- point to my window def
		MOVEQ	R1,R4			;Yes -- use theirs then
		MOV	R2,#lb__defSize		;Copy this much
		BL	fastMove		;Copy the definition across
		LDR	R1,[R10,#lb__height]	;y0
		RSB	R1,R1,#0		;Negate it
		LDR	R2,[R10,#lb__width]	;x1
		ADD	R14,R13,#44		;The workarea extent block
		STMIA	R14,{R1,R2}		;Store the desired extent
		BL	resspr_area		;Find the sprite area
		STR	R0,[R13,#64]		;Store this in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_CreateWindow	;Try to create the window
		ADD	R13,R13,#88		;Get the stack back
		BVS	%98lb_create		;Return on error
		STR	R0,[R10,#lb__wHandle]	;Store my window handle

		; --- Set up the event handler ---

		ADR	R1,lb__eventHandler	;Point to the event handler
		MOV	R2,R10			;R10 value to call with
		MOV	R3,#0			;R12 value to call with
		BL	win_eventHandler	;Add in the event handler

		; --- Add it as a pane to the parent (if we need to) ---

		MOV	R2,R0			;The window handle
		MOV	R0,R4			;Parent window handle
		MOVS	R1,R5			;Parent icon handle
		BLPL	pane_add		;Register the pane (if reqd)

		; --- Add scroll bars if we need too ---

		ORR	R10,R10,#(1<<31)	;Don't open the window
		BL	lb__rescanSize		;Re-scan the size nicely
		BIC	R10,R10,#(1<<31)	;Clear silly bit again
		BVS	%98lb_create		;Return om error

		LDR	R1,[R10,#lb__wHandle]	;Return window handle in R1
		MOV	R0,R10			;Return the listbox handle
		B	%99lb_create		;Return to caller

98lb_create	MOV	R0,R10			;Point to the allocated block
		BL	free			;Free the block
99lb_create	LDMFD	R13!,{R2,R3,R10,R14}	;Get registers back
		ORRVSS	PC,R14,#V_flag		;And return either with...
		BICVCS	PC,R14,#V_flag		;...or without error

		LTORG

lb__windowDef	DCD	320,256,960,768
		DCD	0,0
		DCD	-1
		DCD	&80000122
		DCB	7,2,7,255,3,1,12,0
		DCD	0,0,0,0
		DCD	&39,10<<12
		DCD	0,0
		DCB	"ListBox",0,0
		DCD	0

lb__defSize	EQU	{PC}-lb__windowDef

; --- lb__rescanSize ---
;
; On entry:	R10 == pointer to listbox
;		  Bit 31 == don't call open the window
;
; On exit:	VS and R0 == pointer to error or
;		VC and R0 == window handle
;
; Use:		Scans the list, and adds/removes scroll bars if necessary

lb__rescanSize	ROUT

		STMFD	R13!,{R1-R10,R14}	;Stack some registers
		BIC	R10,R10,#(1<<31)	;Clear the top bit

		; --- First get the window state of the current window ---

		SUB	R13,R13,#36		;Get a block
		LDR	R0,[R10,#lb__wHandle]	;Get the window handle
		STR	R0,[R13,#0]		;Store in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetWindowState	;Get the window state
		LDR	R6,[R1,#32]		;Get the window flags
		ADD	R13,R13,#36		;Get the block back again
		LDR	R9,[R10,#lb__descriptor] ;Point to descriptor block
		LDR	R0,[R10,#lb__list]	;Point to the list itself
		MOV	R14,PC			;Set up the return address
		ADD	PC,R9,#lb_items		;Call the function
		LDR	R7,[R10,#lb__iHeight]	;The height of each item
		MUL	R7,R1,R7		;The required box y extent
		LDR	R4,[R10,#lb__width]	;Overall box width
		LDR	R5,[R10,#lb__height]	;Overall box height
		BL	tspr_borderWidths	;Get the border widths
		BL	screen_getInfo		;Get pixel sizes
		LDR	R14,[R0,#screen_dx]	;x pixel size
		SUB	R4,R4,R14,LSL#1		;Correct overall width
		ADD	R1,R1,R14		;Set up border width
		LDR	R14,[R0,#screen_dy]	;y pixel size
		SUB	R5,R5,R14,LSL#1		;Correct overall height
		SUB	R2,R2,R14		;Set up border height
		LDR	R14,[R10,#lb__pIHandle]	;Get the parent icon handle
		CMP	R14,#-1			;Is this a pane dialogue?
		BEQ	%50lb__rescanSize	;Yes -- jump ahead then

		; --- Which scroll bars do we want? ---

		MOV	R14,#0			;No flags so far
		LDR	R0,[R10,#lb__maxWidth]	;Get the width required
		CMP	R0,R4			;Is width>overall?
		ORRGT	R14,R14,#(1<<30)	;Yes -- we need horz. scr bar
		SUBGT	R5,R5,R2		;Reduce overall height

		CMP	R7,R5			;Do we need a vertical bar?
		ORRGT	R14,R14,#(1<<28)	;Yes -- make a note of this
		SUBGT	R4,R4,R1		;Reduce overall width

		TST	R14,#(1<<30)		;Do we have a horz. bar?
		BNE	%00lb__rescanSize	;Yes -- jump ahead
		CMP	R0,R4			;Is width>overall?
		ORRGT	R14,R14,#(1<<30)	;Yes -- we need horz. scr bar
		SUBGT	R5,R5,R2		;Reduce overall height

		; --- Do we need to add/remove scroll bars? ---

00		AND	R0,R6,#&50000000	;The scroll bars we have
		CMP	R0,R14			;Are the flags the same
		BEQ	%50lb__rescanSize	;Yes -- jump ahead

		; --- Now we need to recreate the window ---

		BIC	R6,R6,#&50000000	;No scroll bars at all
		ORR	R6,R6,R14		;Just the ones we want please

		; --- Now create the new window ---

		SUB	R13,R13,#88		;Get a block
		MOV	R0,R13			;Point to it
		ADR	R1,lb__windowDef	;Point to window definition
		MOV	R2,#lb__defSize		;Copy this much
		BL	fastMove		;Copy the definition across
		RSB	R1,R7,#0		;y0
		LDR	R2,[R10,#lb__maxWidth]	;x1
		ADD	R14,R13,#44		;The workarea extent block
		STMIA	R14,{R1,R2}		;Store the desired extent
		STR	R6,[R13,#28]		;Store the flags word
		BL	resspr_area		;Find the sprite area
		STR	R0,[R13,#64]		;Store this in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_CreateWindow	;Try to create the window
		ADD	R13,R13,#88		;Get the block back
		LDRVS	R0,[R10,#lb__pWHandle]	;On error, get window handle
		LDRVS	R1,[R10,#lb__pIHandle]	;...get the icon handle
		BLVS	pane_remove		;...remove the pane
		BVS	%99lb__rescanSize	;...and Barf with error

		MOV	R1,R0			;Look after the window handle
		LDR	R0,[R10,#lb__wHandle]	;Load the old handle
		BL	win_swapWindow		;Tell win the handle changed
		MOV	R2,R1			;Look after new handle
		ADD	R1,R10,#lb__wHandle	;Point to the old handle
		SWI	Wimp_DeleteWindow	;Destroy the old window
		STR	R2,[R10,#lb__wHandle]	;Store the new window handle

		LDR	R0,[R10,#lb__pWHandle]	;Parent window handle
		LDR	R1,[R10,#lb__pIHandle]	;Parent icon handle
		BL	pane_swap		;Swap the pane
		B	%50lb__rescanSize	;Jump ahead a little

50		; --- Work out the correct extents ---

		BL	screen_getInfo		;Get pixel sizes
		LDR	R14,[R0,#screen_dx]	;x pixel size
		ADD	R4,R4,R14,LSL#1		;Correct overall width

		CMP	R7,R5			;Is required<overall height?
		MOVLT	R7,R5			;Yes -- make them the same
		LDR	R3,[R10,#lb__maxWidth]	;Get the width required
		CMP	R3,R4			;Is required<overall width?
		MOVLT	R3,R4			;Yes -- make them the same
		SUB	R13,R13,#16		;Get a small block
		MOV	R1,#0			;x0 extent
		RSB	R2,R7,#0		;y0 extent
		MOV	R4,#0			;y1 extent
		STMIA	R13,{R1-R4}		;Store the required extent
		MOV	R1,R13			;Point to the block
		LDR	R5,[R10,#lb__wHandle]	;Load the window handle
		MOV	R0,R5			;Put it in R0
		SWI	Wimp_SetExtent		;Set the window extent
		ADD	R13,R13,#16		;Reclaim the block again

		LDR	R14,[R13,#36]		;Load R10 value
		TST	R14,#(1<<31)		;Should we return here
		BNE	%90lb__rescanSize	;Yes -- return then
		LDR	R14,[R10,#lb__pIHandle]	;Get the parent icon
		CMP	R14,#-1			;Is the listbox attached?
		STREQ	R5,[R13,#-36]!		;Yes -- store window handle
		MOVEQ	R1,R13			;Point at the block
		SWIEQ	Wimp_GetWindowState	;Find the current position
		SWIEQ	Wimp_OpenWindow		;And open it again
		ADDEQ	R13,R13,#36		;Restore the stack
		LDRNE	R0,[R10,#lb__pWHandle]	;Parent window handle
		BLNE	pane_open		;Open the listbox cunningly
90		MOV	R0,R5			;Get the window handle back

		; --- Return to caller ---

99		LDMFD	R13!,{R1-R10,R14}	;Load registers
		ORRVSS	PC,R14,#V_flag		;And return with...
		BICVCS	PC,R14,#V_flag		;...or without error

		LTORG

; --- lb_destroy ---
;
; On entry:	R0 == listbox handle
;
; On exit:	--
;
; Use:		Destroys the given listbox.

		EXPORT	lb_destroy
lb_destroy	ROUT

		STMFD	R13!,{R0-R3,R10,R14}	;Stack some registers
		MOV	R10,R0			;Keep the pointer
		ADD	R1,R10,#lb__wHandle	;Point to the window handle
		SWI	XWimp_CloseWindow	;Close the window
		SWI	Wimp_DeleteWindow	;And the delete it

		; --- Remove the pane attachment ---

		LDR	R0,[R10,#lb__pWHandle]	;Get the parent window handle
		LDR	R1,[R10,#lb__wHandle]	;Get the listbox window hnd
		BL	pane_remove		;Remove the pane

		; --- Remove the event handler ---

		MOV	R0,R1			;The window handle
		BL	win_windowDeleted	;Remove the handler

		; --- Finally, free the block ---

		MOV	R0,R10			;Point to the block
		BL	free			;Free it

		LDMFD	R13!,{R0-R3,R10,PC}^	;Return to caller

; --- lb_eventHandler ---
;
; On entry:	R0 == listbox handle
;		R1 == handler function
;		R2 == R10 value to pass
;		R3 == R12 value to pass
;
; On exit:	--
;
; Use:		Registers an event handler for the given listbox.

		EXPORT	lb_eventHandler
lb_eventHandler	ROUT

		STMFD	R13!,{R14}		;Stack some registers
		ADD	R14,R0,#lb__handler	;Point to nice block
		STMIA	R14,{R1-R3}		;Store the information
		LDMFD	R13!,{PC}^		;Return to caller

		LTORG

; --- lb__getMaxWidth ---
;
; On entry:	R10 == pointer to listbox structure
;
; On exit:	R0 == longest item width in the list

lb__getMaxWidth	ROUT

		STMFD	R13!,{R1-R7,R14}	;Stack some registers

		MOV	R4,#0			;The longest width so far
		LDR	R5,[R10,#lb__widthFun]	;Get the width function ptr
		LDR	R6,[R10,#lb__descriptor] ;Point to the descriptor blk
		LDR	R0,[R10,#lb__list]	;Get the list head
		MOV	R1,#0			;From the first item
		MOV	R2,#0			;No mask word
		MOV	R3,#0			;Or test word
00		MOV	R14,PC			;Set up the return address
		ADD	PC,R6,#lb_enumerate	;Get a list item
		BCC	%99lb__getMaxWidth	;No more items -- return
		MOV	R7,R1			;Remember the R1 value
		MOV	R14,PC			;Set up return address
		MOV	PC,R5			;Call the width function
		CMP	R1,R4			;Is the the longest width?
		MOVGT	R4,R1			;Yes -- remember this fact
		MOV	R1,R7			;Put R1 back again
		B	%00lb__getMaxWidth	;Check other items

99		MOV	R0,R4			;Put width in R0
		STR	R0,[R10,#lb__maxWidth]	;And store in the listbox
		LDMFD	R13!,{R1-R7,PC}^	;And return to caller

		LTORG

; --- lb__eventHandler ---
;
; On entry:	R0 == event type
;		R1 == pointer to event block
;
; On exit:	--
;
; Use:		Called to deal with events on the listbox window

lb__eventHandler ROUT

		CMP	R0,#1			;Is it a redraw event?
		BEQ	lb__redraw		;Yes -- handle it then
		CMP	R0,#3			;Is it a close event?
		BEQ	lb__close		;Yes -- report it then
		CMP	R0,#10			;Scroll request?
		BEQ	lb__scroll		;Yes -- let's hope we cope
		CMP	R0,#6			;Is it a click event?
		BEQ	lb__click		;Yes -- handle that
		CMP	R0,#17			;Is it a message (normal)?
		CMPNE	R0,#18			;Or a rubber message?
		BEQ	lb__message		;Yes -- handle it then

		BICS	PC,R14,#C_flag		;Return -- didn't understand

		LTORG

; --- lb__dispatch ---
;
; On entry:	R0 == a list box event code
;		R1-R8 == arguments for the client's event handler
;		R10 == list box handle
;
; On exit:	CC or CS, from the event handler
;
; Use:		Sends an event to the user event handler for the list box.

lb__dispatch	ROUT

		STMFD	R13!,{R9,R10,R12,R14}	;Save some useful registers
		ADD	R14,R10,#lb__handler	;Point to the handler stuff
		LDMIA	R14,{R9,R10,R12}	;Load the appropriate bits
		ADDS	R0,R0,#0		;Clear the carry flag
		TEQ	R9,#0			;Is there a handler defined?
		MOV	R14,PC			;Set up a return address
		MOVNE	PC,R9			;Yes -- call the handler
		LDMFD	R13!,{R9,R10,R12,R14}	;Unstack the registers again
		BICCCS	PC,R14,#C_flag		;If C clear then clear C
		ORRCSS	PC,R14,#C_flag		;If C set then set C

		LTORG

; --- lb__message ---
;
; On entry:	R0 == message type (17 or 18)
;		R1 == event block
;
; On exit:	--
;
; Use:		Called when the listbox recieves a message

lb__message	ROUT

		STMFD	R13!,{R0-R5,R14}	;Stack some messages
		LDR	R0,[R1,#16]		;Get the message type
		CMP	R0,#1			;Is it a save message?
		CMPNE	R0,#3			;Or a load message?
		BEQ	%10lb__message		;Yes -- handle them the same
		LDR	R14,=&502		;The help message code
		CMP	R0,R14			;Is that the message?
		BEQ	%05lb__message		;Yes -- deal with it

		; --- Check for mode/font changes ---

		CMP	R0,R14			;Are they the same
		LDRNE	R14,=&400CF		;Also get a FontChange msg
		CMPNE	R0,R14			;Does this match?
		BLEQ	lb__getMaxWidth		;Yes -- calculate max width
		BLEQ	lb__rescanSize		;...and rescan the size
		LDMFD	R13!,{R0-R5,PC}^	;And return to caller

		; --- Handle a help request ---

05lb__message	ADD	R1,R1,#20		;Point to mouse info
		BL	lb__getItem		;Get the item under pointer
		MOV	R0,#lbEvent_help	;Set up the event type
		BL	lb__dispatch		;Dispatch the event
		LDMFD	R13!,{R0-R5,PC}^	;Return to caller

		; --- Handle data saves and loads ---

10lb__message	CMP	R0,#1			;Is it a save message?
		MOVEQ	R5,#lbDrop_save		;Set up the subreason code
		MOVNE	R5,#lbDrop_load

		MOV	R6,R1			;Remember the block pointer
		SUB	R13,R13,#52		;Get a nice block
		MOV	R1,R13			;Point to it
		SWI	Wimp_GetPointerInfo	;Get pointer information
		BL	lb__getItem		;Get the item under pointer
		ADD	R13,R13,#52		;Get the stack back

		LDR	R2,[R6,#40]		;Load the file type
		ADD	R3,R6,#44		;Point to the filename
		LDR	R4,[R6,#36]		;Load the estimated size
		MOV	R0,#lbEvent_drop	;The event type
		BL	lb__dispatch		;Dispatch the event
		LDMFD	R13!,{R0-R5,PC}^	;Return to caller

		LTORG

; --- lb__getItem ---
;
; On entry:	R1 == pointer to a mouse info block
;		R10 == pointer to the listbox
;
; On exit:	R1 == the item
;
; Use:		Returns the item the pointer is over

lb__getItem	ROUT

		STMFD	R13!,{R0,R2-R4,R9,R14}	;Stack some registers
		MOV	R9,R1			;Keep the mouse info block
		SUB	R13,R13,#36		;Get me a nice block
		LDR	R0,[R9,#12]		;The window handle
		STR	R0,[R13,#0]		;Store it in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetWindowState	;Get the window state

		LDR	R2,[R13,#16]		;y1 coord
		LDR	R14,[R13,#24]		;Scroll y position
		SUB	R2,R2,R14		;Screen coord of origin
		LDR	R14,[R9,#4]		;Mouse y position
		SUB	R2,R14,R2		;Window relative position
		ADD	R13,R13,#36		;Get stack back
		BL	screen_getInfo		;Get screen information
		LDR	R14,[R0,#screen_dy]	;Get y pixel size
		ADD	R0,R2,R14		;Prepare for division
		LDR	R1,[R10,#lb__iHeight]	;By the item height
		RSB	R1,R1,#0		;Negate it
		BL	divide			;Perform the division
		MOV	R1,R0			;Put index in R1
		LDR	R0,[R10,#lb__list]	;And list head in R0
		LDR	R4,[R10,#lb__descriptor] ;Get the descriptor block
		MOV	R14,PC			;Set up the return address
		ADD	PC,R4,#lb_indexToItem	;Get the item itself
		LDMFD	R13!,{R0,R2-R4,R9,PC}^	;Return to caller

		LTORG

; --- lb__redraw ---
;
; On entry:	R1 == pointer to a window handle
;
; On exit:	CS
;
; Use:		Redraws a list box, by sending redraw events to the client.

lb__redraw	ROUT

		STMFD	R13!,{R0-R9,R14}	;Save masses of registers

		; --- Start a redraw job ---

		SWI	Wimp_RedrawWindow	;Start the redraw
		CMP	R0,#0			;Is there anything to do?
		BEQ	%90lb__redraw		;No -- don't do it then

		; --- Do some setting up ---

		ADD	R14,R1,#16		;Point to the top edge
		LDMIA	R14,{R0,R9,R14}		;Load that and the scroll pos
		SUB	R7,R0,R14		;Work out the y origin pos

		; --- Get the left and right sides set up ---

		LDR	R4,[R10,#lb__maxWidth]	;Get the width of the items
		LDR	R14,[R10,#lb__width]	;Get the window width
		CMP	R14,R4			;Is the window width bigger?
		MOVGT	R4,R14			;Yes -- use that instead

		LDR	R9,[R10,#lb__descriptor] ;Load his list descriptor
		LDR	R8,[R10,#lb__list]	;Find the list block

		; --- Work out which bits need drawing ---

10lb__redraw	LDR	R6,[R1,#40]		;Load the top y coordinate
		SUB	R6,R6,R7		;Convert to window coords
		LDR	R14,[R10,#lb__iHeight]	;Get the item height
		ADD	R6,R6,R14		;Get height thingy value

		MOV	R5,#0			;Top of the first item
		MOV	R1,#0			;Start at item -1 (sort of)

		; --- Get items until one is visible ---

15lb__redraw	MOV	R0,R8			;Point to the list block
		MOV	R2,#0			;Don't care about the flags
		MOV	R3,#0			;I *really* don't care at all
		MOV	R14,PC			;Set up the return address
		ADD	PC,R9,#lb_enumerate	;Get a new item in R1
		BCC	%70lb__redraw		;Run out -- 76 for 3

		CMP	R5,R6			;Is this item visible?
		LDRGT	R14,[R10,#lb__iHeight]	;No -- get the item height
		SUBGT	R5,R5,R14		;Move to the next one down
		BGT	%15lb__redraw		;And find one that is then

		; --- Now draw items until they stop being visible ---

		LDR	R6,[R13,#4]		;Get the redraw block pointer
		LDR	R6,[R6,#32]		;Load the bottom y coordinate
		SUB	R6,R6,R7		;And convert to window coords

		; --- The main plotting loop ---

20lb__redraw	CMP	R5,R6			;Is this one visible?
		BLE	%80lb__redraw		;No -- then stop plotting

		MOV	R0,#lbEvent_redraw	;Send a redraw event on
		LDR	R14,[R10,#lb__iHeight]	;Get the item height
		SUB	R3,R5,R14		;Get the item bottom bit
		MOV	R2,#0			;Left hand side is at 0
		BL	lb__dispatch		;Send the event on then

		MOV	R5,R3			;Move to the next item down
		MOV	R0,R8			;Point to the list block
		MOV	R2,#0			;Don't care about the flags
		MOV	R3,#0			;I *really* don't care at all
		MOV	R14,PC			;Set up the return address
		ADD	PC,R9,#lb_enumerate	;Get a new item in R1
		BCS	%20lb__redraw		;More to do -- go round again

		; --- Fill in the rest in grey ---

		CMP	R5,R6			;Do we really have to draw it
		BLE	%80lb__redraw		;No -- then stop plotting

70lb__redraw	MOV	R0,#1			;The background colour
		SWI	Wimp_SetColour		;Set the colour up
		ADD	R2,R5,R7		;Get the top of the area
		BL	screen_getInfo		;Read some screen information
		MOV	R14,#1			;This will be shifty
		LDR	R0,[R0,#screen_yEig]	;Load the y eigen factor
		SUB	R2,R2,R14,LSL R0	;S*dding thing's exclusive
		LDR	R3,[R13,#4]		;Load the redraw block addr
		LDR	R1,[R3,#28]		;Load the left hand side
		MOV	R0,#4			;Move cursor absolute
		SWI	OS_Plot			;Do the plotting bit
		MOV	R0,#101			;Rectangle fill absolute
		LDR	R1,[R3,#36]		;Get the right hand side
		LDR	R2,[R3,#32]		;And the bottom edge
		SWI	OS_Plot			;Fill in the remaining bit

		; --- Go round and get another rectangle ---

80lb__redraw	LDR	R1,[R13,#4]		;Get the redraw block pointer
		SWI	Wimp_GetRectangle	;Get the next rectangle
		CMP	R0,#0			;Is there more to draw?
		BNE	%10lb__redraw		;Yes -- go round again

		; --- We finished at last ---

90lb__redraw	LDMFD	R13!,{R0-R9,R14}	;Unstack the registers again
		ORRS	PC,R14,#C_flag		;More redraws cause flicker

		LTORG

; --- lb__close ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Informs the user when the listbox has been closed.

lb__close	ROUT

		STMFD	R13!,{R0,R14}		;Stack some register
		MOV	R0,#lbEvent_close	;Send this event code
		BL	lb__dispatch		;And dispatch the event
		LDMFD	R13!,{R0,PC}		;Return to caller

		LTORG

; --- lb__scroll ---
;
; On entry:	R0 == 10
;		R1 == event block
;
; On exit:	--

lb__scroll	ROUT

		STMFD	R13!,{R0-R4,R14}	;Stack some registers
		MOV	R4,R1			;Keep the block pointer

		; --- First, try to scroll vertically ---

		LDR	R0,[R4,#24]		;Load the y scroll offset
		ADD	R14,R4,#8		;Find the bottom edge
		LDMIA	R14,{R2,R3,R14}		;Load the y positions
		SUB	R3,R14,R2		;R3 is the window height now
		LDR	R2,[R10,#lb__iHeight]	;Get the item height

		LDR	R14,[R1,#36]		;Get vertical scroll value
		ADD	R14,R14,#1		;Convert into range -1..3
		ADD	PC,PC,R14,LSL #2	;And jump to right routine

		B	%10lb__scroll		;Y scroll page down
		B	%20lb__scroll		;Y scroll line down
		B	%50lb__scroll		;No y scrolling
		B	%40lb__scroll		;Y scroll line up
		B	%30lb__scroll		;Y scroll page up

		; --- Do a y scroll page down ---

10lb__scroll	SUB	R0,R0,R3		;Find the window bottom
		MOV	R1,R2			;Now prepare to divide by it
		SUB	R0,R0,R2		;Make it round towards 0
		BL	divide			;Get the quotient value
		MUL	R0,R2,R0		;And multiply it up again
		ADD	R0,R0,R2,LSL #1		;Find the appropriate top
		STR	R0,[R4,#24]		;Store the scroll offset back
		B	%50lb__scroll		;And now do the x bit

		; --- Do a y scroll line down ---

20lb__scroll	SUB	R0,R0,R3		;Find the window bottom
		MOV	R1,R2			;Now prepare to divide by it
		SUB	R0,R0,R2		;Make it round towards 0
		BL	divide			;Get the quotient value
		MUL	R0,R2,R0		;And multiply it up again
		ADD	R0,R0,R3		;Find the appropriate top
		STR	R0,[R4,#24]		;Store the scroll offset back
		B	%50lb__scroll		;And now do the x bit

		; --- Do a y scroll page up ---

30lb__scroll	MOV	R1,R2			;Now prepare to divide by it
		ADD	R0,R0,#1		;Make it round towards 0
		BL	divide			;Get the quotient value
		MUL	R0,R2,R0		;And multiply it up again
		ADD	R0,R0,R3		;Scroll the whole window up
		SUB	R0,R0,R2,LSL #1		;And scroll back two items
		STR	R0,[R4,#24]		;Store the scroll offset back
		B	%50lb__scroll		;And now do the x bit

		; --- Do a y scroll line up ---

40lb__scroll	MOV	R1,R2			;Now prepare to divide by it
		ADD	R0,R0,#1		;Make it round towards 0
		BL	divide			;Get the quotient value
		MUL	R0,R2,R0		;And multiply it up again
		STR	R0,[R4,#24]		;Store the scroll offset back
		B	%50lb__scroll		;And now do the x bit

		; --- Now do the x scrolling ---

50lb__scroll	LDR	R0,[R4,#20]		;Load the x scroll offset
		ADD	R14,R4,#4		;Find the left hand side
		LDMIA	R14,{R2,R3,R14}		;Load the x positions
		SUB	R3,R14,R2		;Get the current window width

		LDR	R14,[R4,#32]		;Get horizontal scroll value
		CMP	R14,#-2			;Scroll left by a page?
		SUBEQ	R0,R0,R3		;Yes -- do it then
		CMP	R14,#-1			;Scroll left by a column?
		SUBEQ	R0,R0,#32		;Yes -- do it then
		CMP	R14,#1			;Scroll right by a column?
		ADDEQ	R0,R0,#32		;Yes -- do it then
		CMP	R14,#2			;Scroll right by a page?
		ADDEQ	R0,R0,R3		;Yes -- do it then
		STR	R0,[R4,#20]		;Save the x scroll offset

99lb__scroll	MOV	R1,R4			;Point to the block again
		SWI	Wimp_OpenWindow		;Actually do the scrolling
		LDMFD	R13!,{R0-R4,R14}	;Return to caller
		ORRS	PC,R14,#C_flag

		LTORG

; --- lb_plotString ---
;
; On entry:	R0 == pointer to a string
;		R1 == pointer to the list item
;		R2-R5 == window coordinates to plot it
;		R10 == pointer to the listbox
;
; On exit:	--
;
; Use:		Plots a list item consisting of a single string.  It assumes
;		the default selection model.

		EXPORT	lb_plotString
lb_plotString	ROUT

		STMFD	R13!,{R0-R4,R14}	;Save a lot of registers
		SUB	R13,R13,#32		;Make an icon block
		STMIA	R13,{R2-R5}		;Save the coordinates in it
		MOV	R2,#-1			;No validation string
		MOV	R3,#1			;Put in a silly buffer size
		ADD	R14,R13,#20		;Point to icon data section
		STMIA	R14,{R0,R2,R3}		;Save the icon data away
		LDR	R4,[R10,#lb__descriptor] ;Get the descriptor block
		LDR	R0,[R10,#lb__list]	;Point to the list head
		MOV	R2,#0			;Don't alter the flags
		MOV	R3,#0			;Don't alter them at all
		MOV	R14,PC			;Set up return address
		ADD	PC,R4,#lb_setFlags	;Read the item's flags
		LDR	R0,=&07000131		;Set up default flags
		TST	R2,#3			;Is it selected?
		ORRNE	R0,R0,#&00200000	;Yes -- set the selected bit
		STR	R0,[R13,#16]		;Store the flags in the blk
		MOV	R1,R13			;Point to the icon block
		SWI	Wimp_PlotIcon		;Plot the icon then
		ADD	R13,R13,#32		;Recover the icon block
		LDMFD	R13!,{R0-R4,PC}^	;Return to caller

		LTORG

; --- lb_update ---
;
; On entry:	R0 == listbox handle
;
; On exit:	May return an error
;
; Use:		Updates the entire listbox prettily

		EXPORT	lb_update
lb_update	ROUT

		STMFD	R13!,{R0-R4,R10,R14}	;Stack sonme registers

		MOV	R10,R0			;Put list box handle in R10
		BL	lb__getMaxWidth		;Get the maximum width
		BL	lb__rescanSize		;Ensure windows right size
		BVS	%90lb_update		;Barf on error
		LDR	R0,[R10,#lb__wHandle]	;Get the window handle
		MOV	R1,#0			;x0
		LDR	R2,[R10,#lb__height]	;Overall height
		RSB	R2,R2,#0		;Negate it
		LDR	R3,[R10,#lb__width]	;Overall width -- x1
		LDR	R14,[R10,#lb__maxWidth]	;And the widest item width
		CMP	R14,R3			;Which one is bigger?
		MOVGT	R3,R14			;Make R3 biggest
		MOV	R4,#0			;y1
		SWI	Wimp_ForceRedraw	;Force a redraw

		LDMFD	R13!,{R0-R4,R10,PC}^	;Return to caller

90lb_update	ADD	R13,R13,#4		;Point past R0
		LDMFD	R13!,{R1-R4,R10,R14}	;Get back some registers
		ORRS	PC,R14,#V_flag		;Return with error

		LTORG

; --- lb_updateItem ---
;
; On entry:	R0 == list box handle
;		R1 == list item handle
;
; On exit:	--
;
; Use:		Redraws a list item to reflect a change in its state.

		EXPORT	lb_updateItem
lb_updateItem	ROUT

		STMFD	R13!,{R0-R5,R10,R14}	;Save a load of registers

		; --- Find the item's index ---

		MOV	R10,R0			;Keep the list box handle
		LDR	R0,[R10,#lb__list]	;Find the list block
		LDR	R2,[R10,#lb__descriptor] ;Find the list descriptor
		MOV	R14,PC			;Set up the return address
		ADD	PC,R2,#lb_itemToIndex	;Get the item's index

		; --- Now set up the update block ---

		SUB	R13,R13,#44		;Make space for update blk
		LDR	R0,[R10,#lb__wHandle]	;Get the window handle
		LDR	R14,[R10,#lb__iHeight]	;Get the item height
		RSB	R14,R14,#0		;Negate it
		MUL	R5,R14,R1		;Get the top of the item
		ADD	R3,R5,R14		;And get the bottom too
		MOV	R2,#0			;Redraw from left hand side
		LDR	R4,[R10,#lb__width]	;Load the window width
		LDR	R14,[R10,#lb__maxWidth]	;And the widest item width
		CMP	R14,R4			;Which one is bigger?
		MOVGT	R4,R14			;Use the biggest one
		STMIA	R13,{R0,R2-R5}		;Save them in the block

		; --- Update the item ---

		MOV	R1,R13			;Point to the window block
		SWI	Wimp_UpdateWindow	;Start doing the update
		CMP	R0,#0			;Is there anything to do?
		BEQ	%90lb_updateItem	;No -- skip it all then

10lb_updateItem	MOV	R0,#lbEvent_redraw	;Send out a redraw event
		LDR	R1,[R13,#48]		;Get the item handle
		BL	lb__dispatch		;Give the user an event
		MOV	R1,R13			;Point to the update block
		SWI	Wimp_GetRectangle	;Get another rectangle
		CMP	R0,#0			;Was that the last one?
		BNE	%10lb_updateItem	;No -- do another one then

		; --- Tidy up and go home again ---

90lb_updateItem	ADD	R13,R13,#44		;Restore the stack pointer
		LDMFD	R13!,{R0-R5,R10,PC}^	;And return to the caller

		LTORG

; --- lb_select ---
;
; On entry:	R0 == listbox handle
;		R1 == item handle
;		R2 == 0 to unselect, 1 to select, or 2 to toggle
;
; On exit:	--
;
; Use:		Selects or deselects a listbox item, nicely and without
;		flickering it.

		EXPORT	lb_select
lb_select	ROUT

		CMP	R1,#0			;Is there actually an item?
		MOVEQS	PC,R14			;No -- then do nothing here

		STMFD	R13!,{R0-R5,R10,R14}	;Save some registers away
		MOV	R10,R0			;Get the listbox handle

		; --- Read the current flags ---

		LDR	R4,[R10,#lb__descriptor] ;Find the list definition
		MOV	R5,R2			;And look after argument
		MOV	R3,#0			;Want to read the flags
		MOV	R2,#0			;Oh, yes I do
		LDR	R0,[R10,#lb__list]	;Load the list block
		MOV	R14,PC			;Set up the return address
		ADD	PC,R4,#lb_setFlags	;And read the current flags

		; --- Work out new flags ---

		AND	R2,R2,#1		;Only leave selected flag
		CMP	R5,#1			;What is the operation?
		MOVLT	R3,#0			;Clear -- clear the flag
		MOVEQ	R3,#1			;Set -- set the flag
		EORGT	R3,R2,#1		;Toggle -- toggle the flag
		CMP	R3,R2			;Have we made a difference?
		LDMEQFD	R13!,{R0-R5,R10,PC}^	;No -- return now then

		; --- Set the new flags ---

		MOV	R2,#1			;Clear the selected bit
		MOV	R14,PC			;Set up return address
		ADD	PC,R4,#lb_setFlags	;Set the new flags nicely
		MOV	R0,R10			;Get handle in R0
		BL	lb_updateItem		;And redraw the list item
		LDMFD	R13!,{R0-R5,R10,PC}^	;And return to caller

		LTORG

; --- lb_isSelected ---
;
; On entry:	R0 == listbox handle
;		R1 == item handle
;
; On exit:	CS if item is selected, else CC
;
; Use:		Informs you whether an item is selected.

		EXPORT	lb_isSelected
lb_isSelected	ROUT

		CMP	R1,#0			;Is there an icon?
		BICEQS	PC,R14,#C_flag		;No -- not selected then
		STMFD	R13!,{R0-R4,R14}	;Save some registers
		MOV	R2,#0			;Don't clear any flags
		MOV	R3,#0			;Don't toggle any either
		LDR	R4,[R0,#lb__descriptor]	;Find the list block
		LDR	R0,[R0,#lb__list]	;Find the list base
		MOV	R14,PC			;Set up return address
		ADD	PC,R4,#lb_setFlags	;Read the current flags
		TST	R2,#1			;Is it selected then?
		LDMFD	R13!,{R0-R4,R14}	;Restore caller's registers
		ORRNES	PC,R14,#C_flag		;If selected, return C set
		BICEQS	PC,R14,#C_flag		;Otherwise clear C on exit

		LTORG

; --- lb__click ---
;
; On entry:	R1 == pointer to event block
;
; On exit:	CS
;
; Use:		Dispatches click events on the list box

lb__click	ROUT

		ORR	R14,R14,#C_flag		;Set the carry flag
		STMFD	R13!,{R0-R4,R9,R14}	;Stack some registers
		MOV	R9,R1			;Keep a pointer to the block

		BL	lb__getItem		;Get item pointer is over

		LDR	R3,[R9,#8]		;Get the mouse button status
		TST	R3,#&5			;Double select or ajdust
		TSTEQ	R3,#&500		;No -- just select or adjust?
		BNE	%10lb__click		;Yes -- handle it then
		TST	R3,#&50			;Drag with select or adjust?
		BNE	%20lb__click		;Yes -- deal with that
		TST	R3,#&2			;Is it menu click?
		LDMEQFD	R13!,{R0-R3,R9,PC}^	;no -- return then

		; --- Deal with menu clicks ---

		MOV	R0,#lbEvent_menu	;The event type
		BL	lb__dispatch		;Dispatch the event
		B	%99lb__click		;And return to caller

		; --- Click or double click on an item ---

10		MOV	R0,#lbEvent_click	;The event type
		BL	lb__dispatch		;Dispatch the event
		B	%99lb__click		;And return to caller

		; --- There was a drag event ---

20		MOV	R0,#lbEvent_drag	;The event type
		BL	lb__dispatch		;Dispatch the event
		B	%99lb__click		;And return to caller

99		LDMFD	R13!,{R0-R4,R9,PC}^	;Return to caller

		LTORG

; --- lb_clearSelection ---
;
; On entry:	R0 == listbox handle
;		R1 == item handle of item to ignore (or 0 for none)
;
; On exit:	--
;
; Use:		Deselects all items in the listbox.

		EXPORT	lb_clearSelection
lb_clearSelection ROUT

		STMFD	R13!,{R0-R6,R10,R14}	;Stack some registers
		MOV	R10,R0			;Get the listbox handle
		LDR	R6,[R10,#lb__list]	;And load the list pointer
		MOV	R5,R1			;Remember this item
		LDR	R4,[R10,#lb__descriptor] ;Point to the descriptor blk

		MOV	R1,#0			;Start with the first item
00		MOV	R0,R6			;Get the list handle
		MOV	R2,#1			;Only read selected items
		MOV	R3,#1			;Don't want the others
		MOV	R14,PC			;Set up return address
		ADD	PC,R4,#lb_enumerate	;Read the item and flags
		BCC	%90			;No more items -- skip

		CMP	R1,R5			;Is this the special item?
		BEQ	%b00			;Yes -- ignore it then
		MOV	R0,R10			;No -- get the list handle
		MOV	R2,#0			;Deselect it please
		BL	lb_select		;And unselect the item
		B	%b00			;And loop back again

90		LDMFD	R13!,{R0-R6,R10,PC}^	;Return to caller

		LTORG

; --- lb_clickS ---
;
; On entry:	R0 == listbox handle
;		R1 == pointer to list item
;		R3 == mouse button status
;
; On exit:	--
;
; Use:		Provides a default action for clicking on an item in a
;		list box.
;
;		Only one selection is possible at any one time.

		EXPORT	lb_clickS
lb_clickS	ROUT

		STMFD	R13!,{R0,R2,R14}	;Stack some registers
		BL	lb_clearSelection	;Clear all the other items
		TST	R3,#&100		;Is it an adjust click?
		TSTEQ	R3,#&001		;Better check the double too
		MOVNE	R2,#2			;Yes -- then toggle the item
		MOVEQ	R2,#1			;Otherwise just select
		BL	lb_select		;Go and select the item
		LDMFD	R13!,{R0,R2,PC}^	;Return to caller

		LTORG

; --- lb_clickM ---
;
; On entry:	R0 == listbox handle
;		R1 == pointer to list item
;		R3 == mouse button status
;
; On exit:	--
;
; Use:		Provides a default action for clicking on an item in a
;		list box.
;
;		The multiple selection model is used.

		EXPORT	lb_clickM
lb_clickM	ROUT

		STMFD	R13!,{R0,R2,R14}	;Stack some registers
		TST	R3,#&100		;Is it an adjust click?
		TSTEQ	R3,#&001		;Better check the double too
		BEQ	%50lb_clickM		;No -- then deal elsewhere
		MOV	R2,#2			;Just toggle the item
		BL	lb_select		;Do the selecting bit
		B	%90lb_clickM		;And skip onwards
50lb_clickM	BL	lb_isSelected		;Is the item selected?
		BLCC	lb_clearSelection	;No -- then clear the others
		MOVCC	R2,#1			;Select this item
		BLCC	lb_select		;Go and do that
90lb_clickM	LDMFD	R13!,{R0,R2,PC}^	;Return to caller

		LTORG

; --- lb__idleHandler ---
;
; On entry:	R10 == listbox handle
;		R12 == workspace pointer					;
; On exit:	--
;
; Use:		Called on NULL events during a drag

lb__idleHandler	ROUT

		STMFD	R13!,{R0-R9,R14}	;Stack some registers

		SUB	R13,R13,#56		;Get a block
		MOV	R1,R13			;Point to it
		SWI	Wimp_GetPointerInfo	;Get pointer position
		LDR	R0,[R10,#lb__wHandle]	;Get the window handle
		STR	R0,[R13,#20]		;Store in the block
		ADD	R1,R13,#20		;Point to the block
		SWI	Wimp_GetWindowState	;Get the window state
		LDR	R9,[R13,#36]		;Get the y1 coordinate
		LDR	R14,[R13,#44]		;The scroll coordinate
		SUB	R9,R9,R14		;Window origin
		LDR	R14,[R13,#4]		;Mouse y position
		SUB	R9,R14,R9		;Make it window relative

		BL	screen_getInfo		;Get screen information
		LDR	R14,[R0,#screen_dy]	;Get y pixel size
		ADD	R0,R9,R14		;Prepare for division
		LDR	R1,[R10,#lb__iHeight]	;By the item height
		RSB	R1,R1,#0		;Negate it
		BL	divide			;Perform the division
		CMP	R0,#0			;Is index negative?
		MOVLT	R0,#0			;Yes -- make it 0

		LDR	R6,ws__dragItem		;Get start drag index
		LDR	R3,ws__dragLast		;And last drag position
		MOV	R8,R0			;Copy index into R1
		MOV	R7,R0			;And R7
		MOV	R5,R0			;And R5

		; --- Find minimum and maximum values ---

		CMP	R7,R6			;R0>R2?
		MOVGT	R7,R6			;Yes == R0:=R2
		CMP	R7,R3			;R0>R3?
		MOVGT	R7,R3			;Yes == R0:=R3

		CMP	R8,R6			;R0<R2?
		MOVLT	R8,R6			;Yes == R0:=R2
		CMP	R8,R3			;R0<R3?
		MOVLT	R8,R3			;Yes == R0:=R3

		; --- Summary ---
		;
		; R5 == the current index
		; R6 == the initial index
		; R7 == minimum of current,last and initial drag indicies
		; R8 == maximum of current,last and initial drag indicies
		; R9 == window relative position of the mouse position

		STMFD	R13!,{R12}		;We need this -- blurgg!!
		LDR	R12,[R10,#lb__descriptor] ;Point to descriptor block
		MOV	R4,#-1			;Current item index (i)
		MOV	R3,#-1			;The selection type
		LDR	R0,[R10,#lb__list]	;Get the list head
		MOV	R1,#0			;Start from the first item

00		STMFD	R13!,{R3}		;Preserve R3 over call
		MOV	R2,#0			;Interested in all items
		MOV	R3,#0			;Interested in all items
		MOV	R14,PC			;Set up return address
		ADD	PC,R12,#lb_enumerate	;Call enumeration function
		LDMFD	R13!,{R3}		;Get R3 back again
		BCC	%50lb__idleHandler	;No more -- exit the loop
		ADD	R4,R4,#1		;Increment index count

		CMP	R4,R7			;i == start index?
		MOVEQ	R3,#0			;Yes -- sel=0
		CMP	R6,R5			;Initial index<index
		BGT	%20lb__idleHandler	;No -- jump ahead then

		CMP	R4,R6			;i=initial index?
		MOVEQ	R3,#1			;Yes -- sel=1
		CMP	R4,R5			;i>current index?
		MOVGT	R3,#0			;Yes -- sel=0
		B	%30lb__idleHandler	;Jump ahead a bit

20		CMP	R4,R5			;i=current index?
		MOVEQ	R3,#1			;Yes -- sel=1
		CMP	R4,R6			;i>initial index?
		MOVGT	R3,#0			;Yes -- sel=0
		B	%30lb__idleHandler	;Jump ahead a bit

30		CMP	R4,R8			;Are we at the end?
		BGT	%50lb__idleHandler	;Yes -- jump out of loop

		; --- Redraw the item if we need to ---

		CMP	R3,#-1			;Do we need to do anything?
		BEQ	%00lb__idleHandler	;No -- keep going round
		STMFD	R13!,{R0-R4}		;Stack some registers
		LDR	R0,[R10,#lb__list]	;Get the list head
		MOV	R4,R3			;Look after thing in R3
		MOV	R2,#0			;Read the flags
		MOV	R3,#0			;No writing here
		MOV	R14,PC			;Set up return address
		ADD	PC,R12,#lb_setFlags	;Read the flags
		ORR	R3,R2,R2,LSR#1		;Is bit 0 or 1 set?
		AND	R3,R3,#1		;Just interested in this bit
		CMP	R3,R4			;Is there a change to be made
		BEQ	%40lb__idleHandler	;No -- keep on looping then
		ANDS	R2,R2,#1		;Just get selected bit
		BEQ	%35lb__idleHandler	;If its clear -- jump ahead
		CMP	R2,R4			;Is this the same?
		BNE	%40lb__idleHandler	;No -- return
35		MOV	R2,#2			;Just alter this bit
		MOV	R3,R4,LSL#1		;Set or clear appropriately
		LDR	R0,[R10,#lb__list]	;Get the list head
		MOV	R14,PC			;Set up return address
		ADD	PC,R12,#lb_setFlags	;Set the flags
		MOV	R0,R10			;Put listbox handle in R0
		BL	lb_updateItem		;Update the item

40		LDMFD	R13!,{R0-R4}		;Restore registers
		B	%00lb__idleHandler	;Keep going round

50		LDMFD	R13!,{R12}		;Get R12 back again
		STR	R5,ws__dragLast		;Store last item visited

		; --- Scroll the window nicely ---

		LDR	R0,[R13,#44]		;Get the y scroll position
		CMP	R9,R0			;Is mouse y>y scroll?
		MOVGT	R0,R9			;Yes -- y scroll=mouse y
		BGT	%55lb__idleHandler	;...and jump ahead
		LDR	R2,[R13,#36]		;Get y1 coordinate
		LDR	R3,[R13,#28]		;Get y0 coordinate
		SUB	R3,R2,R3		;Get window height
		ADD	R9,R9,R3		;Add on mouse position
		CMP	R9,R0			;Is it less than y scroll pos
		MOVLT	R0,R9			;Yes -- use y scroll pos
55		STR	R0,[R13,#44]		;Store new scroll position
		ADD	R1,R13,#20		;Point to the block
		SWI	Wimp_OpenWindow		;Open the window

99		ADD	R13,R13,#56		;Reclaim the stack
99		LDMFD	R13!,{R0-R9,PC}^	;Return to caller

		LTORG

; --- lb__unknownHandler ---
;
; On entry:	R0 == event type
;		R1 == event block
;		R10 == listbox handle
;		R12 == workspace pointer
;
; On exit:	--
;
; Use:		Called when the drag is ended


lb__unknownHandler ROUT

		CMP	R0,#7			;Are we interested?
		MOVNES	PC,R14			;No -- return PDQ

		STMFD	R13!,{R0-R4,R14}	;Stack some registers

		; --- First get rid of the idle claimer ---

		MOV	R0,#0			;Call very frequently
		ADR	R1,lb__idleHandler	;Call this routine
		MOV	R2,R10			;Call with this R10 handle
		MOV	R3,R12			;And this R12 value
		BL	idle_removeHandler	;Add in the handler

		; --- Now this  unknown handler ---

		ADR	R0,lb__unknownHandler	;Call this routine
		MOV	R1,#0			;R4 value
		MOV	R2,R10			;Call with this R10 handle
		MOV	R3,R12			;And this R12 value
		BL	win_removeUnknownHandler ;Add in the handler

		; --- Make temporary selections permanant ---

		LDR	R4,[R10,#lb__descriptor] ;Point to descriptor block
		LDR	R0,[R10,#lb__list]	;Get the list head
		MOV	R1,#0			;Start from the top
00		MOV	R2,#2			;Just interested in this bit
		MOV	R3,#2			;And it must be set
		MOV	R14,PC			;Set up the return address
		ADD	PC,R4,#lb_enumerate	;Enumerate the list
		BCC	%99lb__unknownHandler	;No more -- return

		LDR	R0,[R10,#lb__list]	;Get the list head
		MOV	R2,#3			;Clear these bits
		MOV	R3,#1			;And set this one
		MOV	R14,PC			;Set up the return address
		ADD	PC,R4,#lb_setFlags	;And set the flags
		B	%00lb__unknownHandler	;Keep going round for more

99		LDMFD	R13!,{R0-R4,PC}^	;Return to caller

		LTORG

; --- lb_drag ---
;
; On entry:	R0 == listbox handle
;		R1 == pointer to list item
;		R3 == mouse button status
;
; On exit:	--
;
; Use:		Starts a drag operation to allow for easy multiple
;		selection.

		EXPORT	lb_drag
lb_drag		ROUT

		STMFD	R13!,{R0-R3,R10,R12,R14} ;Stack some registers
		WSPACE	lb__wSpace		;Locate my workspace
		MOV	R10,R0			;Move listbox handle into R10

		LDR	R0,[R10,#lb__list]	;Point to the list head
		LDR	R2,[R10,#lb__descriptor] ;Point to descriptor block
		CMP	R1,#0			;Is there an item
		MOVEQ	R3,#lb_items		;No -- use the item count
		MOVNE	R3,#lb_itemToIndex	;Yes -- find its index
		MOV	R14,PC			;Set up return address
		ADD	PC,R2,R3		;Convert item to an index
		STR	R1,ws__dragItem		;The start drag item
		STR	R1,ws__dragLast		;Last item visited

		; --- First we must start the drag box ---

		SUB	R13,R13,#40		;Get me a block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetPointerInfo	;Get mouse position
		LDR	R0,[R1,#0]		;Get mouse x position
		LDR	R1,=&9001		;Parent y0
		MOV	R2,R0			;Parent x1
		LDR	R3,=&6FFE		;Parent Parent y1
		ADD	R14,R13,#24		;Point to right place
		STMIA	R14,{R0-R3}		;Store parent coords
		MOV	R0,#7			;The drag type
		STR	R0,[R13,#4]		;Store nicely in block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_DragBox		;Start the drag

		; --- Set up the idle event handler ---

		MOV	R0,#0			;Call very frequently
		ADR	R1,lb__idleHandler	;Call this routine
		MOV	R2,R10			;Call with this R10 handle
		MOV	R3,R12			;And this R12 value
		BL	idle_handler		;Add in the handler

		; --- Set up the unknown handler ---

		ADR	R0,lb__unknownHandler	;Call this routine
		MOV	R1,#0			;R4 value
		MOV	R2,R10			;Call with this R10 handle
		MOV	R3,R12			;And this R12 value
		BL	win_unknownHandler	;Add in the handler

		; --- Now return to the caller ---

		ADD	R13,R13,#40		;Reclaim the stack
		LDMFD	R13!,{R0-R3,R10,R12,PC}^ ;Return to caller

		LTORG

; --- lb_inserted ---
;
; On entry:	R0 == pointer to the listbox
;		R1 == pointer to the new item
;
; On exit:	--
;
; Use:		Informs the listbox that an item has been inserted,
;		and causes a flicker free insert to occur if possible.

		EXPORT	lb_inserted
lb_inserted	ROUT

		STMFD	R13!,{R0-R8,R10,R14}	;Stack some registers

		MOV	R10,R0			;Put listbox handle in R10
		MOV	R7,R1			;Look after item
		BL	lb__getMaxWidth		;Get the maximum width
		LDR	R2,[R10,#lb__wHandle]	;Get the old window handle
		BL	lb__rescanSize		;Ensure windows right size
		CMP	R2,R0			;Has the window changed?
		BNE	%99lb_inserted		;Yes -- return

		; --- Now do the block copy ---

		LDR	R0,[R10,#lb__list]	;Get the list head
		LDR	R2,[R10,#lb__descriptor] ;Get the descriptor
		MOV	R14,PC			;Set up the return address
		ADD	PC,R2,#lb_itemToIndex	;Convert item to index

		LDR	R8,[R10,#lb__iHeight]	;Get the item height
		RSB	R8,R8,#0		;Negate it
		MUL	R4,R8,R1		;Get top of item
		MOV	R14,PC			;Set up return address
		ADD	PC,R2,#lb_items		;Get the item count
		MUL	R2,R1,R8		;The overall height
		LDR	R0,[R10,#lb__wHandle]	;Get the window handle
		MOV	R1,#0			;The minimum x coord
		LDR	R3,[R10,#lb__width]	;Load the window width
		LDR	R6,[R10,#lb__maxWidth]	;And the widest item width
		CMP	R6,R3			;Which one is bigger?
		MOVGT	R3,R6			;Use the biggest one
		MOV	R5,#0			;x coord to move to
		ADD	R6,R2,R8 		;y coord to move to
		SWI	Wimp_BlockCopy		;Do a block copy

		; --- Finally, update the new item ---

		MOV	R0,R10			;Point to the listbox
		MOV	R1,R7			;Put item pointer in R1
		BL	lb_updateItem		;Update the item

99lb_inserted	LDMFD	R13!,{R0-R8,R10,PC}^	;Return to caller

		LTORG

; --- lb_removed ---
;
; On entry:	R0 == pointer to the listbox
;		R1 == index of item removed
;
; On exit:	--
;
; Use:		Informs the listbox that an item has been removed, and
;		causes a flicker free remove to occur, if possible.

		EXPORT	lb_removed
lb_removed	ROUT

		STMFD	R13!,{R0-R8,R10,R14}	;Stack some registers

		MOV	R10,R0			;Put listbox handle in R10
		MOV	R7,R1			;Look after item
		BL	lb__getMaxWidth		;Get the maximum width
		LDR	R2,[R10,#lb__wHandle]	;Get the old window handle
		BL	lb__rescanSize		;Ensure windows right size
		CMP	R2,R0			;Has the window changed?
		BNE	%99lb_removed		;Yes -- return

		; --- Now do the block copy ---

		LDR	R2,[R10,#lb__descriptor] ;Get the descriptor block
		LDR	R8,[R10,#lb__iHeight]	;Get the item height
		RSB	R8,R8,#0		;Negate it
		MUL	R4,R8,R1		;Get top of item
		ADD	R4,R4,R8		;Now get the bottom of it
		LDR	R0,[R10,#lb__list]	;Get the list head
		MOV	R14,PC			;Set up return address
		ADD	PC,R2,#lb_items		;Get the item count
		MUL	R2,R1,R8		;The overall height
		LDR	R3,[R10,#lb__height]	;Get overall window height
		SUB	R2,R2,R3		;Subtract that for luck
		LDR	R0,[R10,#lb__wHandle]	;Get the window handle
		MOV	R1,#0			;The minimum x coord
		LDR	R3,[R10,#lb__width]	;Load the window width
		LDR	R6,[R10,#lb__maxWidth]	;And the widest item width
		CMP	R6,R3			;Which one is bigger?
		MOVGT	R3,R6			;Use the biggest one
		MOV	R5,#0			;x coord to move to
		SUB	R6,R2,R8 		;y coord to move to
		SWI	Wimp_BlockCopy		;Do a block copy

99lb_removed	LDMFD	R13!,{R0-R8,R10,PC}^	;Return to caller

		LTORG

; --- lb_window ---
;
; On entry:	R0 == listbox handle
;
; On exit:	R0 == window handle
;
; Use:		Returns the window handle of the listbox

		EXPORT	lb_window
lb_window	ROUT

		LDR	R0,[R0,#lb__wHandle]	;Load the window handle
		MOVS	PC,R14			;Return to caller

		LTORG

; --- lb_init ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Initialises the listbox unit.

		EXPORT	lb_init
lb_init		ROUT

		STMFD	R13!,{R12,R14}		;Stack some registers
		WSPACE	lb__wSpace		;Locate my workspace

		; --- Ensure that we are not already initialised ---

		LDR	R14,ws__flags		;Get the flags word
		TST	R14,#wsFlag__inited	;Are we initialised?
		BNE	%99lb_init		;Yes -- return
		ORR	R14,R14,#wsFlag__inited	;We are initialised now
		STR	R14,ws__flags		;Store back modified flags

		; --- Return to caller ---

99lb_init	LDMFD	R13!,{R12,PC}^		;Return to caller

		LTORG

lb__wSpace	DCD	0

;----- List events ----------------------------------------------------------

		^	0
lbEvent_close	#	1			;Listbox has been closed

lbEvent_redraw	#	1			;Redraw a list item
						;R1 == pointer to list item
						;R2-R5 == window coords
lbEvent_click	#	1			;Click/Double on listbox
						;R1 == pointer to list item
						;R2 == window relative y pos
						;R3 == button type (10)
lbEvent_menu	#	1			;Menu click
						;R1 == pointer to list item
						;R2 == window relative y pos
						;R3 == button type (10)
lbEvent_drag	#	1			;Drag on listbox
						;R1 == pointer to list item
						;R2 == window relative y pos
						;R3 == button type (10)
lbEvent_help	#	1			;R1 == pointer to list item
lbEvent_drop	#	1			;R1 == pointer to list item
						;R2 == filetype
						;R3 == pointer to filename
						;R4 == estimated file size
						;R5 == subreason cde

		^	0
lbDrop_load	#	1
lbDrop_save	#	1

;----- Workspace layout -----------------------------------------------------

; --- listbox block descriptor ---

		^	0

lb_itemToIndex	#	4			;Item to index routine
lb_indexToItem	#	4			;Index to item routine
lb_enumerate	#	4			;Enumeration function
lb_items	#	4			;Function to return items
lb_setFlags	#	4			;Function to set/read flags

; --- listbox individual layout ---

		^	0

lb__descriptor	#	4			;Pointer to descriptor above
lb__list	#	4			;The list itself
lb__widthFun	#	4			;Function to return width
lb__iHeight	#	4			;Height of each item
lb__pWHandle	#	4			;Parent window handle
lb__pIHandle	#	4			;Parent icon handle
lb__handler	#	4			;The handler function
lb__R10		#	4			;The R10 value to pass
lb__R12		#	4			;The R12 value to pass
lb__width	#	4			;Overall box width
lb__height	#	4			;Overall box height
lb__flags	#	4			;Listbox flags
lb__wHandle	#	4			;The listbox window handle
lb__maxWidth	#	4			;The maximum item width
lb__size	#	4			;Size of this structure

; --- Main workspace ---

		^	0,R12

ws__flags	#	4			;The flags word
ws__dragItem	#	4			;Item from which drag starts
ws__dragLast	#	4			;Last drag item visited

ws__size	EQU	{VAR}-ws__flags		;The size of the workspace

wsFlag__inited	EQU	(1<<0)			;We are initialised

		AREA	|Sapphire$$LibData|,CODE,READONLY

		DCD	ws__size
		DCD	lb__wSpace
		DCD	0
		DCD	lb_init

;----- That's all, folks ----------------------------------------------------

		END
