/* Copyright 1989, 1990 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_listMouse[] =
	    "$Id: listMouse.c 63 2004-12-24 18:19:43Z ctm $";
#endif

/* Forward declarations in ListMgr.h (DO NOT DELETE THIS LINE) */

#include "rsys/common.h"
#include "EventMgr.h"
#include "ToolboxEvent.h"
#include "ToolboxUtil.h"
#include "OSEvent.h"
#include "ControlMgr.h"
#include "ListMgr.h"

#include "rsys/cquick.h"
#include "rsys/list.h"
#include "rsys/screen.h"
#include "rsys/pstuff.h"
#include "rsys/hook.h"

A2(PRIVATE, void, findcell, Cell *, cp, ListHandle, list)
{
    cp->h = CW((CW(cp->h) - Hx(list, rView.left)) / Hx(list, cellSize.h) +
						       Hx(list, visible.left));
    cp->v = CW((CW(cp->v) - Hx(list, rView.top))  / Hx(list, cellSize.v) +
						        Hx(list, visible.top));

    if (CW(cp->h) >= Hx(list, visible.right))
	cp->h  = CWC(32767);
    if (CW(cp->v) >= Hx(list, visible.bottom))
	cp->v  = CWC(32767);
}

A4(PRIVATE, void, setselectnilflag, BOOLEAN, setit, Cell, cell,
					ListHandle, list, BOOLEAN, hiliteempty)
{
    GrafPtr saveport;
    RgnHandle saveclip;
    Rect r;
    INTEGER *ip, off0wbit, off0, off1;
    LISTDECL();

    if ((ip = ROMlib_getoffp(cell, list))) {
	off0wbit = CW(*ip);
	if (setit)
	    *ip = CW(off0wbit | 0x8000);
	else
	    *ip = CW(off0wbit & 0x7FFF);
	if (PtInRect(cell, &HxX(list, visible)) &&
					     (!(off0wbit & 0x8000) ^ !setit)) {
	    off0 = off0wbit & 0x7FFF;
	    off1 =    CW(ip[1]) & 0x7FFF;
	    if (hiliteempty || off0 != off1) {

		C_LRect(&r, cell, list);

		saveport = thePort;
		SetPort(HxP(list, port));
		saveclip = PORT_CLIP_REGION_X (thePort);
		PORT_CLIP_REGION_X (thePort) = RM (NewRgn ());
		ClipRect(&r);

		LISTBEGIN(list);
/* #define TEMPORARY_HACK_DO_NOT_CHECK_IN */
#if !defined(TEMPORARY_HACK_DO_NOT_CHECK_IN)
		LISTCALL(lHiliteMsg, setit, &r, cell, off0, off1 - off0, list);
#endif
		LISTEND(list);

		DisposeRgn (PORT_CLIP_REGION (thePort));
		PORT_CLIP_REGION_X (thePort) = saveclip;
		SetPort (saveport);
	    }
	}
    }
}

A4(PRIVATE, void, rectvalue, register Rect *, rp, register INTEGER, value,
			       register ListHandle, list, BOOLEAN, hiliteempty)
{
    register INTEGER *ip, *ep;
    register INTEGER *sp;
    Cell c;
    LISTDECL();

    LISTBEGIN(list);
    for (c.v = CW(rp->top) ; c.v < CW(rp->bottom); c.v++) {
	c.h = CW(rp->left);
	if ((sp = ip = ROMlib_getoffp(c, list))) {
	    for (ep = ip + (CW(rp->right) - CW(rp->left)); ip != ep; ip++)
		if (!(CW(*ip) & 0x8000) ^ !value) {
		    c.h = CW(rp->left) + (ip - sp);
		    setselectnilflag(value, c, list, hiliteempty);
		}
	}
    }
    LISTEND(list);
}

A5(PRIVATE, void, rect2value, register Rect *, in, register Rect *, butnotin,
	    register INTEGER, value, register ListHandle, list,
							BOOLEAN, hiliteempty)
{
    register INTEGER *ip;
    Cell c;

    for (c.v = CW(in->top) ; c.v < CW(in->bottom); c.v++)
	for (c.h = CW(in->left) ; c.h < CW(in->right); c.h++)
	    if (!PtInRect(c, butnotin) && (ip = ROMlib_getoffp(c, list)))
		if (!(CW(*ip) & 0x8000) ^ !value)
		    setselectnilflag(value, c, list, hiliteempty);
}

