[comp.sources.amiga] v02i095: snipit - cut/paste text from/to window with mouse v1.2

page@swan.ulowell.edu (Bob Page) (12/11/88)

Submitted-by: scott@applix.uucp (Scott Evernden)
Posting-number: Volume 2, Issue 95
Archive-name: intuition/snipit12.1

                   - SnipIt 1.2 - 

    SnipIt is an input handler wedge which allows
    you to clip text from any window, and then paste
    that text anywhere, as though you had typed it
    on the keyboard.

    New in SnipIt 1.2 are the following:

    o Now attempts to work in all windows and consoles.
      P-Edit and VT100 now can be snipped from.

    o Automatically detaches itself and runs in the
      background- no Run/RunBack required.

    o Built-in mini help (type: snipit h).

    o Keyboard option to insert a short string at the
      beginning of each pasted line.

    o Options to move/adjust/correct snip/recognizer cell
      area independently for consoles and windows.

    o Can recognize highlighted text.

    o Option to freeze window layer while snipping.

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	readme
#	makefile
#	hs.h
#	detach.c
#	hand.c
#	main.c
#	patch.c
#	reco.c
#	snipit.keys
#	patch.doc
#	snipit.doc
# This archive created: Sun Dec 11 00:43:21 1988
cat << \SHAR_EOF > readme

                   - SnipIt 1.2 - 

    SnipIt is an input handler wedge which allows
    you to clip text from any window, and then paste
    that text anywhere, as though you had typed it
    on the keyboard.

    New in SnipIt 1.2 are the following:

    o Now attempts to work in all windows and consoles.
      P-Edit and VT100 now can be snipped from.

    o Automatically detaches itself and runs in the
      background- no Run/RunBack required.

    o Built-in mini help (type: snipit h).

    o Keyboard option to insert a short string at the
      beginning of each pasted line.

    o Options to move/adjust/correct snip/recognizer cell
      area independently for consoles and windows.

    o Can recognize highlighted text.

    o Option to freeze window layer while snipping.


      - Scott Evernden

SHAR_EOF
cat << \SHAR_EOF > makefile
CFLAG = 
LFLAGS = -w
CFLAGS = $(CFLAG) +ihs.i
OBJS = main.o hand.o reco.o

snipit: $(OBJS) detach.o
	ln $(LFLAGS) -o $* $(OBJS) detach.o -lc
	beep

detach.o: detach.c

$(OBJS): hs.i

hs.i: hs.h
	cc -o nil: +hhs.i hs.h

final:
	@make LFLAGS=

SHAR_EOF
cat << \SHAR_EOF > hs.h
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <exec/devices.h>
#include <exec/execbase.h>
#include <exec/interrupts.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <devices/conunit.h>
#include <devices/keymap.h>
#include <devices/input.h>
#include <devices/inputevent.h>

struct ConsoleBase {
	struct Device conDevice;
	char reserved[26];
	struct List conUnits;
};

#include <functions.h>

/* mouse states */
#define M_UP	0
#define M_MOVE	1
#define M_DOWN	2

/* 'snip' container defines */
#define CON_SNIP	1
#define WIN_SNIP	2

#define PRE_SIZE	32


SHAR_EOF
cat << \SHAR_EOF > detach.c
/* Copyright (C) 1986,1987 by Manx Software Systems, Inc. */

/* Hacked up for SnipIt 1.2 use - Scott Evernden */

#include <exec/memory.h>
#include <libraries/dosextens.h>
#include <functions.h>

struct Process *_FindTask();
void *_AllocMem();
long _Open();

#define STACK_SIZE	3000L

extern BPTR output;
extern long running;

extern char *_procname;
extern char *_detach_name;
static long _alen = 0;
static char *_aptr = 0;

#asm
	dseg
	public	__savsp
	cseg
#endasm

do_detach(alen, aptr)
long *alen;
char **aptr;
{
	register struct CommandLineInterface *cli;
	register struct Process *pp;
	register unsigned short c;
	register char *cp;
	register long l;
	long *lp, *sav;
	struct MemList *mm;

	pp = _FindTask(0L);

	/* first time through!! */
	if (pp->pr_CLI) {

		cli = (struct CommandLineInterface *) ((long)pp->pr_CLI << 2);
		l = cli->cli_Module;

		output = (BPTR) _Open("*", MODE_OLDFILE);

		_alen = *alen;
		_aptr = _AllocMem(_alen, 0L);
		movmem(*aptr, _aptr, (int)_alen);

		cp = (char *)((long)cli->cli_CommandName << 2);
		_detach_name = AllocMem((long)cp[0]+1, 0L);
		movmem(cp, _detach_name, cp[0]+1);
#asm
		move.l	__savsp,-(sp)
#endasm
		cli->cli_Module = 0;
		CreateProc(_procname, 0L, l, STACK_SIZE);
		while (running == 0)
			Delay(5L);
#asm
		move.l	(sp)+,sp
		move.l	#0,d0
		rts
#endasm
	}

	/* second time through */
	else if (strcmp(pp->pr_Task.tc_Node.ln_Name, _procname) == 0) {

		lp = (long *)((long)pp->pr_SegList << 2);
		lp = (long *)(lp[3] << 2);
		sav = lp;
		c = 2;
		while (lp) {
			lp = (long *)(*lp << 2);
			c++;
		}
		mm = _AllocMem((long)sizeof(struct MemList)+
							(c-1)*sizeof(struct MemEntry), 0L);
		lp = sav;
		mm->ml_NumEntries = c;
		c = 0;
		while (lp) {
			mm->ml_me[c].me_Addr = (APTR)lp - 1;
			mm->ml_me[c].me_Length = lp[-1];
			lp = (long *)(*lp << 2);
			c++;
		}
		mm->ml_me[c].me_Addr = (APTR)_aptr;
		mm->ml_me[c++].me_Length = _alen;
		mm->ml_me[c].me_Addr = (APTR)_detach_name;
		mm->ml_me[c++].me_Length = _detach_name[0] + 1;

		AddTail(&((struct Task *)pp)->tc_MemEntry, mm);

		*alen = _alen;
		*aptr = _aptr;
	}
}

SHAR_EOF
cat << \SHAR_EOF > hand.c
/* :ts=4
 *
 * Amiga SnipIt 1.2
 * (c) (opyright 1987,1988 - Scott Evernden - All Rights Reserved
 * 
 * hand.c - input event handler & snipper & lib function replacements
 *
 */

#include "hs.h"

int state;					/* mouse state */
int snip;					/* container type which is highlighted */
int maxx, maxy;				/* maximum cells in snipped layer */
int origx, origy;			/* pixel origin in conunit */
int charx, chary;			/* pixel size of char cell of conunit */
int lastx, lasty;			/* last cell highlighted */
int con_offx, con_offy;		/* correction offsets for console windows */
int win_offx, win_offy;		/* correction offsets for non-console windows */
int snipx[2], snipy[2];		/* snip area; top/left to bottom/right */
int left, right;			/* sides of snip area, irrespective of rows */

struct TextFont *capttf;	/* non-NULL indicates recognition required */
struct Window *snipw;		/* clipped text window */
struct RastPort *sniprp;	/* rastport of clipped text window */

struct RastPort captrp;		/* rastport holding clipped snip area */
struct BitMap captbm;		/* bitmap into which snipped area is copied */
int captx, capty;			/* dimensions of capture map */
int jobFlag;				/* flag input handler to ignore mouse events */
int lock_layer;				/* lock window/console layer while snipping */
int windows=1;				/* include windows in layer search */

