/*
 *  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 <clib/exec_protos.h>
#include <clib/console_protos.h>
#include <clib/intuition_protos.h>
#include <clib/exec_pragmas.h>
#include <clib/console_pragmas.h>
#include <clib/intuition_pragmas.h>
#include "ed.h"

#define NL 0

extern struct Window *Window;

/* Font sizes */

short   FONT_Y;
#define FONT_X 8

/* Button Codes */

#define LEFT_BUTTON 1
#define RIGHT_BUTTON 2

/* Copies of this structure will get "stamped" into many more */
/* Allocated later */
struct IntuiText generic = {
  0, 1,                        /* Bluepen, Whitepen */
  JAM2, 5,                     /* mode,  LeftEdge */
  1, NL,                       /* Top (to be filled later), Font */
  NL,                          /* Name (to be filled later) */
  NL                           /* Next one */
};

#define NUM_MENUS 5

/* Menu numbers */
#define PRO_MENU 0
#define REG_MENU 1
#define COM_MENU 2
#define OPT_MENU 3
#define M2E_MENU 4
#define OHM_MENU 5

/* Menu Widths */

#ifdef DEUTSCH
#define COM_WIDTH  85
#define ITEM_WIDTH 170
#else
#define COM_WIDTH  72
#define ITEM_WIDTH 162
#endif

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

                            /** FILE MENU ITEMS  **/
#define NUM_PRO_ITEMS  10
#define NUM_REG_ITEMS   5
#define NUM_COM_ITEMS  10
#define NUM_OPT_ITEMS   8
#define NUM_M2E_ITEMS   3
#define NUM_OHM_ITEMS   2
#define NUM_SUB_ITEMS  18

#define OPEN_ITEM       0
#define FILEV_ITEM      1
#define FILEI_ITEM      2
#define RENAME_ITEM     3
#define NEWBUF_ITEM     4
#define DELBUF_ITEM     5
#define SAVE_ITEM       6
#define SAVEAS_ITEM     7
#define QUITNSAVE_ITEM  8
#define QUIT_ITEM       9

#define KILL_ITEM       0
#define COPY_ITEM       1
#define UNKILL_ITEM     2
#define ERASE_ITEM      3
#define FILEYANK_ITEM   4

#define SPLIT_ITEM      0
#define CLOSE_ITEM      1
#define ONE_ITEM        2
#define SEARCH_ITEM     3
#define QUERY_ITEM      4
#define STARTM_ITEM     5
#define ENDM_ITEM       6
#define EXECM_ITEM      7
#define NEW_ITEM        8
#define CD_ITEM         9

#define AUTOIND_ITEM    0
#define CASESENS_ITEM   1
#define ICON_ITEM       2
#define BACKUP_ITEM     3
#define STRIP_ITEM      4
#define FILEREQ_ITEM    5
#define DISPTAB_ITEM    6
#define INTERLACE_ITEM  7

#define M2ERROR_ITEM    0
#define M2LIST_ITEM     1
#define M2COMPILE_ITEM  2

#define OHM_PROJECT     0
#define OHM_CALL        1


struct MenuItem  pro_items[NUM_PRO_ITEMS];
struct IntuiText pro_names[NUM_PRO_ITEMS];
struct MenuItem  reg_items[NUM_REG_ITEMS];
struct IntuiText reg_names[NUM_REG_ITEMS];
struct MenuItem  com_items[NUM_COM_ITEMS];
struct IntuiText com_names[NUM_COM_ITEMS];
struct MenuItem  opt_items[NUM_OPT_ITEMS];
struct IntuiText opt_names[NUM_OPT_ITEMS];
struct MenuItem  m2e_items[NUM_M2E_ITEMS];
struct IntuiText m2e_names[NUM_M2E_ITEMS];
struct MenuItem  ohm_items[NUM_M2E_ITEMS];
struct IntuiText ohm_names[NUM_M2E_ITEMS];
struct MenuItem  sub_items[NUM_SUB_ITEMS];
struct IntuiText sub_names[NUM_SUB_ITEMS];

char  *promenu_names[] = {
#ifdef DEUTSCH
   "Öffne Datei     ^x^r",
   "Lies Datei      ^x^v",
   "Füge Datei ein  ^x^i",
   "Neuer Dateiname ^x^f",
   "Benutze Textsp. ^xb",
   "Lösche Textsp.  ^xk",
   "Sichern         ^x^s",
   "Sichern als     ^x^w",
   "Sichern & Ende  ^z",
   "Ende            ^x^c"
#else
   "Open          ^x^r",
   "Visit         ^x^v",
   "Rename        ^x^f",
   "Insert        ^x^i",
   "Use Buffer    ^xb",
   "Kill Buffer   ^xk",
   "Save          ^x^s",
   "Save As       ^x^w",
   "Save & Quit   ^z",
   "Quit          ^x^c"
#endif
};

