/**********************************************************************/
/*                                                                    */
/*	CRISP - Programmable editor                                   */
/*	===========================                                   */
/*                                                                    */
/*  File:          crwin.c                                            */
/*  Author:        P. D. Fox                                          */
/*  Created:       6 Jun 1991                      		      */
/*                                                                    */
/*  Copyright (c) 1990, 1991 Paul Fox                                 */
/*                All Rights Reserved.                                */
/*                                                                    */
/*                                                                    */
/*--------------------------------------------------------------------*/
/*  Description:  Crisp ANSI Color Window widget                      */
/*                                                                    */
/*   This  widget  is  designed  to  handle  the  CRISP  window  and  */
/*   display the ANSI colors.					      */
/**********************************************************************/

/*static char sccs_id[] = "%Z% %M% %R%.%L%";*/


/* # include 	<X11/copyright.h> */
# include 	<X11/IntrinsicP.h>
# include 	<X11/StringDefs.h>
# include 	<X11/Shell.h>
# include	<X11/keysym.h>
# include	<X11/Xatom.h>
# include	<list.h>
# include	<alt.h>
# include	<signal.h>
# include	"x11r3.h"
# include 	"crwinP.h"

/**********************************************************************/
/*   Default size of window if too small or not defined.	      */
/**********************************************************************/
# define	DEFAULT_HEIGHT	410
# define	DEFAULT_WIDTH	480
# define	DEFAULT_FONT	XtDefaultFont

/**********************************************************************/
/*   Structure  defining  how  to  map keys in their various shifted  */
/*   states.							      */
/**********************************************************************/
struct keymaps {
	int	code;
	unsigned short	normal;
	unsigned short	shift;
	unsigned short	control;
	unsigned short	meta;
	};
	
/**********************************************************************/
/*   Following  table  is  how  to  interpret the Rn keys. These are  */
/*   the keypad keys on a Sun.					      */
/**********************************************************************/
static struct keymaps	km_Rn[] = {
{0, KEYPAD_PAUSE, KEYPAD_PAUSE, MOD_CTRL | KEYPAD_PAUSE, MOD_META | KEYPAD_PAUSE},
{0, KEYPAD_PRTSC, KEYPAD_PRTSC, MOD_CTRL | KEYPAD_PRTSC, MOD_META | KEYPAD_PRTSC},
{0, KEYPAD_SCROLL, KEYPAD_SCROLL, MOD_CTRL | KEYPAD_SCROLL, MOD_META | KEYPAD_SCROLL},
{0, KEYPAD_EQUAL, KEYPAD_EQUAL, MOD_CTRL | KEYPAD_EQUAL, MOD_META | KEYPAD_EQUAL},
{0, KEYPAD_DIV, KEYPAD_DIV, MOD_CTRL | KEYPAD_DIV, MOD_META | KEYPAD_DIV},
{0, KEYPAD_STAR, KEYPAD_STAR, MOD_CTRL | KEYPAD_STAR, MOD_META | KEYPAD_STAR},
{0, KEYPAD_7, MOD_SHIFT | KEYPAD_7, MOD_CTRL | KEYPAD_7, MOD_META | KEYPAD_7},
{0, KEYPAD_8, MOD_SHIFT | KEYPAD_8, MOD_CTRL | KEYPAD_8, MOD_META | KEYPAD_8},
{0, KEYPAD_9, MOD_SHIFT | KEYPAD_9, MOD_CTRL | KEYPAD_9, MOD_META | KEYPAD_9},
{0, KEYPAD_4, MOD_SHIFT | KEYPAD_4, MOD_CTRL | KEYPAD_4, MOD_META | KEYPAD_4},
{0, KEYPAD_5, MOD_SHIFT | KEYPAD_5, MOD_CTRL | KEYPAD_5, MOD_META | KEYPAD_5},
{0, KEYPAD_6, MOD_SHIFT | KEYPAD_6, MOD_CTRL | KEYPAD_6, MOD_META | KEYPAD_6},
{0, KEYPAD_1, MOD_SHIFT | KEYPAD_1, MOD_CTRL | KEYPAD_1, MOD_META | KEYPAD_1},
{0, KEYPAD_2, MOD_SHIFT | KEYPAD_2, MOD_CTRL | KEYPAD_2, MOD_META | KEYPAD_2},
{0, KEYPAD_3, MOD_SHIFT | KEYPAD_3, MOD_CTRL | KEYPAD_3, MOD_META | KEYPAD_3},
};

