
#include "Snap.h"

IMPORT struct SnapRsrc *SnapRsrc;

#define COPY 0xC0L
#define INVCOPY 0x30L
#define CopyChar(_x, _y, _m)                                  \
    BltBitMap(&MyBM, (LONG)_x, (LONG)_y,                      \
      &TempBM, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight,     \
      _m, -1L, NULL);                                         \
    WaitBlit()

WORD Unit;
UWORD Pattern[5] =
{
	0,
	0x0c3f,			/* Frame ....oo....oooooo */
	0x3333,			/* Char  ..oo..oo..oo..oo */
	0x1f1f,			/* Word  ...ooooo...ooooo */
	0xffff			/* Line  oooooooooooooooo */
};

LONG xl;			/* leftmost x position */
LONG xr;			/* rightmost x position */
LONG yt;			/* topmost y position */
LONG yb;			/* bottommost y position */
LONG minx;			/* left limit */
LONG maxx;			/* right limit */
LONG maxy;			/* bottom limit */
LONG tl, tr;			/* used by findword - left and right edge of word */
WORD fw, fh;			/* Font width and height used when drawing the frame */
WORD GZZ;
WORD SBM;

LONG mx, my;			/* Mouse position in character steps */

#define closetop     0
#define closebottom  1
WORD closey;

#define closeleft   0
#define closeright  1
WORD closex;


struct Window *window;		/* The window we're snapping from */

/* Data for font being snapped */
UWORD FontHeight;
UWORD FontWidth;
UWORD Underscore;
UBYTE FontType;
UBYTE LoChar;
UBYTE HiChar;
UWORD Modulo;
UWORD *CharLoc;
UWORD *CharKern;
UBYTE *SrcData;
IMPORT UBYTE *CharData;
UBYTE IFlags;

IMPORT struct RastPort TempRp, MyRP;
IMPORT struct BitMap TempBM, MyBM;

IMPORT UWORD *TempRaster;	/* Used for character recognition */

IMPORT struct Screen *theScreen;
IMPORT struct RastPort rp;
struct Layer *LockedLayer;

IMPORT struct timerequest MyTR;

IMPORT LONGBITS cancelsignal, donesignal, movesignal;
IMPORT LONGBITS clicksignal, ticksignal, timersignal;
IMPORT WORD action;

WORD starting;

/* Init vars with font data.
 */

WORD SetSnapFont(font)
	struct TextFont *font;
{
	if (!font)
	{
		return 0;
	}
	FontHeight = font->tf_YSize;
	if (FontHeight > CHEIGHT)
	{
		return 0;
	}
	Underscore = font->tf_Baseline + 1;
	FontType = font->tf_Flags;
	if (FontType & FPF_PROPORTIONAL)
	{
		return 0;
	}
	FontWidth = font->tf_XSize;
	LoChar = font->tf_LoChar;
	HiChar = font->tf_HiChar;
	Modulo = font->tf_Modulo;
	CharLoc = (UWORD *) font->tf_CharLoc;
	CharKern = (UWORD *) font->tf_CharKern;
	Modulo = font->tf_Modulo;
	SrcData = (UBYTE *) font->tf_CharData;
	BltClear((char *)CharData, 256L * (CHEIGHT * 2), 0L);
	WaitBlit();
	CopyFont();
	return 1;
}

/* Check if the character at x, y is a space
 */

WORD SnapIsSpace(x, y)
	LONG x, y;
{
	REGISTER WORD i = FontHeight - 1;
	REGISTER UWORD *data = &TempRaster[i];

	/* Copy character at x, y */
	BltClear((char *)TempRaster, CHEIGHT * 2L, 0L);
	ClipBlit(&rp, x, y,
		 &TempRp, 0L, 0L, (LONG) FontWidth, (LONG) FontHeight, COPY);
	WaitBlit();

	if (*data)
	{			/* Try inverted copy */
		ClipBlit(&rp, x, y,
			 &TempRp, 0L, 0L, (LONG) FontWidth, (LONG) FontHeight, INVCOPY);
		WaitBlit();
	}
	while (i--)
	{
		if (*data--)
		{
			return 0;
		}
	}
	return 1;
}

#define ShortFrame 4L		/* Square frame - columnar select */
#define LongFrame  8L		/* Strange frame - char or word select */
IMPORT LONG OFType;		/* Old frame type: ShortFrame/LongFrame */
IMPORT UWORD Ptrn;
IMPORT Point OldFrame[];
IMPORT Point NewFrame[];