int cmd1 = IEQUALIFIER_LCOMMAND;
int cmd2 = IEQUALIFIER_LALT;

extern struct IntuitionBase *IntuitionBase;
extern struct ConsoleBase *ConsoleBase;
extern struct Task *myTask;
extern int mySignal;

/* this does one at a time */
struct InputEvent *doEvent();

/*********************************************/
#asm

	public	_geta4

; Trap these Gfx/Intui functions; purpose is to remove hilighting, if nec.
; Not all possible cases are handled (window resizing, for example), but
; the most common ones are.

	public	_myRectFill
	public	_myScrollRaster
	public	_myCloseWindow

	public	_oldRectFill
	public	_oldScrollRaster
	public	_oldCloseWindow

_myRectFill:			; pardon me
	move.l	a4,-(sp)
	jsr		_geta4				; establish addressing
	tst.w	_snip
	beq.s	1$					; no highlighted area; get out
	cmpa.l	_sniprp,a1
	bne.s	1$					; not the same RastPort; get out
	movem.l	a1/d0-d5,-(sp)
	jsr		_clearHi			; de-highlight
	movem.l	(sp)+,a1/d0-d5
1$:	movea.l	_oldRectFill,a0
	movea.l	(sp)+,a4
	jmp		(a0)				; continue into library

_myScrollRaster:		; 'scuse
	move.l	a4,-(sp)
	jsr		_geta4
	tst.w	_snip
	beq.s	1$
	cmpa.l	_sniprp,a1
	bne.s	1$
	movem.l	a1/d0-d5,-(sp)
	jsr		_clearHi
	movem.l	(sp)+,a1/d0-d5
1$:	movea.l	_oldScrollRaster,a0
	movea.l	(sp)+,a4
	jmp		(a0)

_myCloseWindow:
	move.l	a4,-(sp)
	jsr		_geta4
	tst.w	_snip
	beq.s	1$
	cmpa.l	_snipw,a0
	bne.s	1$					; not the same Window; get out
	clr.w	_snip
	clr.l	_capttf				; Window closing; reset indicators
1$:	movea.l	_oldCloseWindow,a1
	movea.l	(sp)+,a4
	jmp		(a1)

;************************

; input event stream glue
	public	_handlerCode

_handlerCode:
	movem.l	d2/d3/a0/a1/a4/a6,-(sp)	; i think this is right
	jsr		_geta4
	jsr		_myHandler
	movem.l	(sp)+,d2/d3/a0/a1/a4/a6
	rts

#endasm
/*********************************************/

/* SnipIt's input event handler */
struct InputEvent *myHandler(d2, d3, ev)
long d2, d3;
register struct InputEvent *ev;
{
	register struct InputEvent *inev, **evp;
	struct InputEvent *outev;

	if (jobFlag)
		return ev;

	inev = ev;
	outev = NULL;

	/* back-pointer */
	evp = &outev;

	/*
	 * Loop thru the possibly linked list of events.
	 * We remove the ones having meaning to SnipIt, and leave the others
	 */
	while (inev) {
		ev = inev;
		inev = ev->ie_NextEvent;

		/* ensure that main code isn't running and this is a mouse event */
		if (ev->ie_Class == IECLASS_RAWMOUSE)
			ev = doEvent(ev);

		/* patch back-pointer */
		if (*evp = ev)
			evp = &ev->ie_NextEvent;
	}

	/* pass 'em along */
	return outev;
}

/*********************************************/

/* handle a single (mouse) input event */
struct InputEvent *doEvent(ev)
register struct InputEvent *ev;
{
	static int lflag, rflag, cmd;
	register UWORD code;
	register int x, y;

	x = ev->ie_X;
	y = ev->ie_Y;
	code = ev->ie_Code;

	/* in mid-snip? */
	if (lflag && code == IECODE_NOBUTTON &&
							(ev->ie_Qualifier & IEQUALIFIER_LEFTBUTTON)) {

		mouse(M_MOVE, x, y);
		return ev;
	}

	/* button transition event... */
	code &= ~IECODE_UP_PREFIX;

	/* mouse button down event? */
	if ((ev->ie_Code & IECODE_UP_PREFIX) == 0)  {

		if (cmd = ev->ie_Qualifier & (cmd1 | cmd2)) {

			if (!rflag && code == IECODE_LBUTTON) {
				lflag++;
				mouse(M_DOWN, x, y);
			}

			else if (!lflag && code == IECODE_RBUTTON)
				rflag++;		/* note transition */

			return NULL;
		}
	}

	/* release of left mouse button? */
	else if (lflag && code == IECODE_LBUTTON) {
		lflag = 0;
		mouse(M_UP, x, y);
		return NULL;
	}

	/* release of right mouse button? */
	else if (rflag && code == IECODE_RBUTTON) {
		rflag = 0;

		/* kick main code */
		jobFlag = cmd;
		Signal(myTask, 1L << mySignal);	
		return NULL;
	}

	/* event back into stream */
	return ev;
}

/*********************************************/

/* find a console containing the layer holding (x,y); return success flag */
int whichConsole(layer, x, y)
struct Layer *layer;
int x, y;
{
	register struct ConUnit *cu, *rcu;
	register struct Node *node;

	/* result conunit */
	rcu = NULL;

	_Forbid();

	/* begin at the beginning */
	node = ConsoleBase->conUnits.lh_Head;

	/* next is NULL when back at listhead */
	while (node->ln_Succ) {
		cu = (struct ConUnit *) node;

		/* see if console window's layer is the same */
		if (cu && cu->cu_Window && cu->cu_Window->RPort->Layer == layer) {
			rcu = cu;	/* found it */
			break;
		}

		node = node->ln_Succ;
	}

	Permit();

	if (rcu) {
		snip = CON_SNIP;
		snipw = rcu->cu_Window;
		sniprp = snipw->RPort;
		charx = rcu->cu_XRSize;
		chary = rcu->cu_YRSize;
		origx = rcu->cu_XROrigin;
		origy = rcu->cu_YROrigin;
		maxx = rcu->cu_XMax;
		maxy = rcu->cu_YMax;
	}

	return rcu != NULL;
}

/*********************************************/

/* find a window containing the layer holding (x,y); return success flag */
int whichWindow(s, layer, x, y)
struct Screen *s;
struct Layer *layer;
int x, y;
{
	register struct Window *w, *rw;

	if (!windows)
		return NULL;

	/* result window */
	rw = NULL;

	_Forbid();

	w = s->FirstWindow;
	while (w) {
		if (w->WLayer == layer) {
			rw = w;
			break;
		}

		w = w->NextWindow;
	}

	Permit();

	if (rw) {
		snip = WIN_SNIP;
		snipw = rw;
		sniprp = snipw->RPort;
		charx = sniprp->Font->tf_XSize;
		chary = sniprp->Font->tf_YSize;
		origx = rw->BorderLeft;
		origy = rw->BorderTop;
		maxx = (rw->Width - rw->BorderRight - origx) / charx - 1;
		maxy = (rw->Height - rw->BorderBottom - origy) / chary - 1;
	}

	return rw != NULL;
}

/*********************************************/

