;
; dbx.numWrite.s
;
; Numeric writable icons (MDW)
;
;  1994 Straylight
;

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

		GET	libs:header
		GET	libs:swis

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

		GET	sapphire:dbox
		GET	sapphire:keyMap
		GET	sapphire:string

		GET	sapphire:dbx.dbx
		GET	sapphire:dbx._dbxMacs

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

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- numWrite ---
;
; Control data:	+0 == minimum value
;		+4 == maximum value
;		+8
;
; Workspace:	+0
;
; Flags:	--
;
; Use:		Control type for numeric writable icons.

		EXPORT	numWrite
numWrite	ROUT

		DBXWS	0			;No need for workspace
		DCD	dbxMask_key		;Interested in keypresses

		STMFD	R13!,{R0-R7,R14}	;Save lots of registers
		MOV	R6,R2			;Keep the new character safe

		MOV	R0,R10			;And the dialogue box handle
		BL	dbox_getField		;Read the current contents
		MOV	R7,R2			;Keep this pointer

		; --- Find out where the caret is ---

		SUB	R13,R13,#24		;Make space for caret block
		MOV	R1,R13			;Point at this block
		SWI	Wimp_GetCaretPosition	;Find where the caret is
		LDR	R5,[R13,#20]		;Load the icon index

		; --- Find out if this keypress is interesting ---

		CMP	R5,#0			;Is the cursor at the start?
		LDREQB	R14,[R7,#0]		;Yes -- get the first byte
		CMPEQ	R14,#'-'		;Is it a minus sign?
		BEQ	%50numWrite		;Yes -- disallow it then

		SUB	R14,R6,#'0'		;Convert keypress to digit
		CMP	R14,#10			;Is it a proper digit?
		BCC	%10numWrite		;Yes -- deal with it then
		SUB	R14,R6,#key_k0		;Try again with keypad
		CMP	R14,#10			;Is it a proper digit?
		SUBCC	R6,R6,#key_k0-'0'	;Yes -- convert to ASCII
		BCC	%10numWrite		;Yes -- deal with it then
		CMP	R6,#'-'			;Is is a `-' sign?
		CMPNE	R6,#key_kMinus		;Or the one on the keypad?
		MOVEQ	R6,#'-'			;Either -- make it a `-'
		BNE	%50numWrite		;No -- ignore it then
		LDR	R3,[R9,#0]		;Load the minimum value
		CMP	R3,#0			;Does it allow negative nos?
		BGE	%50numWrite		;No -- ignore it then
		CMP	R5,#0			;Is the cursor at the start?
		BNE	%50numWrite		;No -- ignore it then

		; --- We've got a character to insert ---

10numWrite	MOV	R2,R7			;Point at the icon's text
		MOV	R7,#0			;An initial index
20numWrite	CMP	R7,R5			;Is this the caret position?
		MOVEQ	R14,R6			;Yes -- insert my char
		LDRNEB	R14,[R2],#1		;Otherwise load from icon
		STRB	R14,[R11,R7]		;Save in position
		CMP	R14,#32			;Is there more to go?
		ADDGE	R7,R7,#1		;Increment the index
		BGE	%20numWrite		;Yes -- go round again
		SUB	R7,R7,#1		;This is the original length

		; --- Fill the icon in nicely ---

		MOV	R2,R11			;Point to my nice new string
		BL	numWrite__read		;Convert it to an integer
		MOV	R6,R2			;Take a copy of this value
		LDR	R1,[R13,#28]		;Load the icon number
		LDMIA	R9,{R3,R4}		;Load the boundary values
		SUB	R4,R4,R3		;Get the range size
		SUB	R14,R2,R3		;Subtract the bottom end
		CMP	R14,R4			;Is the value in range?
		MOVLS	R0,R10			;Yes -- get dialogue handle
		MOVLS	R2,R11			;Point at the string I made
		BLLS	dbox_setField		;And just write the string
		BLHI	numWrite__set		;Otherwise set the value
		MOVHI	R6,R2			;If changed, copy value
		BL	dbx_sendEvent		;Send the user an event

		; --- Move the caret on ---

		MOV	R0,R10			;Get the dialogue handle
		BL	dbox_getField		;Find the text address
		MOV	R0,R2			;Point to the buffer
		BL	str_len			;Find the string length
		SUBS	R14,R0,R7		;Find the length difference
		BEQ	%40numWrite		;No movement -- forget it
		ADD	R5,R5,R14		;Move caret position on 1
		CMP	R5,R0			;Is the caret in range?
		LDMLSIA	R13,{R0-R4}		;Load rest of caret state
		SWILS	Wimp_SetCaretPosition	;Set this as the new pos

		; --- Pass an event to the user ---

		MOV	R0,#numWrite_event	;Get the event number
		MOV	R2,#numWrite_change	;The value has been changed
		MOV	R3,R6			;Pass the value in R3
		BL	dbx_sendEvent		;Pass the event on

40numWrite	ADD	R13,R13,#24		;Restore the stack now
		LDMFD	R13!,{R0-R7,R14}	;Unstack the registers
		ORRS	PC,R14,#C_flag		;I used the character

		; --- Handle a weird character ---

50numWrite	ADD	R13,R13,#24		;Restore the stack

		; --- Make sure it's an arrow key ---

		TST	R6,#&100		;Is the top bit set?
		BEQ	%90numWrite		;No -- just return then
		BIC	R6,R6,#&130		;Ignore modifier bits
		CMP	R6,#key_Tab-&100	;Is it tab?
		CMPNE	R6,#key_sTab-&100	;Or is it shift-tab?
		CMPNE	R6,#key_Up-&100		;Or any of the cursor keys?
		CMPNE	R6,#key_Down-&100
		CMPNE	R6,#key_cUp-&100	;Or maybe with control
		CMPNE	R6,#key_cDown-&100
		BNE	%90numWrite		;No -- just return then

		LDR	R1,[R13,#4]		;Load the icon number
		MOV	R0,R10			;Get my dialogue handle
		BL	numWrite_read		;Read the current value
		BL	numWrite__set		;Set this as the new value
		MOV	R0,#numWrite_event	;Get my event code
		MOV	R3,R2			;Pass value in R3
		MOV	R2,#numWrite_move	;The cursor has moved
		BL	dbx_sendEvent		;Send the user an event

90numWrite	LDMFD	R13!,{R0-R7,R14}	;Unstack the registers
		BICS	PC,R14,#C_flag		;Let someone else have it

		LTORG

; --- numWrite_set ---
;
; On entry:	R0 == dialogue box handle
;		R1 == icon number within dialogue
;		R2 == value to set in the icon
;
; On exit:	R2 == value actually set
;
; Use:		Writes the specified numeric value into the given writable
;		icon.  The icon must be a dbx control with numWrite type
;		for this to work.

		EXPORT	numWrite_set
numWrite_set	ROUT

		STMFD	R13!,{R0,R1,R8-R10,R14}	;Save some registers
		MOV	R10,R0			;Keep the dialogue handle
		BL	dbx_findData		;Look up all the dbx info
		BL	numWrite__set		;Do the actual work
		LDMFD	R13!,{R0,R1,R8-R10,PC}^	;Return to caller

		LTORG

; --- numWrite__set ---
;
; On entry:	R1 == icon handle to set it in
;		R2 == value to set
;		R9 == pointer to minimum and maximum values
;		R10 == dialogue box handle
;
; On exit:	R2 == actual value set
;
; Use:		Forces the given value between the limits for the icon
;		and writes it into the specified icon.

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

		; --- Force the value in range ---

		LDMIA	R9,{R0,R1}		;Load min and max values
		CMP	R2,R0			;Is it big enough?
		MOVLT	R2,R0			;No -- make it bigger then
		CMP	R2,R1			;Is it small enough?
		MOVGT	R2,R1			;No -- make it smaller then
		STR	R2,[R13,#8]		;Return this modified value

		; --- Translate it into a string ---

		MOV	R0,R2			;Get value in R0 now
		MOV	R1,R11			;Build string in scratchpad
		MOV	R2,#256			;Specify the scratchpad size
		SWI	OS_ConvertInteger4	;Convert it to a string

		; --- Write it into the icon ---

		MOV	R2,R0			;Point at my nice new string
		LDR	R1,[R13,#4]		;Load the icon handle back
		MOV	R0,R10			;Get the dialogue handle
		BL	dbox_setField		;And write it into the icon
		LDMFD	R13!,{R0-R2,PC}^	;Return to caller

		LTORG

; --- numWrite_read ---
;
; On entry:	R0 == dialogue box handle
;		R1 == icon handle
;
; On exit:	CC if icon contains a valid integer, and
;		  R2 == value shown in the icon
;		else CS and
;		  R2 == 0
;
; Use:		Reads the numeric value within the icon specifed.

		EXPORT	numWrite_read
numWrite_read	ROUT

		STMFD	R13!,{R14}		;Save the link register
		BL	dbox_getField		;Find the icon string
		BL	numWrite__read		;Convert it to an integer
		LDMFD	R13!,{PC}		;Return it and the flags

		LTORG

; --- numWrite__read ---
;
; On entry:	R2 == pointer to a string
;
; On exit:	R2 == integer represented by string and CC or 0 and CS
;
; Use:		Converts a string showing a decimal integer to the value
;		it represents.

numWrite__read	ROUT

		STMFD	R13!,{R0,R1,R14}

		; --- Set up some initial values ---

		MOV	R0,R2			;Point to the string start
		MOV	R1,#0			;Not found any digits yet
		MOV	R2,#0			;A good accumulator

		; --- Check for a leading sign ---

		LDRB	R14,[R0],#1		;Load a byte from the string
		CMP	R14,#'-'		;Is it a `-' sign?
		ORREQ	R1,R1,#1		;Set the `negative' bit
		LDREQB	R14,[R0],#1		;Yes -- load another byte

		; --- Now read bytes from the string ---
		;
		; We stop as soon as we find a character which isn't a
		; digit.

10		SUB	R14,R14,#'0'		;Convert to an integer
		CMP	R14,#10			;Is it in range?
		BCS	%20numWrite__read	;No -- that's it then
		TST	R1,#1			;Is the number negative?
		ADD	R2,R2,R2,LSL #2		;Multiply by 5
		ADDEQ	R2,R14,R2,LSL #1	;If +ve, double and add new
		RSBNE	R2,R14,R2,LSL #1	;Otherwise, subtract it
		ORR	R1,R1,#2		;Say we read something good
		LDRB	R14,[R0],#1		;Load a new byte
		B	%10numWrite__read	;Go round the loop again

		; --- Return the value ---

20		TST	R1,#2			;Was the number valid?
		LDMFD	R13!,{R0,R1,R14}	;Restore the registers
		ORREQS	PC,R14,#C_flag		;No -- set C as error warning
		BICNES	PC,R14,#C_flag		;Otherwise clear it

		LTORG

; --- numWrite_bump ---
;
; On entry:	R0 == dialogue box handle
;		R1 == icon handle
;		R2 == increment to apply to it
;
; On exit:	R2 == updated value in the icon
;
; Use:		Adjusts the value in a writable icon by a given increment.

		EXPORT	numWrite_bump
numWrite_bump	ROUT

		STMFD	R13!,{R0,R1,R3,R14}	;Save some registers
		MOV	R3,R2			;Look after the increment
		BL	numWrite_read		;Read the current value
		ADDCC	R2,R2,R3		;If valid, apply increment
		BL	numWrite_set		;And write the value back
		LDMFD	R13!,{R0,R1,R3,PC}^	;Return to caller

		LTORG

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

numWrite_event	EQU	&80000004		;R1 == icon number changed
						;R2 == subreason code
						;R3 == new value of icon

numWrite_change	EQU	0			;Subreason -- number changed
numWrite_move	EQU	1			;Subreason -- cursor moved

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

		END
