;
; colourBox.s
;
; Gives you a nice box from which the user may choose a WIMP colour
;
;  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

		GET	libs:stream

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

		GET	sapphire:dbox
		GET	sapphire:msgs
		GET	sapphire:sapphire

		GET	sapphire:keyMap

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

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- colourBox ---
;
; On entry:	R0 == pointer to a title string (message tag)
;		R1 == the current colour in bottom byte, and flags:
;			bit 8 == allow transparent colour
;		R2 == event handler to call
;		R3 == R10 value to pass to handler
;		R4 == R12 value to pass to handle
;
; On exit:	May return an error
;
; Use:		Opens a dialogue box which allows the user to choose
;		one of the wimp colours.  A transparent colour is supported,
;		and represented as colour 255.

		EXPORT	colourBox
colourBox	ROUT

		STMFD	R13!,{R0-R5,R12,R14}
		WSPACE	cb__wSpace

		; --- Set up some workspace ---

		STMIB	R12,{R2-R4}		;Save useful information
		STRB	R1,cb__defColour	;Store default colour

		; --- Create my dialogue box ---

		ADR	R0,cb__name		;Point to the dbox name
		BL	dbox_create		;Try to create it
		BVS	%90colourBox		;Return possible error
		MOV	R4,R0			;Look after dialogue handle

		; --- Set up current and old colours ---

		MOV	R2,#-1			;No old icon yet
		BL	cb__borderIcon		;Draw the border

		STRB	R1,cb__currentCol	;Currently selected colour
		AND	R14,R1,#&FF		;Get the actual colour
		CMP	R14,#&FF		;Is colour transparent?
		MOVEQ	R14,#7			;Yes -- use black then
		STRB	R14,cb__oldCol		;Make this last colour

		; --- If necessary, delete the transparent icon ---

		TST	R1,#256			;Is the transparent bit set?
		BNE	%10colourBox		;Yes -- leave it then
		BL	dbox_window		;Find dialogue's window
		MOV	R1,#cbIcon__trans	;Get transparent icon handle
		MOV	R2,#1<<23		;Set the deleted flag
		MOV	R3,#1<<23
		STMFD	R13!,{R0-R3}		;Save that on the stack
		MOV	R1,R13			;Point to the block
		SWI	Wimp_SetIconState	;And set the icons up
		ADD	R13,R13,#16		;Restore the stack

		; --- Set up the dialogue box now ---