/* handle meaningful mouse activity */
mouse(event, mx, my)
int event;
register int mx, my;
{
	static struct Screen *s;
	static struct Layer *layer;

	if (event == M_DOWN)
		s = IntuitionBase->FirstScreen;

	/* correct raw positions according to this screen */
	if (!(s->ViewPort.Modes & LACE))
		my /= 2;

	mx += s->MouseX - 2;		/* accumulate deltas */
	my += s->MouseY - 1;		/* x,y adjusted for feel */

	if (event == M_DOWN) {

		/* de-hilight, if nec. */
		if (snip)
			hiSnip(0);

		/* turn flag off */
		snip = 0;

		/* locate pointed-at layer */
		if ((layer = WhichLayer(&s->LayerInfo, (long) mx, (long) my)) &&
		    (whichConsole(layer, mx, my) || whichWindow(s, layer, mx, my))) {

			/* if mouse down in title line area, remain clear */
			if (my - snipw->TopEdge < origy)
				snip = 0;
		}
	}

	/* nothing interesting */
	if (!snip)
		return;

	/* mouse to window-relative cell coordinates */
	mx = (mx - snipw->LeftEdge - origx) / charx;
	if (mx < 0)
		mx = 0;
	else if (maxx < mx)
		mx = maxx;

	my = (my - snipw->TopEdge  - origy) / chary;
	if (my < 0)
		my = 0;
	else if (maxy < my)
		my = maxy;

	/* highlighting games ahead */

	if (event == M_DOWN) {
		state = M_DOWN;
		if (lock_layer)
			LockLayerRom(layer);

		snipx[0] = mx;
		snipy[0] = my;
		hiLite(mx, my, 1, 1);
	}

	else if (event == M_UP) {
		snipx[1] = mx;
		snipy[1] = my;
		if (state == M_MOVE)
			hiLite(lastx, lasty, 1, 1);
		hiLite(snipx[0], snipy[0], 1, 1);
		hiSnip(charx == 8);

		state = M_UP;
		if (charx != 8)		/* only able to do 8 pixel wide fonts */
			clearHi();

		if (lock_layer)
			UnlockLayerRom(layer);
	}

	else if (mx != lastx || my != lasty) {
		if (state == M_MOVE)
			hiLite(lastx, lasty, 1, 1);
		hiLite(lastx = mx, lasty = my, 1, 1);
		state = M_MOVE;
	}
}

/*********************************************/

/* de-highlight; snip is known to be set */
clearHi()
{
	if (state == M_UP)
		hiSnip(0);

	else if (lock_layer)
		return;

	else {
		hiLite(snipx[0], snipy[0], 1, 1);
		if (state == M_MOVE)
			hiLite(lastx, lasty, 1, 1);
	}

	snip = 0;
}

/*********************************************/

/* local cleanup for this module */
finiHand()
{
	int i;
	PLANEPTR p;

	if (snip)
		clearHi();

	if (p = captbm.Planes[0])
		FreeRaster(p, (long) captx, (long) capty);
}

/*********************************************/

/* manufacture rastport with bitmap, ready to receive captured bitmap */
int allocMap(sx, sy)
int sx, sy;
{
	PLANEPTR p;

	/* release previous, if any */
	if (p = captbm.Planes[0]) {
		FreeRaster(p, (long) captx, (long) capty);
		captbm.Planes[0] = NULL;
	}

	/* record new map size */
	captx = sx;
	capty = sy;

	/* build new bitmap to contain snipped area */
	InitBitMap(&captbm, 1L, (long) captx, (long) capty);
	InitRastPort(&captrp);
	captrp.BitMap = &captbm;

	/* allocate bitmap planes */
	p = (PLANEPTR) AllocRaster((long) captx, (long) capty);
	if (!p)
		return 0;

	captbm.Planes[0] = p;
	return 1;
}

/*********************************************/

/* video reverse the capture area; perform capture if flagged */
hiSnip(capt)
int capt;
{
	register int px, py, qx, qy, tx, ty;
	long ox, oy;

	/* need these vars to be handy */
	tx = maxx;

	/* take care of backward snip areas */
	if (snipy[0] < snipy[1] ||
		snipy[0] == snipy[1] && snipx[0] <= snipx[1]) {

		px = snipx[0];
		py = snipy[0];
		qx = snipx[1];
		qy = snipy[1];
	}
	else {
		px = snipx[1];
		py = snipy[1];
		qx = snipx[0];
		qy = snipy[0];
	}

	/* only if capturing more than 1 cell... */
	if (capt && (px != qx || py != qy)) {

		/* left and right sides */
		left = px;
		right = qx;

		/* get a new bitmap */
		if (allocMap((tx + 1) * charx, (qy - py + 1) * chary)) {

			/* copy console contents into snip-map */
			ox = origx;
			oy = origy + py * chary;

			if (snip == CON_SNIP) {		/* user-specified corrections */
				ox += con_offx;
				oy += con_offy;
			}
			else {
				ox += win_offx;
				oy += win_offy;
			}

			ClipBlit(sniprp, ox, oy, &captrp, 0L, 0L,
							(long) captx, (long) capty, 0xC0L);

			/* note new info in snip-map */
			capttf = sniprp->Font;
		}

		else {
			/* no memory, act dumb */
			snip = captx = capty = 0;
			return;
		}
	}

	/* perform hilighting */
	while (py <= qy) {

		if (py == qy)
			tx = qx;

		hiLite(px, py, tx - px + 1, 1);

		px = 0;
		py++;
	}
}

/*********************************************/

/* low-lever video reverser */
hiLite(x, y, xs, ys)
int x, y, xs, ys;
{
	register long ox, oy;

	ox = origx + x * charx;
	oy = origy + y * chary;

	if (snip == CON_SNIP) {		/* user-specified corrections */
		ox += con_offx;
		oy += con_offy;
	}
	else {
		ox += win_offx;
		oy += win_offy;
	}

	ClipBlit(sniprp, ox, oy,  sniprp, ox, oy,
						(long) (xs * charx), (long) (ys * chary), 0x50L);
}

/*********************************************/
SHAR_EOF
cat << \SHAR_EOF > main.c
/* :ts=4
 *
 * Amiga SnipIt 1.2
 * (c) (opyright 1987,1988 - Scott Evernden - All Rights Reserved
 *
 * main.c - cutter/paster like SunTools
 *
 * Permission is granted to modify and redistribute as long
 * as this notice is left intact.
 *
 */

/**********************

	The strategy here begins with the installation of some input handler
	code into the input event handler chain.  This code looks for mouse
	actions and keyboard presses that indicate that text is being snipped,
	or is being pasted.

	Snipped text (plane 0) is always immediately copied into a temp 1-plane
	bitmap without further processing.  Subsequents "snips" cause the old
	snipped bitmap to be thrown away, and a new one captured.

	Pasting requires that the text in the capture bitmap be recognized.
	This is not done in the input handler code, since it would cause all
	pending input events to pile up while the recognizer is running.
	Instead, the input handler code signals the main snipit code (RUNning
	in the background, itself) to do the recognition and fake the kb events.
	During this period, the input handler code temporarily suspends its
	special processing waiting for the main code (recognizer) to finish.

	Once a capture bitmap has been recognized and converted into a sequence
	of key codes, it is deallocated, and subsequent pastes don't require
	the recognition step.

**********************/

#include "hs.h"

struct Task *_FindTask();
struct Library *_OpenLibrary();

BPTR output = 0;

long LayersBase;					/* for WhichLayer() */
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
struct ConsoleBase *ConsoleBase;	/* defined in hs.h */

/* communication with input.device */
struct IOStdReq *inpReq;
struct MsgPort *inpPort;

/* communication with console.device */
struct MsgPort *conPort;
struct IOStdReq *conReq;

/* who I am */
struct Task *myTask;

/* snipit's port */
struct MsgPort *myPort;

struct MyMessage {
	struct Message m;
	int done, huh, off;
	int w_offx, w_offy;
	int c_offx, c_offy;
	long alen;
	char *aptr;
} myMsg, *msgp;