/* update_frame calculates the new frame,
   ** erases the old frame and draws the new one.
   ** It's all pretty obvious if you take it slowly.
 */
VOID update_frame()
{
	LONG ft;
	REGISTER LONG l, b, r, t, ml, mr;

	l = xl - 1;
	r = xr + fw;
	t = yt - 1;
	b = yb + fh;
	ml = minx - 1;
	mr = maxx + fw;
	if (l < 0)
		l = 0;
	if (r > window->Width - 1)
		r = window->Width - 1;
	if (t < 0)
		t = 0;
	if (b > window->Height - 1)
		b = window->Height - 1;
	if (ml < 0)
		ml = 0;
	if (mr > window->Width - 1)
		mr = window->Width - 1;

	switch (Unit)
	{
	case UNIT_FRAME:
		{
	      /*********\
              *         *
              *         *
              \*********/
			NewFrame[0].x = l;
			NewFrame[0].y = t;
			NewFrame[1].x = r;
			NewFrame[1].y = t;
			NewFrame[2].x = r;
			NewFrame[2].y = b;
			NewFrame[3].x = l;
			NewFrame[3].y = b;
			NewFrame[4].x = l;
			NewFrame[4].y = t;
			ft = ShortFrame;
			break;
		}
	case UNIT_CHAR:
	case UNIT_WORD:
		{
			if (yt == yb)
			{	/* On the same line - same as UNIT_FRAME */
				NewFrame[0].x = l;
				NewFrame[0].y = t;
				NewFrame[1].x = r;
				NewFrame[1].y = t;
				NewFrame[2].x = r;
				NewFrame[2].y = b;
				NewFrame[3].x = l;
				NewFrame[3].y = b;
				NewFrame[4].x = l;
				NewFrame[4].y = t;
				ft = ShortFrame;
			}
			else
			{
		      /*****\
                 ******     *
                 *          *
                 *      *****
                 *******/
				NewFrame[0].x = l;
				NewFrame[0].y = t;
				NewFrame[1].x = mr;
				NewFrame[1].y = t;
				NewFrame[2].x = mr;
				NewFrame[2].y = yb;
				NewFrame[3].x = r;
				NewFrame[3].y = yb;
				NewFrame[4].x = r;
				NewFrame[4].y = b;
				NewFrame[5].x = ml;
				NewFrame[5].y = b;
				NewFrame[6].x = ml;
				NewFrame[6].y = yt + fh;
				NewFrame[7].x = l;
				NewFrame[7].y = yt + fh;
				NewFrame[8].x = l;
				NewFrame[8].y = t;
				ft = LongFrame;
			}
			break;
		}
	case UNIT_LINE:
		{
			NewFrame[0].x = ml;
			NewFrame[0].y = t;
			NewFrame[1].x = mr;
			NewFrame[1].y = t;
			NewFrame[2].x = mr;
			NewFrame[2].y = b;
			NewFrame[3].x = ml;
			NewFrame[3].y = b;
			NewFrame[4].x = ml;
			NewFrame[4].y = t;
			ft = ShortFrame;
			break;
		}
	default:
		{
			break;
		}
	}
	draw_frame(ft);
}

VOID FindWord()
{
	/* Must remove frame to be able to search for spaces */
	WaitTOF();
	erase_frame();
	tl = mx;
	/* Find a space to the left... */
	while (!SnapIsSpace(tl, my))
	{
		tl -= fw;
		if (tl < minx)
		{
			break;
		}
	}
	tl += fw;
	tr = mx;
	/* ...and to the right */
	while (!SnapIsSpace(tr, my))
	{
		tr += fw;
		if (tr + fw > maxx)
		{
			break;
		}
	}
	tr -= fw;
	if (tr < tl)
	{
		tl = xl;
		tr = xr;
	}
}

/* ChangeUnit cycles the unit of selection. The differents units
   are: character, word and line.
 */

VOID ChangeUnit()
{

	switch (Unit)
	{
	case UNIT_FRAME:
		{
			Unit = UNIT_CHAR;
			break;
		}
	case UNIT_CHAR:
		{
			Unit = UNIT_WORD;
			FindWord();
			xl = tl;
			xr = tr;
			break;
		}
	case UNIT_WORD:
		{
			Unit = UNIT_LINE;
			xl = minx;
			xr = maxx;
			break;
		}
	case UNIT_LINE:
		{
			Unit = UNIT_FRAME;
			xl = xr = mx;
			break;
		}
	}
	if (SnapRsrc->CrawlPtrn == 0)
	{
		Ptrn = Pattern[Unit];
	}
}

