;
; pane.s
;
; Pane handling facilities (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:event
		GET	sapphire:screen
		GET	sapphire:sapphire
		GET	sapphire:suballoc
		GET	sapphire:tspr

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

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- pane_add ---
;
; On entry:	R0 == window handle of parent window
;		R1 == icon handle in parent window
;		R2 == window handle of pane window
;
; On exit:	May return an error
;
; Use:		This call registers a pane to be associated with the given
;		window. The pane is always opened to fit exactly within
;		the given icon -- border widths are taken into account
;		if there are scroll bars etc.
;
;		You must call pane_closed if the parent window is closed,
;		since there is no way for pane to trap this occurence.

		EXPORT	pane_add
pane_add	ROUT

		STMFD	R13!,{R0-R4,R12,R14}	;Stack some registers
		WSPACE	pane__wSpace		;Locate my workspace

		; --- First, we must scan to see if the parent
		;     window is already registered

		MOV	R4,R0			;Preserve parent handle
		BL	pane__windowBlock	;Find the window block
		CMP	R0,#0			;Is there one?
		MOVNE	R3,R0			;Yes -- point to it
		BNE	%50pane_add		;And add in the pane

		; --- The window isn't registered ---

10		MOV	R0,#wl__size		;Allocate this much memory
		BL	sub_alloc		;Get it then
		SWIVS	OS_GenerateError	;...and generate error
		LDR	R3,ws__wList		;Load first window defn
		STR	R3,[R0,#wl__next]	;Store as next pointer
		CMP	R3,#0			;Is there any more?
		STRNE	R0,[R3,#wl__prev]	;Yes -- update prev pointer
		MOV	R3,#0			;A NULL pointer
		STR	R3,[R0,#wl__prev]	;No previous for new block
		STR	R3,[R0,#wl__pList]	;No pane list yet
		MOV	R3,R0			;Put pointer in R3
		STR	R4,[R3,#wl__wHandle]	;Store parent window handle
		STR	R3,ws__wList		;Store at list head

		; --- R3 points to the window list block ---

50		MOV	R0,#pl__size		;Allocate this much memory
		BL	sub_alloc		;Go on then
		SWIVS	OS_GenerateError	;...and generate error
		LDR	R4,[R3,#wl__pList]	;Load first pane defn
		STR	R4,[R0,#pl__next]	;Store as next pointer
		CMP	R4,#0			;Is there any more?
		STRNE	R0,[R4,#pl__prev]	;Yes -- update prev pointer
		MOV	R14,#0			;A NULL pointer
		STR	R14,[R0,#pl__prev]	;No previous for new block
		STR	R1,[R0,#pl__iHandle]	;Store the icon handle
		STR	R2,[R0,#pl__wHandle]	;Store the window handle
		STR	R0,[R3,#wl__pList]	;Store at list head

		; --- Return to caller ---

		LDMFD	R13!,{R0-R4,R12,PC}^	;Return

		LTORG

; --- pane_remove ---
;
; On entry:	R0 == window handle for which pane was registered
;		R1 == window handle of the pane window itself
;
; On exit:	--
;
; Use:		Removes the pane from the given window. This call will
;		close the given pane, but will not actually delete it
;		(ie. with a Wimp_DeleteWindow).

		EXPORT	pane_remove
pane_remove	ROUT

		STMFD	R13!,{R0-R5,R12,R14}	;Stack some registers
		WSPACE	pane__wSpace		;Locate my workspace

		BL	pane__windowBlock	;Find the window block
		CMP	R0,#0			;Is there one?
		BEQ	%90pane_remove		;No -- remove
		MOV	R3,R0			;Put pointer in R3

		; --- Remove the pane from the window list ---

50		LDR	R4,[R3,#wl__pList]	;Get the first entry
55		CMP	R4,#0			;Is there a pane definition?
		BEQ	%90pane_remove		;No -- return
		LDR	R5,[R4,#pl__wHandle]	;Get the pane handle
		CMP	R5,R1			;Is this the one we want
		LDRNE	R4,[R4,#wl__next]	;No -- get next in list
		BNE	%55pane_remove		;...and keep looking

		; --- We have now found the pane block ---

		ADD	R1,R4,#pl__wHandle	;Point to the window handle
		SWI	XWimp_CloseWindow	;Close the window
		LDR	R1,[R4,#pl__prev]	;Get the previous pointer
		LDR	R2,[R4,#pl__next]	;And the next pointer
		CMP	R2,#0			;Is there a next field?
		STRNE	R1,[R2,#pl__prev]	;Yes -- update previous
		CMP	R1,#0			;Is there a previous field
		STRNE	R2,[R1,#pl__next]	;Yes -- update next field
		STREQ	R2,[R3,#wl__pList]	;No -- store next as head

		MOV	R0,R4			;Point to the block
		MOV	R1,#pl__size		;It's this big
		BL	sub_free		;Free the block
		LDR	R1,[R3,#wl__pList]	;Get the pane list
		CMP	R1,#0			;Is there one?
		MOVEQ	R0,R3			;No -- point to window block
		BLEQ	pane__removeWindow	;...remove the definition

		; --- Return to caller ---

90		LDMFD	R13!,{R0-R5,R12,PC}^	;Return

		LTORG

; --- pane__removeWindow ---
;
; On entry:	R0 == pointer to window block
;
; On exit:	--
;
; Use:		Removes the give window block from the list. All associated
;		panes are removed too.

pane__removeWindow ROUT

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

		MOV	R3,R0			;Keep a pointer to this block
		LDR	R2,[R3,#wl__pList]	;Get the first pane block
		CMP	R2,#0			;Is there one?
		BEQ	%10pane__removeWindow	;No -- free window block
00		ADD	R1,R2,#pl__wHandle	;Point to the window handle
		MOV	R14,R2			;Get block pointer in R14
		LDR	R2,[R2,#pl__next]	;Get next pointer
		SWI	XWimp_CloseWindow	;Close the window
		MOV	R0,R14			;Block pointer in R0
		MOV	R1,#pl__size		;It's this big
		BL	sub_free		;Free the block
		CMP	R2,#0			;Are there more blocks?
		BNE	%00pane__removeWindow	;Yes -- remove next pane too

		; --- Remove the window block ---

10		LDR	R1,[R3,#wl__prev]	;Get the previous pointer
		LDR	R2,[R3,#wl__next]	;And the next pointer
		CMP	R2,#0			;Is there a next field?
		STRNE	R1,[R2,#wl__prev]	;Yes -- update previous
		CMP	R1,#0			;Is there a previous field
		STRNE	R2,[R1,#wl__next]	;Yes -- update next field
		STREQ	R2,ws__wList		;No -- store next as head
		MOV	R0,R3			;Point to the block
		MOV	R1,#wl__size		;It's this big
		BL	sub_free		;And free it

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

		LTORG

; --- pane_closed ---
;
; On entry:	R0 == window handle of parent
;
; On exit:	--
;
; Use:		Informs pane that a parent window has closed.
;		All associated panes are then closed.

		EXPORT	pane_closed
pane_closed	ROUT

		STMFD	R13!,{R0-R2,R12,R14}	;Stacks some registers
		WSPACE	pane__wSpace		;Locate workspace

		BL	pane__windowBlock	;Find the block
		CMP	R0,#0			;Is there one?
		BEQ	%90pane_closed		;No -- return

		LDR	R2,[R0,#wl__pList]	;Get the first pane pointer
00pane_closed	ADD	R1,R2,#pl__wHandle	;Point to the window handle
		SWI	XWimp_CloseWindow	;Close the window
		LDR	R2,[R2,#pl__next]	;Get the next pane
		CMP	R2,#0			;Is there one?
		BNE	%00pane_closed		;Yes -- close it then

90pane_closed	LDMFD	R13!,{R0-R2,R12,PC}^	;Return to caller

		LTORG

; --- pane_deleted ---
;
; On entry:	R0 == window handle of parent
;
; On exit:	--
;
; Use:		Informs pane that a parent window has been deleted.
;		All associated panes are then closed, and there
;		registration with the pane library module is
;		terminated.

		EXPORT	pane_deleted
pane_deleted	ROUT

		STMFD	R13!,{R0-R3,R12,R14}	;Stacks some registers
		WSPACE	pane__wSpace		;Locate workspace

		BL	pane__windowBlock	;Find the block
		CMP	R0,#0			;Is there one?
		BLNE	pane__removeWindow	;Yes -- remove definitions

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

		LTORG

; --- pane__windowBlock ---
;
; On entry:	R0 == window handle
;
; On exit:	R0 == pointer to window block
;
; Use:		Find the window block associated with the given
;		window handle. If it is not found, 0 is returned.

pane__windowBlock ROUT

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

		MOV	R1,R0			;Keep the window handle
		LDR	R0,ws__wList		;Load first window defn
00		CMP	R0,#0			;Is there one?
		BEQ	%90pane__windowBlock	;No -- return
		LDR	R2,[R0,#wl__wHandle]	;Get the window handle
		CMP	R2,R1			;Is this the one we want?
		BEQ	%90pane__windowBlock	;Yes -- return
		LDR	R0,[R0,#wl__next]	;Get the next in list
		B	%00pane__windowBlock	;And keep looking

90		LDMFD	R13!,{R1,R2,PC}^	;Return to caller

		LTORG

; --- pane_swap ---
;
; On entry:	R0 == window handle of parent window
;		R1 == icon handle within parent window
;		R2 == window handle of new pane
;
; On exit:	--
;
; Use:		This call will replace the pane in associated with icon R1
;		in window R0, with the pane in R2.
;
;		The exisiting pane is closed, and the new pane is
;		opened in it's place. No error is generated if the existing
;		pane does not exist; this allows the caller to delete the
;		window before doing the swap.

		EXPORT	pane_swap
pane_swap	ROUT

		STMFD	R13!,{R0-R3,R12,R14}	;Stack some registers
		WSPACE	pane__wSpace		;Locate my workspace

		BL	pane__windowBlock	;Find the window block
		CMP	R0,#0			;Is there one?
		BEQ	%90pane_swap		;No -- return

		; --- Find the pane in the pane window list ---

		LDR	R3,[R0,#wl__pList]	;Get the first entry
00		CMP	R3,#0			;Is there a pane definition?
		BEQ	%90pane_swap		;No -- return
		LDR	R14,[R3,#pl__iHandle]	;Get the icon handle
		CMP	R14,R1			;Is this the one we want
		LDRNE	R3,[R3,#wl__next]	;No -- get next in list
		BNE	%00pane_swap		;...and keep looking

		; --- Do the swap ---

		MOV	R14,R0			;Preserve R0
		ADD	R1,R3,#pl__wHandle	;Point to the window handle
		SWI	XWimp_CloseWindow	;Close it
		STR	R2,[R3,#pl__wHandle]	;Store the new window handle
		SUB	R13,R13,#36		;Get a block
		LDR	R0,[R14,#wl__wHandle]	;Get parent handle
		STR	R0,[R13,#0]		;Store it in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetWindowState	;Get the window state
		MOV	R2,R1			;Put state in R2
		MOV	R0,R14			;The window block
		MOV	R1,R3			;Point to the pane block
		MOV	R3,R2			;Now put state in R3
		BL	pane__open		;Open the new pane
		ADD	R13,R13,#36		;Reclaim my stack

90pane_swap	LDMFD	R13!,{R0-R3,R12,PC}^	;Return to caller

		LTORG

; --- pane_open ---
;
; On entry:	R0 == window handle
;
; On exit:	--
;
; Use:		Opens all the panes associated with the given window.

		EXPORT	pane_open
pane_open	ROUT

		STMFD	R13!,{R0-R3,R12,R14}	;Stack some registers
		WSPACE	pane__wSpace		;Load my workspace pointer

		BL	pane__windowBlock	;Find the window block
		CMP	R0,#0			;Is there one?
		BEQ	%90pane_open		;No -- return

		; --- Find the pane in the pane window list ---

		SUB	R13,R13,#36		;Get a block
		LDR	R14,[R0,#wl__wHandle]	;Get the window handle
		STR	R14,[R13,#0]		;Store in the block
		MOV	R1,R13			;Point to the block
		MOV	R2,R0			;Preserve R0
		SWI	Wimp_GetWindowState	;Get the window state
		MOV	R0,R2			;Get R0 back
		MOV	R3,R1			;Put state in R3

		LDR	R1,[R0,#wl__pList]	;Get the first entry
00pane_open	BL	pane__open		;Open the pane
		LDR	R1,[R1,#pl__next]	;Get next pane
		CMP	R1,#0			;Is there one?
		BNE	%00pane_open		;Yes -- keep looping
		ADD	R13,R13,#36		;Get stack back

90pane_open	LDMFD	R13!,{R0-R3,R12,PC}^	;Return to caller

		LTORG

; --- pane__open ---
;
; On entry:	R0 == pointer to window block
;		R1 == pointer to pane block
;		R3 == pointer to parent window state
;
; On exit:	--
;
; Use:		Opens the pane in the right place.

pane__open	ROUT

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

		MOV	R4,R0			;Preserve window block
		MOV	R6,R1			;Preserve R1
		BL	screen_justChangedMode	;Due to a mode change?
		MOVCS	R1,R3			;Point to open window block
		BLCS	tspr_adjustBox		;Adjust the box

		LDR	R5,[R3,#28]		;Get the back value
		CMP	R5,#-2			;Opening at the back?
		MOVEQ	R1,R3			;Yes -- point to the block
		SWIEQ	Wimp_OpenWindow		;Open the window
		SWIEQ	Wimp_GetWindowState	;Get the window state

		MOV	R2,R6			;Get R1 back again
		SUB	R13,R13,#36		;Get me a block
		LDR	R14,[R2,#pl__wHandle]	;Get pane window handle
		STR	R14,[R13,#0]		;Store in the block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetWindowState	;Get the window state
		LDR	R9,[R13,#32]		;Load the window's flags
		SUB	R13,R13,#40		;Get another block
		LDR	R14,[R4,#wl__wHandle]	;Get parent handle
		STR	R14,[R13,#0]		;Store the handle
		LDR	R14,[R2,#pl__iHandle]	;Get the icon handle
		STR	R14,[R13,#4]		;Store that too
		MOV	R1,R13			;Point to the block
		SWI	Wimp_GetIconState	;Get the icon state
		ADD	R1,R13,#8		;Point to coordinates
		LDMIA	R1,{R5-R8}		;Get the icon coordinates
		ADD	R13,R13,#40		;Point at window state
		LDR	R0,[R3,#4]		;Visible area x0 coord
		LDR	R1,[R3,#16]		;Visible area y1 coord
		ADD	R5,R0,R5		;Make icon coords screen rel.
		ADD	R6,R1,R6
		ADD	R7,R0,R7
		ADD	R8,R1,R8

		STMFD	R13!,{R2-R4}		;Preserve registers
		BL	screen_getInfo		;Get screen information
		ADD	R0,R0,#screen_dx	;Point to the pixel sizes
		LDMIA	R0,{R3,R4}		;Load the pixel sizes
		BL	tspr_borderWidths	;Get the border widths
		TST	R9,#&0f000000		;Is there a title bar?
		SUBNE	R8,R8,R0		;Yes -- correct y1 coord
		SUBEQ	R8,R8,R4		;No -- subtract pixel size
		TST	R9,#&10000000		;Is there a vertical bar?
		SUBNE	R7,R7,R1		;Yes -- correct x1 coord
		SUBEQ	R7,R7,R3		;No -- subtract pixel size
		TST	R9,#&40000000		;Is there a horizontal bar?
		ADDNE	R6,R6,R2		;Yes -- correct y0 coord
		ADDEQ	R6,R6,R4		;No -- add pixel size
		ADD	R5,R5,R3		;Add pixel size to x0
		LDMFD	R13!,{R2-R4}		;Get registers back again

10pane__open	STMIB	R13,{R5-R8}		;Store new coorinates
		LDR	R5,[R3,#28]		;Get behind value to use
		LDR	R6,[R13,#0]		;And the pane handle
		CMP	R6,R5			;Are they the same?
		STRNE	R5,[R13,#28]		;No -- Store it in the block
		STR	R6,[R3,#28]		;New behind for parent window
		MOV	R1,R13			;Point to the block
		SWI	Wimp_OpenWindow		;Open the pane window

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

		LTORG

; --- pane__fakeHandler ---
;
; On entry:	R0 == event type
;		R1 == event block
;
; On exit:	CC
;
; Use:		Although this is called as a fake handler, it does not
;		generate fake event. Instead, it is used to trap
;		open window event before any handler can claim them. This
;		allows the panes to be moved automatically.

pane__fakeHandler ROUT

		TEQ	R0,#2			;Open window request?
		MOVNES	PC,R14			;No -- return PDQ

		STMFD	R13!,{R0,R14}		;Stack some registers
		LDR	R0,[R1,#0]		;Get the window handle
		BL	pane__windowBlock	;Find out if it's registered
		CMP	R0,#0			;Is there a block
		LDMEQFD	R13!,{R0,PC}^		;No -- return

		; --- We are moving a window with panes in it ---

		STMFD	R13!,{R1,R3}		;Stack some more registers
		MOV	R3,R1			;Put window state in R3
		LDR	R1,[R0,#wl__pList]	;Get the first pane
00		BL	pane__open		;Open the given pane

		; --- Keep looking for more panes ---

		LDR	R1,[R1,#pl__next]	;Get the next pane
		CMP	R1,#0			;Is there one?
		BNE	%00pane__fakeHandler	;Yes -- move it then

		LDMFD	R13!,{R1,R3}		;Get back first lot
		LDMFD	R13!,{R0,PC}^		;And return

		LTORG

; --- pane_init ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Initialises the pane unit.

		EXPORT	pane_init
pane_init	ROUT

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

		; --- Set up a fake handler ---

		BL	event_init		;Initialise event
		ADR	R0,pane__fakeHandler	;Call this routine
		MOV	R1,R12			;With this R12 value
		BL	event_fakeHandler	;Add in the handler

		; --- Initalise some workspace ---

		MOV	R0,#0			;A nice NULL word
		STR	R0,ws__wList		;No window list yet

		LDMFD	R13!,{R0,R1,R12,PC}^	;Return to caller

		LTORG

pane__wSpace	DCD	0

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

; --- Window list ---

		^	0

wl__next	#	4			;The next window in list
wl__prev	#	4			;The previous window in list
wl__wHandle	#	4			;The window handle
wl__pList	#	4			;List of panes
wl__size	#	0			;The size of this structure

; --- Pane list ---

		^	0

pl__next	#	4			;The next pane in the list
pl__prev	#	4			;The previous pane in list
pl__wHandle	#	4			;The window handle of pane
pl__iHandle	#	4			;Icon handle in window
pl__size	#	0			;The size of this structure

; --- The workspace ---

		^	0,R12

ws__start	#	0			;Workspace start

ws__wList	#	4			;The window list

ws__size	EQU	{VAR}-ws__start		;Workspace size

		AREA	|Sapphire$$LibData|,CODE,READONLY

		DCD	ws__size		;Workspace size
		DCD	pane__wSpace		;Workspace pointer
		DCD	0			;Scratchpad size
		DCD	pane_init		;Initialisation code

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

		END