10colourBox	MOV	R0,R4			;Fetch dialogue handle
		ADR	R1,cb__eventHandler	;Point to the event handler
		MOV	R2,#0			;No R10 required
		MOV	R3,R12			;Pass workspace in R12
		BL	dbox_eventHandler	;Set up the event handler

		; --- Fill in the dialogue title ---

		LDR	R0,[R13,#0]		;Load title pointer
		BL	msgs_lookup		;Translate the tag
		MOV	R2,R0			;Put this in R2
		MOV	R0,R4			;Get handle back
		MOV	R1,#-1			;Change the title
		BL	dbox_setField		;Set the title

		; --- Display the dialogue ---

		MOV	R1,#dbOpen_pointer	;Open over the pointer
		BL	dbox_open		;Open the dbox then

		; --- Move the caret out of the icon ---

		BL	dbox_window		;Get the window handle
		MOV	R1,#-1			;Move out of any icons
		MOV	R2,#&FFFFFF		;Move well away from visible
		MOV	R3,#&FFFFFF		;area so no-one will see
		MOV	R4,#16			;Make the caret fairly small
		MOV	R5,#-1			;No sensible string index
		SWI	Wimp_SetCaretPosition	;Set the caret position

		; --- That's it, then ---

		LDMFD	R13!,{R0-R5,R12,R14}	;Load back registers
		BICS	PC,R14,#V_flag		;Return without error

90colourBox	ADD	R13,R13,#4		;Skip over R0
		LDMFD	R13!,{R1-R5,R12,R14}	;Load back registers
		ORRS	PC,R14,#V_flag		;Return with error

cb__name	DCB	"colourBox",0

		LTORG

; --- cb__borderIcon ---
;
; On entry:	R0 == dbox handle
;		R1 == colour to `border'
;		R2 == old colour (-1 for none)
;
; On exit:	--
;
; Use:		Removes the border from the old icon, and puts one on the
;		new icon.

cb__borderIcon	ROUT

		CMP	R1,R2			;Are colours the same?
		MOVEQS	PC,R14			;Yes -- return then

		; --- Sort out what to do now ---

		STMFD	R13!,{R0-R5,R14}	;Save some registers
		SUB	R13,R13,#80		;Make a big block
		AND	R5,R1,#&FF		;Clear extraneous flag bits
		MOV	R4,R0			;Look after dialogue handle
		CMP	R2,#-1			;Old colour to clear?
		BEQ	%10cb__borderIcon	;No -- don't bother then

		; --- Clear border from old colour ---

		CMP	R2,#255			;Is colour transparent?
		BEQ	%05cb__borderIcon	;Yes -- handle that then

		BL	dbox_window		;Get window handle of dbox
		ADD	R1,R2,#cbIcon__colours	;Find icon handle of colour
		MOV	R2,#0			;Don't set any bits
		MOV	R3,#5			;Clear border and text bits
		STMIA	R13,{R0-R3}		;Save them in my block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_SetIconState	;And change the icon
		B	%10cb__borderIcon	;Now border the new colour

		; --- Turn off the `transparent' switch ---

05		MOV	R1,#cbIcon__trans	;Get the icon handle
		MOV	R2,#0			;Turn the icon off
		BL	dbox_select		;And deselect the icon

		; --- Now turn on the new colour ---

10		CMP	R5,#&FF			;Is new colour transparent?
		BEQ	%15cb__borderIcon	;Yes -- handle that then

		; --- Sort out a contrasting colour ---
		;
		; This is actually quite hard.  RISC_OSLib uses a hack
		; to do this job.  I use ColourTrans instead, because it's
		; easier.  The algorithm is simple: take the original
		; colour, and decide which, out of black and white, is
		; furthest away from it.  Then try and find the closest
		; match to that in the Wimp palette.

		MOV	R1,R13			;Point to my big block
		SWI	Wimp_ReadPalette	;Read the Wimp's palette
		LDR	R0,[R13,R5,LSL #2]	;Load the palette entry
		MOV	R1,#0			;Choose a two-colour mode
		ADR	R2,cb__bwPal		;Point to black'n'white pal
		SWI	ColourTrans_ReturnOppColourNumberForMode ;!!!
		LDR	R0,[R2,R0,LSL #2]	;Load `black' or `white'
		MOV	R1,#12			;Choose a 16 colour mode
		MOV	R2,R13			;And point at Wimp palette
		SWI	ColourTrans_ReturnColourNumberForMode ;!!

		; --- Now set the icon border ---

		MOV	R2,R0,LSL #24		;Set this as foreground col
		MOV	R0,R4			;Get the dialogue handle
		BL	dbox_window		;Get a window handle from it
		ADD	R1,R5,#cbIcon__colours	;Find the icon handle
		ORR	R2,R2,#5		;Set the border and text bits
		MOV	R3,#5			;Change border and text
		ORR	R3,R3,#&0F000000	;Also change foreground col
		STMIA	R13,{R0-R3}		;Save them in my block
		MOV	R1,R13			;Point to the block
		SWI	Wimp_SetIconState	;And change the icon
		B	%50cb__borderIcon	;Tidy up and go home

cb__bwPal	DCD	&00000000
		DCD	&FFFFFF00

		; --- Turn on the `transparent' switch ---
		;
		; `Hey, transparent switch, I really dig you.'

15		MOV	R0,R4			;Get the dialogue handle
		MOV	R1,#cbIcon__trans	;Get transparent icon handle
		MOV	R2,#1			;Turn the icon on
		BL	dbox_select		;Select this icon

		; --- Tidy up and return ---

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

		LTORG

; --- cb__eventHandler ---
;
; On entry:	R0 == dbox event
;		R1-R8 depend on event code
;		R9 == dbox handle
;
; On exit:	--
;
; Use:		Handles events onthe colour box.

cb__eventHandler ROUT

		CMP	R0,#dbEvent_close	;Has the dbox been closed?
		BEQ	%10cb__eventHandler	;Yes -- destroy it then
		CMP	R0,#dbEvent_cancel	;Was cancel pressed?
		CMPNE	R0,#cbIcon__cancel
		BEQ	%20cb__eventHandler	;Yes -- do the right thing
		CMP	R0,#dbEvent_OK		;How about OK?
		CMPNE	R0,#cbIcon__ok		;Well -- it had to happen
		BEQ	%30cb__eventHandler	;Yes -- do the right thing
		CMP	R0,#cbIcon__trans	;Click on transparent switch?
		BEQ	%40cb__eventHandler	;Yes -- do the right thing
		CMP	R0,#dbEvent_key		;Was a key pressed?
		BEQ	%50cb__eventHandler	;Yes -- deal with it then

		; --- Must be a click on a colour ---

		STMFD	R13!,{R0-R2,R14}	;Stack registers
		SUB	R1,R0,#cbIcon__colours	;Turn into a colour
		CMP	R1,#16			;Is it in range?
		LDMCSFD	R13!,{R0-R2,PC}^	;No -- then ignore it
		LDRB	R2,cb__currentCol	;Load the old colour
		MOV	R0,R9			;Get the handle
		BL	cb__borderIcon		;Select it
		STRB	R1,cb__currentCol	;Store back the new colour
		STRB	R1,cb__oldCol		;Remember this
		LDMFD	R13!,{R0-R2,PC}^	;Return to caller

		; --- Dialogue box has been closed ---

10		STMFD	R13!,{R0,R14}		;Stack registers
		MOV	R0,R9			;Put handle in R0
		BL	dbox_destroy		;Destroy the dbox
		LDMFD	R13!,{R0,PC}^		;Return to caller

		; --- User clicked the cancel button ---

20		STMFD	R13!,{R0-R3,R14}	;Stack registers
		MOV	R0,R9			;Get the dbox handle
		MOV	R3,R1			;Remember the button status
		MOV	R1,#cbIcon__cancel	;The cancel button
		BL	dbox_slab		;Slab in the icon
		TST	R3,#1			;Was Adjust clicked?
		BEQ	%25cb__eventHandler	;No -- jump ahead

		LDRB	R2,cb__currentCol	;Load the old colour
		LDRB	R1,cb__defColour	;And the default colour
		BL	cb__borderIcon		;Select it
		STRB	R1,cb__currentCol	;Store back the new colour
		CMP	R1,#255			;Is the colour transparent?
		MOVEQ	R1,#7			;Yes -- use black instead
		STRB	R1,cb__oldCol		;Save old colour

25		TST	R3,#1			;Was Adjust clicked?
		BLEQ	dbox_close		;No -- close the dbox
		BL	dbox_unslab		;Unslab the icon
		BLEQ	dbox_destroy		;No -- destroy the dbox
		LDMFD	R13!,{R0-R3,PC}		;Return to caller

		; --- User clicked OK ---

30		STMFD	R13!,{R0-R3,R14}	;Stack registers
		MOV	R0,R9			;Get the dbox handle
		MOV	R3,R1			;Remember the button status
		MOV	R1,#cbIcon__ok		;The OK button
		BL	dbox_slab		;Slab in the icon

		LDRB	R2,cb__currentCol	;Load the current colour
		STRB	R2,cb__defColour	;Store this as default colour
		MOV	R1,R2			;Put the colour in R1
		MOV	R0,#cbEvent_select	;The event type
		BL	cb__dispatch		;Dispatch the event

		MOV	R0,R9			;Get the handle back again
		TST	R3,#1			;Was `Adjust' clicked?
		BLEQ	dbox_close		;No -- close the dbox
		BL	dbox_unslab		;Unslab the icon
		BLEQ	dbox_destroy		;No -- destroy the dbox
		LDMFD	R13!,{R0-R3,PC}^	;Return to caller

		; --- User clicked on transparent ---

40		STMFD	R13!,{R0-R2,R14}	;Save some registers
		MOV	R0,R9			;Fetch dialogue handle
		MOV	R1,#cbIcon__trans	;Get transparent icon handle
		BL	dbox_isSelected		;Is this icon selected?
		MOVCC	R1,#255			;No -- turn it on
		LDRCCB	R2,cb__currentCol	;And get the old colour
		LDRCSB	R1,cb__oldCol		;Otherwise get previous col
		MOVCS	R2,#255			;And say current is trans
		BL	cb__borderIcon		;Border the icon nicely
		STRB	R1,cb__currentCol	;And update current colour
		LDMFD	R13!,{R0-R2,PC}^	;Return to caller

		; --- User pressed a key ---

50		CMP	R1,#&100		;Is the code in top half?
		MOVCCS	PC,R14			;No -- ignore the code

		STMFD	R13!,{R0-R2,R14}	;Save some registers
		LDRB	R2,cb__currentCol	;What's the current colour?
		CMP	R2,#&FF			;Is it transparent?
		LDMEQFD	R13!,{R0-R2,PC}^	;And return to caller

		AND	R1,R1,#&FF		;Keep only the bottom byte
		MOV	R14,#0			;Clear the mask value

		CMP	R1,#key_Left :AND: &FF	;Is this a left cursor?
		MOVEQ	R14,#2_1100		;Yes -- check top bits
		MOVEQ	R0,#-1			;And subtract one
		CMP	R1,#key_Right :AND: &FF	;Is this a right cursor?
		MOVEQ	R14,#2_1100		;Yes -- check top bits
		MOVEQ	R0,#1			;And add one
		CMP	R1,#key_Up :AND: &FF	;Is this an up cursor?
		MVNEQ	R14,#2_1100		;Yes -- check bottom bits
		MOVEQ	R0,#-4			;And subtract four
		CMP	R1,#key_Down :AND: &FF	;Is this a down cursor?
		MVNEQ	R14,#2_1100		;Yes -- check bottom bits
		MOVEQ	R0,#4			;And add four

		CMP	R14,#0			;Is R14 now set?
		LDMEQFD	R13!,{R0-R2,PC}^	;No -- then leave now

		ADD	R1,R2,R0		;Add the correct value
		EOR	R0,R1,R2		;Check which bits changed
		TST	R0,R14			;Did something wrong happen?
		MOVEQ	R0,R9			;Fetch dialogue handle
		BLEQ	cb__borderIcon		;Border the correct icon
		STREQB	R1,cb__currentCol	;And store the new colour
		STREQB	R1,cb__oldCol		;And as the last non-trans
		LDMFD	R13!,{R0-R2,R14}	;Restore registers
		ORRS	PC,R14,#C_flag		;Claim the keypress

		LTORG

; --- cb__dispatch ---
;
; On entry:	R0-R8 to be sent to event handler
;
; On exit:	--
;
; Use:		Sends an event the owner of the colour box.

cb__dispatch	ROUT

		STMFD	R13!,{R9,R10,R12,R14}	;Stak registers
		LDMIB	R12,{R9,R10,R12}	;Load data things
		CMP	R9,#0			;Sanity check
		MOV	R14,PC			;Set up return address
		MOVNE	PC,R9			;Call the handler
		LDMFD	R13!,{R9,R10,R12,PC}^	;Return to caller

		LTORG

cb__wSpace	DCD	0

;----- Events ---------------------------------------------------------------

		^	0
cbEvent_select	#	1			;User selected a colour
						;R1 == colour selected

;----- Icon numbers ---------------------------------------------------------

cbIcon__ok	EQU	0			;The OK button
cbIcon__cancel	EQU	1			;The Cancel button
cbIcon__trans	EQU	2			;The Transparent switch
cbIcon__colours	EQU	4			;Base of colour patches

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

		^	0,R12
cb__wStart	#	0

cb__defColour	#	1			;The default colour
cb__currentCol	#	1			;Currently selected colour
cb__oldCol	#	1			;Last non-transparent colour
cb__handler	#	12			;User handler information

cb__wSize	EQU	{VAR}-cb__wStart

		AREA	|Sapphire$$LibData|,CODE,READONLY

		DCD	cb__wSize
		DCD	cb__wSpace
		DCD	0
		DCD	0

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

		END