A1(PRIVATE, void, scrollbyvalues, ListHandle, list)
{
    INTEGER h, v;
    ControlHandle ch;
    Point p;

    h = (ch = HxP(list, hScroll)) ? GetCtlValue(ch) : Hx(list, visible.left);
    v = (ch = HxP(list, vScroll)) ? GetCtlValue(ch) : Hx(list, visible.top);
    C_LScroll(h - Hx(list, visible.left), v - Hx(list, visible.top), list);
    HxX(list, visible.left) = CW(h);
    HxX(list, visible.top)  = CW(v);
    p.h = Hx(list, cellSize.h);
    p.v = Hx(list, cellSize.v);
    C_LCellSize(p, list);
}

P2(PUBLIC, pascal void,  ROMlib_mytrack, ControlHandle, ch, INTEGER, part)
{
    INTEGER quant, page;
    ListPtr lp;

    lp = (ListPtr) (long) STARH((Handle) (long) MR(HxX(ch, contrlRfCon)));

    page = ch == MR(lp->hScroll) ?
		     CW(lp->visible.right)  - CW(lp->visible.left) - 1
		 :
		     CW(lp->visible.bottom) - CW(lp->visible.top)  - 1;

    switch (part) {
    case inUpButton:
	quant = -1;
	break;
    case inDownButton:
	quant =  1;
	break;
    case inPageUp:
	quant = -page;
	break;
    case inPageDown:
	quant =  page;
	break;
    default:
	gui_assert(0);
	quant = 0;
	break;
    }
    SetCtlValue(ch, GetCtlValue(ch) + quant);
    scrollbyvalues((ListHandle) (long) MR(HxX(ch, contrlRfCon)));
}

#if !defined (BINCOMPAT)

#define CALLCLICK(f)	(CallPascalB(f))

#else /* BINCOMPAT */

typedef pascal BOOLEAN (*clickproc)( void );

#define CALLCLICK(f)	ROMlib_CALLCLICK((clickproc)(f))

A1(static inline, BOOLEAN, ROMlib_CALLCLICK, clickproc, fp)
{
    BOOLEAN retval;

    ROMlib_hook(list_clicknumber);
    HOOKSAVEREGS();
    retval = CToPascalCall(fp, CTOP_Button);
    HOOKRESTOREREGS();
    return retval;
}

#endif /* BINCOMPAT */