/* for snipit's input handler */
struct Interrupt handler;

/* flags handler installation for cleanup */
int handlerIn;

/* snipit's allocated signal bit */
int mySignal = -1L;

/* snipit's input handler code */
VOID handlerCode();

/* snipit replacement functions */
VOID myRectFill(), myScrollRaster(), myCloseWindow();

/* remember original library vectors */
long oldRectFill, oldScrollRaster, oldCloseWindow;

/* vector offsets of certain functions */
extern int lvoRectFill, lvoScrollRaster, lvoCloseWindow;

/* detach stuff */
long running = 0;
char *_procname = "\x9B33mSnipIt 1.2\x9B0m";

char *huh_msg =   "Huh?  (for help type: snipit h)";

/* "Can't open"... forms */
char *i_libname = "~intuition.library";
char *g_libname = "~graphics.library";
char *l_libname = "~layers.library";
char *i_devname = "~input.device";
char *c_devname = "~console.device";

char *port_name = "^\x9B33mSnipIt 1.2\x9B0m Port";

long _savsp;

extern int jobFlag;
extern int con_offx, con_offy;
extern int win_offx, win_offy;
extern int cmd1, cmd2;
extern int snip, lock_layer, windows;
extern char preString[];

/*********************************************/
/* pick up the ABS symbols describing gfx/intui lib offsets
 */
#asm
	dseg
	public	_LVORectFill
	public	_LVOScrollRaster
	public	_LVOCloseWindow

_lvoRectFill		dc.w	_LVORectFill
_lvoScrollRaster	dc.w	_LVOScrollRaster
_lvoCloseWindow		dc.w	_LVOCloseWindow

	cseg
#endasm

/*********************************************/

writes(str)
register char *str;
{
	register char *piece;
	register long len;

	len = 0;

	if (*str == '!') {
		piece = "Can't ";
		len = 6;
	}
	else if (*str == '~') {
		piece = "Can't open ";
		len = 11;
	}
	else if (*str == '^') {
		piece = "Can't create ";
		len = 13;
	}
	else if (*str == '_') {
		piece = _procname;
		len = 17;
	}

	if (len) {
		str++;
		_Write(output, piece, len);
	}

	/* output a message & newline */
	_Write(output, str, (long) strlen(str));
	_Write(output, "\n", 1L);
}

/*********************************************/

fini()
{
	long stat;

	/* cleanup in other modules */
	finiHand();
	finiReco();

	/* restore gfx/intui library vectors */
	if (oldRectFill)
		SetFunction(GfxBase, (long) lvoRectFill, oldRectFill);

	if (oldScrollRaster)
		SetFunction(GfxBase, (long) lvoScrollRaster, oldScrollRaster);

	if (oldCloseWindow)
		SetFunction(IntuitionBase, (long) lvoCloseWindow, oldCloseWindow);

	/* remove snipit input handler */
	if (handlerIn) {
		inpReq->io_Command = IND_REMHANDLER;
		inpReq->io_Data = (APTR) &handler;
		DoIO(inpReq);
	}

	/* close down console.device */
	if (conReq) {
		if (conReq->io_Device)
			CloseDevice(conReq);
		DeleteStdIO(conReq);
	}
	if (conPort)
		DeletePort(conPort);

	/* close down input.device */
	if (inpReq) {
		if (inpReq->io_Device)
			CloseDevice(inpReq);
		DeleteStdIO(inpReq);
	}
	if (inpPort)
		DeletePort(inpPort);

	/* release snipit's port & signal bit */
	if (myPort)
		DeletePort(myPort);

	if (mySignal != -1)
		FreeSignal(1L << mySignal);

	/* close libraries */
	if (GfxBase)
		_CloseLibrary(GfxBase);

	if (LayersBase)
		_CloseLibrary(LayersBase);

	if (IntuitionBase)
		_CloseLibrary(IntuitionBase);

	if (output)
		_Close(output);

#asm
	moveq	#0,d0			;pick up return exit code
	move.l	__savsp,sp		;get back original stack pointer
	rts						;and exit
#endasm
}

/*********************************************/

int init()
{
	long stat;

	/* global record of this task's ID */
	myTask = _FindTask((char *) NULL);

	/* need intuitionbase to read mouse pos. & screen layer */
	IntuitionBase = (struct IntuitionBase *) _OpenLibrary(i_libname + 1, 0L);
	if (IntuitionBase == NULL) {
		writes(i_libname);
		return 0;
	}

	/* need graphics since we do */
	GfxBase = (struct GfxBase *) _OpenLibrary(g_libname + 1, 0L);
	if (GfxBase == NULL) {
		writes(g_libname);
		return 0;
	}

	/* layers for WhichLayer() */
	LayersBase = (long) _OpenLibrary(l_libname + 1, 0L);
	if (LayersBase == NULL) {
		writes(l_libname);
		return 0;
	}

	/* signal from input event handler to main code */
	mySignal = AllocSignal(-1L);
	if (mySignal == -1) {
		writes("!allocate a signal bit.");
		return 0;
	}

	/* a signal to this port will terminate us */
	myPort = CreatePort(port_name + 1, 0L);
	if (myPort == NULL) {
		writes(port_name);
		return 0;
	}

	/* port to the input.device */
	inpPort = CreatePort((char *) NULL, 0L);
	if (inpPort == NULL) {
		writes("^input.device port.");
		return 0;
	}

	/* need an i/o request */
	inpReq = CreateStdIO(inpPort);
	if (inpReq == NULL) {
		writes("^input.device i/o request.");
		return 0;
	}

	/* open input.device */
	stat = OpenDevice(i_devname + 1, 0L, inpReq, 0L);
	if (stat != 0) {
		writes(i_devname);
		return 0;
	}

	/* port to the console.device */
	conPort = CreatePort((char *) NULL, 0L);
	if (!conPort) {
		writes("^console.device port.");
		return 0;
	}

	/* need an i/o request */
	conReq = CreateStdIO(conPort);
	if (!conReq) {
		writes("^console.device i/o request.");
		return 0;
	}

	/* open console.device */
	stat = OpenDevice(c_devname + 1, -1L, conReq, 0L);
	if (stat != 0) {
		writes(c_devname);
		return 0;
	}

	/* check for consolebase */
	ConsoleBase = (struct ConsoleBase *) conReq->io_Device;
	if (!ConsoleBase) {
		writes("!find ConsoleBase.");
		return 0;
	}

	/* handler command stuff */
	handler.is_Code = handlerCode;
	handler.is_Node.ln_Pri = 51;

	/* install new input event handler */
	inpReq->io_Command = IND_ADDHANDLER;
	inpReq->io_Data = (APTR) &handler;

	stat = DoIO(inpReq);
	if (stat != 0) {
		writes("ADDHANDLER request failed.");
		return 0;
	}
	handlerIn = 1;

	/* function catchers */

	oldRectFill = SetFunction(GfxBase, (long) lvoRectFill, myRectFill);
	if (oldRectFill == 0) {
		writes("!replace RectFill()");
		return 0;
	}

	oldScrollRaster = SetFunction(GfxBase, (long) lvoScrollRaster,
									myScrollRaster);
	if (oldScrollRaster == 0) {
		writes("!replace ScrollRaster()");
		return 0;
	}

	oldCloseWindow = SetFunction(IntuitionBase, (long) lvoCloseWindow,
									myCloseWindow);
	if (oldCloseWindow == 0) {
		writes("!replace CloseWindow()");
		return 0;
	}

	return 1;
}

