/* @(#)tkeys.c	19.1 (ES0-DMD) 02/25/03 14:00:49 */
/*===========================================================================
  Copyright (C) 1995 European Southern Observatory (ESO)
 
  This program 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 of 
  the License, or (at your option) any later version.
 
  This program 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 this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

.TYPE		Module
.IDENTIFICATION tkeys.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Window Management
.ENVIRONMENT	TermWindows
.COMMENTS	This module contains simple application of TermWindows:
\begin{TeX}
\begin{itemize}
\item {\em ta\_yes}	which creates a ``yes/no'' window, and prompts
	for this simple answer.
\item {\em ta\_dummy}	which creates a ``dummy'' window of $1\times 1$
\end{itemize}

\end{TeX}


.VERSION 3.0	20-Jun-1988: Creation
----------------------------------------------------------------------------*/

#define		DEBUG		0	/* For debugging only		*/

#define PM_LEVEL 	LEVEL_TW-1

#include <stesodef.h>
#include <buffer.h>
#include <str.h>
#include <proto_tbl.h>

typedef struct 	{		/* Defines Map Keys / Fcts / Help */
	short keys[2];
	int (*action)();
	int  name;		/* Offset in nam_buf	*/
	} SYMBOL;


static struct MAP_KEYS {
	BUFFER sym_buf ;		/* Collects Symbols */
	BUFFER nam_buf ;		/* Collects Names   */
	} keys_head = { 
		BUF_Init(SYMBOL, 32), 
		BUF_Init(char, 128)
	};

#define asym	(&keys_head.sym_buf)
#define anam	(&keys_head.nam_buf)

static SYMBOL *find();
static SYMBOL *cfind();

/*==========================================================================
 *			tk_open
 *==========================================================================*/
int tk_open()
/*+++
.PURPOSE Initialise 
.RETURNS OK
.REMARKS Does nothing ...
---*/
{

  ENTER("*tk_open");

  EXIT(OK);
}

/*==========================================================================
 *			tk_add
 *==========================================================================*/
int tk_add(key1, key2, action, name)
/*+++
.PURPOSE Add a new key
.RETURNS OK (key added or replaced) / NOK (error)
.REMARKS 
---*/
	short	key1;		/* IN: First Key */
	short	key2;		/* IN: 2nd Key */
	int	(*action)();	/* IN: ACtion to execute	*/
	char	*name;		/* IN: Name of action		*/
{
  	SYMBOL	*ps;
  	int	status;

  ENTER("tk_add");
  
  if_not (ps = find(key1, key2))	/* A new */
  {
	ps = BUF_AllocateItem(asym, SYMBOL);
  }

  if (ps)
  {
	ps->keys[0] = key1;
	ps->keys[1] = key2;
  	if(ps->keys[0] == 0)	ps->keys[0] = ps->keys[1], ps->keys[1] = 0;
  	ps->action = action;
  	ps->name   = anam->used;
  	BUF_SaveString(anam, name);
	status = OK;
  }
  else	status = NOK;
    
  EXIT(status);
}

/*==========================================================================
 *			tk_find
 *==========================================================================*/
char *tk_find(key1, key2)
/*+++
.PURPOSE Find a key's name
.RETURNS The associated name.
.REMARKS 
---*/
	short	key1;		/* IN: First Key */
	short	key2;		/* IN: 2nd Key */
{
	SYMBOL	*ps;
	char	*p;

  ENTER("*tk_find");
  
  if (ps = find(key1, key2))
  {	
  	p = anam->buf + ps->name;
  	TRACE(p);
  }
  else	p = NULL_PTR(char);
  
  EXITp(p);
}

/*==========================================================================
 *			tk_check
 *==========================================================================*/
int tk_check(key1)
/*+++
.PURPOSE Find if a key exists / requires a second one
.RETURNS 0 (unknown) / 1 (exists) / 2 (a second key is required)
.REMARKS 
---*/
	short	key1;		/* IN: First Key */
{
	SYMBOL	*ps;
	int	status;

  ENTER("tk_check");
  
  if (ps = find(key1, -1))
  	status = (ps->keys[1] ? 2 : 1);
  else	status = 0;
  
  EXIT(status);
}

/*==========================================================================
 *			tk_cexec
 *==========================================================================*/
int tk_cexec(action_name)
/*+++
.PURPOSE Execute Action
.RETURNS The status of Action / -1 if not found
.REMARKS 
---*/
	char	*action_name;		/* IN: Action to execute */
{
	SYMBOL	*ps;
	int	status;

  ENTER("tk_cexec");
  
  if (ps = cfind(action_name))
  {	
  	status = (*(ps->action))(anam->buf + ps->name);
  }
  else	status = -1;
  
  EXIT(status);
}

/*==========================================================================
 *			tk_exec
 *==========================================================================*/
