Subject: Re: HyperNeWS 1.4 & TNT 2.0
From: Arthur van Hoff <[email-masked]>
To: Don Hopkins <[email-masked]>, Owen Densmore <[email-masked]>, Rafael Bracho <[email-masked]>
Cc: Tim Niblett <[email-masked]>, Jim Rudolf <[email-masked]>, Cathy Waite <[email-masked]>
Date: Thu, 3 Jan 91 10:29:33 BST

Hi Don,

Happy NeWS year. I think you are in for some trouble if you really want to
port HN1.4 to TNT2.0. I never got the stuff you gave me to work (the installation
instructions were missing). However I did get a 40% port of HN1.4
ready (my own effort). I think you are best of continueing with the port you
already have and fix at least the tracking. I have written two interfaces for
TNT to do tracking and menu handling in a HyperNeWS way (like getanimated). This
works well. Note HN should subclass ClassTrackingCanvas to make the canvas
trackable and to be able to catch the mouse events. Note that specially the
track_state function is VERY useful to implement OpenLook things.
Following are 4 files. I am sure that you can work out how to implement
GetAnimated etc.. from this....

TRACK: explanation of ClassTrackingCanvas
track.ps: implementation of ClassTrackingCanvas
MENU: explanation of ClassMenuCanvas and ClassHNMenu
menu.ps: implementation of ClassMenuCanvas and ClassHNMenu

ps: I am not sure what is going to happen when TNT is going to allow
    pinned menus but I am sure it is going to affect the API, so I cannot
    guarantee that the routines below will remain the same over time. Hopefully
    they will only be extended.

Have fun,

	Arthur van Hoff 		     [email-masked]

	The Turing Institute
	36 North Hannover street, Glasgow G1 2AD, +44 41 5526400
	Beltstraat 94a, 7512 AB Enschede, Holland, +31 53 324366

-------------------- TRACK ------------------------------------------------------
HyperNeWS requires a 'getanimated' type of tracking service.
The ClassTrackingCanvas tnt class provides these facillities. 
The routines below return the following track states:

/mouse_cancel:	There was no mouse down or the user cancelled, take no action.
/mouse_drag:	The mouse was dragged.
/mouse_up:	The mouse is up again, take action.
/mouse_click:	The mouse was clicked but not moved, take action.

subclasser routines
-------------------

A sub-class of ClassTrackingCanvas should define the following routines to
handle mouse down events:

/onselect		% --
	Select button was clicked

/onadjust		% --
	Adjustbutton was clicked

track routines
--------------

/trackable		% -- boolean
	Returns true if the currentprocess is allowed to call 'track'.

/track_state		% -- status
	Returns /mouse_click, /mouse_up, /mouse_drag or /mouse_cancel. 
	An allows the previewing of the mouse motion. For example: if 
	you click on the border of an X window you won't see a drag-rectangle
	until you move the mouse. Must be called before /track.

/track			% proc -- status
	Executes 'proc' repeatedly for every mouse move. When 'proc'
	is executed the event will be on the stack.
	Returns /mouse_click, /mouse_up or /mouse_cancel. The final
	position can be obtained with mousexy.

/track_overlay		% proc -- status
	Same as /track but an overlay is created first. erasepage is called
	when appropriate.

event management
----------------

/mouseevent		% -- event
	Return the last mouse event, or an nullevent if there wasn't one.

/eventxy		% event -- x y
	Return the x,y location of the event in the current coordinate system.

/mousexy		% -- x y
	Return the x,y location of the last mouseevent in the 
	current coordinate system.

/mouselevel		% -- n
	Return the click-level (double, triple click) of the last 
	mouse click.

-------------------- track.ps ------------------------------------------------------
%
% ClassTrackingCanvas, a tnt canvas with external tracking properties
%

/ClassTrackingCanvas ClassCanvas []
classbegin

/Trackable? 	true def
/track_time	0 def
/track_lev	0 def

%
% subclasses responsibility
%

/onselect	nullproc def
/onadjust	nullproc def

%
% send an event to the tracking process
%

/track_send {	% event action --
	1 index begin
	/ClientData exch def
	/Canvas self def
	/Name /tRackEvent def
	end sendevent
} def

%
% tracking service interface
%

/TrackStart {	% event -- list true
	.1 blockinputqueue

	% determine multi click
	dup /TimeStampMS get track_time sub
	UserProfile /MultiClickThresh get le {track_lev} 0 ifelse 1 add
	/track_lev exch promote
	/track_time 1 index /TimeStampMS get promote

	{
		newprocessgroup 
		currentprocess /ProcessName (HyperNeWS tracker) put
		currentprocess /track_evt 3 -1 roll put 
		clear

		createevent dup begin
		/Name /tRackEvent def
		end expressinterest 

		unblockinputqueue

		InteractionLock {
			self setcanvas
			currentprocess /track_state null put
			currentprocess /track_evt get /Name get 
			AdjustButton eq {onadjust} {onselect} ifelse
		} monitor
		currentprocess /track_evt undef
	} fork soften
	/tracker exch promote
	pop /Default true
} def

/TrackStop {	% event --
	{
		currentprocess /track_evt 3 -1 roll put
		currentprocess /track_mvd known 
		/mouse_up /mouse_click ifelse exit
	} track_send 
	/tracker unpromote
} def

/TrackCancel {	% event --
	{pop /mouse_cancel exit} track_send 
	/tracker unpromote
} def

/TrackMotion {	% event --
	{currentprocess /track_mvd true put $track_proc} track_send
} def

classend def

%
% Track utillities
%

