;
; viewer.s
;
; Filer-like windows with re-arranging icons (MDW)
;
;  1995-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

		GET	libs:stream

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

		GET	sapphire:akbd
		GET	sapphire:alloc
		GET	sapphire:divide
		GET	sapphire:drag
		GET	sapphire:fastMove
		GET	sapphire:msgs
		GET	sapphire:sapphire
		GET	sapphire:screen
		GET	sapphire:wimp
		GET	sapphire:win
		GET	sapphire:winUtils

;----- Macros ---------------------------------------------------------------

		MACRO
$label		MSGB	$msg,$branch
$label		DCD	$msg
		B	$branch
		MEND

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

		AREA	|Sapphire$$Code|,CODE,READONLY

;----- Creating and deleting viewers ----------------------------------------

; --- viewer_create ---
;
; On entry:	R0 == pointer to a viewer definition block
;		R1 == pointer to a list
;		R2 == sprite area for window
;
; On exit:	R0 == viewer handle
;		May return an error
;
; Use:		Creates a viewer window.  The viewer definition block
;		contains various interesting bits of information about the
;		viewer which are likely to be known at assembly time:
;
;		(word)	 address of a list manager definition block
;		(word)	 address of a shape handler function (or 0)
;		(word)	 standard width of icons
;		(word)	 standard height of icons
;		(string) banner text message tag, or empty
;
;		The shape function is used to allow viewer icons to have a
;		non-rectangular shape.  The function is called with a reason
;		code in R0; entry and exit conditions depend on this:
;
;							vwShape_size
;		On entry
;		  R1 == pointer to list item
;		  R2 == standard width of icon
;		  R3 == standard height of icon
;
;		On exit
;		  R2 == width of this icon
;		  R3 == height of this icon
;
;		Use
;		  This routine is used to find the actual size of an icon.
;		  The icons are aligned on a grid according to the largest
;		  one: this routine is used to find out which one that is.
;
;							vwShape_intersects
;		On entry
;		  R1 == pointer to list item
;		  R2 == address of bounding box of this icon
;		  R3 == address of bounding box to compare
;
;		On exit
;		  CS if boxes intersect, else CC
;
;		Use
;		  For detecting mouse clicks etc. on an icon.  viewer has
;		  already ensured that the box in R3 intersects with the
;		  bounding box, so for rectangular icons, you can just return
;		  with C set always.  This entry is provided so that you
;		  can check against the sprite and text of a text+sprite
;		  icon separately.
;
;		More reason codes may be added later; it will always be
;		sensible to return immediately preserving all registers and
;		flags.

		EXPORT	viewer_create
