implementation module StdIOCommon


import	StdArray, StdBool, StdFunc, StdInt, StdList, StdOverloaded, StdString
import	id, StdBitmap, StdIOBasic, StdKey, StdMaybe
from commondef import stateMap2


/*	The SelectState type.								*/

::	SelectState
	=	Able | Unable

instance == SelectState where
	(==) Able	select = enabled select
	(==) Unable	select = not (enabled select)
instance ~ SelectState where
	~ Able	= Unable
	~ Unable	= Able
instance toString SelectState where
	toString Able   = "Able"
	toString Unable = "Unable"

enabled :: !SelectState -> Bool
enabled Able	= True
enabled _		= False


/*	The MarkState type.									*/

::	MarkState
	=	Mark | NoMark

instance == MarkState where
	(==) Mark	mark = marked mark
	(==) NoMark	mark = not (marked mark)
instance ~ MarkState where
	~ Mark	= NoMark
	~ _		= Mark
instance toString MarkState where
	toString Mark   = "Mark"
	toString NoMark = "NoMark"

marked :: !MarkState -> Bool
marked Mark   	= True
marked _		= False


/*	The KeyboardState type.								*/

::	KeyboardState
	=	CharKey		Char		KeyState			// ASCII character input
	|	SpecialKey	SpecialKey	KeyState Modifiers	// Special key input
	|	KeyLost										// Key input lost while key was down
::	KeyState
	=	KeyDown		IsRepeatKey						// Key is down
	|	KeyUp										// Key goes up
::	IsRepeatKey										// Flag on key down:
	:==	Bool										// True iff key is repeating
::	Key
	=	IsCharKey	 Char
	|	IsSpecialKey SpecialKey
::	KeyboardStateFilter								// Predicate on KeyboardState:
	:==	KeyboardState -> Bool						// evaluate KeyFunction only if predicate holds and SelectState is Able

getKeyboardStateKeyState :: !KeyboardState -> KeyState
getKeyboardStateKeyState (CharKey    _ kstate  ) = kstate
getKeyboardStateKeyState (SpecialKey _ kstate _) = kstate
getKeyboardStateKeyState KeyLost                 = KeyUp

getKeyboardStateKey :: !KeyboardState -> Maybe Key
getKeyboardStateKey (CharKey    char  _) = Just (IsCharKey   char)
getKeyboardStateKey (SpecialKey key _ _) = Just (IsSpecialKey key)
getKeyboardStateKey KeyLost              = Nothing

