/* cut-n-paste
 *
 * Gives the x3270 emulator the ability to pass text from
 * one screen to another.
 *
 * This program contains code fragments extracted from the MIT
 * X11 tape, therefore the MIT copyright applies.
 *
 * Maj Brian Boyter, US Army Foreign Science & Technology Center
 * boyter@fstc-chville.army.mil
 *
 * Modified by Pekka Nikander, Telecom Finland <Pekka.Nikander@ajk.tele.fi>
 *
 * Modified by George Haddad, Michigan State U.<georgeh@ibm.cl.msu.edu>:
 *	02/93 -	Fix loop in insert_selection() switch
 *	04/93 -	Apply patchlevel 01 to correct complier warnings
 */

/*
 * Copyright 1988 by the Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided 
 * that the above copyright notice appear in all copies and that both that 
 * copyright notice and this permission notice appear in supporting 
 * documentation, and that the name of M.I.T. not be used in advertising
 * or publicity pertaining to distribution of the software without specific, 
 * written prior permission. M.I.T. makes no representations about the 
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * The X Window System is a Trademark of MIT.
 *
 * The interfaces described by this header file are for miscellaneous utilities
 * and are not part of the Xlib standard.
 */

#include <stdio.h>
#include <sys/types.h>
#ifndef NOUTSNAME
#include <sys/utsname.h>
#endif
#include <sys/socket.h>
#include <netdb.h>
#include <X11/Intrinsic.h>
#include <X11/Xatom.h>
#include "3270.h"
#include "3270_enc.h"
#include "X.h"

extern unsigned char *screen_buf;
extern Bool kybdlock;
extern int char_height;
extern int char_width;
extern unsigned char cg2asc[256];
extern unsigned char asc2cg[256];
extern Widget toplevel;
static int copy_cursor_start = -1;	/* screen pos - start of copy opn */
static int copy_cursor_stop  = -1;	/* screen pos - end of copy opn   */
static int copy_cursor_pstop = -1; 	/* screen pos - prev end of copy opn */
static char clip_buf[3600];		/* selection buffer */
Atom the_selection = None;

/*
 *  This routine is called to highlight or unhighlight the given region
 */

highlight(cursor_start, cursor_stop, on_off)
int cursor_start, cursor_stop;
int on_off;
{
	int i;
	int row_start;
	int row_stop;
	int row;
	char row_buf[144];

	if (cursor_start > cursor_stop) {
		i = cursor_start;
		cursor_start = cursor_stop;
		cursor_stop = i;
	}

	row_start = BA_TO_ROW(cursor_start);
	row_stop  = BA_TO_ROW(cursor_stop);

	if (row_start == row_stop) {
		get_text (row_buf, cursor_start, cursor_stop);
		for (i=0; i<strlen(row_buf); i++)
			put_copy_cursor(cursor_start+i, on_off);
	} else {
		for (row=row_start; row<row_stop+1; row++) {
			if (row == row_start) {
				get_text(row_buf, cursor_start, row*COLS+COLS-1);
				for (i=0; i<strlen(row_buf); i++)
					put_copy_cursor(cursor_start+i, on_off);
			}
			if (row == row_stop) {
				get_text(row_buf, row*COLS, cursor_stop);
				for (i=0; i<strlen(row_buf); i++)
					put_copy_cursor(row*COLS+i, on_off);
			}
			if (row!=row_start && row!=row_stop) {
				get_text(row_buf, row*COLS, row*COLS+COLS-1);
				for (i=0; i<strlen(row_buf); i++)
					put_copy_cursor(row*COLS+i, on_off);
			}
		}
	}
}
/*
 *  This routine is called to indicate the end of the text
 *  we want to "cut". (In OpenLook, this event occurs when the middle
 *  button is pressed).
 */