viewer_create	ROUT

		STMFD	R13!,{R1-R5,R10,R14}	;Save some registers

		; --- Move arguments into other registers ---

		MOV	R5,R0			;Look after block def
		MOV	R4,R2			;And the sprite area

		; --- Allocate a viewer block ---

		MOV	R0,#vw__size		;Get the block size
		BL	alloc			;Allocate the memory
		BLCS	alloc_error		;If it failed, get error
		BCS	%99viewer_create	;And return to caller
		MOV	R10,R0			;Look after the pointer

		; --- Fill in bits of the block ---

		STR	R1,[R10,#vw__list]	;Store the list address
		LDMIA	R5!,{R0-R3}		;Load regs from def block
		STR	R0,[R10,#vw__listDef]	;Store list definition addr
		CMP	R1,#0			;Is the shape function 0?
		ADREQ	R1,vw__dummyShape	;Yes -- use default one
		STR	R1,[R10,#vw__shape]	;Store that away nicely
		ADD	R14,R10,#vw__stdDimens	;Point to standard icon sz
		STMIA	R14,{R2,R3}		;Store them away nicely
		LDRB	R0,[R5,#0]		;Load first byte of banner
		CMP	R0,#0			;Is it actually defined?
		MOVNE	R0,R5			;Yes -- point to it then
		BLNE	msgs_lookup		;And translate the tag
		STR	R0,[R10,#vw__banner]	;Store that away nicely

		; --- Now create a window ---

		MOV	R0,R11			;Copy definition to scratch
		ADR	R1,vw__windDef		;Point to my definition
		MOV	R2,#88			;Size of window definition
		BL	fastMove		;Copy the data over

		STR	R4,[R11,#64]		;Store caller's sprite area
		ADD	R14,R10,#vw__title	;Point to title buffer
		STR	R14,[R11,#72]		;Save this in window block
		MOV	R1,R11			;Point to window definition
		SWI	XWimp_CreateWindow	;Try to do that then
		BVS	%98viewer_create	;If it failed, tidy up
		STR	R0,[R10,#vw__window]	;Save the window handle

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

		ADR	R1,vw__events		;Point to event handler
		MOV	R2,R10			;Use viewer handle as R10
		MOV	R3,R12			;Pass workspace in R12 (*)
		BL	win_eventHandler	;Set up the event handler
		BVS	%97viewer_create	;If it failed, tidy up

		; --- Fix up rest of viewer block ---

		MOV	R14,#0			;No handler defined yet
		STR	R14,[R10,#vw__handler]	;Save that in my block
		STR	R14,[R10,#vw__flags]	;Clear the flags too
		STR	R14,[R10,#vw__tempSel]	;No temporary selection

		; --- Now return to caller ---

		MOV	R0,R10			;Get the viewer handle
		LDMFD	R13!,{R1-R5,R10,R14}	;Restore registers
		BICS	PC,R14,#V_flag		;And return to caller

		; --- Handle minor mishaps ---

97viewer_create	MOV	R5,R0			;Look after error pointer
		ADD	R1,R10,#vw__window	;Point to window handle
		SWI	Wimp_DeleteWindow	;Destroy the window
		MOV	R0,R5			;And get the error back

98viewer_create	MOV	R5,R0			;Look after error pointer
		MOV	R0,R10			;Point to viewer block
		BL	free			;Free the block up
		MOV	R0,R5			;And get the error back

99viewer_create	ADD	R2,R0,#4		;Point to error text
		ADR	R0,vw__createErr	;Point to error skeleton
		BL	msgs_error		;Translate and substitute
		LDMFD	R13!,{R1-R5,R10,R14}	;Restore registers
		ORRS	PC,R14,#V_flag		;And return the error

vw__dummyShape	ORRS	PC,R14,#C_flag		;Dummy shape function

vw__createErr	DCD	1
		DCB	"vwCRTE",0

vw__windDef	DCD	0,0,0,0
		DCD	0,0
		DCD	0
		DCD	&BF000002
		DCB	7,2,7,1,3,1,12,0
		DCD	0,0,0,0
		DCD	&1700013D
		DCD	&0000A000
		DCD	1
		DCW	1,0
		DCD	0,-1,256
		DCD	0

		LTORG

; --- viewer_destroy ---
;
; On entry:	R0 == viewer handle
;
; On exit:	--
;
; Use:		Destroys a viewer, removing it from the screen etc.

		EXPORT	viewer_destroy
viewer_destroy	ROUT

		STMFD	R13!,{R0,R1,R10,R14}	;Save some registers
		MOV	R10,R0			;Look after the handle
		LDR	R0,[R10,#vw__window]	;Load the window handle
		BL	win_windowDeleted	;Say it's been deleted
		ADD	R1,R10,#vw__window	;Find the window handle
		SWI	Wimp_DeleteWindow	;Destroy the window
		MOV	R0,R10			;Point to the viewer block
		BL	free			;Release the memory
		LDMFD	R13!,{R0,R1,R10,PC}^	;And return to caller

		LTORG

;----- Opening and closing --------------------------------------------------

; --- viewer_open ---
;
; On entry:	R0 == viewer handle
;		R1 == opening style
;		R2,R3 == extra arguments
;
; On exit:	--
;
; Use:		Opens a viewer window on the screen.

		EXPORT	viewer_open
viewer_open	ROUT

		STMFD	R13!,{R0,R1,R4,R10,R14}	;Save some registers
		MOV	R10,R0			;Get the viewer handle
		MOV	R4,R1			;Look after opening style

		SUB	R13,R13,#36		;Make a window state block
		LDR	R14,[R10,#vw__window]	;Load the window handle
		STR	R14,[R13,#0]		;Save it in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetWindowState	;And read the window state

		; --- If window is open, skip on ---

		BL	vw__open		;Is the window open?
		BCS	%10viewer_open		;Yes -- skip onwards then

		; --- Open the window onto the screen ---

		LDR	R1,[R10,#vw__listDef]	;Find the list definition
		LDR	R0,[R10,#vw__list]	;And the list base
		MOV	R14,PC			;Set up return address
		ADD	PC,R1,#vw__items	;Find how many items
		STR	R1,[R10,#vw__icons]	;Store this away

		MOV	R1,R13			;Point at window state blk
		BL	vw__tWidth		;Work out title width
		BL	vw__rescanSize		;Rescan the item size
		BL	vw__extend		;Yes -- make it big then
		BL	vw__resize		;Work out new arrangement
		BL	vw__setExtent		;Set the window's extent

		; --- Finally work out where to open it ---

10viewer_open	MOV	R0,R4			;Get the opening style
		BL	winUtils_setPosition	;Set the window position
		BL	vw__openWindow		;Open the window there
		ADD	R13,R13,#36		;Reclaim the stack space
		LDR	R14,[R10,#vw__flags]	;Load the flags word
		ORR	R14,R14,#vwFlag__opened	;We've now opened it
		STR	R14,[R10,#vw__flags]	;Store the flags back
		LDMFD	R13!,{R0,R1,R4,R10,PC}^	;And return to caller

		LTORG

; --- viewer_close ---
;
; On entry:	R0 == viewer handle
;
; On exit:	--
;
; Use:		Closes a viewer window.

		EXPORT	viewer_close
viewer_close	ROUT

		STMFD	R13!,{R0,R1,R14}	;Save some registers
		ADD	R1,R0,#vw__window	;Find the window handle
		SWI	Wimp_CloseWindow	;Close the window
		LDMFD	R13!,{R0,R1,PC}^	;And return to caller

		LTORG

;----- Event handling -------------------------------------------------------

; --- viewer_eventHandler ---
;
; On entry:	R0 == viewer handle
;		R1 == pointer to event handler
;		R2 == value to pass in R10
;		R3 == value to pass in R12
;
; On exit:	R1-R3 == old values
;
; Use:		Sets up the event handle for the viewer.

		EXPORT	viewer_eventHandler
viewer_eventHandler ROUT

		STMFD	R13!,{R4-R6,R14}	;Save some registers
		ADD	R14,R0,#vw__handler	;Point to handler block
		LDMIA	R14,{R4-R6}		;Load the old handler
		STMIA	R14,{R1-R3}		;Save the new one
		MOV	R1,R4			;Transfer information over
		MOV	R2,R5			;All of it, don't let it
		MOV	R3,R6			;Get away!
		LDMFD	R13!,{R4-R6,PC}^	;And return to caller

		LTORG

; --- vw__events ---
;
; On entry:	R0 == event code
;		R1 == pointer to event block
;
; On exit:	CS if handled, else CC
;
; Use:		Handles events for a viewer window.

vw__events	ROUT

		CMP	R0,#17			;Is this a message?
		CMPNE	R0,#18			;Check *both* types
		BEQ	%10vw__events		;Yes -- handle them
		CMP	R0,#9			;Is the event interesting?
		ADDCC	PC,PC,R0,LSL #2		;Yes -- dispatch it then
		MOVS	PC,R14			;Otherwise ignore it

		; --- Branch table ---

		MOVS	PC,R14
		B	vw__evRedraw
		B	vw__evOpen
		B	vw__evClose
		MOVS	PC,R14
		MOVS	PC,R14
		B	vw__evMouse
		MOVS	PC,R14
		B	vw__evKey

		; --- Handle messages ---
		;
		; This is a little odd, in the interests of expandability.

10vw__events	STMFD	R13!,{R2-R4,R14}	;Save some registers
		LDR	R2,[R1,#16]		;Load the message code
		ADR	R3,vw__msgTable		;Point to the message table
00		LDR	R4,[R3],#8		;Load the message code
		CMP	R4,R2			;Do I recognise this event?
		SUBEQ	R3,R3,#4		;Yes -- point to branch instr
		STREQ	R3,[R13,#12]		;Store as return address
		CMPNE	R4,#-1			;Is it the end of the list?
		BNE	%b00			;No -- keep looping then
		LDMFD	R13!,{R2-R4,PC}^	;Call the routine

		; --- Message table ---

vw__msgTable	MSGB	&1,vw__mDataSave
		MSGB	&3,vw__mDataLoad
		MSGB	&502,vw__mHelpRq
		MSGB	&400CF,vw__mFontChnge
		DCD	-1,-1

		LTORG

; --- vw__dispatch ---
;
; On entry:	R0 == event code
;		R1-R5 set up for event
;
; On exit:	CS or CC according to event handler
;
; Use:		Dispatches an event to the user's handler.

		EXPORT	vw__dispatch
vw__dispatch	ROUT

		STMFD	R13!,{R8-R10,R12,R14}	;Save some registers
		ADD	R14,R10,#vw__handler	;Find the handler routine
		MOV	R9,R10			;Pass viewer handle in R9
		LDMIA	R14,{R8,R10,R12}	;Load the things out
		ADDS	R0,R0,#0		;Clear the carry flag
		TEQ	R8,#0			;Is there a routine?
		MOVNE	R14,PC			;Yes -- set up return addr
		MOVNE	PC,R8			;And call the routine
		LDMFD	R13!,{R8-R10,R12,R14}	;Restore registers
		ORRCSS	PC,R14,#C_flag		;If it set C, we do too
		BICCCS	PC,R14,#C_flag		;Otherwise we clear it

		LTORG

; --- vw_evRedraw ---
;
; On entry:	R1 == pointer to window handle in block
;
; On exit:	CS
;
; Use:		Redraws a viewer window.

vw__evRedraw	ROUT

		STMFD	R13!,{R0-R7,R14}	;Save some registers
		SWI	Wimp_RedrawWindow	;Start the redraw job
		CMP	R0,#0			;Is this the end yet?
		BEQ	%90vw__evRedraw		;Yes -- do nothing then

		; --- Find the window origin ---

		MOV	R7,R1			;Remember the block pointer
		LDR	R2,[R7,#4]		;Load the left hand side
		ADD	R14,R7,#16		;Point to top and scroll pos
		LDMIA	R14,{R3-R5}		;Load those positions out
		SUB	R6,R3,R5		;Find the top position
		SUB	R5,R2,R4		;And the left side
		SUB	R13,R13,#32		;Space for rectangle block

		; --- The main redraw loop ---
		;
		; First of all, handle the banner.

10vw__evRedraw	LDR	R14,[R10,#vw__banner]	;Load the banner address
		CMP	R14,#0			;Is there one defined?
		BEQ	%f00			;No -- skip ahead then

		; --- See if we need to do this ---

		LDR	R14,[R7,#28+16]		;Load the top coordinate
		CMP	R14,R6			;Is this above the line?
		BLT	%f00			;No -- don't bother then

		; --- Render the banner's background ---

		MOV	R0,#3			;Get the banner colour
		SWI	Wimp_SetColour		;Set this as the colour

		MOV	R0,#4			;Move the graphics cursor
		LDR	R1,[R7,#28+0]		;Find left side of window
		MOV	R2,R6			;Baseline on window origin
		SWI	OS_Plot			;Do the cursor move
		MOV	R0,#101			;Rectangle fill absolute
		LDR	R1,[R7,#28+8]		;Load right side of window
		ADD	R2,R6,#vw__banHeight	;Get the banner height
		SWI	OS_Plot			;Do that too

		; --- Now plot the banner text ---

		MOV	R14,R13			;Point at the stack block

		MOV	R0,#28			;We have an odd left side
		MOV	R1,#0			;Baseline is along origin
		LDR	R2,[R10,#vw__fixedWidth] ;Load width of the banner
		MOV	R3,#vw__banHeight	;Get the banner's height
		STMIA	R14!,{R0-R3}		;Save the coordinates

		LDR	R0,=&37000131		;Get the icon flags word
		LDR	R1,[R10,#vw__banner]	;Load the banner pointer
		MOV	R2,#-1			;No validation string
		MOV	R3,#1			;Bogus string length
		STMIA	R14!,{R0-R3}		;Save the other bits

		MOV	R1,R13			;Point to the block
		SWI	Wimp_PlotIcon		;Plot the text as an icon

		; --- Now alter the graphics rectangle ---

00		ADD	R14,R7,#28		;Find the clipping rectangle
		LDMIA	R14,{R0-R3}		;Load the rectangle out
		SUB	R0,R0,R5		;Convert to window coords
		SUB	R1,R1,R6		;This isn't terribly hard
		SUB	R2,R2,R5		;Just subtract origin posn
		SUB	R3,R3,R6		;For all the coordinates
		STMIA	R14,{R0-R3}		;Store those in the block

		; --- Plot each interesting icon ---

		MOV	R0,#vwEvent_redraw	;Say this is a redraw event
		MOV	R1,#0			;Start at the beginning
		MOV	R2,R13			;Use my stack buffer
		ADD	R3,R7,#28		;Point to the clip block
00		BL	vw__enum		;Get next icon ready
		BCC	%f00			;If no more, skip onwards
		BL	vw__intSimple		;Do quick clipping check
		BLCS	vw__dispatch		;Yes -- package off the event
		B	%b00			;Loop round for the rest

		; --- Finish off the redraw loop ---

00		MOV	R1,R7			;Point at the event block
		BL	drag_redraw		;Draw the drag box if reqd
		SWI	Wimp_GetRectangle	;Get another rectangle
		CMP	R0,#0			;Is there more to do?
		BNE	%10vw__evRedraw		;Yes -- loop back to do it

		ADD	R13,R13,#32		;Restore the stack pointer

90vw__evRedraw	LDMFD	R13!,{R0-R7,R14}	;Unstack the registers
		ORRS	PC,R14,#C_flag		;And *claim the event*

		LTORG

; --- vw__evOpen ---
;
; On entry:	R1 == pointer to an open-window block
;
; On exit:	CS
;
; Use:		Opens a viewer window.

vw__evOpen	ROUT

		STMFD	R13!,{R0,R14}		;Save some registers
		MOV	R14,#0			;Force horizontal scroll
		STR	R14,[R1,#20]		;To stop it looking odd
		BL	screen_justChangedMode	;Just had a mode change?
		BCS	%50vw__evOpen		;Yes -- handle that

		; --- Just rescan the size like nice people ---

		BL	vw__resize		;Modify the arrangement
		BLCS	vw__setExtent		;Maybe modify the extent too
		BLCS	vw__refresh		;If so, force a redraw
		BL	vw__openWindow		;Whatever, open the window
		LDMFD	R13!,{R0,R14}		;Restore the registers
		ORRS	PC,R14,#C_flag		;And claim the event

		; --- Just changed mode -- anything could have happened ---

50vw__evOpen	BL	vw__tWidth		;Rescan the title width
		BL	vw__rescanSize		;Rescan the icon sizes
		BL	vw__resize		;Rearrange the icons
		BL	vw__setExtent		;Rework the extent
		BL	vw__refresh		;Redraw the work area
		BL	vw__openWindow		;And reopen the window
		LDMFD	R13!,{R0,R14}		;Restore the registers
		ORRS	PC,R14,#C_flag		;And claim the event

		LTORG

; --- vw__evClose ---
;
; On entry:	R1 == pointer to block
;
; On exit:	CC or CS
;
; Use:		Handles a close request for a viewer,

vw__evClose	ROUT

		STMFD	R13!,{R0,R14}		;Save some registers
		MOV	R0,#vwEvent_close	;Get the event code
		BL	vw__dispatch		;Send it to the user
		LDMFD	R13!,{R0,PC}		;And return this state

		LTORG

; --- vw__evMouse ---
;
; On entry:	R1 == pointer to pointer info block
;
; On exit:	CS
;
; Use:		Handles a mouse click on the viewer.

vw__evMouse	ROUT

		STMFD	R13!,{R0-R4,R14}	;Save some registers
		MOV	R4,R1			;Look after this pointer
		LDMIA	R4,{R2,R3}		;Load the coordinates out
		BL	vw__whichIcon		;Which icon is that?

		LDR	R2,[R4,#8]		;Load the button state
		TST	R2,#&002		;Is this a menu click?
		MOVNE	R0,#vwEvent_menu	;Yes -- pass that event
		TST	R2,#&005		;Another normal click?
		MOVNE	R0,#vwEvent_double	;Yes -- that's a double
		TST	R2,#&050		;Is it a drag?
		MOVNE	R0,#vwEvent_drag	;Yes -- use that event then
		TST	R2,#&500		;Or a normal click?
		MOVNE	R0,#vwEvent_click	;Yes -- that's a real one
		BL	vw__dispatch		;Sent the event on
		LDMFD	R13!,{R0-R4,R14}	;Save some registers
		ORRS	PC,R14,#C_flag		;We dealt with it

		LTORG

; --- vw__evKey ---
;
; On entry:	R1 == pointer to caret info block
;
; On exit:	CC if unclaimed, else CS
;
; Use:		Handles a keypress while the viewer has the input focus.

vw__evKey	ROUT

		STMFD	R13!,{R0,R1,R14}	;Save some registers
		LDR	R0,[R1,#24]		;Load the character code
		BL	akbd_translate		;Try translating the key
		MOV	R1,R0			;Put this back in R1
		MOV	R0,#vwEvent_key		;Get the event code
		BL	vw__dispatch		;Dispatch the event
		LDMFD	R13!,{R0,R1,R14}	;Restore registers
		ORRCSS	PC,R14,#C_flag		;Claim if he claimed it
		BICCCS	PC,R14,#C_flag		;Don't claim if he didn't

		LTORG

; --- vw__mDataSave ---
;
; On entry:	R0 == message code
;		R1 == pointer to message block
;
; On exit:	--
;
; Use:		Handles a file dropped on the viewer.

vw__mDataSave	ROUT

		STMFD	R13!,{R0-R4,R14}	;Save some registers
		MOV	R4,#vwDrop_save		;Say it's a save event
		ADD	R3,R1,#44		;Point to the filename
		LDR	R2,[R1,#36]		;Load the estimated size
		LDR	R1,[R1,#40]		;And the filetype
		MOV	R0,#vwEvent_drop	;Get the event code
		BL	vw__dispatch		;And send it to the client
		LDMFD	R13!,{R0-R4,PC}^	;And return to caller

		LTORG

; --- vw__mDataLoad ---
;
; On entry:	R0 == message code
;		R1 == pointer to message block
;
; On exit:	--
;
; Use:		Handles a file dropped on the viewer.

vw__mDataLoad	ROUT

		STMFD	R13!,{R0-R4,R14}	;Save some registers
		MOV	R4,#vwDrop_load		;Say it's a load event
		ADD	R3,R1,#44		;Point to the filename
		LDR	R2,[R1,#36]		;Load the estimated size
		LDR	R1,[R1,#40]		;And the filetype
		MOV	R0,#vwEvent_drop	;Get the event code
		BL	vw__dispatch		;And send it to the client
		LDMFD	R13!,{R0-R4,PC}^	;And return to caller

		LTORG

; --- vw__mHelpRq ---
;
; On entry:	R0 == message code
;		R1 == pointer to message block
;
; On exit:	--
;
; Use:		Handles a help request for the viewer.

vw__mHelpRq	ROUT

		STMFD	R13!,{R0-R3,R14}	;Save some registers
		ADD	R14,R1,#20		;Find the mouse position
		LDMIA	R14,{R2,R3}		;Load the coordinates
		BL	vw__whichIcon		;Find the icon pointed at
		MOV	R0,#vwEvent_help	;It's a help request
		BL	vw__dispatch		;Send it to the client
		LDMFD	R13!,{R0-R3,PC}^	;And return to caller

		LTORG

; --- vw__mFontChnge ---
;
; On entry:	R0 == message code
;		R1 == pointer to message block
;
; On exit:	--
;
; Use:		Handles font changed events for the viewer.

vw__mFontChnge	ROUT

		STMFD	R13!,{R0-R4,R14}	;Save some registers
		SUB	R13,R13,#36		;Make a window state block
		LDR	R14,[R10,#vw__window]	;Load viewer's window handle
		STR	R14,[R13,#0]		;Store it in the block
		MOV	R1,R13			;Point to this block
		SWI	Wimp_GetWindowState	;Read the window information
		BL	vw__tWidth		;Rescan the title width
		BL	vw__rescanSize		;Rescan the icon sizes
		BL	vw__resize		;Rearrange the icons
		BL	vw__setExtent		;Rework the extent
		BL	vw__refresh		;Redraw the work area
		BL	vw__openWindow		;And reopen the window
		ADD	R13,R13,#36		;Restore the stack pointer
		LDMFD	R13!,{R0-R4,PC}^	;Return to caller

		LTORG

;----- Icon position handling -----------------------------------------------

; --- vw__callShape ---
;
; On entry:	R0 == reason code
;		R1-R? == other arguments
;		R10 == pointer to viewer block
;
; On exit:	R0-R? and C returned from shape function
;
; Use:		Calls a shape function and returns the result.

		EXPORT	vw__callShape
vw__callShape	ROUT

		STMFD	R13!,{R8-R10,R12,R14}	;Save some registers
		LDR	R8,[R10,#vw__shape]	;Load the shape function addr
		MOV	R9,R10			;Point to the viewer block
		ADD	R14,R10,#vw__handler+4	;Find owner's R10 and R12
		LDMIA	R14,{R10,R12}		;Load them out nicely too
		CMP	R0,R0			;Set the C flag on entry
		MOV	R14,PC			;Set up the return address
		MOV	PC,R8			;Call the shape function
		LDMFD	R13!,{R8-R10,R12,R14}	;Restore registers
		ORRCSS	PC,R14,#C_flag		;If it set C, return C set
		BICCCS	PC,R14,#C_flag		;Otherwise return C clear

		LTORG

; --- vw__whichIcon ---
;
; On entry:	R2,R3 == mouse coordinates (screen relative)
;		R10 == pointer to viewer block
;
; On exit:	R1 == icon beneath the mouse pointer
;
; Use:		Works out which icon the user is pointing at, should this be
;		interesting.

vw__whichIcon	ROUT

		STMFD	R13!,{R0,R2-R6,R14}	;Save lots of registers

		; --- Find the window's state ---

		SUB	R13,R13,#36		;Drop the stack to make a blk
		LDR	R14,[R10,#vw__window]	;Get the window handle
		STR	R14,[R13,#0]		;Store that in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetWindowState	;Read the window state

		LDR	R0,[R13,#4]		;Load the left hand side
		ADD	R14,R13,#16		;Point to the top edge
		LDMIA	R14,{R4-R6}		;Load those values out
		SUB	R5,R0,R5		;Work out x origin position
		SUB	R6,R4,R6		;And the y origin position

		; --- Find the click position ---

		SUB	R2,R2,R5		;Translate click coordinates
		SUB	R3,R3,R6		;Do that nicely
		MOV	R14,R13			;Point to my nice block
		STMIA	R14!,{R2,R3}		;Save coordinates away
		STMIA	R14!,{R2,R3}		;And do it again

		; --- Now do the enumeration ---

		MOV	R1,#0			;Start at the beginning
		ADD	R2,R13,#16		;Point to spare bit of block
		MOV	R3,R13			;Point to my coordinates
		BL	vw__withinBox		;Try to find a match
		MOVCC	R1,#0			;No match -- no icon
		ADD	R13,R13,#36		;Recover stack space
		LDMFD	R13!,{R0,R2-R6,PC}^	;And return to caller

		LTORG

; --- vw__rescanSize ---
;
; On entry:	R10 == pointer to viewer block
;
; On exit:	--
;
; Use:		Recalculates the sizes of icons in the viewer.

vw__rescanSize	ROUT

		STMFD	R13!,{R0-R7,R14}	;Save some registers

		; --- Set up for the loop ---

		LDR	R6,[R10,#vw__listDef]	;Find the list block
		MOV	R1,#0			;No item found yet
		ADD	R7,R10,#vw__stdDimens	;Find standard dimensions
		LDMIA	R7,{R4,R5}		;These are minimum sizes

		; --- Now read all the items ---

00		LDR	R0,[R10,#vw__list]	;Find the list head
		MOV	R2,#0			;Match any flags
		MOV	R3,#0			;Still match any flags
		MOV	R14,PC			;Set up return address
		ADD	PC,R6,#vw__enumerate	;Read the next list item
		BCC	%10vw__rescanSize	;If no more, skip on
		LDMIA	R7,{R2,R3}		;Load standard sizes
		MOV	R0,#vwShape_size	;Get the reason code
		BL	vw__callShape		;And call the shape function
		CMP	R4,R2			;Now update the sizes
		MOVCC	R4,R2			;Use the biggest on both
		CMP	R5,R3			;Check the height too
		MOVCC	R5,R3			;Use the biggest again
		B	%b00			;Now loop round for more

		; --- Finished -- store result away ---

10		ADD	R14,R10,#vw__iconWidth	;Find current sizes
		STMIA	R14,{R4,R5}		;Store these new sizes
		LDMFD	R13!,{R0-R7,PC}^	;And return to caller

		LTORG

; --- vw__intSimple ---
;
; On entry:	R2 == pointer to a box
;		R3 == pointer to another box
;
; On exit:	CS if boxes intersect, else CC
;
; Use:		Informs you whether boxes intersect.  Saves lots of
;		registers.  This is typically used before calling the
;		caller's shape routine to see if it's really worth it.

		EXPORT	vw__intSimple
vw__intSimple	ROUT

		STMFD	R13!,{R0-R6,R14}	;Save some registers
		LDMIA	R3,{R4-R6,R14}		;Load second box out
		LDMIA	R2,{R0-R3}		;Load first box out
		CMP	R2,R4			;Now do the compare
		CMPGE	R3,R5
		CMPGE	R6,R0
		CMPGE	R14,R1
		LDMFD	R13!,{R0-R6,R14}	;Restore registers
		ORRGES	PC,R14,#C_flag		;Return C set if OK
		BICLTS	PC,R14,#C_flag		;Otherwise return CC

		LTORG

; --- vw__enum ---
;
; On entry:	R1 == 0 for first call, or item pointer
;		R2 == pointer to 16-byte coordinate buffer
;		R4 == continuation value from old call
;		R10 == pointer to viewer block
;
; On exit:	CS if more icons, and
;		  R1 == item handle
;		  R4 == new continuation value
;		else CC and
;		  R1,R4 corrupted
;
; Use:		Scans through icons, returning their coordinates in the
;		block.
;
;		This routine is exported, although it isn't for user
;		consumption.  People using it in application code will be
;		shot.

		EXPORT	vw__enum
vw__enum	ROUT

		STMFD	R13!,{R0,R2,R3,R5-R8,R14} ;Save some registers

		; --- Set up for first go round ---

		CMP	R1,#0			;Is this the first go?
		BNE	%05vw__enum		;No -- skip onwards then

		MOV	R4,#0			;Yes -- start at top left

		ADD	R14,R10,#vw__iconWidth	;Find the icon dimensions
		LDMIA	R14,{R7,R14}		;Load them out of the block
		MOV	R5,#vw__iconGap		;Set up the gap nicely
		ADD	R7,R5,R7		;Work out right hand side
		MOV	R8,#-vw__iconGap	;Work out top edge
		SUB	R6,R8,R14		;And set up the bottom
		STMIA	R2,{R5-R8}		;Save all of those away
		B	%10vw__enum		;Now skip on to next bit

		; --- Sort out a subsequent round ---

05vw__enum	LDMIA	R2,{R5,R6}		;Load bottom left of icon
		ADD	R14,R10,#vw__iconWidth	;Find the icon dimensions
		LDMIA	R14,{R7,R8}		;Load them out of the block
		ADD	R4,R4,#1		;Bump horizontal position
		LDR	R14,[R10,#vw__across]	;How many icons going across?
		CMP	R4,R14			;Reached the edge yet?
		ADDCC	R5,R5,R7		;No -- move along then
		ADDCC	R5,R5,#vw__iconGap	;Add on our extra spacing
		MOVCS	R5,#vw__iconGap		;Otherwise go back to left
		MOVCS	R4,#0			;Return left side position
		SUBCS	R6,R6,R8		;Drop down one row
		SUBCS	R6,R6,#vw__iconGap	;And add on the spacing
		ADD	R7,R5,R7		;Work out other box position
		ADD	R8,R6,R8		;To make the box right
		STMIA	R2,{R5-R8}		;Store all that away

		; --- Now advance the icon pointer ---

10vw__enum	LDR	R0,[R10,#vw__list]	;Load the list base
		MOV	R2,#0			;Give me all the icons
		MOV	R3,#0			;I really mean that
		LDR	R5,[R10,#vw__listDef]	;Find the list block
		MOV	R14,PC			;Set up return address
		ADD	PC,R5,#vw__enumerate	;Get the next item
		LDMFD	R13!,{R0,R2,R3,R5-R8,R14} ;Unstack registers
		ORRCSS	PC,R14,#C_flag		;If more to come, OK
		BICCCS	PC,R14,#C_flag		;Otherwise return the end

		LTORG

; --- vw__withinBox ---
;
; On entry:	R1 == 0 for first call, or pointer from previous
;		R2 == pointer to 16-byte buffer
;		R3 == pointer to coordinate box (window relative)
;		R4 == value from previous call
;		R10 == pointer to viewer block
;
; On exit:	CS if match found, and
;		  R1 == item handle of match
;		  R4 == continuation value
;		else CC and
;		  R1,R4 corrupted
;
; Use:		Enumerates icons which intersect a given rectangle.  The
;		coordinates of a matching icon are left in the block; these
;		must be set up correctly for the next call.

vw__withinBox	ROUT

		STMFD	R13!,{R0,R14}		;Save some registers
		MOV	R0,#vwShape_intersects	;Get shape fn reason code
00		BL	vw__enum		;Get another icon
		BCC	%10vw__withinBox	;If no more, return now
		BL	vw__intSimple		;Check simple intersection
		BLCS	vw__callShape		;And then the complex one
		BCC	%b00			;If no match, skip back
		LDMFD	R13!,{R0,R14}		;Restore registers
		ORRS	PC,R14,#C_flag		;And return match

10vw__withinBox	LDMFD	R13!,{R0,R14}		;Restore registers
		BICS	PC,R14,#C_flag		;Return no match

		LTORG

; --- vw__iconBox ---
;
; On entry:	R1 == item pointer
;		R2 == pointer to a block to fill in
;		R10 == viewer handle
;
; On exit:	--
;
; Use:		Works out the bounding box of an icon (within the window).

vw__iconBox	ROUT

		STMFD	R13!,{R0-R5,R14}	;Save some registers

		; --- First, get the icon index ---

		LDR	R0,[R10,#vw__list]	;Load the list head pointer
		LDR	R5,[R10,#vw__listDef]	;Find the definition too
		MOV	R14,PC			;Set up return address
		ADD	PC,R5,#vw__itemToIndex	;Find the item's index

		; --- Now work out its across and down position ---

		MOV	R0,R1			;Get the item index
		LDR	R1,[R10,#vw__across]	;Find icons going across
		BL	divide			;Do the division

		; --- Finally find the actual position ---

		ADD	R14,R10,#vw__iconWidth	;Point to icon dimens
		LDMIA	R14,{R4,R5}		;Load the width and height
		ADD	R3,R4,#vw__iconGap	;Add on the gap here
		ADD	R14,R5,#vw__iconGap	;And again, please
		MUL	R14,R0,R14		;Work out vertical placing
		MUL	R0,R1,R3		;And the horizontal placing
		RSB	R14,R14,#0		;Make vertical position -ve
		ADD	R0,R0,#vw__iconGap	;Push the icon in a little
		SUB	R14,R14,#vw__iconGap	;And push it down a little
		ADD	R3,R0,R4		;Work out the right side
		SUB	R1,R14,R5		;And the bottom edge
		STMIA	R2,{R0,R1,R3,R14}	;Save them in my block
		LDMFD	R13!,{R0-R5,PC}^	;And return to caller

		LTORG

;----- Default selection model ----------------------------------------------

; --- viewer_select ---
;
; On entry:	R0 == viewer handle
;		R1 == icon handle
;		R2 == 0 to unselect, 1 to select or 2 to toggle
;
; On exit:	--
;
; Use:		Selects an icon, or maybe unselects it.  Whatever, it doesn't
;		flicker if it doesn't need to.

		EXPORT	viewer_select
viewer_select	ROUT

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

		STMFD	R13!,{R0-R5,R10,R14}	;Save some registers
		MOV	R10,R0			;Get the viewer block pointer

		; --- Read the current flags ---

		LDR	R4,[R10,#vw__listDef]	;Get the list definition
		MOV	R5,R2			;And get the argument
		MOV	R3,#0			;Don't toggle flags
		MOV	R2,#0			;Don't clear any flags
		LDR	R0,[R10,#vw__list]	;Load the list base address
		MOV	R14,PC			;Set up return address
		ADD	PC,R4,#vw__setFlags	;Read the current flags

		; --- Work out the new ones ---

		AND	R2,R2,#1		;Leave only 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 now ---

		MOV	R2,#1			;Clear the selected bit
		MOV	R14,PC			;Set up return address
		ADD	PC,R4,#vw__setFlags	;Set the new flags
		MOV	R0,R10			;Get handle in R0
		BL	viewer_update		;Update the icon
		LDMFD	R13!,{R0-R5,R10,PC}^	;Return to caller

		LTORG

; --- viewer_isSelected ---
;
; On entry:	R0 == viewer handle
;		R1 == icon handle
;
; On exit:	CS if icon is selected, else CC
;
; Use:		Informs you whether an icon is selected.

		EXPORT	viewer_isSelected
viewer_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,#vw__listDef]	;Find the list block
		LDR	R0,[R0,#vw__list]	;Find the list base
		MOV	R14,PC			;Set up return address
		ADD	PC,R4,#vw__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

; --- viewer_selectAll ---
;
; On entry:	R0 == viewer handle
;		R2 == 0 to deselect, or 1 to select
;
; On exit:	--
;
; Use:		Selects or deselects all the icons in a viewer.

		EXPORT	viewer_selectAll
viewer_selectAll ROUT

		STMFD	R13!,{R0-R5,R10,R14}	;Save some registers
		MOV	R10,R0			;Look after the handle
		MOV	R1,#0			;Start at the beginning
		MOV	R4,R2			;Look after selection state
		LDR	R5,[R10,#vw__listDef]	;Find the list block

00		LDR	R0,[R10,#vw__list]	;Find the list head
		MOV	R2,#1			;Check the selected bit
		EOR	R3,R4,#1		;Find interesting icons
		MOV	R14,PC			;Set up return address
		ADD	PC,R5,#vw__enumerate	;Find next icon
		BCC	%f00			;If all done, skip on
		MOV	R0,R10			;Get viewer handle
		MOV	R2,R4			;Get selection state
		BL	viewer_select		;Do the selection
		B	%b00			;And skip back for the rest

00		MOV	R14,#0			;Clear temporary selection
		STR	R14,[R10,#vw__tempSel]	;Store that in the block
		LDMFD	R13!,{R0-R5,R10,PC}^	;Return to caller when done

		LTORG

; --- viewer_click ---
;
; On entry:	R0 == viewer handle
;		R1 == icon handle (or 0)
;		R2 == mouse button state
;
; On exit:	--
;
; Use:		Handles a click, drag etc. according to the standard
;		selection model.

		EXPORT	viewer_click
viewer_click	ROUT

		TST	R2,#&002		;Is this a menu click?
		BNE	%20viewer_click		;Yes -- handle it then
		TST	R2,#&400		;Is this a SELECT click?
		BNE	%10viewer_click		;Yes -- handle that
		TST	R2,#&050		;Is this a drag?
		BNE	%50viewer_click		;Yes -- start a drag box
		TST	R2,#&100		;Is this an ADJUST click?
		MOVEQS	PC,R14			;No -- do nothing then

		; --- Handle an ADJUST click ---

		STMFD	R13!,{R2,R14}		;Save some registers
		BL	viewer_clearTemp	;Clear temporary selection
		MOV	R2,#2			;Now select interesting icon
		BL	viewer_select		;Do the selection op
		LDMFD	R13!,{R2,PC}^		;And return to caller

		; --- Handle a SELECT click ---

10viewer_click	STMFD	R13!,{R2,R14}		;Save some registers
		BL	viewer_clearTemp	;Clear temporary selection
		BL	viewer_isSelected	;Is the icon selected?
		LDMCSFD	R13!,{R2,PC}^		;Yes -- do nothing then
		MOV	R2,#0			;Clear entire selection
		BL	viewer_selectAll	;Do that then
		MOV	R2,#1			;Now select my icon
		BL	viewer_select		;Go do that then
		LDMFD	R13!,{R2,PC}^		;And return to caller

		; --- Handle a MENU click ---

20viewer_click	STMFD	R13!,{R0-R5,R10,R14}	;Save some registers
		MOV	R10,R0			;Look after the handle
		BL	viewer_clearTemp	;Clear temporary selection
		MOV	R4,R1			;Look after icon handle
		LDR	R5,[R10,#vw__listDef]	;Find the list definition
		LDR	R0,[R10,#vw__list]	;Find the list base
		MOV	R1,#0			;Start at the beginning
		MOV	R2,#1			;Check selected bit
		MOV	R3,#1			;Any with it set?
		MOV	R14,PC			;Set return address
		ADD	PC,R5,#vw__enumerate	;Find first selected icon
		LDMCSFD	R13!,{R0-R5,R10,PC}^	;Robust selection -- quit now
		MOV	R0,R10			;Get the handle back
		MOV	R1,R4			;Get clicked icon
		MOV	R2,#1			;Select the icon
		BL	viewer_select		;Go select it then
		STR	R1,[R10,#vw__tempSel]	;This is temporary selection
		LDMFD	R13!,{R0-R5,R10,PC}^	;Return to caller

		; --- Handle a drag ---

50viewer_click	CMP	R1,#0			;Is there actually an icon?
		BNE	%60viewer_click		;Yes -- handle that
		STMFD	R13!,{R0-R5,R14}	;Save some registers
		MOV	R4,R0			;Pass viewer handle in R10
		MOV	R5,#0			;I have no workspace
		LDR	R0,[R4,#vw__window]	;Load the window handle
		MOV	R1,#0			;No flags to set
		ADR	R2,vw__dragSel		;Point to drag handler
		MOV	R3,#0			;No magic number in R9
		BL	drag_start		;Start the drag operation
		LDMFD	R13!,{R0-R5,PC}^	;And return to caller

		; --- Handle drag on an icon -- select it ---

60viewer_click	STMFD	R13!,{R2,R14}		;Save some registers
		MOV	R2,#1			;Select the icon
		BL	viewer_select		;Do that then
		LDMFD	R13!,{R2,PC}^		;And return to caller

		LTORG

; --- vw__dragSel ---
;
; On entry:	R0 == drag op event code
;		Other registers depend on this
;
; On exit:	--
;
; Use:		Handles events during a marquee-select drag operation.

vw__dragSel	ROUT

		CMP	R0,#7			;Is this a kosher event?
		ADDCC	PC,PC,R0,LSL #2		;Yes -- then dispatch it
		MOVS	PC,R14			;Otherwise ignore it

		B	vw__dragRender		;Draw the drag box
		B	vw__dragRender		;Undraw the drag box
		B	vw__dragRender		;Rotate the drag box
		MOVS	PC,R14			;Do no coordinate conversion
		B	vw__dragScroll		;Auto-scroll the viewer
		B	vw__dragDone		;Completed the drag OK
		MOVS	PC,R14			;Drag operation cancelled

		; --- Draw the drag box ---

vw__dragRender	STMFD	R13!,{R0-R5,R14}	;Save some registers

		; --- Set the colour up ---

		MOV	R0,#1			;Background colour is 1
		MOV	R1,#7			;Make lines black, please
		BL	drag_eorColour		;Set the colour up

		; --- Now render the drag box ---

		SUB	R4,R4,R2		;Get the box width
		SUB	R5,R5,R3		;And the box height

		MOV	R0,#4			;Move cursor absolute
		ADD	R1,R6,R2		;Find the start position
		ADD	R2,R7,R3		;Set up the y position too
		SWI	OS_Plot			;Move the graphics cursor

		MOV	R0,#17			;Relative dotted plot
		MOV	R1,R4			;Plot bottom line
		MOV	R2,#0			;Keep y constant
		SWI	OS_Plot
		MOV	R0,#49			;Relative dotted plot
		MOV	R1,#0			;Keep x constant
		MOV	R2,R5			;Plot right hand side
		SWI	OS_Plot
		MOV	R0,#49			;Relative dotted plot
		RSB	R1,R4,#0		;Plot top line
		MOV	R2,#0			;Keep y constant
		SWI	OS_Plot
		MOV	R0,#57			;Relative dotted plot
		MOV	R1,#0			;Keep x constant
		RSB	R2,R5,#0		;Plot left hand side
		SWI	OS_Plot			;Plot that too

		LDMFD	R13!,{R0-R5,PC}^	;And return to caller

		; --- Scroll the window during the drag ---

vw__dragScroll	STMFD	R13!,{R0-R3,R14}	;Save some registers
		BL	drag_scroll		;Get the scroll position
		STR	R3,[R1,#24]		;Save vertical scroll posn
		BL	vw__openWindow		;Reopen the window
		LDMFD	R13!,{R0-R3,PC}^	;And return to caller

		; --- Handle the end of the drag ---

vw__dragDone	STMFD	R13!,{R0-R5,R14}	;Save some registers

		; --- Fiddle the coordinates ---

		CMP	R2,R4			;Are these the right way?
		EORGT	R2,R2,R4		;No -- swap them round then
		EORGT	R4,R2,R4
		EORGT	R2,R2,R4

		CMP	R3,R5			;Are these the right way?
		EORGT	R3,R3,R5		;No -- swap them round then
		EORGT	R5,R3,R5
		EORGT	R3,R3,R5

		; --- Now enumerate matching boxes ---

		STMFD	R13!,{R2-R5}		;Stuff the drag box on stack
		SUB	R13,R13,#16		;And make another block

		MOV	R0,R10			;Get the viewer handle
		MOV	R1,#0			;Start at the beginning
		ADD	R3,R13,#16		;Point at the drag box

00		MOV	R2,R13			;Point to my spare block
		BL	vw__withinBox		;Get next matching icon
		MOVCS	R2,#1			;Select this icon please
		BLCS	viewer_select		;Please do that
		BCS	%b00			;And loop back

		; --- Finished -- return ---

		ADD	R13,R13,#32		;Restore the stack block
		LDMFD	R13!,{R0-R5,PC}^	;And return to caller

		LTORG

; --- viewer_clearTemp ---
;
; On entry:	R0 == viewer handle
;
; On exit:	--
;
; Use:		Clears the temporary selection (use when you receive a
;		menu closed event).

viewer_clearTemp ROUT

		STMFD	R13!,{R1,R2,R14}	;Save some registers
		LDR	R1,[R0,#vw__tempSel]	;Find temporary selection
		CMP	R1,#0			;Is that defined?
		LDMEQFD	R13!,{R1,R2,PC}^	;No -- return now
		MOV	R2,#0			;Yes -- deselect the icon
		BL	viewer_select		;Go do that then
		MOV	R14,#0			;And also clear temp select
		STR	R14,[R0,#vw__tempSel]	;Store that in the block
		LDMFD	R13!,{R1,R2,PC}^	;Return to caller when done

		LTORG

; --- viewer_dragSelection ---
;
; On entry:	R0 == viewer handle
;
; On exit:	--
;
; Use:		Starts a drag of the icons within the viewer.  When the drag
;		is finished, you get sent a vwEvent_dragged event.

		EXPORT	viewer_dragSelection
viewer_dragSelection ROUT

		STMFD	R13!,{R0-R10,R14}	;Save lots of registers
		MOV	R10,R0			;Look after the viewer handle
		LDR	R9,[R10,#vw__listDef]	;Find the list definition

		; --- First, see if we need DragASprite ---

		MOV	R0,#161			;Read CMOS locations
		MOV	R1,#28			;Various strange status flags
		SWI	OS_Byte			;Do the read op
		TST	R2,#2			;Is the bit set?
		BEQ	%50viewer_dragSelection	;No -- use normal Wimp box

		; --- Work out what sprite to use ---
		;
		; We need to ask the client here.

		LDR	R0,[R10,#vw__list]	;Load the list base pointer
		MOV	R1,#0			;Start at the beginning
		MOV	R2,#1			;Check the selected bit
		MOV	R3,#1			;Make sure it's on
		MOV	R14,PC			;Set up return address
		ADD	PC,R9,#vw__enumerate	;Read first matching item
		BCC	%90viewer_dragSelection	;No selection -- no drag
		MOVCS	R4,R1			;Match -- get icon handle
		MOVCS	R14,PC			;And set up again
		ADDCS	PC,R9,#vw__enumerate	;Read next matching iten
		MOVCS	R4,#-1			;If more than one, say `many'

		MOV	R1,R4			;Give this to the client
		MOV	R0,#vwEvent_sprite	;Pretend it's an event
		BL	vw__dispatch		;And send it off nicely
		BCC	%50viewer_dragSelection	;Told to use Wimp box

		; --- Now use DragASprite nicely ---

		MOV	R2,R1			;Look after the name
		MOV	R3,R0			;And the sprite area
		SUB	R13,R13,#20		;Make a small block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetPointerInfo	;Read the pointer position
		MOV	R14,R13			;Point to the block
		LDMIA	R14,{R0,R1}		;Load the coordinates out
		SUB	R0,R0,#64		;Turn into a little box
		SUB	R1,R1,#64		;Chop off a little bit
		STMIA	R14!,{R0,R1}		;Store them back
		ADD	R0,R0,#128		;Work out the other side
		ADD	R1,R1,#128		;Add on quite a lot actually
		STMIA	R14!,{R0,R1}		;Store them back

		MOV	R0,#&C5			;DragASprite flags
		MOV	R1,R3			;Get the sprite area
		MOV	R3,R13			;Point to my rectangle block
		SWI	XDragASprite_Start	;Start the drag operation
		ADD	R13,R13,#20		;Reclaim the stack pointer
		BVC	%70viewer_dragSelection	;If it worked, set up handler

		; --- Try and do a dotted outline ---

50		SUB	R13,R13,#40		;Make a small block
		LDR	R0,[R10,#vw__list]	;Find the list definition
		MOV	R1,#0			;Start at the beginning
		MOV	R2,#1			;Check selected bits
		MOV	R3,#1			;Make sure they're on
		MOV	R14,PC			;Set up return address
		ADD	PC,R9,#vw__enumerate	;Do the enumeration
		ADDCC	R13,R13,#40		;Restore stack pointer
		BCC	%90viewer_dragSelection	;No selection -- no drag

		MOV	R2,R13			;Point to a block
		BL	vw__iconBox		;Find the icon's bounding box
		LDMIA	R2,{R4-R7}		;Load the bounding box

		; --- Now work out the bounding box of selected icons ---

00		MOV	R2,#1			;Check selected bits
		MOV	R3,#1			;Make sure they're on
		MOV	R14,PC			;Set up return address
		ADD	PC,R9,#vw__enumerate	;Do the enumeration
		BCC	%f00			;No more -- skip onwards
		MOV	R2,R13			;Point to a block
		BL	vw__iconBox		;Find the bounding box
		LDMIA	R2,{R2,R3,R8,R14}	;Load these coordinates

		CMP	R4,R2
		MOVGT	R4,R2
		CMP	R5,R3
		MOVGT	R5,R3
		CMP	R6,R8
		MOVLT	R6,R8
		CMP	R7,R14
		MOVLT	R7,R14

		B	%b00			;Loop back for the rest now

		; --- Translate coordinates to screen relative ---

00		MOV	R1,R13			;Point to my block
		SWI	Wimp_GetPointerInfo	;Read the pointer position
		LDMIA	R13,{R8,R9}		;Load the position out

		LDR	R14,[R10,#vw__window]	;Load the window handle
		STR	R14,[R13,#0]		;Store it in the block
		SWI	Wimp_GetWindowState	;Read the window position
		LDR	R0,[R13,#4]		;Load the left hand side
		ADD	R14,R13,#16		;Point to top edge/scroll
		LDMIA	R14,{R1-R3}		;Load those out nicely
		SUB	R2,R0,R2		;Work out x origin pos
		SUB	R3,R1,R3		;And the y origin pos

		ADD	R4,R4,R2
		ADD	R5,R5,R3
		ADD	R6,R6,R2
		ADD	R7,R7,R3

		SUB	R4,R4,#vw__iconGap /2
		SUB	R5,R5,#vw__iconGap /2
		ADD	R6,R6,#vw__iconGap /2
		ADD	R7,R7,#vw__iconGap /2

		ADD	R14,R13,#8		;Point to bit of block
		STMIA	R14,{R4-R7}		;Save the drag box position

		; --- Work out the parent box position ---

		SUB	R4,R4,R8		;Work out min x position
		SUB	R5,R5,R9		;And the min y position
		SUB	R6,R6,R8		;Fiddle with max x
		SUB	R7,R7,R9		;And max y
		BL	screen_getInfo		;Read the screen information
		ADD	R14,R0,#screen_width	;Find the screen dimensions
		LDMIA	R14,{R8,R9}		;Load them out nicely
		ADD	R6,R6,R8		;Work out max x position
		ADD	R7,R7,R9		;And the max y position
		ADD	R14,R0,#screen_dx	;Get the pixel sizes
		LDMIA	R14,{R8,R9}		;Load those out too
		SUB	R6,R6,R8		;Subtract a pixel off
		SUB	R7,R7,R9		;To make things nice
		ADD	R14,R13,#24		;Point to parent box area
		STMIA	R14,{R4-R7}		;Save that lot away

		; --- Start the drag (phew...) ---

		MOV	R14,#5			;Drag fixed sized box
		STR	R14,[R13,#4]		;Store it in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_DragBox		;And start the drag op
		ADD	R13,R13,#40		;Reclaim my drag box block

		; --- Set up my handler at the end of it ---

70		ADR	R0,vw__dragIcn		;Point to my handler
		MOV	R2,R10			;Pass viewer handle in R10
		MOV	R3,R12			;And workspace (?) in R12
		BL	win_unknownHandler	;Register that routine please
90		LDMFD	R13!,{R0-R10,PC}^	;And return to caller

		LTORG

; --- vw__dragIcn ---
;
; On entry:	R0 == event code
;		R1 == pointer to event block
;
; On exit:	--
;
; Use:		Handles events during a selection drag.

vw__dragIcn	ROUT

		CMP	R0,#7			;Is it a drag end event?
		MOVNES	PC,R14			;Nope -- nothing to do

		STMFD	R13!,{R0-R2,R14}	;Save some registers
		SWI	XDragASprite_Stop	;Turn off any sprite drags
		SUB	R13,R13,#20		;Make a little block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetPointerInfo	;Read the pointer position
		ADD	R14,R1,#12		;Point to window handle
		LDMIA	R14,{R1,R2}		;Load window and icon
		ADD	R13,R13,#20		;Restore stack block
		MOV	R0,#vwEvent_dragged	;Get the event code
		BL	vw__dispatch		;Send it the event
		LDMFD	R13!,{R0-R2,PC}^	;And return to caller

		LTORG

;----- Miscellaneous --------------------------------------------------------

; --- viewer_window ---
;
; On entry:	R0 == viewer handle
;
; On exit:	R0 == window handle
;
; Use:		Returns the window handle of the viewer.

		EXPORT	viewer_window
viewer_window	ROUT

		LDR	R0,[R0,#vw__window]	;Load the window handle
		MOVS	PC,R14			;And return to caller

		LTORG

; --- viewer_update ---
;
; On entry:	R0 == viewer handle
;		R1 == icon handle
;
; On exit:	--
;
; Use:		Updates (redraws) a given icon.

		EXPORT	viewer_update
viewer_update	ROUT

		STMFD	R13!,{R2,R14}		;Save some registers
		MOV	R2,#vwEvent_redraw	;Normal is redraw event
		BL	vw__doUpdate		;Go and do the update
		LDMFD	R13!,{R2,PC}^		;And return to caller

		LTORG

; --- vw__doUpdate ---
;
; On entry:	R0 == viewer handle
;		R1 == icon handle
;		R2 == event code to send
;
; On exit:	--
;
; Use:		Updates an item, passing a given event to the client.  This
;		is purely for the use of gallery ;-)

		EXPORT	vw__doUpdate
vw__doUpdate	ROUT

		STMFD	R13!,{R0-R7,R10,R14}	;Save some registers
		MOV	R10,R0			;Get the viewer handle in R10
		MOV	R7,R2			;Find the event code

		; --- Start the update job ---

		SUB	R13,R13,#32+44		;Make a rectangle block
		MOV	R2,R13			;Point to space for box
		BL	vw__iconBox		;Find the icon's box

		LDMIA	R2,{R2-R5}		;Load the box coordinates
		ADD	R14,R13,#32+4		;Find the update block
		STMIA	R14,{R2-R5}		;Store the rectangle there
		LDR	R14,[R10,#vw__window]	;Load the window handle
		STR	R14,[R13,#32+0]		;Store in my nice block
		MOV	R4,R1			;Look after icon handle
		ADD	R1,R13,#32		;Point to update block
		SWI	Wimp_UpdateWindow	;Start the update job
		CMP	R0,#0			;Anything to do?
		BEQ	%90vw__doUpdate		;No -- skip to end then

		; --- Read the window origin ---

		LDR	R2,[R13,#32+4]		;Load the left hand side
		ADD	R14,R13,#32+16		;Find top and scroll offsets
		LDMIA	R14,{R3,R5,R6}		;Load those out too
		SUB	R6,R3,R6		;Find the y origin position
		SUB	R5,R2,R5		;And the x origin position

		; --- Now embark on the main loop ---

00		ADD	R14,R13,#32+28		;Point to graphics rectangle
		LDMIA	R14,{R0-R3}		;Load that out
		SUB	R0,R0,R5		;Adjust the block by origin
		SUB	R1,R1,R6		;This is just to keep the
		SUB	R2,R2,R5		;User's event handler happy
		SUB	R3,R3,R6		;I know what needs to be done
		STMIA	R14,{R0-R3}		;Store that lot back

		MOV	R0,R7			;Get the redraw event ready
		MOV	R1,R4			;Point to the item
		MOV	R2,R13			;Point to his icon block
		ADD	R3,R13,#32+28		;Point to clipping block
		BL	vw__dispatch		;Send to the handler

		; --- Do any other bits of drawing ---

		ADD	R1,R13,#32		;Point to update block
		CMP	R7,#vwEvent_draw	;Gallery redraw event? (yuk)
		BLEQ	drag_redraw		;Yes -- update drag rectangle
		SWI	Wimp_GetRectangle	;Do that rectangle
		CMP	R0,#0			;Any more to do?
		BNE	%b00			;Yes -- do it then

		; --- Wrap up and return ---

90		ADD	R13,R13,#32+44		;Restore the stack
		LDMFD	R13!,{R0-R7,R10,PC}^	;And return to caller

		LTORG

; --- viewer_setTitle ---
;
; On entry:	R0 == viewer handle
;		R1 == title string
;
; On exit:	--
;
; Use:		Sets the viewer window's title.

		EXPORT	viewer_setTitle
viewer_setTitle	ROUT

		STMFD	R13!,{R0-R2,R10,R14}	;Save some registers
		MOV	R10,R0			;Get the viewer handle
		LDR	R2,[R10,#vw__window]	;Load the window handle
		MOV	R0,R1			;Get the string to set
		ADD	R1,R10,#vw__title	;Point to title bar buffer
		BL	winUtils_setTitle	;Do the title setting
		BL	vw__open		;Is the window open?
		LDMCCFD	R13!,{R0-R2,R10,PC}^	;No -- do nothing then

		SUB	R13,R13,#36		;Make a window state block
		LDR	R14,[R10,#vw__window]	;Load the window handle
		STR	R14,[R13,#0]		;Store it in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetWindowState	;Read the current position

		BL	vw__tWidth		;Yes -- reset the width
		BL	vw__setExtent		;And reset the extent
		BL	vw__openWindow		;Open the window, in case
		ADD	R13,R13,#36		;And reset the stack pointer
		LDMFD	R13!,{R0-R2,R10,PC}^	;And return to caller

		LTORG

; --- vw__open ---
;
; On entry:	R10 == pointer to viewer block
;
; On exit:	CS if window is open, else CC
;
; Use:		Sees if a viewer window is currently open.

vw__open	ROUT

		STMFD	R13!,{R0,R1,R14}	;Save some registers
		SUB	R13,R13,#36		;Make some space
		LDR	R14,[R10,#vw__window]	;Load the window handle
		STR	R14,[R13,#0]		;Save it in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetWindowState	;Read the information
		LDR	R14,[R13,#32]		;Load the flags word
		TST	R14,#1<<16		;Is the window open?
		ADD	R13,R13,#36		;Reclaim the stack space
		LDMFD	R13!,{R0,R1,R14}	;Restore registers
		ORRNES	PC,R14,#C_flag		;Return with C set if so
		BICEQS	PC,R14,#C_flag		;Otherwise clear C

		LTORG

; --- vw__openWindow ---
;
; On entry:	R1 == pointer to window state block
;		R10 == pointer to viewer block
;
; On exit:	R0 corrupted (just like Wimp_OpenWindow)
;
; Use:		Opens a viewer window, and informs the user through the
;		vwEvent_open event.

vw__openWindow	ROUT

		SWI	Wimp_OpenWindow		;Reopen the window as ordered
		MOV	R0,#vwEvent_open	;Send the event along nicely
		B	vw__dispatch		;And report, and return

		LTORG

; --- vw__tWidth ---
;
; On entry:	R10 == pointer to viewer block
;
; On exit:	--
;
; Use:		Updates the window's fixed width parameter.

vw__tWidth	ROUT

		STMFD	R13!,{R0,R1,R14}	;Save some registers
		ADD	R0,R10,#vw__title	;Point to title buffer
		BL	wimp_strWidth		;Get the string width
		ADD	R1,R0,#vw__titleAdd	;Add on a fudge factor
		LDR	R0,[R10,#vw__banner]	;Load the banner address
		CMP	R0,#0			;Is the banner there?
		BLNE	wimp_strWidth		;Yes -- get its width
		ADDNE	R0,R0,#vw__banAdd	;And add on its fudge
		CMP	R1,R0			;Which is bigger?
		MOVCC	R1,R0			;Use the biggest one
		STR	R1,[R10,#vw__fixedWidth] ;Store the new width
		LDMFD	R13!,{R0,R1,PC}^	;And return to caller

		LTORG

; --- viewer_rescan ---
;
; On entry:	R0 == viewer handle
;
; On exit:	--
;
; Use:		Rescans all the icons in the viewer and forces a redraw,
;		in case icons have been added or deleted (or renamed).  Note
;		that the redraw is done *anyway* -- it's your responsibility
;		to avoid calling this routine when you don't need to.

		EXPORT	viewer_rescan
viewer_rescan	ROUT

		STMFD	R13!,{R0,R1,R10,R14}	;Save some registers
		MOV	R10,R0			;Get the viewer handle
		BL	vw__open		;Is the window open at all?
		LDMCCFD	R13!,{R0,R1,R10,PC}^	;No -- handle on open then

		SUB	R13,R13,#36		;Make a window state block
		LDR	R14,[R10,#vw__window]	;Load the window handle
		STR	R14,[R13,#0]		;Save it in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetWindowState	;And read the window state

		; --- Open the window onto the screen ---

		LDR	R1,[R10,#vw__listDef]	;Find the list definition
		LDR	R0,[R10,#vw__list]	;And the list base
		MOV	R14,PC			;Set up return address
		ADD	PC,R1,#vw__items	;Find how many items
		STR	R1,[R10,#vw__icons]	;Store this away

		MOV	R1,R13			;Point at window state blk
		BL	vw__rescanSize		;Rescan the item size
		BL	vw__extend		;Yes -- make it big then
		BL	vw__resize		;Work out new arrangement
		BL	vw__setExtent		;Set the window's extent
		BL	vw__refresh		;Force update of display
		BL	vw__openWindow		;And open the window
		ADD	R13,R13,#36		;Reclaim my stack space
		LDMFD	R13!,{R0,R1,R10,PC}^	;And return to caller

		LTORG

;----- Icon rearrangement routines ------------------------------------------

; --- vw__resize ---
;
; On entry:	R1 == pointer to window state block
;		R10 == pointer to viewer block
;
; On exit:	CS if the size has changed, else CC
;
; Use:		Updates the icon arrangement within a window, returning
;		whether the arrangement is different now.  Note that this
;		doesn't redraw the window; it just recaches the extent.
;		If the size has changed, you'll probably need to call
;		vw__setExtent and vw__refresh to get everything looking nice.

vw__resize	ROUT

		STMFD	R13!,{R0-R3,R14}	;Save some registers

		; --- Now work out how many icons we should have ---

		LDMIB	R1,{R1-R3}		;Load the x coords from win
		SUB	R0,R3,R1		;Work out the window width
		SUB	R0,R0,#vw__iconGap	;Subtract off extra border
		LDR	R2,[R10,#vw__iconWidth]	;Load the icon width out
		ADD	R1,R2,#vw__iconGap	;Add on the gap
		BL	divide			;Divide one by the other
		MOV	R3,R0			;Icons which fit across win

		BL	screen_getInfo		;Find the screen information
		LDR	R0,[R0,#screen_width]	;Load the screen width
		SUB	R0,R0,#40+vw__iconGap	;Fudge for scroll bar
		ADD	R1,R2,#vw__iconGap	;Get icon width+gap again
		BL	divide			;Divide one by the other
		MOV	R2,R0			;Icons which fit across scrn

		; --- Fiddle these numbers appropriately ---

		LDR	R0,[R10,#vw__icons]	;Load number of icons
		CMP	R3,R0			;Too many across window?
		MOVCS	R3,R0			;Yes -- use number we have
		CMP	R2,R0			;Too many across screen?
		MOVCS	R2,R0			;Yes -- use number we have
		CMP	R2,R3			;More than fit across screen?
		MOVCS	R2,R3			;Use -- use that then (erk)
		CMP	R2,#0			;No icons at all?
		MOVEQ	R2,#1			;Then have at least one

		; --- Work out how many go down ---

		MOV	R1,R2			;Get number going across
		BL	divide			;Do the division
		CMP	R1,#0			;Any extra ones on bottom?
		ADDNE	R0,R0,#1		;Yes -- add an extra row
		MOVS	R3,R0			;Look after this value
		MOVEQ	R3,#0			;If it's zero, use one

		; --- Now see if anything's changed ---

		ADD	R14,R10,#vw__across	;Find old across counter
		LDMIA	R14,{R0,R1}		;Load across and down values
		CMP	R0,R2			;Compare with old ones
		CMPEQ	R1,R3			;Are they the same?
		STMNEIA	R14,{R2,R3}		;If not, store new ones
		LDMFD	R13!,{R0-R3,R14}	;Restore caller's registers
		ORRNES	PC,R14,#C_flag		;If changed, return C set
		BICEQS	PC,R14,#C_flag		;Otherwise return C clear

		LTORG

; --- vw__setExtent ---
;
; On entry:	R10 == pointer to viewer block
;
; On exit:	--
;
; Use:		Resets the extent of a viewer window, in case the
;		arrangement of icons or banner/title text has changed.
;		If the window is currently open, you should follow this
;		call with a Wimp_OpenWindow SWI.

vw__setExtent	ROUT

		STMFD	R13!,{R0-R3,R14}	;Save some registers

		; --- Work out correct horizontal extent ---

		BL	screen_getInfo		;Find the screen information
		LDR	R0,[R0,#screen_width]	;Load the screen width
		SUB	R0,R0,#40+vw__iconGap	;Fudge for scroll bar
		MOV	R2,R0			;Look after this value
		LDR	R3,[R10,#vw__iconWidth]	;Load the icon width
		ADD	R3,R3,#vw__iconGap	;Add on the inter-icon gap
		MOV	R1,R3			;Divide by this
		BL	divide			;Divide one by the other
		LDR	R14,[R10,#vw__icons]	;Load the number of icons
		CMP	R0,R14			;Is this bigger?
		MOVCS	R0,R14			;Yes -- then use that
		CMP	R0,#0			;Unless there's none
		MOVEQ	R0,#1			;In which case use one
		MUL	R2,R3,R0		;Work out the correct width
		ADD	R2,R2,#vw__iconGap	;Add on extra border around
		LDR	R14,[R10,#vw__fixedWidth] ;Load width of title/banner
		CMP	R2,R14			;Do we have enough here?
		MOVCC	R2,R14			;No -- then use more

		; --- And now the vertical extent ---

		LDR	R1,[R10,#vw__iconHeight] ;Load the icon height
		ADD	R1,R1,#vw__iconGap	;Add on the gap
		LDR	R14,[R10,#vw__down]	;Load vertical number of icns
		MUL	R1,R14,R1		;Work out the size
		ADD	R1,R1,#vw__iconGap	;Add on extra border

		; --- Work out the other two ---

		RSB	R1,R1,#0		;And vertical extent -ve
		LDR	R3,[R10,#vw__banner]	;Load banner pointer
		CMP	R3,#0			;Do we have a banner?
		MOVNE	R3,#vw__banHeight	;Yes -- then add above origin
		MOV	R0,#0			;Start at the left side

		; --- Make sure the extent is big enough ---

		SUBS	R14,R2,#vw__minWidth	;Get the minimum width
		SUBCC	R2,R2,R14		;Make bigger if needs be
		SUB	R14,R3,R1		;Work out extent height
		SUBS	R14,R14,#vw__minHeight	;Get the minimum height
		ADDCC	R1,R1,R14		;Make bigger if needs be

		; --- Finally set up the extent ---

		ADD	R14,R10,#vw__extent	;Set the window extent
		STMIA	R14,{R0-R3}		;Save the values in there
		LDR	R0,[R10,#vw__window]	;Load the window handle
		ADD	R1,R10,#vw__extent	;Point to the extent block
		SWI	Wimp_SetExtent		;Set the window's extent
		LDMFD	R13!,{R0-R3,PC}^	;And return to caller

		LTORG

; --- vw__refresh ---
;
; On entry:	R10 == pointer to a viewer block
;
; On exit:	--
;
; Use:		Forces a redraw of the contents of a viewer.  Use in
;		conjunction with vw__resize.

vw__refresh	ROUT

		STMFD	R13!,{R0-R4,R14}	;Save some registers
		LDR	R0,[R10,#vw__window]	;Load the window handle
		MOV	R1,#-&FFFFFFF		;Force a redraw of the world
		MOV	R2,#-&FFFFFFF		;This hack prevents problems
		MOV	R3,#&FFFFFFF		;with the extent being bodged
		MOV	R4,#&FFFFFFF		;by the Wimp.
		SWI	Wimp_ForceRedraw	;Redraw the whole lot
		LDMFD	R13!,{R0-R4,PC}^	;And return to caller

		LTORG

; --- vw__extend ---
;
; On entry:	R1 == pointer to window open block
;		R10 == pointer to a viewer block
;
; On exit:	--
;
; Use:		Extends the viewer window if necessary to accomodate new
;		items.  The new coordinates to open are written back to the
;		window open block.  Correct procedure for adding items is
;		as follows:
;
;		1. Get window state
;		2. Call vw__extend
;		3. Call vw__resize
;		4. Call vw__setExtent
;		5. Call vw__refresh

vw__extend	ROUT

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

		; --- Load current and maximum width and height ---

		MOV	R8,R1			;Look after this address
		LDMIB	R8,{R4-R7}		;Load the window dimensions
		SUB	R6,R6,R4		;Find the window width
		SUB	R7,R7,R5		;And its height

		ADD	R14,R10,#vw__extent	;Find the current extent
		LDMIA	R14,{R0-R3}		;Load the dimensions out
		SUB	R4,R2,R0		;Find the maximum width
		SUB	R5,R3,R1		;And the maximum height
		LDR	R14,[R10,#vw__flags]	;Load the flags word
		TST	R14,#vwFlag__opened	;Has viewer ever been opened?
		BNE	%f00			;Yes -- skip onwards

		; --- Set up special values for first time ---

		MOV	R4,#0			;No -- force width to max
		MOV	R5,#0			;And height forced to max
		LDR	R6,[R10,#vw__fixedWidth] ;Set a default width though
		CMP	R6,#vw__minWidth	;Get the minimum width
		MOVCC	R6,#vw__minWidth	;If too small, use this
		MOV	R7,#vw__minHeight	;Use the minimum height too

		; --- Find out if we have anything to do horizontally ---
		;
		; We only try to extend if the window is currently as wide
		; as it will go.

00		CMP	R6,R4			;Does the width shape up?
		BCC	%50vw__extend		;No -- skip to vertical

		; --- Extend the width ---
		;
		; We ensure that the width of the window is the smaller
		; of the maximum width we allow and the width of the number
		; of icons in the window.

		MOV	R0,#vw__maxWidth	;Get the maximum width
		LDR	R3,[R10,#vw__iconWidth]	;Find the icon width
		ADD	R3,R3,#vw__iconGap	;And add on the gap as usual
		MOV	R1,R3			;Divide by this
		BL	divide			;Divide one by the other
		LDR	R1,[R10,#vw__icons]	;Load total number of icns
		CMP	R0,R1			;Which one is bigger?
		MOVCS	R0,R1			;Use the smaller
		CMP	R0,#0			;Are there no icons at all?
		MOVEQ	R0,#1			;None -- make space for one
		MUL	R0,R3,R0		;Work out new improved width
		ADD	R0,R0,#vw__iconGap	;Add on the extra border
		CMP	R6,R0			;Which is bigger?
		MOVCC	R6,R0			;Use the bigger of the two

		; --- Now do things vertically ---
		;
		; This is basically the same.  We only extend if the window
		; is as tall at it will go.

50vw__extend	CMP	R7,R5			;Does the height shape up?
		BCC	%90vw__extend		;No -- skip on then

		; --- Extend the height ---
		;
		; This is trickier, because we have to work out how many
		; icons there will be vertically.

		LDR	R3,[R10,#vw__iconHeight] ;Load the icon height
		ADD	R3,R3,#vw__iconGap	;Add on the inter-icon gap
		SUB	R0,R6,#vw__iconGap	;Get the current width
		LDR	R1,[R10,#vw__iconWidth]	;Find the icon width
		ADD	R1,R1,#vw__iconGap	;And add on the gap as usual
		BL	divide			;Find how many will fit

		MOV	R1,R0			;We will divide by this
		LDR	R0,[R10,#vw__icons]	;Load the number of icons
		BL	divide			;Work out number down
		CMP	R1,#0			;Is there a remainder?
		ADDNE	R0,R0,#1		;Yes -- extra one then
		CMP	R0,#0			;Are there no icons?
		MOVEQ	R0,#1			;None -- make space for one
		MOV	R2,R0			;Look after this value

		MOV	R0,#vw__maxHeight	;Get the maximum height
		MOV	R1,R3			;And the icon height
		BL	divide			;Do the division again
		CMP	R0,R2			;Which one is bigger?
		MOVCS	R0,R2			;Use the smaller
		MUL	R0,R3,R0		;Work out new improved height
		ADD	R0,R0,#vw__iconGap	;Add on the extra border
		LDR	R14,[R10,#vw__banner]	;Do we have a banner string?
		CMP	R14,#0			;Quick check for that...
		ADDNE	R0,R0,#vw__banHeight	;Yes -- add that on too
		CMP	R7,R0			;Which is bigger?
		MOVCC	R7,R0			;Use the bigger

		; --- Now update the window block ---

90vw__extend	LDMIB	R8,{R0-R3}		;Load the current values
		ADD	R2,R0,R6		;Work out new right side
		SUBS	R1,R3,R7		;And the new bottom
		SUBLT	R3,R3,R1		;Move the top if hit bottom
		SUBLT	R1,R1,R1		;And move the bottom too
		STMIB	R8,{R0-R3}		;Save that lot back again
		LDMFD	R13!,{R0-R8,PC}^	;And return to caller

		LTORG

;----- Viewer block structure -----------------------------------------------
;
; This is shadowed by gallery.s -- keep them in step.

		^	0

		; --- Information about the window ---

vw__window	#	4			;Window handle
vw__extent	#	16			;Current window extent
vw__flags	#	4			;Any flags of interest
vw__list	#	4			;Pointer to list head
vw__listDef	#	4			;Pointer to list handler
vw__handler	#	12			;Viewer's event handler
gl__handler	#	4			;Gallery's event handler
vw__shape	#	4			;Shape handler function
vw__banner	#	4			;Pointer to banner string
vw__fixedWidth	#	4			;Width of banner/title

		; --- Icon sizing information ---

vw__icons	#	4			;Cache number of icons
vw__stdDimens	#	8			;`Standard' width and height
vw__iconWidth	#	4			;Width of icons currently
vw__iconHeight	#	4			;Height of icons currently
vw__across	#	4			;Number of icons across
vw__down	#	4			;Number of icons down

		; --- Data for default selection model ---

vw__tempSel	#	4			;Temporary selected icon

		; --- Big buffers at the end ---

vw__title	#	256			;Title bar buffer

vw__size	#	0			;Size of a viewer block

		; --- Flags bits ---
		;
		; Gallery `borrows' top-end flags bits here

vwFlag__opened	EQU	(1<<0)			;Has the window been opened?

;----- Constants ------------------------------------------------------------

vw__iconGap	EQU	16			;Gap between icons
vw__banHeight	EQU	48			;Height of viewer banner

vw__minWidth	EQU	380			;Minimum width for a viewer
vw__minHeight	EQU	160			;Minimum height

vw__titleAdd	EQU	164			;Fudge factor to add to title
vw__banAdd	EQU	64			;Fudge factor for the banner

vw__maxWidth	EQU	900-vw__iconGap		;Maximum width of a viewer
vw__maxHeight	EQU	700-vw__iconGap		;Maximum height of a viewer

;----- List definition format -----------------------------------------------

		^	0
vw__itemToIndex	#	4			;Item to index routine
						;Entry:	R0 == pointer to list
						;	R1 == pointer to item
						;Exit:	R1 == index, or -1

vw__indexToItem	#	4			;Index to item routine
						;Entry:	R0 == pointer to list
						;	R1 == index of item
						;Exit:	R1 == item ptr, or 0

vw__enumerate	#	4			;Enumeration function
						;Entry:	R0 == list pointer
						;	R1 == item, or 0
						;	R2 == BIC mask
						;	R3 == test mask
						;Exit:	CS if match, and
						;	R1 == item ptr

vw__items	#	4			;Function to return items
						;Entry:	R0 == list pointer
						;Exit:	R1 == number of items

vw__setFlags	#	4			;Function to set/read flags
						;Entry:	R1 == pointer to item
						;	R2 == BIC mask
						;	R3 == EOR mask
						;Exit:	R2 == new flags

;----- Shape function reason codes ------------------------------------------

		^	0
vwShape_size	#	1			;Read an icon's size
						;Entry:	R1 == list item
						;	R2,R3 == std size
						;Exit:	R2,R3 == actual size

vwShape_intersects #	1			;Does icon intersect box?
						;Entry:	R1 == list item
						;	R2 == ptr to icon box
						;	R3 == ptr to box
						;Exit:	CS if intersect

vwShape_slowBit	#	1			;Does slow bit need redraw?
						;Entry: R1 == list item
						;	R2 == ptr to icon box
						;	R3 == ptr to box
						;Exit:	CS if intersect

;----- Viewer event codes ---------------------------------------------------

		^	0
vwEvent_close	#	1			;User has closed the window

vwEvent_click	#	1			;User has clicked an icon
						;R1 == icon handle (or 0)
						;R2 == mouse status

vwEvent_double	#	1			;User has double-clicked
						;R1 == icon handle (or 0)
						;R2 == mouse status

vwEvent_drag	#	1			;User has dragged the mouse
						;R1 == icon handle (or 0)
						;R2 == mouse status

vwEvent_menu	#	1			;User has clicked menu
						;R1 == icon handle (or 0)
						;R2 == mouse status

vwEvent_redraw	#	1			;Redraw a viewer icon
						;R1 == icon handle
						;R2 == pointer to coords blk
						;R3 == pointer to clip blk
						;R5,R6 == window origin

vwEvent_drop	#	1			;File dropped on the viewer
						;R1 == filetype of data
						;R2 == estimated size
						;R3 == address of filename
						;R4 == drop type

vwEvent_help	#	1			;Help request for the viewer
						;R1 == icon handle, or 0

vwEvent_key	#	1			;Key pressed
						;R1 == key code (translated)
						;Return CS if used, else CC

vwEvent_dragged	#	1			;Icons dropped on a window
						;R1 == destination window
						;R2 == destination icon

vwEvent_sprite	#	1			;Return sprite name to use
						;Entry:	R1 == icon handle
						;	  (-1 for many)
						;Exit:	CS if sprite found,
						;	R0 == ptr to spr area
						;	R1 == pointer to name
						;	else CC

vwEvent_open	#	1			;The viewer has been moved
						;R1 == ptr to open/state blk

vwEvent_draw	#	1			;Slowly redraw icon
						;R1 == icon handle
						;R2 == pointer to coords blk
						;R3 == pointer to clip blk
						;R5,R6 == window origin
						;(Event only used by gallery)

vwEvent_unDraw	#	1			;Undraw temporary part
						;R1 == icon handle
						;R2 == pointer to coords blk
						;R3 == pointer to clip blk
						;R5,R6 == window origin
						;(Event only used by gallery)

		; --- Drop event subreason codes ---

		^	0
vwDrop_save	#	1			;File from another app
vwDrop_load	#	1			;File from filing system

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

		END
