;
; help.s
;
; Sending and handling help messages (MDW)
;
;  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:event
		GET	sapphire:idle
		GET	sapphire:msgs
		GET	sapphire:sapphire
		GET	sapphire:string

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

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- help_init ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Initialises the help system for use.

		EXPORT	help_init
help_init	ROUT

		STMFD	R13!,{R0,R1,R12,R14}	;Save some registers

		; --- Make sure we're not going already ---

		WSPACE	help__wSpace		;Locate my workspace pointer
		LDR	R14,help__flags		;Get my flags word
		TST	R14,#hFlag__inited	;Am I initialised already?
		LDMNEFD	R13!,{R0,R1,R12,PC}^	;Yes - return to caller
		ORR	R14,R14,#hFlag__inited	;I will be initialised soon
		STR	R14,help__flags		;Store the flags word back

		; --- Set up the workspace ---

		MOV	R14,#20			;Start message size as 20
		STR	R14,help__message+0	;Store this in the block
		MOV	R14,#0			;No message to reply to
		STR	R14,help__msgTask	;Store in the your_ref field

		; --- Set up my prefilter ---

		BL	event_init		;Make sure event is awake
		ADR	R0,help__preFilter	;Point to the prefilter
		MOV	R1,R12			;Pass along my workspace
		BL	event_preFilter		;Register it with event
		ADR	R0,help__postFilter	;Point to the postfilter
		BL	event_postFilter	;Register that with event too
		LDMFD	R13!,{R0,R1,R12,PC}^	;Return to caller

		LTORG

help__wSpace	DCD	0

; --- help__preFilter ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Dispatches the currently waiting help reply, if there is
;		one.  Also fakes hint requests and dispatches finished hints
;		to windows.

help__preFilter	ROUT

		STMFD	R13!,{R14}		;Save a register

		; --- Check for hints first ---

		LDR	R14,help__flags		;Get my flags word
		TST	R14,#hFlag__hinted	;Do we have a waiting hint?
		BNE	%50help__preFilter	;Yes -- dispatch it to window
		TST	R14,#hFlag__hinting	;Do we need to make a hint?
		BNE	%60help__preFilter	;Yes -- build the block then

		; --- Otherwise, check for finished help messages ---

		LDR	R14,help__msgTask	;Get the your_ref for this
		CMP	R14,#0			;Is this sensible?
		LDMEQFD	R13!,{PC}^		;No -- return

		STMFD	R13!,{R0-R3}		;Save some more registers
		LDR	R14,help__message+0	;Load the message length
		ADD	R14,R14,#4		;Word align the size as reqd.
		BIC	R14,R14,#3		;Yep, indeedy
		STR	R14,help__message+0	;Store length back again

		ADR	R1,help__message	;Point to the message block
		LDR	R2,help__msgTask	;Load the task handle out
		MOV	R0,#0			;No more messages waiting
		STR	R0,help__msgTask	;So zero the task handle
		MOV	R0,#17			;Don't care if it bounces
		SWI	Wimp_SendMessage	;Send the message out
		LDMFD	R13!,{R0-R3,PC}^	;Return to caller

		; --- Send a finished hint to the hint window ---