/* ExtendSelection extends the current selection according to
   the mouse position and the selected unit.
   Note that ExtendSelection doesn't optimize moves that don't
   make any difference. FIXME
 */

VOID ExtendSelection()
{
	/* Fix which row we're talking about */
	if (closey == closetop)
	{			/* Find closest row */
		yt = my;	/* change top row */
	}
	else
	{
		yb = my;	/* change bottom row */
	}

	/* Take care of left and right character pos */
	switch (Unit)
	{
	case UNIT_FRAME:
		{
			if (closex == closeleft)
			{
				xl = mx;
			}
			else
			{
				xr = mx;
			}
			break;
		}
	case UNIT_CHAR:
		{
			if (yt == yb)
			{	/* One line */
				if (closex == closeleft)
				{
					xl = mx;
				}
				else
				{
					xr = mx;
				}
			}
			else
			{	/* Multiple lines */
				if (yt == my)
				{
					xl = mx;	/* At top - set left */
				}
				else
				{
					xr = mx;	/* At bottom - set right */
				}
			}
			break;
		}
	case UNIT_WORD:
		{
			FindWord();	/* Find the word */
			if (yt == yb)
			{	/* One line */
				if (closex == closeleft)
				{	/* Find closest char pos */
					xl = tl;
				}
				else
				{
					xr = tr;
				}
			}
			else
			{	/* Multiple lines */
				if (yt == my)
				{	/* Where am I */
					xl = tl;	/* At top - set left */
				}
				else
				{
					xr = tr;	/* At bottom - set right */
				}
			}
			break;
		}
	case UNIT_LINE:
		{		/* Always full width */
			break;
		}
	}
	if (yt - fh == yb)
	{
		yb += fh;
	}
	if (yt == yb && xl - fw == xr)
	{
		xr += fw;
	}
	if (xr > maxx)
	{			/* Check for window bounds */
		xr = maxx;
	}
	if (xl < minx)
	{			/* Check for window bounds */
		xl = minx;
	}
	if (yb > maxy)
	{			/* Check for window bounds */
		yb = maxy;
	}
	if (xl > maxx)
	{
		xl = maxx;
	}
	if (xr < minx)
	{
		xr = minx;
	}
}

/* The actual character snapper. It actually works. :-) */

