/* :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);
}

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