/*
 * mouse.c - mouse support for "dialog"
 *
 * Copyright 1994   rubini@ipvvis.unipv.it (Alessandro Rubini)
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 ********/

#include "dialog.h"

#include <gpm.h>

static Gpm_Connect connectInfo;
static Gpm_Handler mouse_handler;

static int basex, basey;

typedef struct Region {
    int x, y, X, Y, code;
    struct Region *next;
} Region;

static Region *regionList = NULL;

static struct bigRegion {
    int x, y, X, Y;
    int height, nitems;
    int xth;
    int step, mode;
} bigRegion;			/* have a static one */

static int bigregionFlag = 0;	/* no bigRegion at startup */

/*==================== simple functions =============*/

void
mouse_open (void)
{
    gpm_zerobased = gpm_visiblepointer = 1;
    connectInfo.eventMask = ~0;
    connectInfo.defaultMask = GPM_MOVE & GPM_HARD;
    connectInfo.minMod = connectInfo.maxMod = 0;
    Gpm_Open (&connectInfo, 0);
    gpm_handler = mouse_handler;
}

void
mouse_close (void)
{
    Gpm_Close ();
}

void
mouse_setbase (int x, int y)
{
    basex = x;
    basey = y;
}

/*=========== region related functions =============*/

void
mouse_mkbigregion (int y, int x, int height, int width, int nitems,
		   int th, int mode)
{
    bigRegion.x = basex + x;
    bigRegion.X = basex + x + width;
    bigRegion.y = basey + y;
    bigRegion.Y = basey + y + height;
    bigRegion.height = height - 3;
    bigRegion.nitems = nitems;
    bigRegion.xth = basex + th;
    bigRegion.step = nitems > height ? nitems / height : 1;
    if (mode == 2)		/* text box */
	bigRegion.step = height - 3;
    bigRegion.mode = mode;
    bigregionFlag++;
}

void
mouse_mkregion (int y, int x, int height, int width, int code)
{
    Region *butPtr;

    butPtr = malloc (sizeof (Region));
    if (!butPtr)
	return;			/* WARN */
    butPtr->y = basey + y;
    butPtr->Y = basey + y + height;
    butPtr->x = basex + x;
    butPtr->X = basex + x + width;
    butPtr->code = code;
    butPtr->next = regionList;
    regionList = butPtr;
}

/*=================================================== the mouse handler ====*/

#include <unistd.h>		/* select(); */
#include <sys/time.h>		/* timeval */


/*
 * This is the mouse handler.
 * It generates integer numbers, to simulate keys
 */

int
mouse_handler (Gpm_Event * event, void *unused)
{
    static Region *prevPtr;
    Region *butPtr;
    static int grabkey = 0;
    static int reptkey = 0;
    static int dragging = 0;
    static int prevY;
    static int prevMouseDown;	/* xterm gives only generic up events */

#define CURRENT_POS (M_EVENT+event->y-bigRegion.y-1)

#if 0
    fprintf (stderr,
	     "grab %i, more %i, drag %i, x %i, y %i, butt %i, type %x\n",
	     grabkey, gpm_morekeys, dragging, event->x,
	     event->y, event->buttons, event->type);
#endif

    if (grabkey) {		/* return key or 0 */
	if (Gpm_Repeat (100))
	    return grabkey;	/* go on (slow to ease xterm( */
	else
	    return grabkey = gpm_morekeys = 0;	/* no more grab */
    }
    if (gpm_morekeys) {		/* repeat a key */
	gpm_morekeys--;
	return reptkey;
    }
    if (dragging) {
	/* respond to motions, independently of where they are */
	int delta;
	if (!(event->type & GPM_DRAG)) {
	    dragging--;
	    return CURRENT_POS;
	}
	delta = event->y - prevY;
	if (!delta)
	    return 0;
	prevY = event->y;
	reptkey = (delta > 0 ? KEY_DOWN : KEY_UP);
	gpm_morekeys = bigRegion.step * abs (delta);
	return (reptkey == KEY_UP ? M_EVENT : M_EVENT + bigRegion.height);
    }
    /* the big region */

    if (bigregionFlag &&
	event->y >= bigRegion.y && event->y < bigRegion.Y &&
	event->x >= bigRegion.x && event->x < bigRegion.X) {
	/* top border */
	if (event->y == bigRegion.y) {
	    if ((event->type & GPM_DOWN) && (event->buttons & GPM_B_LEFT)) {
		grabkey = KEY_UP;
		gpm_morekeys = 1;
		return KEY_UP;
	    } else
		return 0;
	}
	/* bottom border */
	if (event->y + 1 == bigRegion.Y) {
	    if ((event->type & GPM_DOWN) && (event->buttons & GPM_B_LEFT)) {
		grabkey = KEY_DOWN;
		gpm_morekeys = 1;
		return KEY_DOWN;
	    } else
		return 0;
	}
	/* right half */
	reptkey = 0;
	if (event->x > bigRegion.xth) {
	    if (event->type & GPM_DOWN)
		switch (event->buttons) {
		case GPM_B_LEFT:
		    reptkey = KEY_DOWN - KEY_UP;
		case GPM_B_RIGHT:
		    reptkey += KEY_UP;
		    gpm_morekeys = event->y - bigRegion.y;
		    return (reptkey == KEY_UP ? M_EVENT
			    : M_EVENT + bigRegion.height);

		case GPM_B_MIDDLE:
		    prevY = event->y;
		    dragging++;
		    return 0;
		}
	    return CURRENT_POS;
	}
	/* left half */
	else {			/* WARN *//* check down events as well */
	    if ((event->type & GPM_UP) && (event->buttons & GPM_B_LEFT)) {
		if (event->type & GPM_SINGLE) {
		    reptkey = ' ';
		    gpm_morekeys = (bigRegion.mode == 0);
		    return M_EVENT + 'o';
		}
		return '\n';
	    }
	    return CURRENT_POS;
	}

	return 0;
    }
    /* smaller regions */

    /* retrieve the frame under the pointer */
    for (butPtr = regionList; butPtr; butPtr = butPtr->next) {
	if (event->y < butPtr->y || event->y >= butPtr->Y)
	    continue;
	if (event->x < butPtr->x || event->x >= butPtr->X)
	    continue;
	break;			/* found */
    }


    switch (GPM_BARE_EVENTS (event->type)) {
    case GPM_MOVE:
	if (butPtr && butPtr != prevPtr) {	/* enter event */
	    prevPtr = butPtr;
	    return M_EVENT + butPtr->code;
	}
	break;

    case GPM_DRAG:
	if (butPtr && butPtr != prevPtr)	/* enter event (with button) */
	    return M_EVENT + butPtr->code;
	break;

    case GPM_DOWN:		/* down: remember where */
	prevPtr = butPtr;
	prevMouseDown = event->buttons;		/* to handle xterm b-up event */
	break;

    case GPM_UP:		/* WARN multiple presses are bad-behaving */
	switch (prevMouseDown) {	/* event->buttons doesn't run with xterm */
	case GPM_B_RIGHT:
	    return '\n';
	case GPM_B_MIDDLE:
	    return '\t';
	case GPM_B_LEFT:

	    if (butPtr && butPtr == prevPtr) {
		/* complete button press *//* return two keys... */
		gpm_morekeys++;
		reptkey = butPtr->code;
		return M_EVENT + toupper (butPtr->code);
	    }
	    prevPtr = butPtr;
	    return M_EVENT + butPtr->code;	/* no, only an enter event */
	}
    }
    return 0;			/* nothing relevant */
}