WORD SnapChars()
{
	LONG width;
	LONG height;
	UBYTE *SnapSpace;
	ULONG SnapSize;
	ULONG counter;
	REGISTER LONG x, y;

	/* Check coordinates */
	if (yt - fh == yb)
	{			/* No rows, shouldn't happen */
		return 0;
	}
	if (yt == yb && xl - fw == xr)
	{			/* Nothing at all */
		return 0;
	}

	/* Calculate stuff */
	width = maxx - (minx + 1) + fw + fw;	/* Add one for a LF */
	height = yb - yt + fh;
	SnapSize = ((width / fw) + 1) * (height / fh);
	counter = 0;

	/* Initialize things */
	InitRastPort(&MyRP);
	InitBitMap(&MyBM, 1L, width, height);
	MyRP.BitMap = &MyBM;
	SnapSpace = AllocMem(SnapSize, MEMF_PUBLIC | MEMF_CLEAR);
	/* Please insert more memory */
	if (!SnapSpace)
	{
		return 0;
	}
	MyBM.Planes[0] = AllocRaster(width, height);
	if (!MyBM.Planes[0])
	{
		FreeMem(SnapSpace, SnapSize);
		return 0;
	}
	IFlags = 0;
	/* Make a local copy of the snapped chars */
	ClipBlit(&rp, minx, yt, &MyRP, 0L, 0L, width, height, COPY);

	/* Ok, now we've got a copy of the character data */
	/* Now it's ok to mess with the layers again */
	UnlockLayer(LockedLayer);

	/* Clear our work area */
	BltClear((char *)TempRaster, CHEIGHT * 2, 0L);

	/* Calculate bounds */
	xl -= minx;
	xr -= minx;
	maxx -= minx;
	minx = 0;
	yb -= yt;
	yt = 0;

	/* Single line - needs to be handled separately */
	if (yt == yb)
	{			/* Ok, we've got one */

		/* Read from left to right */
		for (x = xl; x <= xr; x += fw, counter++)
		{
			CopyChar(x, yt, COPY);
			if (!(SnapSpace[counter] = interpret(TempRaster)))
			{
				SnapSpace[counter] = SnapRsrc->BadChar;		/* Unrecognized */
			}
		}
		if (Unit == UNIT_LINE)
		{
			while (counter && SnapSpace[counter - 1] == ' ')
			{
				counter--;
			}
		}
	}
	else
	{			/* Multiple lines */

		if (Unit == UNIT_FRAME)
		{
			minx = xl;
			maxx = xr;
		}

		/* Read first line */
		for (x = xl; x <= maxx; x += fw, counter++)
		{
			CopyChar(x, yt, COPY);
			if (!(SnapSpace[counter] = interpret(TempRaster)))
			{
				SnapSpace[counter] = SnapRsrc->BadChar;		/* Unrecognized */
			}
		}
		if (Unit == UNIT_FRAME)
		{
			SnapSpace[counter++] = 10;
		}
		else
		{
			SHORT endspace = (SnapSpace[counter - 1] == ' ');

			/* Remove trailing blanks */
			while (counter && SnapSpace[counter - 1] == ' ')
			{
				counter--;
			}
			if (endspace || !(SnapRsrc->flags & JOINLONG))
			{
				SnapSpace[counter++] = 10;
			}
		}

		/* If more than two rows - read full middle rows */
		if (yt + fh != yb)
		{
			for (y = yt + fh; y < yb; y += fh)
			{
				for (x = minx; x <= maxx; x += fw, counter++)
				{
					CopyChar(x, y, COPY);
					if (!(SnapSpace[counter] = interpret(TempRaster)))
					{
						SnapSpace[counter] = SnapRsrc->BadChar;		/* Unrecognized */
					}
				}
				if (Unit == UNIT_FRAME)
				{
					SnapSpace[counter++] = 10;
				}
				else
				{
					SHORT endspace = (SnapSpace[counter - 1] == ' ');

					/* Remove trailing blanks */
					while (counter && SnapSpace[counter - 1] == ' ')
					{
						counter--;
					}
					if (endspace || !(SnapRsrc->flags & JOINLONG))
					{
						SnapSpace[counter++] = 10;
					}
				}
			}
		}

		/* Read last line */
		for (x = minx; x <= xr; x += fw, counter++)
		{
			CopyChar(x, yb, COPY);
			if (!(SnapSpace[counter] = interpret(TempRaster)))
			{
				SnapSpace[counter] = SnapRsrc->BadChar;		/* Unrecognized */
			}
		}
		/* Remove trailing blanks */
		while (counter && SnapSpace[counter - 1] == ' ')
		{
			counter--;
		}
	}
	FreeRaster(MyBM.Planes[0], width, height);
	SaveClip(SnapSpace, counter);
	FreeMem(SnapSpace, SnapSize);
	return 1;
}


/* HandleChars is the part of the Snap state machine that handles
   snapping of characters. The selection is done in different
   units: char, word, line.
 */