copy_stop (w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal num_params;
{
	int neg, pneg;

	if (kybdlock || copy_cursor_start<0)
		return 0;
	copy_cursor_stop = ROWCOL_TO_BA(Y_TO_ROW (event->xbutton.y), 
		X_TO_COL (event->xbutton.x));

	pneg = copy_cursor_pstop < copy_cursor_start;
	neg  = copy_cursor_stop  < copy_cursor_start;
				      
	if (copy_cursor_pstop == -1 || neg != pneg) {
		if (copy_cursor_pstop != -1) {
			highlight(copy_cursor_start, copy_cursor_pstop, 0);
		}
		highlight(copy_cursor_start, copy_cursor_stop, 1);
	} else {
		if (copy_cursor_pstop < copy_cursor_stop) {
			highlight(copy_cursor_pstop, copy_cursor_stop, neg == 0);
		} else {
			highlight(copy_cursor_stop, copy_cursor_pstop, neg == 1);
		}
	}

	copy_cursor_pstop = copy_cursor_stop;
}

/*
 *  This routine is called to indicate the beginning of the text
 *  we want to "cut". (In OpenLook, this event occurs when the left
 *  button is pressed).
 */
copy_start (w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal num_params;
{
	void copy_abort ();

	if (kybdlock) return 0;
	copy_abort();
	copy_cursor_start = ROWCOL_TO_BA(Y_TO_ROW (event->xbutton.y), 
					 X_TO_COL (event->xbutton.x));
	put_copy_cursor(copy_cursor_start, 1);
	copy_cursor_stop  = -1;
	copy_cursor_pstop = -1;
}

/*
 *  This routine is called when the cut-n-paste operation is done.
 */
void copy_done (w, selection, target)
Widget w;
Atom *selection;
Atom *target;
{
	void copy_abort ();

#if 0
	if (*target == XA_STRING)
		copy_abort();
#endif
}

/*
 *  This routine is called when we lose ownership of the selection.
 */
void copy_abort ()
{
	int i;
	int row_start;
	int row_stop;
	int row;
	char row_buf[144];

	if (copy_cursor_start < 0 || copy_cursor_stop < 0)
		return;

	highlight(copy_cursor_start, copy_cursor_stop, 0);

	copy_cursor_start = -1;
	copy_cursor_stop  = -1;
	copy_cursor_pstop = -1;

	XtDisownSelection (toplevel, the_selection, CurrentTime);
}

/*
 *  This routine is called when the "copy" key is pressed (L6 on the Sun-4 kybd).
 */
copy(w, copy_event, params, num_params)
Widget w;
XKeyEvent *copy_event;
String *params;
{
	Boolean status;
	void copy_abort();
	void copy_done();
	Boolean copy_convert_proc();
	int cursor_start, cursor_stop;
	int row_start;
	int row_stop;
	int row;
	char row_buf[144];

	if (copy_cursor_start<0 || copy_cursor_stop<0)
		return 0;

	if (copy_cursor_start < copy_cursor_stop) {
		cursor_start = copy_cursor_start;
		cursor_stop  = copy_cursor_stop;
	} else {
		cursor_start = copy_cursor_stop;
		cursor_stop  = copy_cursor_start;
	}
		
	row_start = BA_TO_ROW(cursor_start);
	row_stop  = BA_TO_ROW(cursor_stop);

	if (row_start == row_stop) {
		get_text (clip_buf, cursor_start, cursor_stop);
		truncate (clip_buf);
	} else {
		clip_buf[0] = '\0';
		for (row=row_start; row<row_stop+1; row++) {
			if (row == row_start)
				get_text(row_buf, cursor_start, row*COLS+COLS-1);
			if (row == row_stop)
				get_text(row_buf, row*COLS, cursor_stop);
			if (row!=row_start && row!=row_stop)
				get_text(row_buf, row*COLS, row*COLS+COLS-1);
			truncate (row_buf);
			strcat (clip_buf, row_buf);
			if (row != row_stop)
				strcat (clip_buf, "\n");
		}
	}

	status = XtOwnSelection (toplevel, the_selection,
		copy_event->time, copy_convert_proc, copy_abort, copy_done);
	if (status == FALSE) {
	  	sprintf(row_buf, "copy(XtOwnSelection XA_%s) failed\n",
			XGetAtomName(XtDisplay(w), the_selection));
		XtWarning(row_buf);
	}		
}

/*
 *  This routine is called when the "paste" key is pressed (L8 on the Sun-4 kybd).
 */
paste(w, paste_event, params, num_params)
Widget w;
XKeyEvent *paste_event;
String *params;
{
	void insert_selection();

	XtGetSelectionValue(toplevel, the_selection,
		XA_STRING, insert_selection, NULL, paste_event->time);
}

/*
 *  At the end of a paste operation, this routine transfers the text in
 *  the selection to the screen.
 */
void insert_selection(w, client_data, selection, type, buf, length, format)
Widget w;
XtPointer client_data;
Atom *selection;
Atom *type;
XtPointer buf;
unsigned long *length;
int *format;
{
	char *cp;
	char cgcode;

	if (*type == XT_CONVERT_FAIL || *length <= 0)
		return;

	cp = buf;

	/* 02/93 GJH:                           */
	/* End loop if kybd locks since kybd.c  */
	/* will stop processing chars anyway.   */
	/* Use 'break' instead of 'continue' to */
	/* iterate for special chars to prevent */
	/* infinite loop.                       */

	while (*cp != '\0' && !kybdlock ) {
		switch (*cp) {
		      case '\b': key_Left();		break;
		      case '\f': key_Clear(); 		break;
		      case '\n': key_Newline();		break;
		      case '\r': key_Newline();		break;
		      case '\t': key_FTab();		break;
		      default:
			if ((*cp >= ' ' && *cp <= '~') || (*cp == 0x1B) 
			      || (*cp >= '\240' && *cp <= '\377')) {
				cgcode = asc2cg[*cp];
				if (cgcode != CG_NULLBLANK) {
					(void) key_Character (cgcode);
				}
			}
		}
		cp++;
    	}

	XtFree (buf);
#if 0
	XtDisownSelection (w, the_selection, CurrentTime);
#endif
}

/*
 *  This routine turns the cursor on-or-off at screen location "addr".
 */
put_copy_cursor(addr, on_off)
int addr;
int on_off;
{
	int  color;
	u_char *fa;
	u_char *get_field_attribute();

	fa = get_field_attribute(addr);

	color = fa_color(*fa);
	if (color == FA_INT_ZERO_NSEL)
		color = FA_INT_NORM_SEL;
	put_cursor(addr, color, on_off);
}

/*
 *  This routine reads one line of text from the screen
 *  and transfers to a buffer for processing (called from
 *  routine "copy"). The text is also translated from screen
 *  code to ascii.
 */
get_text(buf, start, stop)
int start;
int stop;
char *buf;
{
	u_char *cp1;
	char *cp2;
	int i;

	cp2 = buf;
	cp1 = &screen_buf[0];
	for (i=start; i<stop+1; i++)
		*cp2++ = cg2asc[*(cp1+i)];
	*cp2 = '\0';
}

/*
 *  remove trailing blanks from a string.
 */
truncate(s)
char *s;
{
	char *cp;

	cp = s + strlen(s) -1;
	while (cp >= s && *cp == ' ')
		*cp-- = '\0';
}

/*
 *  copy_convert_proc
 *  returns data requested from the window that owns the selection
 *  to the window trying to receive the selection.
 *  XA_LENGTH and XA_STRING are required; the other targets are
 *  included for compatability.
 */
Boolean
copy_convert_proc(w, selection, target, type, value, length, format)
Widget w;
Atom *selection;
Atom *target;
Atom *type;
XtPointer *value;
unsigned long *length;
int *format;
{
	XSelectionRequestEvent *req;
	Atom *targetP;
	Atom *std_targets;
	Display *d;
	long *temp;
	int get_text();

	req = XtGetSelectionRequest(w, *selection, (XtRequestId)NULL);
	d = XtDisplay(w);

#ifdef DEBUG
	printf ("copy_convert_proc: called with selection = %d %s\n",
		*selection, XGetAtomName(d, *selection));
	printf ("                 : called with target    = %d %s\n",
		*selection, XGetAtomName(d, *target));
#endif /* DEBUG */

	if (*target == XInternAtom(d, "TARGETS", False)) {
		*value = XtMalloc(sizeof(Atom)*13);
		targetP = *(Atom**)value;
		*targetP++ = XInternAtom(d, "CHARACTER_POSITION", False);
		*targetP++ = XInternAtom(d, "HOSTNAME",           False);
		*targetP++ = XInternAtom(d, "IP_ADDRESS",         False);
		*targetP++ = XInternAtom(d, "LENGTH",             False);
		*targetP++ = XInternAtom(d, "LIST_LENGTH",        False);
		*targetP++ = XInternAtom(d, "OWNER_OS",           False);
		*targetP++ = XInternAtom(d, "STRING",             False);
		*targetP++ = XInternAtom(d, "TEXT",               False);
		*targetP++ = XInternAtom(d, "TIMESTAMP",          False);
		*targetP++ = XInternAtom(d, "USER",               False);
		*length = targetP - *(Atom **) value;
		*type = XA_ATOM;
		*format = 32;
		return True;
	}

	if (*target == XInternAtom(d, "CHARACTER_POSITION", False)) {
		temp = (long *) XtMalloc(2 * sizeof(long));
		temp[0] = (long) 0;
		temp[1] = (long) copy_cursor_stop-copy_cursor_start;
		*value = (caddr_t) temp;
		*type = XInternAtom(d, "SPAN", False);
		*length = 2L;
		*format = 32;
		return True;
	}

	if (*target == XInternAtom(d, "HOSTNAME", False)) {
		char hostname[1024];
		hostname[0] = '\0';
		*length = gethostname(hostname, sizeof hostname);
		if (*length == 0) return False;
		*value = XtNewString(hostname);
		*type = XA_STRING;
		*format = 8;
		return True;
	}

	if (*target == XInternAtom(d, "IP_ADDRESS", False)) {
		char hostname[1024];
		struct hostent *hostent;
		hostname[0] = '\0';
		(void) gethostname (hostname, sizeof hostname);
		hostent = gethostbyname (hostname);
		if (hostent->h_addrtype != AF_INET) return False;
		*length = hostent->h_length;
		*value = XtMalloc(*length);
		bcopy (hostent->h_addr, *value, *length);
		*type = XInternAtom(d, "NET_ADDRESS", False);
		*format = 8;
		return True;
	}

	if (*target == XInternAtom(d, "LIST_LENGTH", False) ||
	    *target == XInternAtom(d, "LENGTH",      False)) {
		temp = (long *) XtMalloc(sizeof(long));
		if (temp == NULL)
			XtError ("could not allocate 4 bytes of memory\n");
		if (*target == XInternAtom(d, "LIST_LENGTH", False))
			*temp = 1L;
		else                    /* *target == XA_LENGTH(d) */
			*temp = (long) strlen(clip_buf);
		*value = (caddr_t) temp;
		*type = XA_INTEGER;
		*length = 1L;
		*format = 32;
#ifdef DEBUG
		printf ("                 : returning XA_INTEGER %d\n", *temp);
#endif /* DEBUG */
		return True;
	}

	if (*target == XInternAtom(d, "OWNER_OS", False)) {
#ifndef NOUTSNAME
		struct utsname *osname;
		uname (osname);
		*value = XtNewString(osname->sysname);
		*type = XInternAtom(d, "STRING", False);
		*length = strlen(osname->sysname);
#else
		*value = XtNewString("SunOS");
		*type  = XInternAtom(d, "STRING", False);
		*length = strlen(*value);
#endif
		*format = 8;
		return True;
	}

	if (*target == XInternAtom(d, "STRING",        False) ||
	    *target == XInternAtom(d, "TEXT",          False) ||
	    *target == XInternAtom(d, "COMPOUND_TEXT", False)) {
		if (*target == XInternAtom(d, "COMPOUND_TEXT", False))
			*type = *target;
		else
			*type = XInternAtom(d, "STRING", False);
		*value  = XtNewString(clip_buf);
		*format = 8;
		*length = strlen(clip_buf);
		return True;
	}

	if (*target == XInternAtom(d, "TIMESTAMP", False)) {
		*value = XtMalloc(4);
		*(long*)*value = req->time;
		*type = XA_INTEGER;
		*length = 1;
		*format = 32;
		return True;
	}

	if (*target == XInternAtom(d, "USER", False)) {
		char *name = (char*)getenv("USER");
		if (name == NULL) return False;
		*value = XtNewString(name);
		*type = XA_STRING;
		*length = strlen(name);
		*format = 8;
		return True;
	}

	return False;
}