char  *regmenu_names[] = {
#ifdef DEUTSCH
   "Ausschneiden",
   "Kopieren",
   "Einsetzen",
   "Löschen",
   "Auf Datei       ^[y"
#else
   "Cut",
   "Copy",
   "Paste",
   "Erase",
   "File Paste     ^[y"
#endif
};

char  *commenu_names[] = {
#ifdef DEUTSCH
   "Teile Fenster     ^x2",
   "Schliesse Fenster ^x0",
   "Nur ein Fenster   ^x1",
   "Suche             ^s",
   "Ersetze           ^[%",
   "Beginne Makro     ^x(",
   "Beende Makro      ^x)",
   "Makro ausführen   ^xe",
   "Neues CLI         ^c",
   "Neues Verzeichnis ^xd"
#else
   "Split Window    ^x2",
   "Close Window    ^x0",
   "One Window      ^x1",
   "Search          ^s",
   "Replace         ^[%",
   "Start Macro     ^x(",
   "End Macro       ^x)",
   "Execute Macro   ^xe",
   "New CLI         ^c",
   "Change Dir      ^xd"
#endif
   };

char  *optmenu_names[] = {
#ifdef DEUTSCH
   "  Aut. Einrücken ^xi",
   "  Gross/Klein    ^xc",
   "  Erzeuge Ikone  ^xw",
   "  Behalte Backup ^xs",
   "  Säubere Zeilen ^xt",
   "  FileRequester  ^xr",
   "  Zeige Tabs '»' ^x^t",
   "  Interlace         ",
#else
   "  Auto Indent    ^xi",
   "  Case Sensitive ^xc",
   "  Make Icon      ^xw",
   "  Keep Backup    ^xs",
   "  Cleanup Lines  ^xt",
   "  FileRequester  ^xr",
   "  Show tabs '»'  ^x^t"
   "  Interlace         ",
#endif
   };

char *m2emenu_names[] = {
#ifdef DEUTSCH
   "Nächster Fehler  ^x^m",
   "Lies Fehlerliste ^xm",
   "Compile Text     ^\\"
#else
   "Next M2 Error  ^x^m",
   "Read Err List  ^xm",
   "Compile        ^\\"
#endif
   };

char *ohmmenu_names[] = {
#ifdef DEUTSCH
   "Ohm Projekt   ^xo",
   "Ohm Aufruf   HELP"
#else
   "Ohm Project   ^xo",
   "Ohm Call     HELP"
#endif
   };

struct Menu pmenu = {
   NULL,                /* Pointer to next menu */
   0, 0, COM_WIDTH, 11, /* LeftEdge, TopEdge, Width, Height */
   MENUENABLED,         /* FLAGS */
#ifdef DEUTSCH
   "Projekt",           /* Menu name */
#else
   "Project",           /* Menu name */
#endif
   pro_items            /* First item structure */
};

struct Menu rmenu = {
   NULL,                /* Pointer to next menu */
   COM_WIDTH, 0, COM_WIDTH, 11,     /* LeftEdge, TopEdge, Width, Height */
   MENUENABLED,         /* FLAGS */
#ifdef DEUTSCH
   " Edit ",            /* Menu name */
#else
   " Edit ",            /* Menu name */
#endif
   reg_items            /* First item structure */
};

struct Menu cmenu = {
   NULL,                /* Pointer to next menu */
   COM_WIDTH*2, 0, COM_WIDTH, 11,   /* LeftEdge, TopEdge, Width, Height */
   MENUENABLED,         /* FLAGS */
#ifdef DEUTSCH
   " Befehle",          /* Menu name */
#else
   " Misc ",          /* Menu name */
#endif
   com_items            /* First item structure */
};

struct Menu omenu = {
   NULL,                /* Pointer to next menu */
   COM_WIDTH*3, 0, COM_WIDTH, 11,   /* LeftEdge, TopEdge, Width, Height */
   MENUENABLED,         /* FLAGS */
#ifdef DEUTSCH
   " Optionen",          /* Menu name */
#else
   " Options",          /* Menu name */
#endif
   opt_items            /* First item structure */
};

struct Menu mmenu = {
   NULL,                /* Pointer to next menu */
   COM_WIDTH*4, 0, 85, 11,   /* LeftEdge, TopEdge, Width, Height */
   MENUENABLED,         /* FLAGS */
   " Modula-2",          /* Menu name */
   m2e_items            /* First item structure */
};