/**********************************************************************/
/*   Following   table   used   to  help  us  interpret  the  keypad  */
/*   (XK_KP_xx) keys.						      */
/**********************************************************************/
static struct keymaps	km_keypad[] = {
{XK_KP_Space, ' ', MOD_SHIFT | ' ', MOD_CTRL | ' ', MOD_META | ' '},
{XK_KP_Tab, '\t', BACK_TAB, MOD_CTRL | '\t', MOD_META | '\t'},
{XK_KP_Enter, KEYPAD_ENTER, SHIFT_KEYPAD_ENTER, CTRL_KEYPAD_ENTER, ALT_KEYPAD_ENTER},
{XK_KP_F1, F(1), MOD_SHIFT | F(1), MOD_CTRL | F(1), MOD_META | F(1)},
{XK_KP_F2, F(2), MOD_SHIFT | F(2), MOD_CTRL | F(2), MOD_META | F(2)},
{XK_KP_F3, F(3), MOD_SHIFT | F(3), MOD_CTRL | F(3), MOD_META | F(3)},
{XK_KP_F4, F(4), MOD_SHIFT | F(4), MOD_CTRL | F(4), MOD_META | F(4)},
{XK_KP_Equal, KEYPAD_EQUAL, SHIFT_KEYPAD_EQUAL, MOD_CTRL | KEYPAD_EQUAL, MOD_META | KEYPAD_EQUAL},
{XK_KP_Multiply, KEYPAD_STAR, SHIFT_KEYPAD_STAR, MOD_CTRL | KEYPAD_STAR, MOD_META | KEYPAD_STAR},
{XK_KP_Add, KEYPAD_PLUS, SHIFT_KEYPAD_PLUS, MOD_CTRL | KEYPAD_PLUS, MOD_META | KEYPAD_PLUS},
{XK_KP_Separator},
{XK_KP_Subtract, KEYPAD_MINUS, SHIFT_KEYPAD_MINUS, MOD_CTRL | KEYPAD_MINUS, MOD_META | KEYPAD_MINUS},
{XK_KP_Decimal, KEYPAD_DEL, SHIFT_KEYPAD_DEL, MOD_CTRL | KEYPAD_DEL, MOD_META | KEYPAD_DEL},
{XK_KP_Divide, KEYPAD_DIV, SHIFT_KEYPAD_DIV, MOD_CTRL | KEYPAD_DIV, MOD_META | KEYPAD_DIV},
{XK_KP_0, KEYPAD_0, SHIFT_KEYPAD_0, MOD_CTRL | KEYPAD_0, MOD_META | KEYPAD_0},
{XK_KP_1, KEYPAD_1, SHIFT_KEYPAD_1, MOD_CTRL | KEYPAD_1, MOD_META | KEYPAD_1},
{XK_KP_2, KEYPAD_2, SHIFT_KEYPAD_2, MOD_CTRL | KEYPAD_2, MOD_META | KEYPAD_2},
{XK_KP_3, KEYPAD_3, SHIFT_KEYPAD_3, MOD_CTRL | KEYPAD_3, MOD_META | KEYPAD_3},
{XK_KP_4, KEYPAD_4, SHIFT_KEYPAD_4, MOD_CTRL | KEYPAD_4, MOD_META | KEYPAD_4},
{XK_KP_5, KEYPAD_5, SHIFT_KEYPAD_5, MOD_CTRL | KEYPAD_5, MOD_META | KEYPAD_5},
{XK_KP_6, KEYPAD_6, SHIFT_KEYPAD_6, MOD_CTRL | KEYPAD_6, MOD_META | KEYPAD_6},
{XK_KP_7, KEYPAD_7, SHIFT_KEYPAD_7, MOD_CTRL | KEYPAD_7, MOD_META | KEYPAD_7},
{XK_KP_8, KEYPAD_8, SHIFT_KEYPAD_8, MOD_CTRL | KEYPAD_8, MOD_META | KEYPAD_8},
{XK_KP_9, KEYPAD_9, SHIFT_KEYPAD_9, MOD_CTRL | KEYPAD_9, MOD_META | KEYPAD_9},
{0}
};
static XtResource resources[] = {
#define offset(field) XtOffset(CrwinWidget, crwin.field)
    /* {name, class, type, size, offset, default_type, default_addr}, */
/*    { XtNcrwinResource, XtCCrwinResource, XtRCrwinResource, sizeof(char*),
	  offset(resource), XtRString, "default" },  */
    { XtNfont, XtCFont, XtRFontStruct, sizeof(char*),
	  offset(font_info), XtRString, DEFAULT_FONT},
	  
    { XtNscreenBackground, XtCWindowBackground, XtRPixel, sizeof(unsigned long),
    	offset(window_background), XtRString, "black"},
    { XtNtextColor, XtCTextColor, XtRPixel, sizeof(unsigned long),
    	offset(text_color), XtRString, "green"},
    { XtNselectedWindow, XtCSelectedWindow, XtRPixel, sizeof(unsigned long),
    	offset(selected_window), XtRString, "cyan"},
    { XtNnormalMessages, XtCNormalMessages, XtRPixel, sizeof(unsigned long),
    	offset(normal_messages), XtRString, "yellow"},
    { XtNerrorMessages, XtCErrorMessages, XtRPixel, sizeof(unsigned long),
    	offset(error_messages), XtRString, "red"},
    { XtNhilightForeground, XtCHilightForeground, XtRPixel, sizeof(unsigned long),
    	offset(hilight_foreground), XtRString, "green"},
    { XtNhilightBackground, XtCHilightBackground, XtRPixel, sizeof(unsigned long),
    	offset(hilight_background), XtRString, "red"},
    { XtNinsertCursorColor, XtCInsertCursorColor, XtRPixel, sizeof(unsigned long),
    	offset(insert_cursor_color), XtRString, "red"},
    { XtNovertypeCursorColor, XtCOvertypeCursorColor, XtRPixel, sizeof(unsigned long),
    	offset(overtype_cursor_color), XtRString, "yellow"},

   { 	XtNtranslations, 
	XtCTranslations, 
	XtRTranslationTable, 
	sizeof(char*),
	offset(table), 
	XtRString, 
	""
     },
#undef offset
};