/*********************************************/

int jump(mode)
int mode;
{
	char ch;

	while (0 < msgp->alen--) {
		ch = *msgp->aptr++;
		if ((ch != ' ' && ch != '\t' && ch != '\n') == mode)
			return ch;
	}
	return 0;
}

/*********************************************/

int innum(base)
int base;
{
	int result, neg, ch;

	result = 0;
	ch = jump(1);
	if (!ch)
		return 0;

	neg = ch == '-';
	if ((neg || ch == '+') && 0 < msgp->alen--)
		ch = *msgp->aptr++;

	while (1) {
		ch -= '0';
		if (9 < ch)
			ch -= 7;	/* was 'A'-'F' */
		if (15 < ch)
			ch -= 32;	/* was 'a'-'f' */

		if (ch < 0 || base <= ch) {
			msgp->alen++;	/* ungetc */
			msgp->aptr--;
			break;
		}

		result = result * base + ch;

		if (msgp->alen-- <= 0)
			break;

		ch = *msgp->aptr++;
	}
	return neg ? -result : result;
}

/*********************************************/

usage()
{
writes("Usage: snipit [opts]");
writes("where opts can be any of:");
writes("   ? or h         Help; this text");
writes("   q              Quit; remove SnipIt from the system");
writes("   p \"prestring\"  When pasting using Command-B key, insert");
writes("                  \"prestring\" before each line.  In quotes");
writes("   l 1 or 0       Lock layer (or don't) while snipping");
writes("   u +/-number    Adjust cell x position in console windows");
writes("   v +/-number    Adjust cell y position in console windows");
writes("   x +/-number    Adjust cell x position in non-console windows");
writes("   y +/-number    Adjust cell y position in non-console windows");
writes("   a hex-number   Qualifier for Command-A key (default LEFT-AMIGA)");
writes("   b hex-number   Qualifier for Command-B key (default LEFT-ALT)");
writes("");
writes("Defaults are:  p\"> \" l0 u0 v0 x0 y0 a0040 b0010");
}

/*********************************************/

int options()
{
	char *cp;
	int temp, ch;

	msgp->done = msgp->huh = msgp->off = 0;

	do {
		/* locate a char after whitespace */
		ch = jump(1);					/* advance to 1st non-white */

		if ('A' <= ch && ch <= 'Z')
			ch = ch - 'A' + 'a';		/* tolower */

		/* act on char */
		switch (ch) {

		case 0:		break;

		case 'q':	msgp->done = 1;
					return 0;			/* die */

		case 'p':	cp = preString;
					ch = jump(1);		/* delim char */
					if (ch) {
						while (0 < msgp->alen-- &&
							   (*cp = *msgp->aptr++) != ch) {
							cp++;
							if (preString + PRE_SIZE <= cp)
								break;
						}
					}
					*cp = 0;
					break;

		case 'x':	if (snip == WIN_SNIP)
						clearHi();
					msgp->off = 1;
					win_offx += innum(10);
					break;

		case 'y':	if (snip == WIN_SNIP)
						clearHi();
					msgp->off = 1;
					win_offy += innum(10);
					break;

		case 'u':	if (snip == CON_SNIP)
						clearHi();
					msgp->off = 1;
					con_offx += innum(10);
					break;

		case 'v':	if (snip == CON_SNIP)
						clearHi();
					msgp->off = 1;
					con_offy += innum(10);
					break;

		case 'a':	if (temp = innum(16));
						cmd1 = temp;
					break;

		case 'b':	if (temp = innum(16));
						cmd2 = temp;
					break;

		case 'l':	ch = jump(1);		/* first char */
					if (ch)
						lock_layer = ch == '1';
					break;

		case 'w':	ch = jump(1);		/* first char */
					if (ch)
						windows = ch == '1';
					break;

		default:	msgp->huh = 1;		/* newline, too - end of string */
					break;
		}

	} while (jump(0));					/* advance to whitespace */

	if (msgp->off) {
		win_offx = win_offx < -32 ? -32 : 32 < win_offx ? 32 : win_offx;
		win_offy = win_offy < -32 ? -32 : 32 < win_offy ? 32 : win_offy;
		con_offx = con_offx < -32 ? -32 : 32 < con_offx ? 32 : con_offx;
		con_offy = con_offy < -32 ? -32 : 32 < con_offy ? 32 : con_offy;

		msgp->w_offx = win_offx;
		msgp->w_offy = win_offy;
		msgp->c_offx = con_offx;
		msgp->c_offy = con_offy;
	}

	return 1;	/* i.e., keep running */
}

/*********************************************/

outnum(n)
int n;
{
	char d;

	if (n < 0) {
		n = -n;
		_Write(output, "-", 1L);
	}
	if (n > 9) {
		d = n / 10 + '0';
		_Write(output, &d, 1L);
	}
	d = n % 10 + '0';
	_Write(output, &d, 1L);
}

/*********************************************/

currAdjust(type, x, y)
char *type;
int x, y;
{
	_Write(output, type, 7L);
	_Write(output, " adjustment now: x=", 19L);
	outnum(x);
	_Write(output, ",y=", 3L);
	outnum(y);
	_Write(output, "\n", 1L);
}

/*********************************************/

checkStatus()
{
	if (myMsg.huh)
		writes(huh_msg);

	else if (myMsg.done)
		writes("_ cancelled.");

	else if (myMsg.off) {
		currAdjust("Console", myMsg.c_offx, myMsg.c_offy);
		currAdjust("Window ", myMsg.w_offx, myMsg.w_offy);
	}
}

/*********************************************/

_main(alen, aptr)	/* DOSBase and ExecBase opened already by crt0.o */
long alen;
char *aptr;
{
	static int pass;
	int keep_going;
	long sige, sigt;
	struct MsgPort *port, *rport;

	/* sever seglists */
	do_detach(&alen, &aptr);

	myMsg.alen = alen;
	myMsg.aptr = aptr;

	/* help requested?? */
	if (*aptr == '?' || *aptr == 'h')
		usage();

	/* installed already? if so, pass arguments */
	else if (port = FindPort(port_name + 1)) {	

		if (alen <= 1)
			writes("_ already installed!");

		else {
			myMsg.m.mn_ReplyPort = rport = CreatePort(NULL, 0L);
			if (!rport)
				writes("^a port");

			else {
				PutMsg(port, &myMsg);
				_WaitPort(rport);		/* wait for reply */
				_GetMsg(rport);
				DeletePort(rport);

				/* check return stats */
				checkStatus();
			}
		}
	}

	/* install & deal with args */
	else if (init()) {

		msgp = &myMsg;
		if (options() && !myMsg.huh) {

			writes("_ installed.  \xA9 1988 - Scott Evernden");

			running = 1;

			/* signals which mean something */
			sige = 1L << mySignal;			/* request for recognition */
			sigt = 1L << myPort->mp_SigBit;	/* options message */

			/* process requests for handler code
			 * while awaiting options signal
			 */

			keep_going = 1;
			while (keep_going) {

				if (Wait(sige | sigt) == sige) {
					buildEvents();
					jobFlag = 0;		/* lower active flag */
				}
				else while (msgp = (struct MyMessage *) GetMsg(myPort)) {
					keep_going = options();
					_ReplyMsg(msgp);
				}
			}
		}

		else { 
			if (myMsg.huh)
				writes(huh_msg);

			writes("_ not installed.");
		}
	}

	running = 1;

	/* terminate */
	Delay(10L);
	fini();
}