struct Menu hmenu = {
   NULL,                     /* Pointer to next menu */
   COM_WIDTH*5, 0, 85, 11,   /* LeftEdge, TopEdge, Width, Height */
   MENUENABLED,              /* FLAGS */
   " Ohm ",                  /* Menu name */
   ohm_items                 /* First item structure */
};

static void
mouse_newmenu( menu, left, item_names,  menu_items, menu_text, num_items,
                      Mwidth, flag)
struct Menu      *menu;           /* Menu structure                       */
int               left;           /* LeftEdge of the menu                 */
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 = ((item_names) ? (UBYTE *) item_names[i] : NULL);
          menu_items[i].NextItem = &menu_items[i+1];  /* Lnk to nxt item */
          menu_items[i].TopEdge = (FONT_Y+2) * i;     /* Top rect of item */
          menu_items[i].LeftEdge = left;
          menu_items[i].Height = FONT_Y+2;
          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;
    if (menu)
      menu->Height = height;
}

void mouse_setup_menu()
{
   mouse_newmenu( &pmenu, 0, promenu_names, pro_items, pro_names,
            NUM_PRO_ITEMS, ITEM_WIDTH, BLACK_FILL);
   mouse_newmenu( &rmenu, 0, regmenu_names, reg_items, reg_names,
            NUM_REG_ITEMS, ITEM_WIDTH, BLACK_FILL);
   mouse_newmenu( &cmenu, 0, commenu_names, com_items, com_names,
            NUM_COM_ITEMS, ITEM_WIDTH+10, BLACK_FILL);
   mouse_newmenu( &omenu, 0, optmenu_names, opt_items, opt_names,
            NUM_OPT_ITEMS, ITEM_WIDTH
#ifdef DEUTSCH
              +8
#else
              +16   /* Englische Optionen sind ein wenig länger geworden */
#endif
            , BLACK_FILL);
   mouse_newmenu( &mmenu, 0, m2emenu_names, m2e_items, m2e_names,
#ifdef DEUTSCH
            NUM_M2E_ITEMS, ITEM_WIDTH+10, BLACK_FILL);
#else
            NUM_M2E_ITEMS, ITEM_WIDTH, BLACK_FILL);
#endif
   mouse_newmenu( &hmenu, 0, ohmmenu_names, ohm_items, ohm_names,
                  NUM_OHM_ITEMS, ITEM_WIDTH+10, BLACK_FILL);
   mouse_newmenu( NULL, (2 * ITEM_WIDTH / 3), NULL, sub_items, sub_names,
            NUM_SUB_ITEMS, ITEM_WIDTH, BLACK_FILL);

   pmenu.NextMenu = &rmenu;
   rmenu.NextMenu = &cmenu;
   cmenu.NextMenu = &omenu;
   omenu.NextMenu = &mmenu;
   mmenu.NextMenu = &hmenu;

   reg_items[0].Command = 'X';
   reg_items[1].Command = 'C';
   reg_items[2].Command = 'V';
   reg_items[3].Command = 'E';

   reg_items[0].Flags |= COMMSEQ;
   reg_items[1].Flags |= COMMSEQ;
   reg_items[2].Flags |= COMMSEQ;
   reg_items[3].Flags |= COMMSEQ;

   opt_items[0].Flags |= CHECKIT|CHECKED;
   opt_items[1].Flags |= CHECKIT|CHECKED;
   opt_items[2].Flags |= CHECKIT|CHECKED;
   opt_items[3].Flags |= CHECKIT|CHECKED;
   opt_items[4].Flags |= CHECKIT|CHECKED;
   opt_items[5].Flags |= CHECKIT|CHECKED;
   opt_items[6].Flags |= CHECKIT|CHECKED;
   opt_items[7].Flags |= CHECKIT;

   SetMenuStrip(Window, &pmenu);      /* Set up the menu here */
}

void redo_buffer_menu()
{
  BUFFER  *bp;
  int      cnt;

  bp = bheadp;        /* For all buffers  */
  cnt = 0;

  while (bp != NULL) {
    if ((cnt < NUM_SUB_ITEMS-1) && ((bp->b_flag & BFTEMP) == 0)) {
      sub_names[cnt++].IText = &bp->b_bname[0];   /* Buffer name    */
    }
    bp = bp->b_bufp;
  }

  sub_items[--cnt].NextItem = NULL;
  for (; cnt >0; cnt--)
    sub_items[cnt-1].NextItem = &(sub_items[cnt]);

  pro_items[NEWBUF_ITEM].SubItem = sub_items;
  pro_items[DELBUF_ITEM].SubItem = sub_items;
  SetMenuStrip(Window, &pmenu);
}