WORD HandleChars()
{
	LONG xoff, yoff;
	LONG ox, oy;
	LONG gzzx, gzzy, normalx, normaly;

	/* Find out which screen we're working on */
	theScreen = WhichScreen();

	/* Oops, no screen? */
	if (!theScreen)
	{
		action = noaction;
		return 0;
	}

	/* Ok, what window? */
	window = WhichWindow(theScreen);

	/* Oh dear, no window. */
	if (!window)
	{
		action = noaction;
		return 0;
	}

	/* Try to get these as early as possible */
	gzzx = window->GZZMouseX;
	gzzy = window->GZZMouseY;
	normalx = window->MouseX;
	normaly = window->MouseY;

	/* No messing with the layers while I think */
	LockedLayer = window->WLayer;
	LockLayer(0L, LockedLayer);

	/* Don't want to wreck somebody's rastport */
	CopyMem((char *)window->RPort, (char *)&rp, (LONG) sizeof (struct RastPort));

	/* Or his picture */
	SetDrMd(&rp, COMPLEMENT);
	rp.Mask = SnapRsrc->FrameMask;

	if (window->Flags & GIMMEZEROZERO)
	{
		GZZ = 1;
	}
	else
	{
		GZZ = 0;
	}
	if (window->Flags & SUPER_BITMAP)
	{
		SBM = 1;
	}
	else
	{
		SBM = 0;
	}

	/* Find a position */
	xl = (GZZ ? gzzx : normalx) + window->RPort->Layer->Scroll_X;
	yt = (GZZ ? gzzy : normaly) + window->RPort->Layer->Scroll_Y;

	if (xl < 0)
	{
		xl = 0;
	}
	if (yt < 0)
	{
		yt = 0;
	}

	/* Check your position */
	if (xl > (GZZ ? window->GZZWidth : window->Width) ||
	    yt > (GZZ ? window->GZZHeight : window->Height))
	{
		UnlockLayer(LockedLayer);
		action = noaction;
		return 0;
	}
	IFlags = 0;

	/* Find out the offset for the clicked character, if any.
	   ** This is the part that makes it special. Simple, isn't it. Hah!
	 */
	{
		REGISTER struct CacheWindow *cw = GetCachedWindow(theScreen, window);
		struct TextFont *font[3];
		WORD fonts = 0;

		/* Collect fonts to try */
		if (SnapRsrc->AlternateFont)
		{		/* The alternate font */
			font[fonts++] = SnapRsrc->AlternateFont;
		}
		font[fonts++] = rp.Font;	/* Second best, the RP's font */
		if (cw)
		{		/* Best bet, the cached font */
			font[fonts++] = cw->font;
		}
		while (fonts--)
		{
			SHORT xadj;
			SHORT yadj;
			SHORT seekw;
			SHORT seekh;

			/* Find out what we're trying to read */
			if (!SetSnapFont(font[fonts]))
			{
				continue;	/* Nope, try next font */
			}

			BltClear((char *)TempRaster, CHEIGHT * 2, 0L);
			if (cw)
			{
				xoff = -((xl - cw->xoff) % cw->fw);
				yoff = -((yt - cw->yoff) % cw->fh);
				ClipBlit(&rp, xl + xoff, yt + yoff,
					 &TempRp, 0L, 0L, (LONG) FontWidth, (LONG) FontHeight, COPY);
				WaitBlit();
				if (interpret(TempRaster))
				{
					goto found;
				}
			}
			/* No cache or cache didn't match */
			xadj = (FontWidth < 8 ? FontWidth : 8);
			yadj = (FontHeight < 12 ? FontHeight : 12);
			seekw = (xadj << 1) - FontWidth;
			seekh = (yadj << 1) - FontHeight;
			xl -= xadj;
			yt -= yadj;
			xoff = 0;
			while (xoff < seekw)
			{
				ClipBlit(&rp, xl + xoff, yt,
					 &TempRp, 0L, 0L, (LONG) FontWidth, CHEIGHT, COPY);
				WaitBlit();
				yoff = 0;
				while (yoff < seekh)
				{
					if (interpret(&TempRaster[yoff]))
					{
						goto found;
					}
					++yoff;
				}
				++xoff;
			}

			/* No character found. Try next font. */
			xl += xadj;	/* Adjust to original values */
			yt += yadj;
		}
		UnlockLayer(LockedLayer);
		action = noaction;
		return 0;

	      found:
		/* Ok, now we know where to look for chars.
		   ** xoff and yoff is character position within our 16xCHEIGHT bitmap.
		 */
		xl = xl + xoff;	/* Adjust x */
		yt = yt + yoff;	/* Adjust y */

		fw = FontWidth;
		fh = FontHeight;

		{
			SHORT temp = fh;

			while (temp <= fh + SnapRsrc->Leading)
			{	/* Check for extra pixel row */
				BltClear((char *)TempRaster, CHEIGHT * 2, 0L);
				ClipBlit(&rp, xl, yt + temp,
					 &TempRp, 0L, 0L, (LONG) FontWidth, (LONG) FontHeight, COPY);
				WaitBlit();
				if (interpret(TempRaster))
				{
					fh = temp;
					break;
				}
				++temp;
			}
		}

		/* Find out offsets within the window */
		xoff = xl % fw;
		yoff = yt % fh;

		if (cw)
		{
			cw->xoff = xoff;
			cw->yoff = yoff;
			cw->fw = fw;
			cw->fh = fh;
			cw->font = font[fonts];
		}
		else
		{
			CacheWindow(window, xoff, yoff, fw, fh, font[fonts]);
		}
	}

	/* Set bounds */
	minx = xoff;
	maxx = minx +
		(((GZZ ?
		   window->GZZWidth :
		   window->Width - window->BorderRight
	/* Hack for borderless conman windows */
		   + (window->Flags & BORDERLESS && window->Flags & WINDOWSIZING ? 14 : 0))
		  - minx - fw) / fw) * fw;
	maxy = yoff +
		(((GZZ ? window->GZZHeight : window->Height) - yoff - fh) / fh) * fh;

	/* Check bounds */
	if (xl > maxx)
	{
		UnlockLayer(LockedLayer);
		action = noaction;
		return 0;
	}
	/* Set box dimensions */
	xr = xl;
	yb = yt;
	ox = xr;
	oy = yt;

	/* Select unit while starting */
	starting = 1;

	/* Starting unit is character or frame */
	Unit = SnapRsrc->StartUnit;
	Ptrn = (SnapRsrc->CrawlPtrn ? SnapRsrc->CrawlPtrn : Pattern[Unit]);
	OFType = 0L;
	update_frame();

	/* Get the state machine running */
	FOREVER
	{
		/* Wait for something to happen */
		REGISTER LONGBITS sig;

		MyTR.tr_time.tv_secs = 0;
		MyTR.tr_time.tv_micro = 500000;
		MyTR.tr_node.io_Command = TR_ADDREQUEST;
		SendIO((struct IORequest *)&MyTR);

		sig = Wait(movesignal | cancelsignal | donesignal |
			   clicksignal | ticksignal | timersignal);

		if ((sig & timersignal) && CheckIO((struct IORequest *)&MyTR))
		{
			WaitIO((struct IORequest *)&MyTR);
			erase_frame();
			UnlockLayer(LockedLayer);	/* Unlock things */
			sig = Wait(ticksignal);		/* Wait for input handler to become avtive */
			LockLayer(0L, LockedLayer);	/* Re-lock */
			/* I guess I should check to see that the window is still
			   there. Aw, what the heck. Apologies to anyone who gets
			   bitten by this. */
			DisplayBeep(NULL);
			update_frame();
		}
		else
		{
			AbortIO((struct IORequest *)&MyTR);
			WaitIO((struct IORequest *)&MyTR);
			SetSignal(0, timersignal);
		}

		if ((sig & ticksignal) && (SnapRsrc->CrawlPtrn != 0xffff))
		{
			crawl_frame(0L);
		}

		mx = (LONG) (GZZ ? window->GZZMouseX : window->MouseX)
			+ window->RPort->Layer->Scroll_X;
		if (mx < 0)
		{
			mx = 0;
		}
		/* Calculate which edge is closest */
		if ((mx - xl) < (xr - mx))
		{
			closex = closeleft;
		}
		else
		{
			closex = closeright;
		}
		/* Only interested in real char pos */
		mx = mx - ((mx - xoff) % fw);

		my = (LONG) (GZZ ? window->GZZMouseY : window->MouseY)
			+ window->RPort->Layer->Scroll_Y;
		if (my < 0)
		{
			my = 0;
		}
		/* Calculate which row is closest */
		if ((my - yt) < (yb - my))
		{
			closey = closetop;
		}
		else
		{
			closey = closebottom;
		}
		my = my - ((my - yoff) % fh);

		/* Hey, it moves! It's alive!! */
		if ((sig & movesignal) && (action == snaptext))
		{
			if (mx != ox || my != oy)
			{	/* Something's happened */
				ExtendSelection();
				update_frame();
				starting = 0;
				ox = mx;
				oy = my;
				sig &= ~clicksignal;
			}
		}

		/* Ok, forget it... */
		if (sig & cancelsignal)
		{
			erase_frame();
			UnlockLayer(LockedLayer);
			return 0;
		}

		/* Click */
		if ((sig & clicksignal) && (action == snaptext))
		{
			/* Selecting unit */
			if (starting)
			{
				if (mx == ox && my == oy)
				{
					ChangeUnit();
					if (Unit == UNIT_CHAR)
					{
						ChangeUnit();
					}
					update_frame();
				}
				else if (Unit == UNIT_FRAME)
				{
					ChangeUnit();
					update_frame();
				}
			}
			if (mx != ox || my != oy)
			{	/* Click in a new place */
				ExtendSelection();
				update_frame();
				starting = 0;
				ox = mx;
				oy = my;
			}
		}

		/* Finished */
		if (sig & donesignal)
		{
			erase_frame();
			return SnapChars();
		}
	}
}