SHAR_EOF
cat << \SHAR_EOF > patch.c
/* "Patch" fix SnipIt to use a different keymap */
/* 9/1/88 - Scott Evernden */

#include <stdio.h>
#include <ctype.h>

FILE *infile, *datafile;

/* original keycode table starts like this... */
unsigned char match[] = {
    0x40, 0x81, 0xAA, 0x83, 0x84, 0x85, 0x87, 0x2A,
    0x89, 0x8A, 0x88, 0x8C, 0x38, 0x0B, 0x39, 0x3A,
    0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0
};

main()
{
	long pos, ftell();
	unsigned char *mp;
	int ch, i;

	/* gonna write into this file */
	infile = fopen("SnipIt", "r+");
	if (!infile) {
		puts("Can't open \"SnipIt\"");
		exit(0);
	}

	/8 need to read this one */
	datafile = fopen("SnipIt.keys", "r");
	if (!datafile) {
		puts("Can't open \"SnipIt.keys\"");
		exit(0);
	}

	/* locate original table */
	mp = match;
	while (*mp && (ch = fgetc(infile)) != EOF) {
		if (ch != *mp)
			mp = match;
		else if (mp++ == match)
			pos = ftell(infile) - 1;
	}

	if (ch == EOF) {
		puts("Are you sure this is an original copy of \"SnipIt\"?");
		fclose(infile);
		fclose(datafile);
		exit(0);
	}

	fputs("Patching keytable...", stdout);

	fseek(infile, pos, 0L);

	for (i = 32; i < 127; i++)
		fputc(innum() & 0xFF, infile);

	fclose(infile);
	fclose(datafile);
	puts("done");
}


innum()
{
	int result;
	int ch;

	do {
		ch = fgetc(datafile);
	} while (!isxdigit(ch));

	if (ch == EOF)
		return 0;

	result = 0;
	do {
		ch -= '0';
		if (9 < ch)
			ch -= 7;
		if (15 < ch)
			ch -= 32;
		result = result * 16 + ch;
		ch = fgetc(datafile);
	} while (isxdigit(ch));

	return result;
}


SHAR_EOF
cat << \SHAR_EOF > reco.c
/* :ts=4
 *
 * Amiga SnipIt 1.2
 * (c) (opyright 1987,1988 - Scott Evernden - All Rights Reserved
 *
 * reco.c - recognize & construct char code input
 *
 */

#include "hs.h"

char *_AllocMem();

/* ascii to keycode (this is a hack; is there a correct way to do this?) */
char table[] = {
/*	         !     "     #     $     %     &     '  */
/*	   (     )     *     +     ,     -     .     /  */
/*     0     1     2     3     4     5     6     7  */
/*     8     9     :     ;     <     =     >     ?  */
/*     @     A     B     C     D     E     F     G  */
/*     H     I     J     K     L     M     N     O  */
/*     P     Q     R     S     T     U     V     W  */
/*     X     Y     Z     [     \     ]     ^     _  */
/*     `     a     b     c     d     e     f     g  */
/*     h     i     j     k     l     m     n     o  */
/*     p     q     r     s     t     u     v     w  */
/*     x     y     z     {     |     }     ~        */
	0x40, 0x81, 0xAA, 0x83, 0x84, 0x85, 0x87, 0x2A,
	0x89, 0x8A, 0x88, 0x8C, 0x38, 0x0B, 0x39, 0x3A,
	0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	0x08, 0x09, 0xA9, 0x29, 0xB8, 0x0C, 0xB9, 0xBA,
	0x82, 0xA0, 0xB5, 0xB3, 0xA2, 0x92, 0xA3, 0xA4,
	0xA5, 0x97, 0xA6, 0xA7, 0xA8, 0xB7, 0xB6, 0x98,
	0x99, 0x90, 0x93, 0xA1, 0x94, 0x96, 0xB4, 0x91,
	0xB2, 0x95, 0xB1, 0x1A, 0x0D, 0x1B, 0x86, 0x8B,
	0x00, 0x20, 0x35, 0x33, 0x22, 0x12, 0x23, 0x24,
	0x25, 0x17, 0x26, 0x27, 0x28, 0x37, 0x36, 0x18,
	0x19, 0x10, 0x13, 0x21, 0x14, 0x16, 0x34, 0x11,
	0x32, 0x15, 0x31, 0x9A, 0x8D, 0x9B, 0x80
};

static struct InputEvent event;
char *evs, *evp;
char preString[PRE_SIZE + 1] = "> ";	/* a prestring */
int evslen;

extern struct IOStdReq *inpReq;
extern struct TextFont *capttf;
extern struct BitMap captbm;
extern int captx, capty;
extern int charx, chary;
extern int left, right;
extern int cmd1, cmd2, jobFlag;

/*********************************************/

/* cleanup, local to this module */
finiReco()
{
	/* event string is allocated */
	if (evs)
		_FreeMem(evs, (long) evslen);
}

/*********************************************/

/* entry to recognizer; generates keycode events from the capture bitmap */
buildEvents()
{
	register char *cp;
	PLANEPTR p;
	int code;

	/* prepare request to input device */
	inpReq->io_Command = IND_WRITEEVENT;
	inpReq->io_Flags = 0;
	inpReq->io_Length = sizeof (struct InputEvent);
	inpReq->io_Data = (APTR) &event;

	/* new snip-map? */
	if (capttf) {

		/* new buffer for keycodes */
		if (evs)
			_FreeMem(evs, (long) evslen);

		evslen = (captx / charx) * (capty / chary);
		evs = (char *) _AllocMem((long) evslen, 0L);

		if (evp = evs)
			buildCodes();

		capttf = NULL;		/* reset "newly-captured" indicator */

		/* reco'ed, so drop the bitmap */
		if (p = captbm.Planes[0]) {
			FreeRaster(p, (long) captx, (long) capty);
			captbm.Planes[0] = NULL;
		}
	}
	else if (evp = evs) {

		while (*evp != -1) {

			if (jobFlag & cmd2) {
				cp = preString;
				while (*cp)
					sendKey(*cp++);
			}

			do
				sendKey(code = *evp++);
			while (code != '\n' && *evp != -1);
		}
	}
}

/*********************************************/

/* inject a (fake) keyboard press/release into the input stream */
sendKey(code)
int code;
{
	/* convert to raw key */
	code = code == '\n' ? 0x44 : table[code - 32];

	/* build key down event */
	event.ie_Class = IECLASS_RAWKEY;
	event.ie_Code = code & 0x7F;
	event.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE;
	if (code & 0x80)
		event.ie_Qualifier |= IEQUALIFIER_LSHIFT;
	event.ie_NextEvent = NULL;

	/* make it happen */
	DoIO(inpReq);

	/* build key up event (req'd?) */
	event.ie_Class = IECLASS_RAWKEY;
	event.ie_Code = (code & 0x7F) | IECODE_UP_PREFIX;
	event.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE;
	if (code & 0x80)
		event.ie_Qualifier |= IEQUALIFIER_LSHIFT;
	event.ie_NextEvent = NULL;

	/* make it happen */
	DoIO(inpReq);
}

/*********************************************/