50		STMFD	R13!,{R1}		;Save some registers away
		BIC	R14,R14,#hFlag__hinted :OR: hFlag__hinting
		STR	R14,help__flags		;Clear all the hint flags
		LDR	R14,help__window	;Get the hint destination
		STR	R14,[R1,#0]		;Store in the poll block
		ADD	R0,R1,#4		;Point to next spare field
		ADR	R1,help__message+20	;Point to hint string
		BL	str_cpy			;Copy it over
		MOV	R0,#-1			;The magic hint reason code
		LDMFD	R13!,{R1,R14}		;Restore registers
		ORRS	PC,R14,#C_flag		;Return to caller

		; --- Try to get a new hint from the window ---

60		STMFD	R13!,{R1-R5}		;Save a load of registers
		ORR	R14,R14,#hFlag__hinted	;Dispatch hint next time
		STR	R14,help__flags		;Save new flags word

		; --- Build a skeleton hint in case of no reply ---

		MOV	R0,#0			;A null string
		STR	R0,help__message+20	;Store over the string start

		; --- Build a help request in the poll block ---

		MOV	R0,#44			;Size of help request message
		MOV	R2,#-1			;A very bogus task handle
		MOV	R3,#-1			;A similarly bogus my_ref
		MOV	R4,#0			;This is not a reply
		MOV	R5,#&500		;Help request message code
		ORR	R5,R5,#&002		;Finish off message code
		STMIA	R1!,{R0,R2-R5}		;Build message header
		STR	R4,help__msgTask	;Zero destination task handle
		SWI	Wimp_GetPointerInfo	;Get the current pointer pos
		MOV	R0,#18			;Make it look real!
		LDMFD	R13!,{R1-R5,R14}	;Restore registers
		ORRS	PC,R14,#C_flag		;Return to caller

		LTORG

; --- help__postFilter ---
;
; On entry:	R0 == event reason code
;		R1 == pointer to event block
;
; On exit:	--
;
; Use:		Catches pointer-entering and pointer-leaving events and
;		sets up the idle claimer appropriately.

help__postFilter ROUT

		; --- Ensure that we want this event ---

		CMP	R0,#4			;Pointer leaving?
		MOVNES	PC,R14			;No -- return now

		; --- Pointer is leaving one of tasks windows ---

		STMFD	R13!,{R0-R3,R14}	;Stack some registers
		LDR	R14,help__flags		;Get my flags word
		TST	R14,#hFlag__hintable	;Is window hintable?
		LDMEQFD	R13!,{R0-R3,PC}^	;No -- ignore this then
		MOV	R0,#5			;Call it this frequently
		ADR	R1,help__idles		;Call this on idle events
		MOV	R2,#0			;Our user handle
		MOV	R3,R12			;Put our workspace in R12
		BL	idle_removeHandler	;Remove handler

		; --- Pointer has just left the window ---
		;
		; It looks really silly if the window we left still has a
		; hint in it, so we send it a dummy hint with a null string.

		MOV	R0,#0			;A zero byte for the string
		STR	R0,help__message+20	;Store over the string start
		LDR	R14,help__flags		;Get my flags word
		ORR	R14,R14,#hFlag__hinted	;There's a hint waiting
		BIC	R14,R14,#hFlag__hintable;Disable the hints system
		STR	R14,help__flags		;Store flags back again
		LDMFD	R13!,{R0-R3,PC}^	;Return to caller

		LTORG

; --- help_sendHints ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Should be called on a pointer-entering-window event.  It
;		enables hint requests for the window beneath the pointer.

		EXPORT	help_sendHints
help_sendHints	ROUT

		STMFD	R13!,{R0-R3,R12,R14}	;Save some registers
		WSPACE	help__wSpace		;Load my workspace address
		LDR	R14,help__flags		;Load my flags word
		TST	R14,#hFlag__hintable	;Are hints enabled?
		LDMNEFD	R13!,{R0-R3,R12,PC}^	;Yes -- then return now
		ORR	R14,R14,#hFlag__hintable;Set the hints enabled flag
		STR	R14,help__flags		;And save the flags back

		MOV	R0,#5			;Call it this frequently
		ADR	R1,help__idles		;Call this on idle events
		MOV	R2,#0			;Our user handle
		MOV	R3,R12			;Put our workspace in R12
		BL	idle_handler		;Add idle handler
		MOV	R0,#-3			;Set up previous icon hnd
		STR	R0,help__icon		;...to a really weird value
		LDMFD	R13!,{R0-R3,R12,PC}^	;And return to caller

		LTORG

; --- help__idles ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Catches pointer movement between icons, and sets the hint
;		flags accordingly.

help__idles	ROUT

		STMFD	R13!,{R0-R2,R14}	;Save some registers away
		SUB	R13,R13,#24		;Make space for a pointer blk
		MOV	R1,R13			;Point to it
		SWI	Wimp_GetPointerInfo	;Get the current ptr posn
		ADD	R14,R13,#12		;Point to window/icon hnds
		LDMIA	R14,{R0,R2}		;Load them out of the block
		ADD	R13,R13,#24		;Reclaim the stack I used

		; --- Find out if we need to get a new hint ---

		LDR	R1,help__icon		;Get the old icon I was on
		CMP	R1,R2			;Do they match?
		BEQ	%90help__idles		;Yes -- nothing more to do

		CMP	R2,#0			;Is ptr over the background?
		MOVLT	R2,#-3			;Yes -- give it a silly value
		STR	R2,help__icon		;No -- store as new old icon
		STR	R0,help__window		;Save the hint window handle
		LDR	R14,help__flags		;Get my flags word
		ORR	R14,R14,#hFlag__hinting	;Get ready to send a hint rq
		STR	R14,help__flags		;Store the flags away again

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

		LTORG

; --- help_add ---
;
; On entry:	R0 == pointer to message string to add
;
; On exit:	--
;
; Use:		Adds a line to the help message being built currently.  Note
;		that overflows are trapped, and errors are generated if one
;		would occur.

		EXPORT	help_add
help_add	ROUT

		STMFD	R13!,{R0-R4,R12,R14}	;Save some registers
		WSPACE	help__wSpace		;Find my workspace area
		LDR	R14,help__msgTask	;Get the destination task
		CMP	R14,#0			;Is there one set up?
		BNE	%10help_add		;Yes -- add a subsequent line

		; --- We're starting a new help reply ---

		BL	event_last		;Get the last event out
		MOV	R2,#&500		;The message code to match
		ORR	R2,R2,#&002		;Can't load in one op
		CMP	R0,#17			;Make sure it's a message
		CMPNE	R0,#18			;Either one will do
		LDREQ	R14,[R1,#16]		;Get the message code
		CMPEQ	R14,R2			;Does this match up?
		LDMNEFD	R13!,{R0-R4,R12,PC}^	;If not, return right now

		; --- Set up the message block ---

		ADD	R4,R2,#1		;The reply code is one larger
		MOV	R0,#20			;Size of the message block
		LDR	R3,[R1,#8]		;Load his my_ref value
		MOV	R14,#0			;Null terminate the string
		STMIB	R12,{R0-R4,R14}		;Build the message block
		LDR	R14,[R1,#4]		;Get the task handle out
		STR	R14,help__msgTask	;Store the handle away

		; --- Set up for main copy loop ---

		LDR	R0,[R13,#0]		;Get the string pointer back
		MOV	R1,R11			;Find a spare buffer
		BL	msgs_build		;Build the message nicely
		ADR	R1,help__message+20	;Current pointer for string
		MOV	R2,#20			;Current length
		B	%20help_add		;Now skip to main code

		; --- Add in a line separator ---

10help_add	MOV	R1,R11			;Find a spare buffer
		BL	msgs_build		;Build the message nicely

		LDR	R2,help__message+0	;Get the current message size
		ADR	R1,help__message	;Point to the message start
		ADD	R1,R1,R2		;Get current pointer
		CMP	R2,#253			;Make sure it will fit
		BGT	%90			;If it doesn't, make error

		LDR	R14,help__flags		;Load my flags word
		TST	R14,#hFlag__hinted	;Am I building a hint?
		MOVEQ	R14,#'|'		;No -- strings get GSTransed
		MOVNE	R14,#' '		;Yes -- spaces not newlines
		STRB	R14,[R1],#1		;Store the character
		MOVEQ	R14,#'M'		;`|M' is a return character
		STRB	R14,[R1],#1		;Store the character
		ADD	R2,R2,#2		;We've added two characters

		; --- Now copy the string over, trapping overflows ---

20help_add	CMP	R2,#256			;Do we have room for another?
		BGE	%90			;No -- moan bitterly
		LDRB	R14,[R0],#1		;Get an input character
		CMP	R14,#' '		;Is this a control character?
		MOVLT	R14,#0			;Yes -- store a zero nicely
		STRB	R14,[R1],#1		;Store it in my message
		ADD	R2,R2,#1		;Bump the character count
		BGE	%20help_add		;Loop round for more

		; --- Set things up for next time ---

		SUB	R2,R2,#1		;Overwrite the last null byte
		STR	R2,help__message+0	;Store the message length
		LDMFD	R13!,{R0-R4,R12,PC}^	;Return to caller

		; --- We overflowed -- complain ---

90help_add	MOV	R0,#0			;Zero the task handle...
		STR	R0,help__msgTask	;...to stop it being sent
		ADR	R0,help__overflow	;Point to the message
		BL	msgs_error		;Translate it as normal
		SWI	OS_GenerateError	;And generate the error

help__overflow	DCD	1
		DCB	"helpOFLOW",0

		LTORG

; --- help_reset ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Resets the help system so that a hint request is sent to an
;		icon that the pointer is already over. The proposed use
;		is that the caller can change a help message for a given
;		icon as soon as it is clicked on.

		EXPORT	help_reset
help_reset	ROUT

		STMFD	R13!,{R12,R14}		;Stack some registers
		WSPACE	help__wSpace		;Load my workspace address
		MOV	R14,#-3			;Set up previous icon hnd
		STR	R14,help__icon		;...to a really weird value
		LDMFD	R13!,{R12,PC}^		;Return to caller

		LTORG

;----- Workspace ------------------------------------------------------------

		^	0,R12
help__wStart	#	0

help__flags	#	4			;Various flags
help__message	#	256			;The actual message to send
help__msgTask	#	4			;Task to send message to
help__msgNext	#	4			;Pointer to message tail
help__window	#	4			;The window the ptr is over
help__icon	#	4			;The icon the ptr is over

help__wSize	EQU	{VAR}-help__wStart

hFlag__inited	EQU	(1<<0)			;Am I initialised?
hFlag__hintable	EQU	(1<<1)			;Current window wants hints
hFlag__hinting	EQU	(1<<2)			;We're gathering up a hint
hFlag__hinted	EQU	(1<<3)			;Is a hint waiting?

		AREA	|Sapphire$$LibData|,CODE,READONLY

		DCD	help__wSize
		DCD	help__wSpace
		DCD	0
		DCD	help_init

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

		END