int tk_exec(key1, key2)
/*+++
.PURPOSE Execute Action
.RETURNS The status of Action / NOK if not found
.REMARKS 
---*/
	short	key1;		/* IN: First Key */
	short	key2;		/* IN: 2nd Key */
{
	SYMBOL	*ps;
	int	status;

  ENTER("tk_exec");
  
  if (ps = find(key1, key2))
  {	
  	status = (*(ps->action))(anam->buf + ps->name);
  }
  else	ERROR("Key has no definition"), status = NOK;
  
  EXIT(status);
}

/*==========================================================================
 *			find
 *==========================================================================*/
static SYMBOL *find(key1, key2)
/*+++
.PURPOSE Find a key's name
.RETURNS The associated name.
.REMARKS 
---*/
	short	key1;		/* IN: First Key */
	short	key2;		/* IN: 2nd Key */
{
	SYMBOL	*ps;
	short	keys[2];
	int	i;

  keys[0] = key1, 	keys[1] = key2;
  
  if(keys[0] == 0)	keys[0] = keys[1],	keys[1] = 0;

  for (i = SET_Items(asym, SYMBOL), ps = SET_FirstItem(asym, SYMBOL); 
  	--i >= 0; ps++)
  {
	if (keys[0] == ps->keys[0]) 
		if (keys[1] == ps->keys[1])	break;
	else	if (keys[1] == -1)		break;		/* First Keys */
  }

  if (i < 0)	ps = NULL_PTR(SYMBOL);	/* Not Found	*/

  return(ps);
}

/*==========================================================================
 *			cfind
 *==========================================================================*/
static SYMBOL *cfind(action_name)
/*+++
.PURPOSE Find an action
.RETURNS The associated symbol -- NULL if ambiguous
.REMARKS 
---*/
	char	*action_name;		/* IN: Name of the Action */
{
	SYMBOL	*ps,	*matching_symbol;
	int	i, m;
	char	*p;

  matching_symbol = (SYMBOL *)0;

  for (i = SET_Items(asym, SYMBOL), ps = SET_FirstItem(asym, SYMBOL); 
  	--i >= 0; ps++)
  {
	p = 	anam->buf + ps->name;
	m = 	stumatch(action_name, p);
	if (action_name[m] == EOS)	/* Found ... */
	{ 	
		if (p[m] == EOS)	return(ps); 
		if (matching_symbol && (ps->action != matching_symbol->action))
                	return(NULL_PTR(SYMBOL)); 
		matching_symbol = ps;
	}
		
  }

  return(matching_symbol);
}

#if DEBUG
/*==========================================================================
 *			Test Program
 *==========================================================================*/
#include <pgm.h>
#include <twhelp.h>
#include <tw.h>
#include <buffer.h>
#include <str.h>

static char *edkey(key)
	short	key;	/* IN: The Key */
{
	static char ed_key[4];
	int	i;

  ed_key[0] = ' ', ed_key[1] = ' ', ed_key[2] = ' ';
  switch(key>>8)
  {
    case _PF_:	ed_key[0] = 'P', ed_key[1] = 'F', ed_key[2] = '0' + (key&0x7f);
    	break;
    case _KEYPAD_:	ed_key[0] = 'k', ed_key[1] = 'p';
    	ed_key[2] = (key&0x7f);
    	break;
    case _ARROW_:	ed_key[0] = 'A';
	switch(key&0xf)
	{
	  case _UP_:	ed_key[1] = 'u';	break;
	  case _DOWN_:	ed_key[1] = 'd';	break;
	  case _LEFT_:	ed_key[1] = 'l';	break;
	  case _RIGHT_:	ed_key[1] = 'r';	break;
	  default:	ed_key[1] = 'h';	break;
	}
    	break;
    case _FK_:	ed_key[0] = 'F', i = key&0xff;
    	if (i < 10)	ed_key[1] = '0' + i;
    	else		ed_key[1] = '0' + i/10, ed_key[2] = '0' + i%10;
    	break;
    default:	
    	ed_key[0] = key;
    	if (iscntrl(ed_key[0]))	
    	{
    		if (ed_key[0] > ' ')	ed_key[0] &= 077;
    		ed_key[1] = '@' | ed_key[0];
    		ed_key[0] = '^';
    	}
    	
  }
  return(ed_key);

}

int list_keys()
{
	static WINDOW *list_window = NULL_WINDOW;
	SYMBOL 	*ps;
	int	i;
	char	*edkey();
	char	ch;

  if_not(list_window)	list_window = OpenWindow("ListKeys", 0,0, 0,0, 
  	_REVERSE_, 0, 0);
  
  ClearWindow(list_window);
  
  for (i = SET_Items(asym, SYMBOL), ps = SET_FirstItem(asym, SYMBOL); 
  	--i >= 0; ps++)
  {
	if (ps->keys[1])	Put(list_window, edkey(ps->keys[0])),
				Put(list_window, edkey(ps->keys[1]));
	else 			Put(list_window, "    "),
				Put(list_window, edkey(ps->keys[0]));
  	Puts(list_window, anam->buf + ps->name);
  }
  RaiseWindow(list_window);
  GetKey(list_window, &ch);
  RemoveWindow(list_window);

}