/* actually makes the keycodes */
buildCodes()
{
	register int code, px, py, wid, lastx, lasty, spaces;
	char *cp;

	/* heuristics for text structure (i.e., newlines) */
	px = left;
	py = 0;
	lastx = captx / charx - 1;
	lasty = capty / chary - 1;
	wid = lastx;

	while (py <= lasty) {

		/* prestring on cmd2 key */
		if (jobFlag & cmd2) {
			cp = preString;
			while (*cp)
				sendKey(*cp++);		/* don't record */
		}

		/* on the last line? */
		if (py == lasty)
			lastx = right;

		/* for each cell... */
		while (px <= lastx) {

			/* recognize single character */
			code = reco(px++, py);

			/* we're gonna count runs of spaces */
			spaces = 0;
			while (code == ' ' && px <= wid) {
				spaces++;
				code = reco(px++, py);
			}

			/* send / record, if no spaces */
			if (!spaces)
				sendKey(*evp++ = code);

			/* if end of line is all spaces, then regard as newline */
			else if (code == ' ' && wid < px)
				sendKey(*evp++ = '\n');

			/* if more text on line after snip area, then not a newline */
			else if (py < lasty || px <= lastx + 1) {
				while (spaces--)
					sendKey(*evp++ = ' ');
				sendKey(*evp++ = code);
			}

			/* emit spaces */
			else {
				spaces -= px - lastx - 2;
				while (spaces--)
					sendKey(*evp++ = ' ');
			}
		}

		/* next is leftside of next row down */
		px = 0;
		py++;
	}

	*evp = -1;		/* record terminator */
}

/*********************************************/

/* return ascii code of character in capture bitmap at (x,y) */
int reco(x, y)
int x, y;
{
	register char *s, *d, *sp, *dp;
	register int hyt, i;

	/* recognize cell at (x,y) */
	sp = (char *) capttf->tf_CharData;
	dp = (char *) captbm.Planes[0] + y * chary * captbm.BytesPerRow + x;

	for (i = 32; i < 128; sp++, i++) {
		s = sp;
		d = dp;
		hyt = capttf->tf_YSize;

		if (*s == *d) {
			do {
				if (--hyt == 0)
					return i;
				s += capttf->tf_Modulo;
				d += captbm.BytesPerRow;
			} while (*s == *d);
		}
		else if (*s == ~*d) {
			do {
				if (--hyt == 0)
					return i;
				s += capttf->tf_Modulo;
				d += captbm.BytesPerRow;
			} while (*s == ~*d);
		}
	}

	return '?';	/* unknown */
}

SHAR_EOF
cat << \SHAR_EOF > snipit.keys
40 81 AA 83 84 85 87 2A
89 8A 88 8C 38 0B 39 3A
0A 01 02 03 04 05 06 07
08 09 A9 29 B8 0C B9 BA
82 A0 B5 B3 A2 92 A3 A4
A5 97 A6 A7 A8 B7 B6 98
99 90 93 A1 94 96 B4 91
B2 95 B1 1A 0D 1B 86 8B
00 20 35 33 22 12 23 24
25 17 26 27 28 37 36 18
19 10 13 21 14 16 34 11
32 15 31 9A 8D 9B 80

SHAR_EOF
cat << \SHAR_EOF > patch.doc
KeyMap patcher:
==============

The program "patch" will "fix" SnipIt to work with foreign, custom, and the
Dvorak keymaps.  This measure is a stopgap "bandaid" (i.e., hack) until a
more appropriate handling of KeyMaps is employed by SnipIt.

Make sure that (a copy of) "snipit" and a file named "snipit.keys" are in
the 'current directory'.  Simply incant:

	> patch

and SnipIt will be modified according to the data in the "snipit.keys" file.

The file "snipit.keys" has a format like the following (this example is
the default- for the 'usa0' keymap):

40 81 AA 83 84 85 87 2A
89 8A 88 8C 38 0B 39 3A
0A 01 02 03 04 05 06 07
08 09 A9 29 B8 0C B9 BA
82 A0 B5 B3 A2 92 A3 A4
A5 97 A6 A7 A8 B7 B6 98
99 90 93 A1 94 96 B4 91
B2 95 B1 1A 0D 1B 86 8B
00 20 35 33 22 12 23 24
25 17 26 27 28 37 36 18
19 10 13 21 14 16 34 11
32 15 31 9A 8D 9B 80

It should be a list of 95 hexadecimal numbers, each corresponding to the
ASCII codes 32-126 (95 codes- ' ' thru '~').  The hex number is the I.D.
of the keyboard key (as revealed in the Appendix to the AmigaDOS Technical
Manual and other sources).  If the keyboard key must be "shifted" in order
to achieve the corresponding ASCII code, then the hex keycode number is
"OR"ed with an 0x80 (high bit on).

The patch program simply searches the SnipIt program file looking for the
original hardwired table, and then overwrites the 95 code values.
Consequently, you need to run "patch" on the original copy of SnipIt.

-scott

SHAR_EOF
cat << \SHAR_EOF > snipit.doc
SnipIt 1.2  -  Copyright (c) 1988 - Scott Evernden,  All Rights Reserved


What is SnipIt?
================

SnipIt is a CLI program which allows you to copy text directly from  the
Amiga  screen  in any window, and then paste it into any other place, as
though you had typed that text at the keyboard.  You mark the  text  you
want  to  'snip' using the mouse.  You also use the mouse to 'paste' the
last snipped text into the active window, requester, or anywhere.

SnipIt is a program which installs itself into the input stream handling
code  of  AmigaDOS.   It  waits  for you to hold down the LEFT-AMIGA (or
optionally another key) while using the mouse.


Running the Program
===================

This program will only run properly from the CLI.  You start it like

  > snipit

at which point, SnipIt will install itself, and print a message.

You can control certain operations and modes of SnipIt  when  you  first
install  it  and  later  while  it  is  running.   You do this by typing
"snipit" with 'option' letters and possible arguments.  For example, you
can tell SnipIt to stop running:

  > snipit q

In  other  words,  type  "snipit"  followed  by a "q" for 'quit'.  Other
options to control SnipIt's behavior will be described below.


Marking Text to be Copied
=========================

To mark text to  be  copied,  simply  depress  the  LEFT-AMIGA  key  (by
default),  move the mouse pointer to a position in any window containing
text and hold down the LEFT mouse button.  While holding down  the  LEFT
mouse button you move the mouse to the opposite end of the text area you
want to copy and then  release.   As  you  do  this,  SnipIt  will  mark
(hilight) both ends of the area you are designating.

Once  you  have  released  the  LEFT  mouse  button,  SnipIt  will fully
highlight and 'snapshot' the text within the area you have marked out.


Pasting the Copied Text
=======================

You can deposit  the  text  you  copied  by,  again,  holding  down  the
LEFT-AMIGA key, and clicking the RIGHT mouse button.

SnipIt  will  inject  keystroke  codes into the input stream, so you can
paste text into any active window, requester, or anything else which  is
expecting keyboard input.

You can paste copied text as many times as you want.

SnipIt  will  un-hilight the selected area whenever any output occurs in
the highlighted window.  You can still paste the copied  text,  however,
even though the highlighting has been removed.


Limitations of This Version
===========================

SnipIt  can  recognize any font in any height, only as long as that font
is fixed width and 8 pixels wide.

SnipIt in its current form cannot recognize text  which  is  italicised,
underlined, or is drawn in certain colors.  In some unusual colored text
cases, SnipIt will be 'blind' thinking it is  seeing  space  characters,
and you will get nothing but spaces while pasting.

SnipIt  can  only  recognize  character  codes  between ASCII 32-127, so
special characters (requiring the ALT key  to  generate)  will  not  get
recognized  properly.   If SnipIt cannot figure out a character, it will
substitute a '?' (question mark) character.

SnipIt appears to work properly  with  the  ConMan  replacement  console
handler by William Hawes.  SnipIt also appears to work properly with the
NEWCON:  window of AmigaDOS  1.3.   SnipIt  may  highlight  areas  in  a
peculiar  fashion  in  windows  having the GIMMEZEROZERO attribute (eg.,
AmigaBasic).