/mouseevent {	% -- event
	currentprocess /track_evt 2 copy 
	known {get} {pop pop createevent} ifelse
} def

/eventxy {	% event -- x y
	begin XLocation YLocation end
} def

/mousexy {	% -- x y
	mouseevent eventxy
} def

/mouselevel {	% -- multi-click-level
	mouseevent /Canvas get dup null ne {
		/track_lev 2 copy known {get} {pop pop 0} ifelse
	} {pop 0} ifelse
} def

%
% low level track procedure
%

/trackable {	% -- boolean
	currentprocess /track_state known
} def

/track_state { % -- status
	trackable {
		currentprocess /track_state get null eq {
			10 dict begin
			currentprocess /track_state undef
			/$track_proc {
				currentprocess /track_state 3 -1 roll put
				/mouse_drag exit
			} def
			{awaitevent dup /ClientData get exec} loop
			end
		} {/mouse_drag} ifelse
	} {/mouse_cancel} ifelse
	
} def

/track {	% proc -- status
	trackable {
		10 dict begin
		/$track_proc exch cvx def
		currentprocess /track_evt get $track_proc
		currentprocess /track_state get 
		dup null ne {$track_proc} {pop} ifelse
		currentprocess /track_state undef
		{awaitevent dup /ClientData get exec} loop
		end
	} {pop /mouse_cancel} ifelse
} def

/track_overlay {	% proc -- status
	trackable {
		gsave matrix currentmatrix
		currentcanvas createoverlay setcanvas setmatrix
		[exch /erasepage load exch /exec load] track
		erasepage
		grestore
	} {pop /mouse_cancel} ifelse
} def

-------------------- MENU ------------------------------------------------------
HyperNeWS requires a menu service that allows the instant showing of menus. 
This is achieved with the following routines.
Firstly only menubutton-events resulting from a ClassMenuCanvas canvas can
be used by the following routines. You must therefore subclass ClassMenuCanvas
and define the 'onmenu' method which is called when the menubutton is clicked in
the canvas (wether it has a menu or not). Note that tracking is not possible on
the menubutton.

subclasser routines of ClassMenuCanvas
--------------------------------------
/onmenu			% --
	The menu button was clicked in the canvas.

The 'onmenu' process can (it doesn't have to) show menus using the showmenu 
and showxymenu. 

menu utillities
---------------
/menuable		% -- boolean
	Returns true if the process can successfully do a showmenu or showxymenu.
	This is only possible if the process results from a ClassMenuCanvas and it
	has NOT shown a menu before.

/showmenu		% menu --
	Shows a menu at the location of the menu-button-click. The default choice
	of the menu will be alingned with the cursor.

/showxymenu		% menu x y --
	Shows a menu with its NothWest corner at the specified location 
	in the current coordinate system.

ClassHNMenu is a subclass of ClassMenu. It only specifies how the Notifier proc
is called. Choosing a menu item results in a Send to the appropriate target
(the target is the object which called the showmenu or showxymenu). It is NOT
required that you use ClassHNMenu.

-------------------- menu.ps ------------------------------------------------------
%
% A HyperNeWS Menu
%
% NOTE: altough ClassMenuCanvas is a  subclass of ClassTrackingCanvas it 
% does NOT use any of the tracking functionality. It could also be a subclass
% of ClassCanvas.
%

/ClassMenuCanvas ClassTrackingCanvas []
classbegin
/menuprocess 	null def
/Menuable?	true def
/onmenu		nullproc def

/MenuStart {	% posname event -- posname event menu? boolean
	{
		currentprocess /ProcessName (HyperNeWS menu handler) put
		currentprocess /track_evt 3 -1 roll put
		currentprocess /menuok true put
		clear {onmenu} stopped pop
		currentprocess /track_evt undef
		currentprocess /menuok undef
	} fork {
		pause dup /State get
		dup /breakpoint eq {
			pop /menuprocess 1 index promote
			/OperandStack get dup length 
			2 copy 1 sub get 3 1 roll 2 sub get
			4 -1 roll pop 3 1 roll true exit
		} if
		/runnable ne {pop false exit} if
	} loop
} def

/MenuStop {	% menu --
	pop menuprocess null ne {
		menuprocess /State get /breakpoint eq {
			menuprocess continueprocess
		} if
		/menuprocess unpromote
	} if
} def
classend def

%
% HyperNeWS menu class
%

/ClassHNMenu ClassMenu []
classbegin
/ExecuteNotifier {	% value notifier --
	dup null ne {
		% make sure this gets executed in the hypernews context
		dup type /nametype eq {
			exch [exch] exch
			[3 1 roll /self cvx /Send cvx] cvx /target self send send
		} {
			[3 1 roll]
			[exch /doit /self cvx /Send cvx] cvx /target self send send
		} ifelse
	} {pop pop} ifelse
} def
classend def

%
% Utillities
%

/menuable {	% -- boolean
	currentprocess /track_evt known
	currentprocess /menuok known and
} def

/doshowmenu {	% posname menu --
	self soften /settarget 2 index send
	soften breakpoint
	null /settarget 2 index send
	null /setinvoker 2 index send
	currentprocess /track_evt undef
	currentprocess /menuok undef
	pop pop
} def

/showmenu {	% menu --
	dup null ne menuable and {
		/Default exch //doshowmenu exec
	} {pop} ifelse
} def

/showxymenu {	% menu x y --
	2 index null ne menuable and {
		mouseevent begin /YLocation exch def /XLocation exch def end
		/NorthWest exch //doshowmenu exec
	} {pop pop pop} ifelse
} def

currentdict /doshowmenu undef