instance == KeyboardState where
	(==) (CharKey char key) keySt			= case keySt of
												(CharKey char` key`)			-> char==char` && key==key`
												_								-> False
	(==) (SpecialKey spec key mods) keySt	= case keySt of
												(SpecialKey spec` key` mods`)	-> spec==spec` && key==key` && mods==mods`
												_								-> False
	(==) KeyLost					keySt	= case keySt of
												KeyLost							-> True
												_								-> False
instance == KeyState where
	(==) KeyUp				key	= case key of
									KeyUp				-> True
									_					-> False
	(==) (KeyDown repeat)	key	= case key of
									(KeyDown repeat`)	-> repeat==repeat`
									_					-> False
instance toString KeyboardState where
	toString (CharKey char keystate)
		= brackify ("CharKey "+++toString char+++" "+++brackify ("ASCII: "+++toString (toInt char))+++" "+++toString keystate)
	toString (SpecialKey special keystate modifiers)
		= brackify ("SpecialKey "+++itemsList " " [toString special,toString keystate,toString modifiers])
	toString KeyLost
		= "KeyLost"
instance toString KeyState where
	toString (KeyDown isRepeat)	= brackify ("KeyDown "+++fromBool isRepeat)
	toString KeyUp				= "KeyUp"


/*	The MouseState type.								*/

::	MouseState
	=	MouseMove	Point2 Modifiers			// Mouse is up		(position & modifiers)
	|	MouseDown	Point2 Modifiers Int		// Mouse goes down	(position & modifiers & nr double down)
	|	MouseDrag	Point2 Modifiers			// Mouse is down	(position & modifiers)
	|	MouseUp		Point2 Modifiers			// Mouse goes up	(position & modifiers)
	|	MouseLost								// Mouse input lost while mouse was down
::	ButtonState
 	=	ButtonStillUp							// MouseMove
 	|	ButtonDown								// MouseDown 1
	|	ButtonDoubleDown						//			 2
	|	ButtonTripleDown						//           >2
	|	ButtonStillDown							// MouseDrag
 	|	ButtonUp								// MouseUp
::	MouseStateFilter							// Predicate on MouseState:
	:==	MouseState -> Bool						// Evaluate MouseFunction only if predicate holds and SelectState is Able

getMouseStatePos :: !MouseState -> Point2
getMouseStatePos (MouseMove pos _)			= pos
getMouseStatePos (MouseDown pos _ _)		= pos
getMouseStatePos (MouseDrag pos _)			= pos
getMouseStatePos (MouseUp   pos _)			= pos
getMouseStatePos MouseLost					= zero

getMouseStateModifiers :: !MouseState -> Modifiers
getMouseStateModifiers (MouseMove _ mods)	= mods
getMouseStateModifiers (MouseDown _ mods _)	= mods
getMouseStateModifiers (MouseDrag _ mods)	= mods
getMouseStateModifiers (MouseUp   _ mods)	= mods
getMouseStateModifiers MouseLost			= NoModifiers

getMouseStateButtonState:: !MouseState	-> ButtonState
getMouseStateButtonState (MouseMove _ _)	= ButtonStillUp
getMouseStateButtonState (MouseDown _ _ nr)	= if (nr==1) ButtonDown 
											 (if (nr==2) ButtonDoubleDown
											 			 ButtonTripleDown
											 )
getMouseStateButtonState (MouseDrag _ _)	= ButtonStillDown
getMouseStateButtonState (MouseUp   _ _)	= ButtonUp
getMouseStateButtonState MouseLost			= ButtonUp

instance == MouseState where
	(==) (MouseMove pos mods)	 mouseSt	= case mouseSt of
												(MouseMove pos` mods`)		-> pos==pos` && mods==mods`
												_							-> False
	(==) (MouseDown pos mods nr) mouseSt	= case mouseSt of
												(MouseDown pos` mods` nr`)	-> pos==pos` && mods==mods` && nr==nr`
												_							-> False
	(==) (MouseDrag pos mods)	 mouseSt	= case mouseSt of
												(MouseDrag pos` mods`)		-> pos==pos` && mods==mods`
												_							-> False
	(==) (MouseUp   pos mods)	 mouseSt	= case mouseSt of
												(MouseUp pos` mods`)		-> pos==pos` && mods==mods`
												_							-> False
	(==) MouseLost				 mouseSt	= case mouseSt of
												MouseLost					-> True
												_							-> False
instance == ButtonState where
	(==) ButtonStillUp		button	= case button of
										ButtonStillUp		-> True
										_					-> False
	(==) ButtonDown			button	= case button of
										ButtonDown			-> True
										_					-> False
	(==) ButtonDoubleDown	button	= case button of
										ButtonDoubleDown	-> True
										_					-> False
	(==) ButtonTripleDown	button	= case button of
										ButtonTripleDown	-> True
										_					-> False
	(==) ButtonStillDown	button	= case button of
										ButtonStillDown		-> True
										_					-> False
	(==) ButtonUp			button	= case button of
										ButtonUp			-> True
										_					-> False
instance toString MouseState where
	toString (MouseMove	pos modifiers)		= brackify ("MouseMove "+++itemsList " " [toString pos,toString modifiers])
	toString (MouseDown	pos modifiers nr)	= brackify ("MouseDown "+++itemsList " " [toString pos,toString modifiers,toString nr])
	toString (MouseDrag	pos modifiers)		= brackify ("MouseDrag "+++itemsList " " [toString pos,toString modifiers])
	toString (MouseUp   pos modifiers)		= brackify ("MouseUp "  +++itemsList " " [toString pos,toString modifiers])
	toString MouseLost						= "MouseLost"
instance toString ButtonState where
	toString ButtonStillUp					= "ButtonStillUp"
	toString ButtonDown						= "ButtonDown"
	toString ButtonDoubleDown				= "ButtonDoubleDown"
	toString ButtonTripleDown				= "ButtonTripleDown"
	toString ButtonStillDown				= "ButtonStillDown"
	toString ButtonUp						= "ButtonUp"



/*	The SliderState type.								*/

::	SliderState
	=	{	sliderMin	:: !Int
		,	sliderMax	:: !Int
		,	sliderThumb	:: !Int
		}

instance == SliderState where								// Equality on SliderState
	(==) s1 s2 = s1.sliderMin==s2.sliderMin && s1.sliderMax==s2.sliderMax && s1.sliderThumb==s2.sliderThumb
instance toString SliderState where
	toString {sliderMin,sliderThumb,sliderMax}
		= curlify (itemsList "," (map recordFieldtoString (zip2	["sliderMin","sliderThumb","sliderMax"]
																[ sliderMin,  sliderThumb,  sliderMax ])))


/*	The UpdateState type.								*/

::	UpdateState
	=	{	oldFrame	:: !ViewFrame
		,	newFrame	:: !ViewFrame
		,	updArea		:: !UpdateArea
		}
::	ViewDomain			:==	Rectangle
::	ViewFrame			:==	Rectangle
::	UpdateArea			:==	[ViewFrame]

instance toString UpdateState where
	toString {oldFrame,newFrame,updArea}
		= curlify (itemsList "," ["oldFrame="+++toString oldFrame
								 ,"newFrame="+++toString newFrame
								 ,"updArea=" +++squarify (itemsList "," (map toString updArea))
								 ]
				  )

rectangleToUpdateState :: !Rectangle -> UpdateState
rectangleToUpdateState frame
	= {oldFrame=frame,newFrame=frame,updArea=[frame]}

/*	viewDomainRange defines the minimum and maximum values for ViewDomains.
	viewFrameRange  defines the minimum and maximum values for ViewFrames.
*/
viewDomainRange :: ViewDomain
viewDomainRange
	= {	corner1 = {x = 0-(2^30),y = 0-(2^30)}
	  ,	corner2 = {x =    2^30 ,y =    2^30 }
	  }

viewFrameRange :: ViewFrame
viewFrameRange
	= {	corner1 = {x = 1-(2^31),y = 1-(2^31)}
	  ,	corner2 = {x = (2^31)-1,y = (2^31)-1}
	  }


/*	Modifiers indicates the meta keys that have been pressed (True) or not (False).	*/

::	Modifiers
	=	{	shiftDown	:: !Bool
		,	optionDown	:: !Bool
		,	commandDown	:: !Bool
		,	controlDown	:: !Bool
		,	altDown		:: !Bool
		}

NoModifiers	:==	{shiftDown=False,optionDown=False,commandDown=False,controlDown=False,altDown=False}
ShiftOnly	:==	{shiftDown=True	,optionDown=False,commandDown=False,controlDown=False,altDown=False}
OptionOnly	:== {shiftDown=False,optionDown=True, commandDown=False,controlDown=False,altDown=True }
CommandOnly	:== {shiftDown=False,optionDown=False,commandDown=True, controlDown=True, altDown=False}
ControlOnly	:== {shiftDown=False,optionDown=False,commandDown=True, controlDown=True, altDown=False}
AltOnly		:==	{shiftDown=False,optionDown=True, commandDown=False,controlDown=False,altDown=True }

instance == Modifiers where
	(==) m1 m2 = m1.shiftDown   == m2.shiftDown
			  && m1.optionDown  == m2.optionDown
			  && m1.commandDown == m2.commandDown
			  && m1.controlDown == m2.controlDown
			  && m1.altDown     == m2.altDown
instance toString Modifiers where
	toString {shiftDown,optionDown,commandDown,controlDown,altDown}
		= curlify (itemsList "," (flatten [	if shiftDown   ["shiftDown"]   []
										  ,	if optionDown  ["optionDown"]  []
										  ,	if commandDown ["commandDown"] []
										  ,	if controlDown ["controlDown"] []
										  ,	if altDown     ["altDown"]     []
										  ]))


/*	The layout language used for windows and controls.	*/

::	ItemPos
	:==	(	ItemLoc
		,	ItemOffset
		)
::	ItemLoc
 //	Absolute:
	=	Fix
 //	Relative to corner:
	|	LeftTop
	|	RightTop
	|	LeftBottom
	|	RightBottom
 //	Relative in next line:
	|	Left
	|	Center
	|	Right
 //	Relative to other item:
	|	LeftOf	Id
	|	RightTo	Id
	|	Above	Id
	|	Below	Id
 //	Relative to previous item:
	|	LeftOfPrev
	|	RightToPrev
	|	AbovePrev
	|	BelowPrev
::	ItemOffset
	=	NoOffset							// Shorthand for OffsetVector zero
	|	OffsetVector Vector2				// A constant offset vector
	|	OffsetAlign  Alignment				// Offset depends on size of other item
	|	OffsetFun    ParentIndex OffsetFun	// Offset depends on orientation
::	Alignment								// Only makes sense for ItemLocs that are relative to other items:
	=	AlignLeft							// Align left  edges below each other
	|	AlignRight							// Align right edges below each other
	|	AlignCenter							// Align centers below/next to each other
	|	AlignTop							// Align top    edges next to each other
	|	AlignBottom							// Align bottom edges next to each other
::	ParentIndex
	:== Int									// The number of parents (1..)
::	OffsetFun
	:==	(ViewDomain,Point2) -> Vector2		// Current view domain and origin

instance zero ItemOffset where
	zero = NoOffset

instance == ItemLoc where
	(==) Fix				itemLoc	= case itemLoc of
										Fix				-> True
										_				-> False
	(==) LeftTop			itemLoc	= case itemLoc of
										LeftTop			-> True
										_				-> False
	(==) RightTop			itemLoc	= case itemLoc of
										RightTop		-> True
										_				-> False
	(==) LeftBottom			itemLoc	= case itemLoc of
										LeftBottom		-> True
										_				-> False
	(==) RightBottom		itemLoc	= case itemLoc of
										RightBottom		-> True
										_				-> False
	(==) Left				itemLoc	= case itemLoc of
										Left			-> True
										_				-> False
	(==) Center				itemLoc	= case itemLoc of
										Center			-> True
										_				-> False
	(==) Right				itemLoc	= case itemLoc of
										Right			-> True
										_				-> False
	(==) (LeftOf	id1)	itemLoc	= case itemLoc of
										LeftOf	id2		-> id1==id2
										_				-> False
	(==) (RightTo	id1)	itemLoc	= case itemLoc of
										RightTo	id2		-> id1==id2
										_				-> False
	(==) (Above		id1)	itemLoc	= case itemLoc of
										Above	id2		-> id1==id2
										_				-> False
	(==) (Below		id1)	itemLoc	= case itemLoc of
										Below	id2		-> id1==id2
										_				-> False
	(==) LeftOfPrev			itemLoc	= case itemLoc of
										LeftOfPrev		-> True
										_				-> False
	(==) RightToPrev		itemLoc	= case itemLoc of
										RightToPrev		-> True
										_				-> False
	(==) AbovePrev			itemLoc	= case itemLoc of
										AbovePrev		-> True
										_				-> False
	(==) BelowPrev			itemLoc	= case itemLoc of
										BelowPrev		-> True
										_				-> False
instance toString ItemLoc where
	toString Fix			= "Fix"
	toString LeftTop		= "LeftTop"
	toString RightTop		= "RightTop"
	toString LeftBottom		= "LeftBottom"
	toString RightBottom	= "RightBottom"
	toString Left			= "Left"
	toString Center			= "Center"
	toString Right			= "Right"
	toString (LeftOf  id)	= brackify ("LeftOf " +++ toString id)
	toString (RightTo id)	= brackify ("RightTo "+++ toString id)
	toString (Above   id)	= brackify ("Above "  +++ toString id)
	toString (Below   id)	= brackify ("Below "  +++ toString id)
	toString LeftOfPrev		= "LeftOfPrev"
	toString RightToPrev	= "RightToPrev"
	toString AbovePrev		= "AbovePrev"
	toString BelowPrev		= "BelowPrev"


/*	The Direction type.									*/

::	Direction
	=	Horizontal
	|	Vertical

instance == Direction where
	(==) Horizontal direction	= case direction of
									Horizontal	-> True
									_			-> False
	(==) Vertical	direction	= case direction of
									Vertical	-> True
									_			-> False
instance toString Direction where
	toString Horizontal = "Horizontal"
	toString Vertical   = "Vertical"


/*	The CursorShape type.							*/

::	CursorShape
	=	StandardCursor
	|	BusyCursor
	|	IBeamCursor
	|	CrossCursor
	|	FatCrossCursor
	|	ArrowCursor
	|	HiddenCursor

instance == CursorShape where
	(==) StandardCursor	cursor	= case cursor of
									StandardCursor	-> True
									_				-> False
	(==) BusyCursor		cursor	= case cursor of
									BusyCursor		-> True
									_				-> False
	(==) IBeamCursor	cursor	= case cursor of
									IBeamCursor		-> True
									_				-> False
	(==) CrossCursor	cursor	= case cursor of
									CrossCursor		-> True
									_				-> False
	(==) FatCrossCursor	cursor	= case cursor of
									FatCrossCursor	-> True
									_				-> False
	(==) ArrowCursor	cursor	= case cursor of
									ArrowCursor		-> True
									_				-> False
	(==) HiddenCursor	cursor	= case cursor of
									HiddenCursor	-> True
									_				-> False
instance toString CursorShape where
	toString StandardCursor	= "StandardCursor"
	toString BusyCursor		= "BusyCursor"
	toString IBeamCursor	= "IBeamCursor"
	toString CrossCursor	= "CrossCursor"
	toString FatCrossCursor	= "FatCrossCursor"
	toString ArrowCursor	= "ArrowCursor"
	toString HiddenCursor	= "HiddenCursor"


/*	Document interface type of interactive processes.	*/

::	DocumentInterface
	=	NDI														// No       Document Interface
	|	SDI														// Single   Document Interface
	|	MDI														// Multiple Document Interface

instance == DocumentInterface where
	(==) NDI xdi	= case xdi of
						NDI	-> True
						_	-> False
	(==) SDI xdi	= case xdi of
						SDI	-> True
						_	-> False
	(==) MDI xdi	= case xdi of
						MDI	-> True
						_	-> False
instance toString DocumentInterface where
	toString NDI = "NDI"
	toString SDI = "SDI"
	toString MDI = "MDI"


/*	Process attributes.									*/

::	ProcessAttribute st									// Default:
	=	ProcessActivate			(IdFun st)				// No action on activate
	|	ProcessDeactivate		(IdFun st)				// No action on deactivate
	|	ProcessClose			(IdFun st)				// Process is closed
 //	Attributes for (M/S)DI process only:
	|	ProcessOpenFiles		(ProcessOpenFilesFunction st)
														// Request to open files
	|	ProcessClipboardChanged	(IdFun st)
	|	ProcessWindowPos		ItemPos					// Platform dependent
	|	ProcessWindowSize		Size					// Platform dependent
	|	ProcessWindowResize		(ProcessWindowResizeFunction st)
														// Platform dependent
 	|	ProcessToolbar			[ToolbarItem st]		// Process has no toolbar
 //	Attributes for MDI processes only:
	|	ProcessNoWindowMenu								// Process has WindowMenu

::	ProcessWindowResizeFunction st
	:==	Size											// Old ProcessWindow size
	 ->	Size											// New ProcessWindow size
	 ->	st -> st
::	ProcessOpenFilesFunction st
	:==	[String]										// The file names to open
	 -> st -> st

::	ToolbarItem st
	=	ToolbarItem Bitmap (Maybe String) (IdFun st)
	|	ToolbarSeparator


/*	Frequently used function types.						*/

::	ModifiersFunction	st	:==	Modifiers		->	st -> st
::	MouseFunction		st	:== MouseState		->	st -> st
::	KeyboardFunction	st	:== KeyboardState	->	st -> st
::	SliderAction		st	:==	SliderMove		->	st -> st
::	SliderMove
	=	SliderIncSmall
	|	SliderDecSmall
	|	SliderIncLarge
	|	SliderDecLarge
	|	SliderThumb Int

instance toString SliderMove where
	toString SliderIncSmall  = "SliderIncSmall"
	toString SliderDecSmall  = "SliderDecSmall"
	toString SliderIncLarge  = "SliderIncLarge"
	toString SliderDecLarge  = "SliderDecLarge"
	toString (SliderThumb x) = brackify ("SliderThumb "+++toString x)


/*	Scrolling function.									*/

::	ScrollFunction
	:==	ViewFrame		->					// Current	view
		SliderState		->					// Current	state of scrollbar
		SliderMove		->					// Action of the user
		Int									// New thumb value

stdScrollFunction :: !Direction !Int -> ScrollFunction
stdScrollFunction direction d
	= stdScrollFunction` direction d
where
	stdScrollFunction` :: !Direction !Int !ViewFrame !SliderState !SliderMove -> Int
	stdScrollFunction` direction d viewFrame {sliderThumb=x} move
		# d				= abs d
		  viewFrameSize	= rectangleSize viewFrame
		  edge			= if (direction==Horizontal) viewFrameSize.w viewFrameSize.h
		= case move of
			SliderIncSmall	-> x+d
			SliderDecSmall	-> x-d
			SliderIncLarge	-> x+edge/d*d
			SliderDecLarge	-> x-edge/d*d
			SliderThumb x	-> x/d*d


/*	Standard GUI object rendering function.				*/

::	Look
	:==	SelectState ->						// Current SelectState of GUI object
		UpdateState ->						// The area to be rendered
		*Picture	-> *Picture				// The rendering action

stdUnfillNewFrameLook :: SelectState !UpdateState !*Picture -> *Picture
stdUnfillNewFrameLook _ {newFrame} picture = unfill newFrame picture

stdUnfillUpdAreaLook :: SelectState !UpdateState !*Picture -> *Picture
stdUnfillUpdAreaLook _ {updArea} picture = stateMap2 unfill updArea picture


/*	Common error report types.							*/

::	ErrorReport													// Usual cause:
	=	NoError													// No error
	|	ErrorViolateDI											// Violation against document interface kind
	|	ErrorIdsInUse											// Object definition contains Ids that are already in use
	|	ErrorUnknownObject										// Object can not be found
	|	ErrorNotifierOpen										// It was tried to open a second send notifier // MW0++
	|	OtherError !String										// Other kind of error

instance == ErrorReport where
	(==) NoError			error	= case error of
										NoError				-> True
										_					-> False
	(==) ErrorViolateDI		error	= case error of
										ErrorViolateDI		-> True
										_					-> False
	(==) ErrorIdsInUse		error	= case error of
										ErrorIdsInUse		-> True
										_					-> False
	(==) ErrorUnknownObject	error	= case error of
										ErrorUnknownObject	-> True
										_					-> False
	(==) ErrorNotifierOpen	error	= case error of
										ErrorNotifierOpen	-> True
										_					-> False
	(==) (OtherError e1)	error	= case error of
										OtherError e2		-> e1==e2
										_					-> False
instance toString ErrorReport where
	toString NoError			= "NoError"
	toString ErrorViolateDI		= "ErrorViolateDI"
	toString ErrorIdsInUse		= "ErrorIdsInUse"
	toString ErrorUnknownObject	= "ErrorUnknownObject"
	toString ErrorNotifierOpen	= "ErrorNotifierOpen" // MW11++
	toString (OtherError s)		= brackify ("OtherError "+++toString s)


//	Some handy functions for toString:
curlify  x = "{"+++x+++"}"
brackify x = "("+++x+++")"
squarify x = "["+++x+++"]"

recordFieldtoString :: (String,a) -> String | toString a	// recordFieldtoString f v -> f=v
recordFieldtoString (field,value) = field+++"="+++toString value

itemsList :: !String ![String] -> String	// itemsList c [a0,...an] -> a0 c a1 c ... c an
itemsList separator [x:xs]
	= x+++itemsList` xs
where
	itemsList` [x:xs]	= separator+++x+++itemsList` xs
	itemsList` _		= ""
itemsList _ _
	= ""

::	OkBool									// iff True, the operation was successful
	:==	Bool
