/*
 *  This file implements memacs support for the Amiga mouse.
 *  Author:  Andy Poggio
 */

#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <devices/console.h>
#include        <stdio.h>
#include        "ed.h"

#define NL 0

extern struct Window *Window;

/* Font sizes */

#define FONT_X 8
#define FONT_Y 8

/* Button Codes */

#define LEFT_BUTTON 1
#define RIGHT_BUTTON 2

/* Menu Definitions */


#define NUM_MENUS 1
 
/* Copies of this structure will get "stamped" into many more */
/* Allocated later */
struct IntuiText generic = {
  0, 1,                        /* Bluepen, Whitepen */
  JAM2, 5,                     /* mode,  LeftEdge */
  0, NL,                       /* Top (to be filled later), Font */
  NL,                          /* Name (to be filled later) */
  NL                           /* Next one */
};
 
/* Menu numbers */
#define COM_MENU 0
 
/* Menu Widths */
#define COM_WIDTH  72
 
int item_widths[ NUM_MENUS ] = { 112 };

/* All items in these menus have the following flags set */
#define BLACK_FILL  ITEMTEXT | ITEMENABLED | HIGHCOMP

                            /** FILE MENU ITEMS  **/
#define NUM_COM_ITEMS 9
#define COPY_ITEM       0
#define KILL_ITEM      1
#define UNKILL_ITEM      2
#define TWO_ITEM     3
#define ONE_ITEM      4
#define QUERY_ITEM      5
#define NEW_ITEM      6
#define QUIT_ITEM      7
#define NOOP_ITEM      8
 
struct MenuItem  com_items[NUM_COM_ITEMS];
struct IntuiText com_names[NUM_COM_ITEMS];
 
char  *commenu_names[] = {
   "Copy Region",
   "Kill Region",
   "Un-Kill",
   "Split Window",
   "One Window",
   "Query Replace",
   "New CLI",
   "Quit",
   "No Operaton"
};
 
struct Menu cmenu = {
  NL,                 /* Pointer to next menu */
  0, 0, COM_WIDTH, 10,       /* LeftEdge, TopEdge, Width, Height */
  MENUENABLED,               /* FLAGS */
  "Commands",                    /* Menu name */
  com_items                 /* First item structure */
};
 
mouse_setup_menu()
{
   mouse_newmenu( &cmenu, commenu_names, com_items, com_names,
            NUM_COM_ITEMS, item_widths[0], BLACK_FILL);
   SetMenuStrip(Window, &cmenu);      /* Set up the menu here */
}

mouse_clear_menu()
{
	ClearMenuStrip(Window, &cmenu);
}

static
do_menu( m, f, n) /* execute a menu command */
{
    int menu, item, sub;

    menu = MENUNUM( m);
    item = ITEMNUM( m);
    sub = SUBNUM( m);

    switch( menu) {
	case COM_MENU:
	    switch( item) {
		case COPY_ITEM:
		    copyregion( f, n);
		    break;

		case KILL_ITEM:
		    killregion( f, n);
		    break;

		case UNKILL_ITEM:
		    yank( f, n);
		    break;

		case TWO_ITEM:
		    splitwind( f, n);
		    break;

		case ONE_ITEM:
		    onlywind( f, n);
		    break;

		case QUERY_ITEM:
		    qreplace( f, n);
		    break;

		case NEW_ITEM:
		    spawncli( f, n);
		    break;

		case QUIT_ITEM:
		    quickexit( f, n);
		    break;

		case NOOP_ITEM:
		    break;
	    }
	    break;
    }
}

mouse_handle_event( execute, f, n)
{
    register char *s;
    char instr[ 81]; /* 8 nine digit numbers with separators + 1 */
    int result, c, class, subclass, keycode, qualifiers, x, y, secs, musecs;
    static int netx, nety;
    static int button_now = 0, button_ever = 0;

    for( s = instr; (*s++ = ttgetc()) != '|';);
    *s = 0; /* terminate the str */
    if( ! execute) return;

    sscanf( instr, "%d;%d;%d;%d;%d;%d;%d;%d|",
     &class, &subclass, &keycode, &qualifiers, &x, &y, &secs, &musecs);

    switch( class) {
	case 2: /* mouse button--only get this for left button */
	    if( keycode & 0x80) { /* key up */
		keycode &= ~0x80; /* clear the up bit */
		button_now &= ~ LEFT_BUTTON;
	    } else { /* key down */
		netx = Window->MouseX - Window->BorderLeft; /* save coords */
		nety = Window->MouseY - Window->BorderTop;
		button_now |= LEFT_BUTTON;
		button_ever |= LEFT_BUTTON;
	    }
	    break;

	case 10: /* menu selection -- right button up only */
	    if( keycode == MENUNULL) {
		netx = Window->MouseX - Window->BorderLeft; /* save coords */
		nety = Window->MouseY - Window->BorderTop;
		button_ever |= RIGHT_BUTTON;
	    } else { /* made a menu selection */
		button_ever = 0; /* ignore other buttons */
		do_menu( keycode, f, n);
	    }
	    break;
    }

    if(( ! button_now) && button_ever) { /* buttons were pushed: interpret */
	switch( button_ever) {
	    case LEFT_BUTTON:
		mouse_to_xy( netx, nety);
		break;

	    case RIGHT_BUTTON:
		mouse_set_mark( netx, nety);
		break;

	    case LEFT_BUTTON | RIGHT_BUTTON:
		backdel( f, n);
		break;
	}
	button_ever = 0;
    }
}