void mouse_clear_menu()
{
  ClearMenuStrip(Window);
}

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

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

  if( kbdmip != NULL) {                           /* collecting macro */
    if ( menu == COM_MENU && item == ENDM_ITEM )  /* End macro */
      ;
    else {
      ctrlg(FALSE, 0);
    }
  }

  switch( menu) {
    case PRO_MENU:
      switch( item) {
        case OPEN_ITEM:       fileread( f, n);   break;
        case FILEV_ITEM:      filevisit( f, n);  break;
        case FILEI_ITEM:      fileinsert( f, n);  break;
        case RENAME_ITEM:     filename( f, n);   break;
        case NEWBUF_ITEM:     usebuffer(TRUE, subitem);  break;
        case DELBUF_ITEM:     killbuffer(TRUE,subitem);  break;
        case SAVE_ITEM:       filesave( f, n);   break;
        case SAVEAS_ITEM:     filewrite( f, n);  break;
        case QUITNSAVE_ITEM:  quickexit( f, n);  break;
        case QUIT_ITEM:       quit( f, n);       break;
      }
      break;
    case REG_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 ERASE_ITEM:      eraseregion( f, n);break;
        case FILEYANK_ITEM:   fileyank( f, n);   break;
      }
      break;
    case COM_MENU:
      switch (item) {
        case SPLIT_ITEM:      splitwind( f, n);  break;
        case CLOSE_ITEM:      closewind(f,n);    break;
        case ONE_ITEM:        onlywind( f, n);   break;
        case SEARCH_ITEM:     forwsearch( f, n); break;
        case QUERY_ITEM:      qreplace( f, n);   break;
        case STARTM_ITEM:     ctlxlp( f, n);     break;
        case ENDM_ITEM:       ctlxrp( f, n);     break;
        case EXECM_ITEM:      ctlxe( f, n);      break;
        case NEW_ITEM:        spawncli( f, n);   break;
        case CD_ITEM:         changedir();       break;
      }
      break;
    case OPT_MENU:
      switch( item ) {
        case AUTOIND_ITEM:    toggleautoindent( f, n);  break;
        case CASESENS_ITEM:   togglecasesens( f, n);    break;
        case ICON_ITEM:       toggleicon(f,n);          break;
        case BACKUP_ITEM:     togglebackup(f,n);        break;
        case STRIP_ITEM:      togglestripline(f,n);     break;
        case FILEREQ_ITEM:    togglerequester(f,n);     break;
        case DISPTAB_ITEM:    toggledisplaytab(f,n);    break;
        case INTERLACE_ITEM:  toggleinterlace(f,n);     break;
      }
      break;
    case M2E_MENU:
      switch( item ) {
        case M2ERROR_ITEM:    NextM2Error();     break;
        case M2LIST_ITEM:     ReadErrDat();      break;
        case M2COMPILE_ITEM:  export();          break;
      }
      break;
    case OHM_MENU:
      switch( item ) {
        case OHM_PROJECT:    ohm_project();     break;
        case OHM_CALL:       call_ohm();      break;
      }
      break;
    default:
      break;
  }
}

void setoption(opt,val)
{
  if (val)
    opt_items[opt].Flags |= CHECKED;
  else
    opt_items[opt].Flags &= ~CHECKED;
}

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

  for ( s = instr; (*s++ = GetChar()) != '|';) ;
  *s = 0; /* terminate the str */
  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, TRUE);
        break;
      case RIGHT_BUTTON:
        mouse_to_xy( netx, nety, FALSE);
        break;
      case LEFT_BUTTON | RIGHT_BUTTON:
        mouse_to_xy( netx, nety, TRUE);
        killregion( f, n);
        break;
    }
    button_ever = 0;
  }
}

static
winddist( w, row, col) /* calc distance of window from this row, col */
WINDOW *w;
{
  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, forw) /* move point to position at coords x,y */
{
  register LINE   *dlp;
  register int row, col;
  WINDOW *w;

  if (y < 0) return(TRUE);

  row = y / FONT_Y; /* convert coords to row and col */
  col = x / FONT_X;
 /*
  * 7.10.90/ms
  * Beim Wegfahren mit der Maus sollen die Leerzeichen auch gelöscht
  * werden.
  */
  stripLine(curwp->w_dotp);
  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)
    if (forw)
      forwpage(FALSE, 1);
    else {
      backpage(FALSE, 1);
    }
  else {
    dlp = curwp->w_linep;
    while (row-- && dlp!=curbp->b_linep) {
      dlp = lforw(dlp);
    }
    curwp->w_dotp  = dlp;
    curgoal = col + curwp->w_leftOff; /* aim for this col */
    curwp->w_doto  = getgoal(dlp);
    if (!forw) {
      setmark(FALSE,1);
    }
  }
  return(TRUE);
}