int find_key()
{
	static WINDOW *list_window = NULL_WINDOW;
	SYMBOL 	*ps;
	char	buffer[80];

  if_not(list_window)	list_window = OpenWindow("FindKeys", -1,0, 1,0, 
  	_REVERSE_, 0, 5);
  
  ClearWindow(list_window);
  RaiseWindow(list_window), ActiveWindow(list_window);
  Gets(list_window, buffer, sizeof(buffer));
  
  if (ps = cfind(buffer))
  	Put(list_window, " ==> "),
  	Put(list_window, anam->buf + ps->name);
  else	Put(list_window, " ??? "), Bell();

  GetKey(list_window, buffer);
  RemoveWindow(list_window);

}

int def_key()
{
	static WINDOW *get_window = NULL_WINDOW;
	short	keys[2];
	int	i, key_group;
	char	*edkey();
	char	ch;
	char	text[200];
	int	standard_action();

  if_not(get_window)
  {
  	get_window = OpenWindow("GetKeys", -3,0, 3,0, 
  			_REVERSE_, _BORDER_|_BORDER2_, 9);
  	EnableKeypad(get_window);
  	SetStopping(get_window, _FK_, "*");
  	SetStopping(get_window, _ARROW_, "*");
  }
  
  ClearWindow(get_window);
  RaiseWindow(get_window);	ActiveWindow(get_window);
  
  Put(get_window, "Number of keys (0/1/2): ");
  
  for (ch = ' ';(ch < '0') || (ch > '2') ; )
  	GetKey(get_window, &ch)  ;

  keys[0] = 0;
  ClearWindow(get_window);
  Put(get_window, "Enter the ");
  Write(get_window, &ch, 1);
  Put(get_window, " keys: ");

  switch(ch)  
  {
    case '0':	goto FIN;

    case '2':
    		key_group = GetKey(get_window, &ch);
    		if (key_group < 0)	goto FIN;
    		keys[0] = key_group<<8 | ch;
    case '1':
    		key_group = GetKey(get_window, &ch);
    		if (key_group < 0)	goto FIN;
    		keys[1] = key_group<<8 | ch;
  }
  
  ClearWindow(get_window);
  Put(get_window, "Text for ");
  if (keys[0])	Put(get_window, edkey(keys[0])), Put(get_window, " ");
  Put(get_window, edkey(keys[1]));
  Put(get_window, ": ");
  SetAttr(get_window, _REVERSE_);
  Gets(get_window, text, sizeof(text));
  SetAttr(get_window, _NORMAL_);
  tk_add(keys[0], keys[1], standard_action, text);
  
  FIN: RemoveWindow(get_window);
  return(OK);
}

static	WINDOW *w;

standard_action(text)
	char	*text;
{
  Put(w, text);
  TouchWindow(w);
  
  return(OK);
}

PGM(test)
{
	char	ch;
	int	key_group;
/*	int	def_keys(), list_keys(); */
	short	key1, key2;
	
  InitWindows((char *)0, (char *)0, TW_cc_VMS);

  DefineControl('D', 0);
  DefineControl('L', 0);
  DefineControl('F', 0);
  w = OpenWindow("Main", 0,0, 0,0, _NORMAL_, _SCROLL_, 0);
  SetStopping(w, _STANDARD_, "DLF")  ;
  SetStopping(w, _ARROW_, "*")  ;
  SetStopping(w, _FK_, "*")  ;
  EnableKeypad(w);

  RaiseWindow(w);
  DeactiveWindow(w);
  
  Puts(w, "Use ^D to define new keys, ^L to list keys, ^F to find keys");

  tk_add(Control_('D'), 0, def_key, "Define New Key");
  tk_add(Control_('L'), 0, list_keys, "List Known Keys");
  tk_add(Control_('F'), 0, find_key , "Find a Key");
  
  while(1)
  {
	TouchWindow(w);
	key_group = GetKey(w, &ch);
	if (key_group == _EOF_)		break;
	if (key_group == _STANDARD_)
	{
		if ((ch == '\r') || (ch == '\0'))
			{ NewLine(w); continue; }
		key_group = 0;
	}
	key1 = key_group<<8 | ch;
	key2 = 0;
	
	switch(tk_check(key1))
	{
	  case 0:	/* Doesn't map to an action */
	  	if(key_group)	Bell();		/* Keypad */
	  	else		Write(w, &ch, 1);
	  	continue;

	  case 2:
		key_group = GetKey(w, &ch);
		if (key_group == _EOF_)	key_group = 0;
		key2 = key_group<<8 | ch;
		
	  case 1:
	  	tk_exec(key1, key2);
	}
  }
  
  EndWindows()  ;
  
}
#endif