static void	initialize();
static void	realize();
static void	Resize();
static void	redisplay();

static void CrwinAction(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinInput(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinString(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinKey(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinExpose(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinPaste(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinButtonDown(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinButtonUp(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinButtonMotion(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinStart_hilight(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinExtend_hilight(/* Widget, XEvent*, String*, Cardinal* */);
static void CrwinMake_sel(/* Widget, XEvent*, String*, Cardinal* */);


static void requestor_callback PROTO((Widget, XtPointer, Atom *, Atom *, XtPointer *, unsigned long *, int *));
static void handle_button PROTO((Widget, int, int, int, int, int));
static void get_xy PROTO((CrwinWidget, int *, int *, int, int));
void	crwin_erase_line PROTO((Widget, int, int));
void	crwin_erase_col PROTO((Widget, int, int));

static XtActionsRec actions[] =
{
  /* {name, procedure}, */
    {"input",		CrwinInput},
    {"key",		CrwinKey},
    {"expose",		CrwinExpose},
    {"string",		CrwinString},
    {"paste",		CrwinPaste},
    {"button_motion",	CrwinButtonMotion},
    {"button_down",	CrwinButtonDown},
    {"button_up",	CrwinButtonUp},
    {"start_hilight",	CrwinStart_hilight},
    {"extend_hilight",	CrwinExtend_hilight},
    {"make_selection",	CrwinMake_sel},
    {"crwin",		CrwinAction},
    {"quit",		CrwinAction},
};
static char translations[] =
"<KeyPress>:		input()\n\
<Expose>:		expose()\n\
<Btn1Up>:		button_up()\n\
<Btn1Down>:		button_down()\n\
<Btn2Up>:		button_up()\n\
<Btn2Down>:		button_down()\n\
<Btn3Up>:		button_up()\n\
<Btn3Down>:		button_down()\n\
<BtnMotion>:		button_motion()\n";

CrwinClassRec crwinClassRec = {
  { /* core fields */
    /* superclass		*/	(WidgetClass) &widgetClassRec,
    /* class_name		*/	"Crwin",
    /* widget_size		*/	sizeof(CrwinRec),
    /* class_initialize		*/	NULL,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	realize,
    /* actions			*/	actions,
    /* num_actions		*/	XtNumber(actions),
    /* resources		*/	resources,
    /* num_resources		*/	XtNumber(resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	NULL,
    /* resize			*/	Resize,
    /* expose			*/	redisplay,
    /* set_values		*/	NULL,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	XtInheritAcceptFocus,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	translations,
    /* query_geometry		*/	XtInheritQueryGeometry,
    /* display_accelerator	*/	XtInheritDisplayAccelerator,
    /* extension		*/	NULL
  },
  { /* crwin fields */
    /* empty			*/	0
  }
};

WidgetClass crwinWidgetClass = (WidgetClass)&crwinClassRec;

/**********************************************************************/
/*   Array of colors corresponding to the ANSI.			      */
/**********************************************************************/
Pixel x11_colors[16];
static Colormap	default_color_map;
extern int need_resize;

unsigned short *cvt_string_to_code();

static void
setup_x11_colors(dpy)
Display	*dpy;
{	static int first_time = TRUE;
	XColor		exact_def;
	int		i;
# if 1
	static char	*color_names[] = {
  "black", "blue", "green", "yellow", "red", "magenta", "cyan", "white", 
  "dim gray", "blue", "green", "cyan", "red", "magenta", "yellow", "white", 
		NULL
		};
# else
	static char	*color_names[] = {
  "black", "gray80", "gray85", "gray90", "gray95", "gray96", "gray98", "white",
  "black", "gray80", "gray85", "gray90", "gray95", "gray96", "gray98", "white",
		NULL
		};
# endif		
	if (!first_time)
		return;
	first_time = FALSE;
	default_color_map = DefaultColormap(dpy, DefaultScreen(dpy));
	for (i = 0; color_names[i]; i++) {
		if (!XParseColor(dpy, default_color_map, color_names[i], &exact_def)) {
			fprintf(stderr, "Color name %s not in database.\n",
				color_names[i]);
			exit(0);
			}
		if (!XAllocColor(dpy, default_color_map, &exact_def)) {
			fprintf(stderr, "Can't allocate color: %s -- all color cells allocated.\n",
				color_names[i]);
			exit(1);
			}
		x11_colors[i] = exact_def.pixel;
		}
}
/* ARGSUSED */
static void
initialize(treq, tnew)
Widget	treq, tnew;
{	CrwinWidget	new = (CrwinWidget) tnew;
	Display	*dpy = XtDisplay(new);
		
	setup_x11_colors(dpy);

	if (new->core.height < 10)
		new->core.height = DEFAULT_HEIGHT;
	if (new->core.width < 10)
		new->core.width = DEFAULT_WIDTH;
	new->crwin.win_height_allocated = new->core.height;
	new->crwin.win_width_allocated = new->core.width;
	new->crwin.x = new->crwin.y = 0;
	new->crwin.cursor_char = ' ';
	new->crwin.insert_cursor = TRUE;
	new->crwin.cursor_bg = 0;

	new->crwin.char_height = new->crwin.font_info->ascent + 
				 new->crwin.font_info->descent;
	new->crwin.char_width = XTextWidth(new->crwin.font_info, "A", 1);

	new->crwin.push_ref = r_init(F_STR, "123456789", 10);
	new->crwin.push_ref->r_used = 0;
	
	new->crwin.first_sizing = TRUE;
}
# define	superclass	(&widgetClassRec)
static void
realize(w, valueMask, attributes)
Widget	w;
XtValueMask	*valueMask;
XSetWindowAttributes	*attributes;
{	CrwinWidget	new = (CrwinWidget) w;
	XGCValues	values;
	Arg	wargs[4];

	(*superclass->core_class.realize)(w, valueMask, attributes);
	new->crwin.gc = XtGetGC(new, 0L, &values);
	XSetFont(XtDisplay(new), new->crwin.gc, new->crwin.font_info->fid);
	XtSetArg(wargs[0], XtNinput, 1);
	XtSetValues(w, wargs, 1);
	
}
/* ARGSUSED */
static void
Resize(w)
Widget	w;
{
}
static void 
CrwinAction(w, event, x, y)
Widget	w;
XEvent	*event;
String	*x;
Cardinal	*y;
{
	printf("****CrwinAction called*******\n");
}
/**********************************************************************/
/*   Xterm compatable string action routine.			      */
/**********************************************************************/
static void 
CrwinString(w, event, x, y)
Widget	w;
XEvent	*event;
String	*x;
Cardinal	*y;
{	register int	i;
	register char	*cp;
	CrwinWidget	cw = (CrwinWidget) w;
	
	for (i = 0; i < *y; i++) {
		cp = x[i];
		while (*cp)
			push_back1(cw->crwin.push_ref, *cp++ & 0xff);
		}
}
/**********************************************************************/
/*   Push  into  the  keyboard  buffer  the  key  specified in CRISP  */
/*   notation.							      */
/**********************************************************************/
static void 
CrwinKey(w, event, x, y)
Widget	w;
XEvent	*event;
String	*x;
Cardinal	*y;
{	int	i;
	char	*cp;
	unsigned short *sp;
	int	len;
	CrwinWidget	cw = (CrwinWidget) w;
	
	for (i = 0; i < *y; i++) {
		cp = x[i];
		sp = cvt_string_to_code(cp, &len);
		while (len-- > 0)
			push_back1(cw->crwin.push_ref, *sp++);
		}
}
/**********************************************************************/
/*   Convert mouse button presses into pseudo keystrokes.	      */
/**********************************************************************/
static void 
CrwinButtonDown(w, event, x, y)
Widget	w;
XEvent	*event;
String	*x;
Cardinal	*y;
{
	handle_button(w, event->xbutton.button, event->xbutton.state, 
		event->xbutton.x,
		event->xbutton.y,
		BUTTON1_DOWN);
}
/**********************************************************************/
/*   Event  handler  to  handle  the mouse moving whilst a button is  */
/*   held down.							      */
/**********************************************************************/
static void 
CrwinButtonMotion(w, event, x, y)
Widget	w;
XEvent	*event;
String	*x;
Cardinal	*y;
{
	handle_button(w, 
		event->xmotion.state & Button1Mask ? Button1 : 
		event->xmotion.state & Button2Mask ? Button2 : 
		event->xmotion.state & Button3Mask ? Button3 : 
		event->xmotion.state & Button4Mask ? Button4 : 
				       Button5,
		event->xmotion.state, 
		event->xmotion.x,
		event->xmotion.y,
		BUTTON1_MOTION);
}
/**********************************************************************/
/*   Convert mouse button presses into pseudo keystrokes.	      */
/**********************************************************************/
static void 
CrwinButtonUp(w, event, x, y)
Widget	w;
XEvent	*event;
String	*x;
Cardinal	*y;
{
	handle_button(w, event->xbutton.button, event->xbutton.state, 
		event->xbutton.x,
		event->xbutton.y,
		BUTTON1_UP);
}
/**********************************************************************/
/*   Common code to handle mouse buttons going up and down.	      */
/**********************************************************************/
static void
handle_button(w, b, state, x, y, but)
Widget	w;
int	b;
int	state;
int	x, y;
int	but;
{
	CrwinWidget	cw = (CrwinWidget) w;
	int	button = b - Button1 + but;
	int	row, col;

	if (state & ControlMask)
		button |= MOD_CTRL;
	else if (state & ShiftMask)
		button |= MOD_SHIFT;
	else if (state & (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask))
		button |= MOD_META;
	/***********************************************/
	/*   Translate   pixel   co-ordinate  into  a  */
	/*   character  position.  The  y value seems  */
	/*   to  be  out  by  one with respect to the  */
	/*   actual   laying   out   policy   of  the  */
	/*   characters.			       */
	/***********************************************/
	get_xy(cw, &row, &col, x, y ? y - 1 : 0);
	push_back2(cw->crwin.push_ref, button, col, row);
	
}
/**********************************************************************/
/*   Come  here  on  a  keypress  event.  We  try  pushing the input  */
/*   character onto the push back stack.			      */
/**********************************************************************/
static void 
CrwinInput(w, event, x, y)
Widget	w;
XEvent	*event;
String	*x;
Cardinal	*y;
{	int	charcount;
	CrwinWidget	cw = (CrwinWidget) w;
	char	buf[BUFSIZ];
	XComposeStatus	compose;
	KeySym	keysym;
	char	*cp;
	int	mod_mask = 0;
	int	i;
	ref_t	*pref = cw->crwin.push_ref;
		
	if (event->type != KeyPress) {
		printf("****CrwinInput bad event received.*******\n");
		return;
		}
	charcount = XLookupString((XKeyEvent *) event, 
			buf, sizeof buf, &keysym, &compose);
	/* printf("KEYSYM: %04x mask=%02x charcount=%02d\r\n", keysym, event->xkey.state,charcount); */
	if (event->xkey.state & (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask))
		mod_mask = MOD_META;
	else if (event->xkey.state & ControlMask)
		mod_mask = MOD_CTRL;
	else if (event->xkey.state & ShiftMask)
		mod_mask = MOD_SHIFT;

	if (keysym >= XK_space && keysym <= XK_asciitilde) {
		if (mod_mask == MOD_META) {
			if (keysym >= XK_a && keysym <= XK_z)
				keysym -= 0x20;
			push_back1(pref, MOD_META | keysym);
			return;
			}
		/***********************************************/
		/*   Use  the  normal  key  mapping  for  non  */
		/*   meta oriented keys.		       */
		/***********************************************/
		for (cp = buf; cp < &buf[charcount]; cp++)
			push_back1(pref, *cp++ & 0xff);
		return;
		}
		
	/***********************************************/
	/*   Check for normal keypad keys.	       */
	/***********************************************/
	switch (keysym) {
		case XK_Left:
			keysym = XK_R10;
			break;
		case XK_Right:
			keysym = XK_R12;
			break;
		case XK_Up:
			keysym = XK_R8;
			break;
		case XK_Down:
			keysym = XK_R14;
			break;
		case XK_Prior:
			keysym = XK_R9;
			break;
		case XK_Next:
			keysym = XK_R15;
			break;
		}
	/***********************************************/
	/*   Check for Sun style keypad keys.	       */
	/***********************************************/
	if (keysym >= XK_R1 && keysym <= XK_R15) {
		i = keysym - XK_R1;
		switch (mod_mask) {
		  case MOD_CTRL:
		  	push_back1(pref, km_Rn[i].control);
			return;
		  case MOD_META:
		  	push_back1(pref, km_Rn[i].meta);
			return;
		  case MOD_SHIFT:
		  	push_back1(pref, km_Rn[i].shift);
			return;
		  default:
		  	push_back1(pref, km_Rn[i].normal);
			return;
		  }
		}
		
	if (keysym >= XK_KP_Space && keysym <= XK_KP_9) {
		int	j;
		i = keysym - XK_KP_Space;
		for (j = 0; km_keypad[j].code; j++) {
			if (km_keypad[j].code == keysym)
				break;
			}
		switch (mod_mask) {
		  case MOD_CTRL:
		  	push_back1(pref, km_keypad[j].control);
			return;
		  case MOD_META:
		  	push_back1(pref, km_keypad[j].meta);
			return;
		  case MOD_SHIFT:
		  	push_back1(pref, km_keypad[j].shift);
			return;
		  default:
		  	push_back1(pref, km_keypad[j].normal);
			return;
		  }
		}
	switch (keysym) {
	  case XK_Tab:
	  	if (mod_mask == MOD_SHIFT) {
			push_back1(pref, BACK_TAB);
			return;
			}
		/* Fallthru.. */
	  case XK_BackSpace:
	  case XK_Linefeed:
	  case XK_Return:
	  case XK_Escape:
	  	push_back1(pref, keysym & 0xff);
		return;
	  }
	if (keysym >= XK_F1 && keysym <= XK_F12) {
		push_back1(pref, mod_mask | (F(1) + keysym - XK_F1));
		return;
		}
		
	switch (keysym) {
	  case XK_Begin:
	  case XK_Home:
	  	push_back1(pref, KEY_HOME);
		break;
	  case XK_End:
	  	push_back1(pref, KEY_END);
		break;
	  case XK_Undo:
	  	push_back1(pref, KEY_UNDO);
		break;
	  case XK_Redo:
	  	push_back1(pref, CTRL_U);
		break;
	  case XK_Help:
	  	push_back1(pref, ALT_H);
		break;
	  case XK_Find:
	  	push_back1(pref, F(5));
		break;
	  case XK_Cancel:
	  	push_back1(pref, ESC);
		break;
	  case XK_Delete:
	  	push_back1(pref, 0x7f);
		break;
	  case XK_Insert:
	  	push_back1(pref, KEY_INS);
		break;
	  }
}
static void
redisplay(w, event)
Widget	w;
XExposeEvent *event;
{	CrwinWidget	cw = (CrwinWidget) w;
	int	font_height, width;
	int	start_row, start_col;
	int	end_row, end_col;
	extern int updating;

	if (updating)
		return;
	/***********************************************/
	/*   Convert     pixel     co-ordinates    to  */
	/*   character positions.		       */
	/***********************************************/
	font_height = cw->crwin.char_height;
	width = cw->crwin.char_width;

	start_row = event->y / font_height;
	start_col = event->x / width;
	end_row = (event->y + event->height) / font_height;
	end_col = (event->x + event->width) / width;
	
	/***********************************************/
	/*   Following  is  dirty  --  we shouldnt be  */
	/*   jumping out of the widget.		       */
	/***********************************************/
	if (start_row > 0)
		start_row--;

	x11_update_region(start_row, start_col, end_row + 1, end_col + 1);
	
	/***********************************************/
	/*   If  region  bounds  the  cursor, then we  */
	/*   need to redraw it.			       */
	/***********************************************/
	if (start_row <= cw->crwin.y && cw->crwin.y <= end_row + 1 &&
	    start_col <= cw->crwin.x && cw->crwin.x <= end_col + 1) {
	    	crwin_show_cursor(w, cw->crwin.y, cw->crwin.x, 
			cw->crwin.insert_cursor,
			&cw->crwin.cursor_char,
			cw->crwin.cursor_bg);
	    	}
}
/**********************************************************************/
/*   Action routine to handle redrawing of window.		      */
/**********************************************************************/
static void 
CrwinExpose(w, event, x, y)
Widget	w;
XEvent	*event;
String	*x;
Cardinal	*y;
{	CrwinWidget	cw = (CrwinWidget) w;

	if (!XtIsRealized(w) || need_resize)
		return;
	/***********************************************/
	/*   If  window  has  changed  size,  then we  */
	/*   need  to  resize  it.  Also  we  need to  */
	/*   clear  any  garbage  which may be at the  */
	/*   bottom or right edge of the window.       */
	/***********************************************/
	if (cw->crwin.win_height_allocated != cw->core.height ||
	    cw->crwin.win_width_allocated != cw->core.width) {
		need_resize = TRUE;
		cw->crwin.win_height_allocated = cw->core.height;
		cw->crwin.win_width_allocated = cw->core.width;
		return;
		}
	redisplay(w, event);
}
/**********************************************************************/
/*   Function to start the hilighting of a region via the mouse.      */
/**********************************************************************/
static void
CrwinStart_hilight(w, event)
Widget	w;
XButtonEvent	*event;
{	CrwinWidget	cw = (CrwinWidget) w;
	int	row, col;
	
	get_xy(cw, &row, &col, event->x, event->y);
}
/**********************************************************************/
/*   This   is   called  when  the  first  button  is  released  (by  */
/*   default).   This  finishes  the  user's  hilighting  and  means  */
/*   trigger ownership of the PRIMARY selection.		      */
/**********************************************************************/
static void
CrwinMake_sel(w, event)
Widget	w;
XButtonEvent	*event;
{	CrwinWidget	cw = (CrwinWidget) w;

}
/**********************************************************************/
/*   EThis  is  called  when  the  mouse  if  being dragged with the  */
/*   first  button  down  (by default). During this time, the region  */
/*   will be expanded.						      */
/**********************************************************************/
static void
CrwinExtend_hilight(w, event)
Widget	w;
XButtonEvent	*event;
{	CrwinWidget	cw = (CrwinWidget) w;

}
/**********************************************************************/
/*   Function to paste the selection into the current buffer.	      */
/**********************************************************************/
static void
CrwinPaste(w, event)
Widget	w;
XButtonEvent	*event;
{	CrwinWidget	cw = (CrwinWidget) w;

	XtGetSelectionValue(cw, XA_PRIMARY, XA_STRING, requestor_callback, 
		event, event->time);
}
/* ARGSUSED */
static void
requestor_callback(w, client_data, selection, type, value, length, format)
Widget	w;
XtPointer	client_data;
Atom	*selection;
Atom	*type;
XtPointer	*value;
unsigned long	*length;
int	*format;
{	CrwinWidget cw = (CrwinWidget) w;

	if ((*value == NULL) && (*length == 0)) {
		XBell(XtDisplay(cw), 100);
		XtWarning("xcr: no selection or selection timed out -- try again\n");
		return;
		}
		
	/***********************************************/
	/*   Check  that  the  <Ins>  key is assigned  */
	/*   to   "paste".   If  not  we're  probably  */
	/*   inside  a  popup  window  and  we  don't  */
	/*   want to paste into that.		       */
	/***********************************************/
	if (strcmp(find_macro_name(KEY_INS), "paste") == 0) {
		/***********************************************/
		/*   Make the insertion undo-able atomically.  */
		/***********************************************/
		u_chain();
		linsert((char *) value, (int) *length);
		update();
		}
	XtFree(value);
	
		
}
/**********************************************************************/
/*   Public  function  to  draw  a  string  at the specified row/col  */
/*   with the specified foreground and background colors.	      */
/**********************************************************************/
void
crwin_draw_string(w, row, col, str, len, fg, bg)
Widget	w;
int	row;
int	col;
char	*str;
int	len;
Pixel	fg;
Pixel	bg;
{	static Pixel last_fg = -1;
	static Pixel last_bg = -1;
	Display	*dpy = XtDisplay(w);
	CrwinWidget cw = (CrwinWidget) w;
	int	width;
	int	font_height;
	
	if (fg != last_fg) {
		last_fg = fg;
		XSetForeground(dpy, cw->crwin.gc, fg);
		}
	if (bg != last_bg) {
		last_bg = bg;
		XSetBackground(dpy, cw->crwin.gc, bg);
		}
	width = cw->crwin.char_width;
	font_height = cw->crwin.char_height;
	XDrawImageString(dpy, XtWindow(cw), cw->crwin.gc, 
		col * width, font_height * (row + 1), 
		str, len);
}
/**********************************************************************/
/*   Find  out  how  big  the screen is in terms of characters based  */
/*   on the current font.					      */
/**********************************************************************/
void
crwin_get_window_size(w, row, col)
Widget	w;
int	*row;
int	*col;
{	CrwinWidget	cw = (CrwinWidget) w;
	
	*row = cw->core.height / cw->crwin.char_height;
	*col = cw->core.width / cw->crwin.char_width;
	/***********************************************/
	/*   Make  some  attempt  at  stopping  silly  */
	/*   window sizes.			       */
	/***********************************************/
	if (*row < 4)
		*row = 4;
	if (*col < 10)
		*col = 10;
	if (cw->crwin.first_sizing == TRUE) {
		cw->crwin.first_sizing = FALSE;
		return;
		}
	/***********************************************/
	/*   Erase   the   bottom   (status  line  of  */
	/*   screen)   as   well   as   the  possibly  */
	/*   non-existant   line  underneath  it.  We  */
	/*   may   have   garbage   on  the  possibly  */
	/*   non-existant line.			       */
	/***********************************************/
	crwin_erase_line(w, *row - 1, *col);
	crwin_erase_line(w, *row, *col);
	/***********************************************/
	/*   Now  erase  the  column  which  is  just  */
	/*   past  the  number of columns. X may have  */
	/*   left the last character there.	       */
	/***********************************************/
	crwin_erase_col(w, *row, *col);
}
/**********************************************************************/
/*   Erase  a  line  at  the  specified  row.  col tells us how many  */
/*   columns we've got.						      */
/**********************************************************************/
void
crwin_erase_line(w, row, col)
Widget	w;
int	row;
int	col;
{
	char	buf[80];
	register int	i;

	for (i = 0; i < sizeof buf; )
		buf[i++] = ' ';
	for (i = 0; i < col; i += sizeof buf)
		crwin_draw_string(w, row, i, buf, sizeof buf, 
			x11_colors[0], x11_colors[0]);
}
/**********************************************************************/
/*   Routine to erase a column (single character width).	      */
/**********************************************************************/
void
crwin_erase_col(w, row, col)
Widget	w;
int	row;
int	col;
{	register int i;

	for (i = 0; i < row; i++)
		crwin_draw_string(w, i, col, " ", 1, 
			x11_colors[0], x11_colors[0]);
}
/**********************************************************************/
/*   Function  to  display  cursor  at  specified  position.  We get  */
/*   passed  two  colors to indicate the fg and bg colors to display  */
/*   the character in.						      */
/**********************************************************************/
void
crwin_show_cursor(w, row, col, ins_cursor, buf, normal_bg)
Widget	w;
int	row;
int	col;
int	ins_cursor;
char	*buf;
int	normal_bg;
{	CrwinWidget	cw = (CrwinWidget) w;
	Pixel	bg;

	cw->crwin.y = row;	
	cw->crwin.x = col;
	cw->crwin.cursor_char = buf[0];
	cw->crwin.insert_cursor = ins_cursor;
	cw->crwin.cursor_bg = normal_bg;
	if (ins_cursor)
		bg = cw->crwin.insert_cursor_color;
	else
		bg = cw->crwin.overtype_cursor_color;
	/***********************************************/
	/*   If   we  are  sitting  on  a  space  and  */
	/*   normal  background  of  character is the  */
	/*   same  as  the cursor color then we would  */
	/*   lose  the  character.  So  we map spaces  */
	/*   to  the  fuzzy blob character so that it  */
	/*   still shows up.			       */
	/***********************************************/
	if (buf[0] == ' ' && bg == x11_colors[normal_bg])
		buf[0] = 0x7f;
	crwin_draw_string(w, row, col, buf, 1, x11_colors[0], bg);
}
/**********************************************************************/
/*   Routine  to  convert  an (x,y) pixel co-ordinate to a character  */
/*   position.							      */
/**********************************************************************/
static void
get_xy(cw, row, col, x, y)
CrwinWidget	cw;
int	*row;
int	*col;
int	x;
int	y;
{
	
	*row = y / cw->crwin.char_height;
	*col = x / cw->crwin.char_width;
}
/**********************************************************************/
/*   Routine  to  read  and/or  test  the  availability  of the next  */
/*   keystroke.							      */
/**********************************************************************/
int
crwin_getkey(w, get_it)
Widget	w;
int	get_it;
{	CrwinWidget	cw = (CrwinWidget) w;
int n;

	n = get_push(cw->crwin.push_ref, get_it);
	return(n);
}