P3(PUBLIC pascal trap, BOOLEAN, LClick, Point, pt,		/* IMIV-273 */
					      INTEGER, mods, ListHandle, list)
{
    ControlHandle ch, scrollh, scrollv;
    Rect r, rswapped;
    BOOLEAN doubleclick, ctlchanged;
    BOOLEAN hiliteempty, onlyone, userects, disjoint, extend;
    BOOLEAN initial;
    enum { Off, On, UseSense } cellvalue;	/* order is important here */
    Byte flags;
    EventRecord evt;
    Rect anchor, oldselrect, newselrect, newcellr, pinrect;
    Cell oldcell, newcell, c, cswapped, oldcellunswapped, newcellunswapped;
    LONGINT l;
    INTEGER dh, dv;
    Point p;

    doubleclick = FALSE;
    if (PtInRect(pt, &HxX(list, rView))) {
	TRAPBEGIN();
	flags = Hx(list, selFlags);
	newcell.h = CW(pt.h);
	newcell.v = CW(pt.v);
	findcell(&newcell, list);
	if (newcell.h == HxX(list, lastClick.h) &&
	    newcell.v == HxX(list, lastClick.v) &&
			     TickCount() < Hx(list, clikTime) + CL(DoubleTime))
	    doubleclick = TRUE;
	HxX(list, lastClick) = newcell;
	hiliteempty = !(flags & lNoNilHilite);
	if (((mods & shiftKey) || (flags & lExtendDrag)) &&
							 !(flags & lOnlyOne)) {
	    onlyone   = FALSE;
	    disjoint  = !(flags & lNoDisjoint);
	    userects  = !(flags & lNoRect);
	    cellvalue =  (flags & lUseSense) ? UseSense : On;
	    extend    =  (flags & lUseSense) ? FALSE : !(flags & lNoExtend);
	} else if ((mods & cmdKey) && !(flags & lOnlyOne)) {
	    onlyone   = FALSE;
	    disjoint  = !(flags & lNoDisjoint);
	    userects  = FALSE;
	    cellvalue = UseSense;
	    extend    = FALSE;
	} else {
	    onlyone   = TRUE;
	    disjoint  = FALSE;
	    userects  = FALSE;
	    cellvalue = On;
	    extend    = FALSE;
	}
	initial = C_LGetSelect(FALSE, &newcell, list);
	if (cellvalue == UseSense)
	    cellvalue = initial ? Off : On;
	if (!disjoint && !initial)
	    rectvalue(&HxX(list, dataBounds), Off, list, hiliteempty);

	if (userects) {
	    anchor.top = anchor.bottom = 0;
	    if (extend) {
		rswapped = HxX(list, dataBounds);
		r.top    = CW(rswapped.top);
		r.left   = CW(rswapped.left);
		r.bottom = CW(rswapped.bottom);
		r.right  = CW(rswapped.right);
		for (c.h = r.left; c.h < r.right ; c.h++)
		    for (c.v = r.top; c.v < r.bottom ; c.v++) {
			cswapped.h = CW(c.h);
			cswapped.v = CW(c.v);
			if (C_LGetSelect(FALSE, &cswapped, list))
			    goto out1;
		    }
		out1:
		c.h = CW(cswapped.h);
		c.v = CW(cswapped.v);
		if (c.h != r.right) {
		    anchor.left = CW(c.h);

		    for (c.h = r.right-1; c.h >= r.left ; c.h--)
			for (c.v = r.top; c.v < r.bottom ; c.v++) {
			    cswapped.h = CW(c.h);
			    cswapped.v = CW(c.v);
			    if (C_LGetSelect(FALSE, &cswapped, list))
				goto out2;
		    }
		    out2:
		    c.h = CW(cswapped.h);
		    c.v = CW(cswapped.v);
		    anchor.right = CW(c.h + 1);

		    cswapped.h = CW(r.left);
		    cswapped.v = CW(r.top);
		    C_LGetSelect(TRUE, &cswapped, list);
		    anchor.top = cswapped.v;

		    for (c.v = r.bottom - 1; c.v >= r.top ; c.v--)
			for (c.h = r.left; c.h < r.right ; c.h++) {
			    cswapped.h = CW(c.h);
			    cswapped.v = CW(c.v);
			    if (C_LGetSelect(FALSE, &cswapped, list))
				goto out3;
		    }
		    out3:
		    anchor.bottom = CW(CW(cswapped.v) + 1);
		}
	    }
	    if (anchor.top == anchor.bottom) {
		anchor.top    = newcell.v;
		anchor.left   = newcell.h;
		anchor.bottom = CW(CW(anchor.top)  + 1);
		anchor.right  = CW(CW(anchor.left) + 1);
	    }
	    c.h = CW(anchor.left);
	    c.v = CW(anchor.top);
	    C_LRect(&rswapped, c, list);
	    if (pt.h < CW(rswapped.right) && pt.v < CW(rswapped.bottom)) {
		anchor.top  = CW(CW(anchor.bottom) - 1);
		anchor.left = CW(CW(anchor.right)  - 1);
	    } else {
		anchor.bottom = CW(CW(anchor.top)  + 1);
		anchor.right  = CW(CW(anchor.left) + 1);
	    }
	    oldselrect = (flags & lUseSense) ? anchor : HxX(list, dataBounds);
	}

	HxX(list, clikTime) = CL(TickCount());
	HxX(list, clikLoc.h)  = CW(pt.h);
	HxX(list, clikLoc.v)  = CW(pt.v);
	oldcell.h = CWC(32767);

        evt.where.h = CW(pt.h);
        evt.where.v = CW(pt.v);
	pinrect = HxX(list, rView);
	pinrect.left = CW(CW(pinrect.left) - 1);
	pinrect.bottom = CW(CW(pinrect.bottom) - 1);
	do {
	    HxX(list, mouseLoc) = evt.where;
	    if (HxP(list, lClikLoop))
		if (CALLCLICK(HxP(list, lClikLoop)))
/*-->*/		    break;
	    p.h = CW(evt.where.h);
	    p.v = CW(evt.where.v);
	    if (!PtInRect(p, &HxX(list, rView))) {
		ctlchanged = FALSE;
		scrollh = HxP(list, hScroll);
		scrollv = HxP(list, vScroll);
		dh = 0;
		dv = 0;
		if (CW(evt.where.h) < Hx(list, rView.left)) {
		    if (scrollh) {
			SetCtlValue(scrollh, GetCtlValue(scrollh)-1);
			ctlchanged = TRUE;
		    } else
			dh = -1;
		} else if (CW(evt.where.h) > Hx(list, rView.right)) {
		    if (scrollh) {
			SetCtlValue(scrollh, GetCtlValue(scrollh)+1);
			ctlchanged = TRUE;
		    } else
			dh = 1;
		}
		if (CW(evt.where.v) < Hx(list, rView.top)) {
		    if (scrollv) {
			SetCtlValue(scrollv, GetCtlValue(scrollv)-1);
			ctlchanged = TRUE;
		    } else
			dv = -1;
		} else if (CW(evt.where.v) > Hx(list, rView.bottom)) {
		    if (scrollv) {
			SetCtlValue(scrollv, GetCtlValue(scrollv)+1);
			ctlchanged = TRUE;
		    } else
			dv = 1;
		}
		if (ctlchanged)
		    scrollbyvalues(list);
		else
		    C_LScroll(dh, dv, list);
	    }
	    p.h = CW(evt.where.h);
	    p.v = CW(evt.where.v);
	    l = PinRect(&pinrect, p);
	    newcell.h = CW(LoWord(l));
	    newcell.v = CW(HiWord(l));
	    findcell(&newcell, list);
	    if (userects) {
		newcellr.top    = newcell.v;
		newcellr.left   = newcell.h;
		newcellr.bottom = CW(CW(newcellr.top)  + 1);
		newcellr.right  = CW(CW(newcellr.left) + 1);
		UnionRect(&anchor, &newcellr, &newselrect);
		rect2value(&oldselrect, &newselrect, !cellvalue, list,
								  hiliteempty);
		rectvalue(&newselrect, cellvalue, list, hiliteempty);
		oldselrect = newselrect;
	    } else {
		if (newcell.h != oldcell.h ||
		    newcell.v != oldcell.v) {
		    if (onlyone && oldcell.h != 32767) {
			oldcellunswapped.h = CW(oldcell.h);
			oldcellunswapped.v = CW(oldcell.v);
			setselectnilflag(FALSE, oldcellunswapped, list,
								  hiliteempty);
		    }
		    newcellunswapped.h = CW(newcell.h);
		    newcellunswapped.v = CW(newcell.v);
		    setselectnilflag(cellvalue, newcellunswapped, list,
								  hiliteempty);
		    oldcell = newcell;
		}
	    }
	} while (!OSEventAvail(mUpMask, &evt) &&
					    (GlobalToLocal(&evt.where), TRUE));
	TRAPEND();
    } else if (((ch = HxP(list, hScroll)) && PtInRect(pt, &HxX(ch, contrlRect))) ||
               ((ch = HxP(list, vScroll)) && PtInRect(pt, &HxX(ch, contrlRect)))) {
	if (TestControl(ch, pt) == inThumb) {
	    TrackControl(ch, pt, (ProcPtr) 0);
	    scrollbyvalues(list);
	} else
	    TrackControl(ch, pt, (ProcPtr) P_ROMlib_mytrack);
    }
    return doubleclick;
    return 0;
}

P1(PUBLIC pascal trap, LONGINT, LLastClick, ListHandle, list)	/* IMIV-273 */
{
    return ((LONGINT) Hx(list, lastClick.v) << 16) |
					(unsigned short) Hx(list, lastClick.h);
}

P3(PUBLIC pascal trap, void, LSetSelect, BOOLEAN, setit,	/* IMIV-273 */
						 Cell, cell, ListHandle, list)
{
    setselectnilflag(setit, cell, list, TRUE);
}