This program will also co-operate with programs like SunMouse and others
which can change window activation automatically.   However,  note  that
each  character  of  any  pasted text will arrive in whatever window the
mouse is currently in.


New Features of Version 1.2
===========================

Automatically runs in the background
------------------------------------
You no longer need to 'run'  SnipIt-  the  program  is  now  capable  of
detaching itself and running automatically in the background.

Snips from both consoles and regular windows
--------------------------------------------
SnipIt  can  now  recognize  text  in  plain  old windows (not only just
console windows anymore).  So, applications like  WPLibrary  P-Edit  and
VT100 terminal programs can be 'snipped' from.  Note that SnipIt assumes
that text is being displayed in the window is some kind  of  cell  array
(like  a terminal).  It probably won't do what you want in programs like
Deluxe Paint, and others which can place text anywhere in the window.

Built in mini-help
------------------
SnipIt will give you a quick rundown of its options if you type:

  > snipit h

or

  > snipit ?

Prestring insertion while pasting
---------------------------------
The program now provides an option to insert a  short  text  string 
before each line of characters as it is pasted.  This can be useful
if  you are 'snipping' text which you wish to 'quote'.  You do this
by depressing  the   LEFT-ALT  key  (by  default),  instead  of the
LEFT-AMIGA key while pasting.  For  example,  if  I  snip, and then
paste this paragraph using LEFT-ALT, I get:

> The program now provides an option to insert a  short  text  string
> before each line of characters as it is pasted.  This can be useful
> if  you are 'snipping' text which you wish to 'quote'.  You do this
> by depressing  the   LEFT-ALT  key  (by  default),  instead  of the
> LEFT-AMIGA key while pasting.  For  example,  if  I  snip, and then
> paste this paragraph using LEFT-ALT, I get:

Note,  a  ">  "  (the  default) string has been prepended to each pasted
line.  This string can be changed at any time by:

  > snipit p "the prestring:  "

where the text appears between quotes after a "p".   Actually,  you  can
use any delimiter, and not only quotes, like:

  > snipit p .the prestring:  .

Just  repeat  the  initial  character  at  the end.  Your prestring must
contain no more than 32 characters.

Character cell location adjustments
-----------------------------------
Sometimes, in non-console  windows,  the  text  may  not  be  placed  in
positions  that SnipIt can locate.  SnipIt 1.2 now provides options that
allow you to slightly adjust its idea of where characters are located in
the window:

  > snipit x +2 y -1

will move the snip area in windows, right by 2 pixels and up by one.  If
you find that SnipIt is producing lots of ???????s while  pasting,  then
you  may  need  to  use this option to adjust the cell area (highlighted
area) slightly.  For example, P-Edit currently requires:

  > snipit x -1

as text characters are draw one pixel higher than SnipIt would otherwise
assume.  Dave Wecker's VT100 currently requires:

  > snipit y -1


You can also correct the x and y cell positions in console windows using
the 'u' and 'v' options:

  > snipit u -3 v +1

will move the snip area in consoles, left by 3 pixels, and down by  one.
For  example,  Eric  Haberfellner's Handshake (VT100 emulator) currently
needs:

  > snipit v +3

In most circumstances, you shouldn't need to use the 'u' and 'v' options
to adjust for console windows.


Improved recognition
--------------------
SnipIt  can  now  recognize  highlighted and inverted characters- you no
longer have to move that darn cursor away from your text.

Freeze windows while snipping
-----------------------------
If you type:

  > snipit l 1

("l" for 'lock') Snipit 1.2 will freeze the window or console which  you
are  snipping from.  This can be useful if you are trying to 'snip' text
in a window that would otherwise scroll.

  > snipit l 0

turns this option off.

Re-assignable keyboard use
--------------------------
Normally, you snip and paste using the LEFT-AMIGA  key;  you  can  paste
with  a  prestring  using  the  LEFT-ALT  key  while pasting.  These key
choices can be changed.  You change the normal, or Command-A,  key  from
the LEFT-AMIGA to another key by:

  > snipit a <hexcode>

where the <hexcode> is from this table:

        LEFT-SHIFT ....  0001
        RIGHT-SHIFT ...  0002
        CONTROL .......  0008
        LEFT-ALT ......  0010 <- default Command-B key
        RIGHT-ALT .....  0020
        LEFT-AMIGA ....  0040 <- default Command-A key
        RIGHT-AMIGA ...  0080

other values won't work.  So, to use the CONTROL key instead, you:

  > snipit a 0008 (or just 8, you don't need the leading 0s).

The  Command-B  key,  normally  the  LEFT-ALT  key,  can be changed, for
example, to the Right-Shift key:

  > snipit b 0002


Default settings
----------------
By default snipit acts as though you had done:

  > snipit p"> " l0 u0 v0 x0 y0 a0040 b0010

meaning:

  o (p) use "> " as the prestring when pasting with the Command-B key
  o (l) don't freeze the window being snipped from
  o (u, v) no adjustment to cell area when snipping from consoles
  o (x, y) no adjustment to cell area when snipping from windows
  o (a) use LEFT-AMIGA for Command-A operation-  regular snips and pastes
  o (b) use LEFT-ALT for Command-B operation- insert prestring on pastes


Bugs
====

I have not gone to any special effort to check if Windows or Screens are
closed,  or  otherwise  disappear,  while  the  mouse  is  being used to
highlight a text area.  The system will likely Guru if this happens.

SnipIt  only  works  properly with the usa0 keymap (more below).  Dvorak
(usa2) will not, and certain  non-usa  keyboards/keymaps  may  not  work
properly with SnipIt.   However, a 'patch' program is included to modify
SnipIt to work with other keymaps.   See the file "PATCH.DOC"  for  more
information.


Ideas for the Future
====================

In approximate order of importance to me:

The next SnipIt will hopefully do a better job in dealing with  KeyMaps.
Currently,  SnipIt  uses  a  hardcoded  table  of  keycodes  for pasting
character codes into the input stream.  I'm not real  happy  with  this,
because  it  is  not the best design.  This will be fixed next.

Recognize, as an option, ALL characters, even ones requiring the ALT key
to generate (full 8-bit ASCII).

An 'appending' snip.  The next version of SnipIt will probably allow you
to 'append' or add newly snipped  material  to  the  end  of  previously
snipped  text.   This  way,  you  could  actually 'compose' a message by
collecting text from different parts of one  or  several  windows.   The
Command-B key is planned for this use while snipping.

Send  snipped text to a device or file instead of into the input stream.
This way, you could direct snipped text into a PIPE:, for example.

Use the clipboard- some day.

Recognize text that isn't 8  pixels  wide,  is  colored,  underlined  or
italicised.  Don't hold your breath on these.

The SnipIt program acts like 2 different programs:
  1. As the resident SnipIt program doing the real work.
  2. As the user interface communicating options to the resident SnipIt.

Currently, the resident SnipIt also carries the code used  by  the  user
interface  portion.   This  is a mild waste of memory (less than 5K).  A
future version of SnipIt may release code sections not required for  the
resident portion, thereby reducing consumption of memory.

 
==========================================================================

Comments and contributions to:
------------------------------

plink:  SCOTT E
bix:    s_evernden
cis:    73116,3451
uucp:   {ames,rutgers}!harvard!m2c!applix!scott
        scott@applix.m2c.org

Scott Evernden
9 Courtland St.
Holliston, MA 01746

SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.