mouse_newmenu( menu, item_names,  menu_items, menu_text, num_items,
                      Mwidth, flag)
struct Menu      *menu;           /* Menu structure                       */
char            *item_names[];    /* Pointer to array of item names       */
struct MenuItem  menu_items[];    /* pointer to array of structures       */
struct IntuiText menu_text[];     /* Pointer to array of text structures  */
int               num_items;      /* Number of items                      */
int               Mwidth;         /* Menu Width */
int                 flag;         /* Special Item flag for ALL items */
{
    int i;
    int height = 0;
 
    for (i=0; i< num_items; i++) {
 
          menu_text[i] = generic;              /* stamp generic template */
          menu_text[i].IText = (UBYTE *) item_names[i];  /* mv string ptrs */
          menu_items[i].NextItem = &menu_items[i+1];  /* Lnk to nxt item */
          menu_items[i].TopEdge = 10 * i;            /* Top rect of item */
          menu_items[i].LeftEdge = 0;
          menu_items[i].Height = 8;
          menu_items[i].ItemFill = (APTR)&menu_text[i];
          menu_items[i].Flags = flag;
          menu_items[i].Width = Mwidth;
          menu_items[i].MutualExclude = 0x0000;
          menu_items[i].Command = 0;
          menu_items[i].SubItem = NL;
          menu_items[i].NextSelect = NL;
          height += 10;
    }
    menu_items[num_items-1].NextItem = NULL;
    menu->Height = height;
}

static
winddist( w, row, col) /* calc distance of window from this row, col */
WINDOW *w;
{
    int d;

    if( row < w->w_toprow) /* above window */
	return( w->w_toprow - row);
    else if( row < (w->w_toprow + w->w_ntrows)) /* within window */
	return( 0);
    else /* below window */
	return( row - (w->w_toprow + w->w_ntrows));
}

WINDOW *
mouse_find_wind( row, col) /* find window containing this row, col */
{
    WINDOW *w, *result_w;
    int distance, result_distance;

    result_distance = HUGE; /* greater than any real distance */
    for( w = wheadp; w != NULL; w = w->w_wndp) {
	distance = winddist( w, row, col);
	if( distance < result_distance) {
	    result_w = w;
	    result_distance = distance;
	}
    }
    return( result_w);
}

static
get_wintop( this_w) /* return row for top of this window */
WINDOW *this_w;
{
    register WINDOW *w;
    register int row;

    row = 0;
    for( w = wheadp; w != this_w; w = w->w_wndp) {
	row += w->w_ntrows +1;
    }
    return( row);
}

mouse_to_xy( x, y) /* move point to position at coords x,y */
{
    register LINE   *dlp;
    register int row, col;
    WINDOW *w;

    row = y / FONT_Y; /* convert coords to row and col */
    col = x / FONT_X;

    w = mouse_find_wind( row, col); /* find the window and make it current */
    curwp = w;
    curbp = curwp->w_bufp;

    row -= get_wintop( curwp); /* adjust to row in window */
    if( row >= curwp->w_ntrows) row = curwp->w_ntrows -1;

    dlp = curwp->w_linep;
    while (row-- && dlp!=curbp->b_linep) {
                dlp = lforw(dlp);
    }
    curwp->w_dotp  = dlp;
    curgoal = col; /* aim for this col */
    curwp->w_doto  = getgoal(dlp);
}

mouse_set_mark( x, y) /* set mark at mouse cursor */
{
    WINDOW *wp;
    LINE *dlp;
    int offset;

    /* save current position */
	wp = curwp;
	dlp = curwp->w_dotp;
	offset = curwp->w_doto;

    /* go to cursor position, set mark, and announce */
	mouse_to_xy( x, y);
	update();
	setmark( FALSE, 1);
	update();

    /* return to former position */
	curwp = wp;
	curbp = curwp->w_bufp;
	curwp->w_dotp = dlp;
	curwp->w_doto = offset;
}

mouse_enable() /* this must be last to keep ctags happy */
{
	ttputs( "[2;10{");	/* get mouse button and menu events */
	ttflush();
}
