home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / mouse.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  25KB  |  960 lines

  1. /************************************************************************
  2.  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
  3.  * provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is *
  5.  * included in all the files.                                           *
  6.  ************************************************************************/
  7.  
  8. /* This file contains all functions associated with mouse buttons being
  9.  * pressed and any movement of the mouse (except on the Macintosh).
  10.  * These procedures will only be called if JOVE is run under xterm,
  11.  * xJove, or Jovetool.
  12.  */
  13.  
  14. #include "jove.h"
  15.  
  16. #ifdef MOUSE    /* the body is the rest of this file */
  17.  
  18. /* #include "argcount.h" */
  19. #include "disp.h"
  20. #include "misc.h"
  21. #include "ask.h"
  22. #include "delete.h"
  23. #include "fmt.h"
  24. #include "insert.h"    /* for lfreelist, in MouseLine */
  25. #include "marks.h"
  26. #include "move.h"
  27. #include "wind.h"
  28. #include "term.h"    /* for putpad */
  29. #include "jctype.h"
  30. #include "mouse.h"
  31. #include "xjove/mousemsg.h"
  32.  
  33. /* xterm control sequences, including those related to mousing, are described
  34.  * in a file called ctlseqs.ms which is part of the X Distribution.
  35.  *
  36.  * We document some xterm bugs/features here: a good place
  37.  * to capture the hard-won knowledge.
  38.  *
  39.  * - In Hilite mode, after a button 1 press (producing ^[[M xy), and after
  40.  *   the program has replied with ^[f;sx;sy;fr;lrT (as required in Hilite
  41.  *   mode), it treats the next character read as if it had been preceded by
  42.  *   "^[[".  If that next character happens to be A, B, C or D you therefore
  43.  *   get a cursor movement.  If it is any other character, it is lost.
  44.  *   See the #ifdef XTERMBUG below for the workaround.
  45.  *
  46.  * - mouse tracking and highlight tracking modes do not support modifiers
  47.  *   other than CTRL, contrary to ctlseqs.ms.  See the #ifdef NEVER below.
  48.  *
  49.  * - mouse hilite tracking mode (MHTM) only seems to work for button 1.
  50.  *
  51.  * - MHTM does not seem to yield mouse-button-1-up events (but if
  52.  *   a non-empty drag occured, that will be reported)
  53.  *
  54.  * - The doco specifies that the ^[ [ T x y x y x y message should
  55.  *   only be generated by MHTM when the user has dragged outside the
  56.  *   specified bounds.  It seems to also be generated:
  57.  *   + by a multiclick (of button 1, of course)
  58.  *   + by a drag to the left or up
  59.  *   + when the button is released to the right of the end of a text line
  60.  *     (but xterm's idea of this may well differ from JOVE's since JOVE
  61.  *     optimizes screen output)
  62.  */
  63.  
  64. bool    XtermMouse = NO;    /* VAR: should we enable xterm mouse? */
  65. private bool    xtMouseState = NO;    /* have we enabled the mouse? */
  66.  
  67. /* sequences to enable/disable mouse hilite tracking in xterm */
  68. private const char
  69.     xtMouseEnable[] = "\033[?1001h",
  70.     xtMouseDisable[] = "\033[?1001l";
  71.  
  72. private int    but_state;    /* button state (and more) at mouse event */
  73. private int    x_coord;    /* mouse x-coordinate, in pixels, origin 0 */
  74. private int    y_coord;    /* mouse y-coordinate, in characters, origin 0 */
  75. private int        /* xterm drag range coordinates */
  76.     startx, starty,
  77.     endx, endy;
  78. private int    font_width;    /* width of a character in pixels */
  79. private Window    *oldwind;
  80. private Mark    *oldpos = NULL;    /* Use a Mark so it is adjusted automatically */
  81.  
  82. #define LMA_NONE    0000
  83. #define LMA_COPY    0001
  84. #define LMA_CUT        0002
  85. #define LMA_PASTE    0010
  86. #define LMA_CHAR    0020
  87. #define LMA_WORD    0030
  88. #define LMA_LINE    0040
  89. private int last_mouse_act = LMA_NONE;
  90.  
  91. void
  92. MouseOn()
  93. {
  94.     if (XtermMouse != xtMouseState) {
  95.         putpad(XtermMouse? xtMouseEnable : xtMouseDisable, 1);
  96.         xtMouseState = XtermMouse;
  97.     }
  98. }
  99.  
  100. void
  101. MouseOff()
  102. {
  103.     if (xtMouseState) {
  104.         putpad(xtMouseDisable, 1);
  105.         xtMouseState = NO;
  106.     }
  107. }
  108.  
  109. /* Set cursor position to that of mouse pointer. */
  110.  
  111. private void
  112. SetCursor()
  113. {
  114.     int    line_pos = in_window(curwind, curline);
  115.     int    offset = PhysScreen[y_coord].s_offset;
  116.     int    num_moves;
  117.  
  118.     if (line_pos < 0) {
  119.         SetLine(curwind->w_top);
  120.         line_pos = in_window(curwind, curline);
  121.     }
  122.     num_moves = y_coord - line_pos;
  123.     if (num_moves > 0)
  124.         line_move(FORWARD, num_moves, NO);
  125.     if (num_moves < 0)
  126.         line_move(BACKWARD, -num_moves, NO);
  127.  
  128.     curchar = how_far(curline,
  129.         (x_coord + font_width/2) / font_width + offset
  130.         - (W_NUMWIDTH(curwind) + SIWIDTH(offset)));
  131. }
  132.  
  133. private void
  134. ScrollToMouse()
  135. {
  136.     register int    lc;
  137.     const int    width = (CO - 1 - (4 * SG)) * font_width;    /* must match size in ModeLine */
  138.     int    top;
  139.  
  140.     /* This code ought to match the scroll-bar layout computed by
  141.      * WindowRange().
  142.      * - a click in the first column ought to force top-of-file
  143.      * - a click in the last column ought to force end-of-file
  144.      * - don't waste space by displaying less than a screenful at end-of-file.
  145.      * - After clicking at a given point in the scroll bar, the cursor
  146.      *   should be found to be exactly in the middle of the highlighted
  147.      *   region (except near the ends, of course).
  148.      */
  149.     lc = LinesTo(curbuf->b_first, (LinePtr)NULL);
  150.     top = 1 + (long)(x_coord-font_width) * (lc-2) / (width-2*font_width)
  151.         - WSIZE(curwind)/2;
  152.     if (top > lc - WSIZE(curwind))
  153.         top = lc - WSIZE(curwind);
  154.     if (top < 0)
  155.         top = 0;
  156.  
  157.     SetTop(curwind, next_line(curbuf->b_first, top));
  158.     if (in_window(curwind, curline) == -1)
  159.         SetLine(next_line(curwind->w_top, WSIZE(curwind)/2));
  160. }
  161.  
  162. private bool
  163. ObeyProc(p)
  164. void (*p) ptrproto((void));
  165. {
  166.     if (BufMinorMode(curbuf, ReadOnly)) {
  167.         rbell();
  168.         message("[Buffer is read-only]");
  169.         return NO;
  170.     } else {
  171.         (*p)();
  172.         return YES;
  173.     }
  174. }
  175.  
  176. /* Get next value in number sequence */
  177.  
  178. private int
  179. NextValue()
  180. {
  181.     int val;
  182.  
  183.     (void) waitchar();
  184.     Digit();
  185.     val = arg_value();
  186.     clr_arg_value();
  187.     return val;
  188. }
  189.  
  190. /* Select appropriate window */
  191.  
  192. private int
  193. SelectWind(winforce)
  194. Window    *winforce;    /* if non-null, must be within this window */
  195. {
  196.     register Window *wp = fwind;
  197.     int    total_lines = wp->w_height;
  198.  
  199.     /* Find which window mouse pointer is in. */
  200.     while (y_coord >= total_lines) {
  201.         wp = wp->w_next;
  202.         if (wp == fwind)
  203.             return -1;
  204.         total_lines += wp->w_height;
  205.     }
  206.     if (winforce != NULL && wp != winforce)
  207.         return -1;
  208.     SetWind(wp);        /* Set current window */
  209.     return (total_lines - y_coord - 1);    /* Cursor pos within window */
  210. }
  211.  
  212. /* get some X Y pair from xterm; return indication of success */
  213.  
  214. private bool
  215. xtGetXY(xp, yp)
  216. int
  217.     *xp,
  218.     *yp;
  219. {
  220.     ZXchar    cx = waitchar();    /* X coordinate */
  221.  
  222.     /* It appears that mouse events near the extreme right hand edge of
  223.      * a window can give coordinates greater that LI; hence the "LI+1"
  224.      * (and "CO+1" for good measure) in what follows.
  225.      */
  226.     if (' ' < cx && cx <= ' '+CO+1) {
  227.         ZXchar    cy = waitchar();    /* Y coordinate */
  228.  
  229.         if (' ' < cy && cy <= ' '+LI+1) {
  230.             *xp = cx - '!';
  231.             *yp = cy - '!';
  232.             font_width = 1;
  233.             return YES;    /* success */
  234.         }
  235.     }
  236.     return NO;    /* failure */
  237. }
  238.  
  239. #define MPROTO_XTERM    0
  240. #define MPROTO_XTDRAG    1
  241. #define MPROTO_JOVETOOL    2
  242.  
  243. #define XtermProto(p)    ((p) <= MPROTO_XTDRAG)
  244.  
  245. private bool
  246. MouseParams(mproto)
  247. int    mproto;
  248. {
  249.     /* The mouse commands will be invoked by hitting a mouse
  250.      * button but can also be invoked by typing the escape sequence
  251.      * CTRL-Xm so the following validation checks are necessary.
  252.      */
  253.  
  254.     static int    wind_pos;        /* reverse y-coordinate within window */
  255.     static bool    mode_mode = NO;        /* true while doing modeline */
  256.  
  257.     /* up_expected is an extended bool: it can be YES, NO, and -1!
  258.      * -1 signifies that we are in xterm mouse hilite tracking mode
  259.      * which appears to fail to yield an up event if it deems the
  260.      * event uninteresting (i.e. no motion).
  261.      */
  262.     static int    up_expected = NO;    /* true while button held down */
  263.     static int    estartx, estarty;    /* from last enable */
  264.  
  265.     bool    input_good = NO;
  266.     /* Format of command to xterm to start or stop mouse hilite tracking:
  267.      * ^[ [ func ; startx ; starty ; firstrow ; lastrow T
  268.      */
  269. #define XTERMBUG
  270. #ifdef XTERMBUG
  271.     static const char    hl_fmt[] = "\033[%d;%d;%d;%d;%dTX";
  272. #else
  273.     static const char    hl_fmt[] = "\033[%d;%d;%d;%d;%dT";
  274. #endif
  275.     char    buf[sizeof(hl_fmt) + 4];
  276.  
  277.     /* This switch reads and decodes the control sequence.
  278.      * - input_good is set to YES if the sequence looks valid.
  279.      * - but_state, x_coord, and y_coord are changed to reflect the new state
  280.      * - if the event could follow an "initiate hilite tracking"
  281.      *   and up_expected is -1, the decoder should adjust up_expected.
  282.      */
  283.     switch (mproto) {
  284.     case MPROTO_XTERM:
  285.         {
  286.             ZXchar    cb = LastKeyStruck;
  287.  
  288.             switch (cb) {
  289.             default:
  290.                 if (' ' <= cb && cb < ' '+040 && xtGetXY(&x_coord, &y_coord)) {
  291.                     /* ^[ [ M buttoninfo mousex mousey
  292.                      * Button event with coordinates.
  293.                      * On a release event we aren't told which button
  294.                      * is being released.
  295.                      */
  296.                     cb -= ' ';
  297.                     if ((cb & 03) == 03) {
  298.                         /* guess that released button is last depressed */
  299.                         but_state = (but_state & JT_BUTMASK) | JT_UPEVENT;
  300.                         /* We are welcome in xterm mouse hilite tracking mode */
  301.                         if (up_expected == -1)
  302.                             up_expected = YES;
  303.                     } else {
  304.                         static const int butcode[] = { JT_LEFT, JT_MIDDLE, JT_RIGHT };
  305.  
  306.                         but_state = butcode[cb & 03] | JT_DOWNEVENT;
  307.                         /* We are welcome in xterm mouse hilite tracking mode */
  308.                         if (up_expected == -1)
  309.                             up_expected = NO;
  310.                     }
  311. #ifdef NEVER    /* surprise: xterm won't generate these modifiers! */
  312.                     if (cb & 04)
  313.                         but_state |= JT_SHIFT;
  314.                     if (cb & 010)
  315.                         but_state |= JT_META;
  316. #endif
  317.                     if (cb & 020)
  318.                         but_state |= JT_CONTROL;
  319.                     input_good = YES;
  320.                 }
  321.                 break;
  322.             }
  323.         }
  324.         break;
  325.  
  326.     case MPROTO_XTDRAG:
  327.         /* handle xterm hilite tracking drag event: ^[ [ t or ^[ [ T
  328.          * These are only generated for left-button acts.
  329.          * Each is some drag-like event.
  330.          */
  331.         switch (LastKeyStruck) {
  332.         case 'T':
  333.             /* ^[ [ T startx starty endx endy mousex mousey
  334.              * returned on Left Button release beyond end-of-line,
  335.              * or (undocumented) a drag to left or up, or
  336.              * (undocumented) for a multi-click select.  These are
  337.              * transformed into three kinds of event, distinguished
  338.              * by JT_CLICK2 and JT_CLICK3.  The JT_CLICKn are used to
  339.              * indicate that it was a multi-click select (by
  340.              * eliminating the other cases).  Note that a double click
  341.              * beyond end-of-line (or on an empty line) is not reported
  342.              * even though a third such click is.  Oh the joys of
  343.              * relying on undocumented features!
  344.              */
  345.             if (xtGetXY(&startx, &starty)
  346.             && xtGetXY(&endx, &endy)
  347.             && xtGetXY(&x_coord, &y_coord))
  348.             {
  349.                 input_good = YES;
  350.                 if (((  endy != estarty || endx != estartx
  351.                      || y_coord > estarty
  352.                      || (y_coord == estarty && x_coord >= estartx)
  353.                      )    /* it isn't a drag left or up */
  354.                 &&   (  endx != 0 || startx == endx
  355.                      )    /* it isn't a drag across an e-o-l */
  356.                     )
  357.                 ||  (but_state & JT_CLICKMASK) == JT_CLICK2)
  358.                     /* it is clearly a third click */
  359.                 {
  360.                     /* a complex selection */
  361.                     if ((but_state & JT_CLICKMASK) == JT_CLICK2 || endx == 0)
  362.                         /* this is a third click:
  363.                          * endx == 0 implies that we
  364.                          * missed the second one
  365.                          */
  366.                         but_state = JT_LEFT | JT_DRAGEVENT | JT_CLICK3;
  367.                     else
  368.                         but_state = JT_LEFT | JT_DRAGEVENT | JT_CLICK2;
  369.                 } else {
  370.                     /* A simple drag.
  371.                      * Note: code is the same as for case 't' below.
  372.                      */
  373.                     but_state = JT_LEFT | JT_DRAGEVENT;
  374.                 }
  375.             }
  376.             break;
  377.  
  378.         case 't':
  379.             /* ^[ [ t mousex mousey
  380.              * returned on Left Button release within valid text.
  381.              * A simple drag.
  382.              * Note: code is duplicated above as part of case 'T'.
  383.              */
  384.             if (xtGetXY(&x_coord, &y_coord)) {
  385.                 but_state = JT_LEFT | JT_DRAGEVENT;
  386.                 input_good = YES;
  387.             }
  388.             break;
  389.         }
  390.         /* We are welcome in xterm mouse hilite tracking mode */
  391.         if (input_good && up_expected == -1)
  392.             up_expected = YES;
  393.         break;
  394.  
  395.     case MPROTO_JOVETOOL:
  396.         if (waitchar() == '(') {
  397.             but_state = NextValue();    /* Read in parameters */
  398.             switch (waitchar()) {
  399.             case ' ':
  400.                 {
  401.                     int x = NextValue();
  402.  
  403.                     if (waitchar() == ' ') {
  404.                         int y = NextValue();
  405.  
  406.                         if (waitchar() == ' ') {
  407.                             font_width = NextValue();
  408.                             if (waitchar() == ')'
  409.                             && waitchar() == '\r')
  410.                             {
  411.                                 input_good = YES;
  412.                                 x_coord = x;
  413.                                 y_coord = y;
  414.                             }
  415.                         }
  416.                     }
  417.                 }
  418.                 break;
  419.             case ')':
  420.                 input_good = (waitchar() == '\r');
  421.                 break;
  422.             }
  423.         }
  424.         break;
  425.     }
  426.     this_cmd = OTHER_CMD;    /* no longer gathering args */
  427.  
  428.     if (!input_good) {
  429.         if (XtermProto(mproto) && but_state == (JT_LEFT | JT_DOWNEVENT)) {
  430.             /* abort Hilite mode to prevent hangups */
  431.             swritef(buf, sizeof(buf), hl_fmt, 0,1,1,1,1);
  432.             putpad(buf, 1);
  433.         }
  434.         complain("[mouse input of wrong format]");
  435.     }
  436.  
  437.     /* Note: at this point, up_expected still reflects previous state.
  438.      * Unfortunately, xterm seems to elide some button-up events!
  439.      * For this reason, some cases above adjust up_expected to
  440.      * prevent the following sanity check from going off.
  441.      */
  442.  
  443.     /* check: button_(was)_held iff currently event is UP or DRAG */
  444.     if (up_expected != ((but_state & JT_EVENTMASK)!=JT_DOWNEVENT)
  445.     || (up_expected == YES && last_cmd != MOUSE_CMD)) {
  446.         if (XtermProto(mproto) && but_state == (JT_LEFT | JT_DOWNEVENT)) {
  447.             /* abort Hilite mode to prevent hangups */
  448.             swritef(buf, sizeof(buf), hl_fmt, 0,1,1,1,1);
  449.             putpad(buf, 1);
  450.         }
  451.         up_expected = NO;    /* resynch in neutral */
  452.         complain("[Mouse events out of order]");
  453.     }
  454.  
  455.     /* update up_expected to reflect new state */
  456.     up_expected = XtermProto(mproto) && (but_state&JT_EVENTMASK) == JT_DRAGEVENT
  457.         ? -1 : (but_state & JT_EVENTMASK) != JT_UPEVENT;
  458.     if (up_expected != NO)
  459.         this_cmd = MOUSE_CMD;
  460.  
  461.     if ((but_state & JT_EVENTMASK) == JT_DOWNEVENT
  462.     && (but_state & JT_PASTEMASK) && (but_state & JT_CSMASK) == 0)
  463.     {
  464.         /* Hugh: ??? is this leak possible? -- checking code added as a probe. */
  465.         /* CHL believes assert oldpos==NULL */
  466.         if (oldpos != NULL)
  467.             complain("[internal error: mark leak from MouseParams]");
  468.         oldpos = MakeMark(curline, curchar);
  469.     }
  470.  
  471.     /* UP events in xterm should do nothing in foreign windows */
  472.     if (XtermProto(mproto)
  473.     && (but_state & JT_EVENTMASK) == JT_UPEVENT
  474.     && curwind != oldwind
  475.     && !mode_mode)
  476.         return NO;
  477.  
  478.     if ((but_state & (JT_CLICKMASK | JT_EVENTMASK)) == JT_DOWNEVENT) {
  479.         /* button down, and first click at that:
  480.          * - ok to changing window
  481.          * - ok to move in or out of modeline (scroll bar)
  482.          */
  483.         oldwind = curwind;
  484.         wind_pos = SelectWind((Window *) NULL);
  485.         mode_mode = (wind_pos == 0);
  486.         if (XtermProto(mproto) && but_state == (JT_LEFT | JT_DOWNEVENT)) {
  487.             /* Initiate or abort mouse hilite tracking.  We use hilite
  488.              * tracking if we are not on the modeline.
  489.              * When hilite tracking is used, apparently the up event is
  490.              * elided if it would report no change in location.
  491.              */
  492.             bool    use_hilite = !mode_mode;
  493.  
  494.             if (use_hilite)
  495.                 up_expected = -1;    /* half-expect an up */
  496.             swritef(buf, sizeof(buf), hl_fmt,
  497.                 use_hilite,
  498.                 (estartx = x_coord)+1,
  499.                 (estarty = y_coord)+1,
  500.                 y_coord + wind_pos - curwind->w_height + 2,
  501.                 y_coord + wind_pos + 1);
  502.             putpad(buf, 1);
  503.         }
  504.     } else if (!mode_mode) {
  505.         /* can only stay within the window, and cannot switch into modeline */
  506.         wind_pos = SelectWind(oldwind);
  507.     }
  508.  
  509.     if (mode_mode) {
  510.         /* An event in mode_mode only scrolls or resizes, with no
  511.          * action (unless it were a first down event, when it might
  512.          * have caused a new window to be selected).  Shifts and
  513.          * multiclicks indicate a confused user.
  514.          */
  515.         if ((but_state & JT_BUTMASK) == JT_LEFT
  516.         && (but_state & JT_EVENTMASK) != JT_UPEVENT)
  517.         {
  518.             ScrollToMouse();
  519.             if (but_state & (JT_CSMASK | JT_CLICKMASK))
  520.                 complain("[You are just scrolling a window]");
  521.         } else if ((but_state & JT_BUTMASK) == JT_MIDDLE
  522.         && (but_state & (JT_UPEVENT | JT_DRAGEVENT)) != 0)
  523.         {
  524.             oldwind = curwind;
  525.             if ((wind_pos = SelectWind((Window *) NULL)) < 0)
  526.                 return NO;
  527.             if (curwind == oldwind->w_next) {
  528.                 wind_pos -= curwind->w_height;
  529.                 curwind = oldwind;
  530.                 SetBuf(curwind->w_bufp);
  531.             }
  532.             if (curwind == oldwind && curwind->w_next != fwind)
  533.                 WindSize(curwind->w_next, wind_pos);
  534.             if (but_state & (JT_CSMASK | JT_CLICKMASK))
  535.                 complain("[You are just resizing a window]");
  536.         }
  537.     } else if ((but_state & JT_PASTEMASK) && (but_state & JT_CSMASK) == 0) {
  538.         /* With JT_PASTE/CUT, window switching is allowed. */
  539.         return YES;
  540.     } else if (curwind != oldwind) {
  541.         /* Clicking within a different window (as opposed to its
  542.          * mode line) only switches to that window, with no action.
  543.          * Other shifts and multiclicks indicate a confused user.
  544.          */
  545.         if (but_state & (JT_CSMASK | JT_CLICKMASK))
  546.             complain("[You were just changing windows]");
  547.     } else if (wind_pos <= 0) {
  548.         /* ignore out-of-window events */
  549.     } else    {
  550.         /* We've run the gauntlet.  Give the OK for action. */
  551.         return YES;
  552.     }
  553.     last_mouse_act = LMA_NONE;
  554.     if (oldpos != NULL) {
  555.         SetWind(oldwind);
  556.         DelMark(oldpos);
  557.         oldpos = NULL;
  558.     }
  559.     return NO;
  560. }
  561.  
  562. private void
  563. MousePoint(mproto)
  564. int    mproto;
  565. {
  566.     last_mouse_act = LMA_NONE;
  567.     if (MouseParams(mproto))
  568.         SetCursor();
  569. }
  570.  
  571. void
  572. xjMousePoint()
  573. {
  574.     MousePoint(MPROTO_JOVETOOL);
  575. }
  576.  
  577. void
  578. xtMousePoint()
  579. {
  580.     MousePoint(MPROTO_XTERM);
  581. }
  582.  
  583. private void
  584. MouseMark(mproto)
  585. int    mproto;
  586. {
  587.     last_mouse_act = LMA_NONE;
  588.     if (MouseParams(mproto)) {
  589.         SetCursor();
  590.         set_mark();
  591.     }
  592. }
  593.  
  594. void
  595. xjMouseMark()
  596. {
  597.     MouseMark(MPROTO_JOVETOOL);
  598. }
  599.  
  600. void
  601. xtMouseMark()
  602. {
  603.     MouseMark(MPROTO_XTERM);
  604. }
  605.  
  606. /* xtMouseYank is a reduced xtMousePointYank.
  607.  * Although xtMousePointYank should be more useful,
  608.  * xtMouseYank is more like XTerm's native behavior.
  609.  */
  610. void
  611. xtMouseYank()
  612. {
  613.     last_mouse_act = LMA_NONE;
  614.     if (MouseParams(MPROTO_XTERM)) {
  615.         ObeyProc(Yank);
  616.         this_cmd = MOUSE_CMD;
  617.     }
  618. }
  619.  
  620. void
  621. xtMousePointYank()
  622. {
  623.     last_mouse_act = LMA_NONE;
  624.     if (MouseParams(MPROTO_XTERM)) {
  625.         SetCursor();
  626.         ObeyProc(Yank);
  627.         this_cmd = MOUSE_CMD;
  628.     }
  629. }
  630.  
  631. void
  632. xtMouseCutPointYank()
  633. {
  634.     Mark *m;
  635.  
  636.     last_mouse_act = LMA_NONE;
  637.     if (MouseParams(MPROTO_XTERM) && ObeyProc(set_mark)) {
  638.         SetCursor();
  639.         set_mark();
  640.         m = curmark;
  641.         PopMark();    /* pop our 2nd set_mark */
  642.         PopMark();    /* pop our 1st set_mark */
  643.         DelReg();    /* because we are (usually) about to
  644.                    yank the same region again */
  645.         ToMark(m);
  646.         Yank();
  647.         this_cmd = MOUSE_CMD;
  648.     }
  649. }
  650.  
  651. void
  652. xtMouseNull()
  653. {
  654.     MouseParams(MPROTO_XTERM);
  655. }
  656.  
  657. /* Double clicking selects either an identifier (according to the current
  658.  * major mode) or, if the current char is not part of an identifier, it
  659.  * selects the gap between two identifiers.  However, the selection is always
  660.  * within one line.  startMouseWord and endMouseWord are called to locate the
  661.  * two ends of the selected (un)identifier.  Note that the selection can be
  662.  * empty.
  663.  */
  664. private void
  665. startMouseWord()
  666. {
  667.     bool    in_id;
  668.  
  669.     if (eolp() && !bolp())
  670.         curchar -= 1;
  671.     in_id = jisident(linebuf[curchar]);
  672.     while (!bolp() && jisident(linebuf[curchar-1]) == in_id)
  673.         curchar -= 1;
  674. }
  675.  
  676. private void
  677. endMouseWord()
  678. {
  679.     bool    in_id;
  680.  
  681.     if (eolp() && !bolp())
  682.         curchar -= 1;
  683.     in_id = jisident(linebuf[curchar]);
  684.     while (!eolp() && jisident(linebuf[curchar]) == in_id)
  685.         curchar += 1;
  686. }
  687.  
  688. private void
  689. doMouseWord()
  690. {
  691.     startMouseWord();
  692.     set_mark();
  693.     endMouseWord();
  694. }
  695.  
  696. private void
  697. doMouseLine()
  698. {
  699.     Bol();
  700.     set_mark();
  701.     line_move(FORWARD, 1, NO);
  702. }
  703.  
  704. /*
  705.  * This command extends the region, at either end, in units of chars, words,
  706.  * or lines depending on last_mouse_act (i.e. on whether the region was
  707.  * selected by dragging, single or double clicking).  If used to shrink the
  708.  * region, it is the point end (not the mark end) that gets moved.
  709.  */
  710. private bool
  711. doMouseExtend()
  712. {
  713.     bool    region_forward, new_forward;
  714.  
  715.     if (last_mouse_act == LMA_NONE)
  716.         return NO;    /* treat as xtMouseNull */
  717.  
  718.     region_forward = !inorder(curline, curchar, curmark->m_line, curmark->m_char);
  719.     ExchPtMark();    /* for better effect when shrinking region */
  720.     set_mark();
  721.     SetCursor();
  722.     new_forward = !inorder(curline, curchar, curmark->m_line, curmark->m_char);
  723.  
  724.     switch (last_mouse_act) {
  725.     case LMA_CHAR:
  726.         if (region_forward != new_forward) {
  727.             PopMark();
  728.             SetCursor();
  729.         }
  730.         break;
  731.     case LMA_WORD:
  732.         if (region_forward != new_forward) {
  733.             PopMark();
  734.             SetCursor();
  735.         }
  736.         if (new_forward)
  737.             endMouseWord();
  738.         else
  739.             startMouseWord();
  740.         break;
  741.     case LMA_LINE:
  742.         if (region_forward != new_forward) {
  743.             PopMark();
  744.             SetCursor();
  745.         }
  746.         Bol();
  747.         if (new_forward)
  748.             line_move(FORWARD, 1, NO);
  749.         break;
  750.     }
  751.     return !(curmark->m_line==curline && curmark->m_char==curchar);
  752. }
  753.  
  754. /* This command is intended to be bound to ^[ [ t and ^[ [ T, which
  755.  * are the button UP events for the left button in Hilite mode.
  756.  * It is not clear whether or not these codes should be produced
  757.  * after a simple click, so the following code plays safe.
  758.  *
  759.  * Should a multiclick select what XTerm shows ("visual fidelity"),
  760.  * thus using XTerm's definition of "word", or should it select based
  761.  * on JOVE's version of "word"?  To select the latter, define XTJOVEWORD.
  762.  *
  763.  * This choice also affects how JOVE decides at which end of the selection
  764.  * "point" should be placed.
  765.  *
  766.  * - When JOVE uses its own definition of word or line (they are of a piece)
  767.  *   it selects the current unit, and then, if necessary, extends to envelop
  768.  *   the current mouse position.  If no extension is needed the point will be
  769.  *   at the right.  If extension is needed (usually because a drag was used)
  770.  *   point will be at the far end of the extension.  This sounds complicated,
  771.  *   but feels quite nice.
  772.  *
  773.  * - When JOVE uses XTerm's selection range, it places point at the end
  774.  *   closest to the final mouse position.  This sounds simple, but it may
  775.  *   be slightly surprising when no drag was done.
  776.  */
  777. void
  778. xtMouseMarkDragPointCopy()
  779. {
  780.     if (MouseParams(MPROTO_XTDRAG)) {
  781.         /* assert((but_state & JT_EVENTMASK) == JT_DRAGEVENT) */
  782.         if ((but_state & JT_CLICKMASK) == 0) {
  783.             /* simple (character-based) drag (from point to mouse) */
  784.             set_mark();
  785.             SetCursor();
  786.             CopyRegion();
  787.             last_mouse_act = LMA_CHAR;
  788. #ifdef XTJOVEWORD
  789.         } else if ((but_state & JT_CLICKMASK) == JT_CLICK2) {
  790.             /* select based on JOVE's notion of words */
  791.             doMouseWord();
  792.             last_mouse_act = LMA_WORD;
  793.             if (doMouseExtend())
  794.                 CopyRegion();
  795.  
  796.         } else /* ((but_state & JT_CLICKMASK) == JT_CLICK3) */ {
  797.             /* select lines */
  798.             if (last_mouse_act == LMA_WORD) {
  799.                 /* Second click was not missed.
  800.                  * Avoid nasty mark and kill buildup.
  801.                  */
  802.                 PopMark();
  803.                 DelKillRing();
  804.             }
  805.             doMouseLine();
  806.             last_mouse_act = LMA_LINE;
  807.             if (doMouseExtend())
  808.                 CopyRegion();
  809.         }
  810. #else
  811.         } else {
  812.             /* select based on xterm's selection: startxy through endxy
  813.              * Set point to whichever is closer to mouse position
  814.              * and set mark to farther.
  815.              */
  816.             int        which_end;    /* distance difference: negative if closer to start */
  817.  
  818.             if (last_mouse_act != LMA_NONE) {
  819.                 /* avoid nasty mark and kill buildup */
  820.                 PopMark();
  821.                 DelKillRing();
  822.             }
  823.             which_end = (y_coord - starty) - (endy - y_coord);
  824.             if (which_end == 0)
  825.                 which_end = (x_coord - startx) - (endx - x_coord);
  826.             x_coord = startx;
  827.             y_coord = starty;
  828.             SetCursor();
  829.             set_mark();
  830.             x_coord = endx;
  831.             y_coord = endy;
  832.             SetCursor();
  833.             if (which_end < 0)
  834.                 ExchPtMark();
  835.             CopyRegion();
  836.             last_mouse_act = last_mouse_act == LMA_WORD? LMA_LINE : LMA_WORD;
  837.         }
  838. #endif
  839.     }
  840. }
  841.  
  842. /* Extend the region to the pointed-at character, word or line
  843.  * (depending on last_mouse_act).  Only valid after an event which
  844.  * selects and copies a region (e.g. xtMouseMarkDragPointCop).
  845.  */
  846. void
  847. xtMouseExtend()
  848. {
  849.     if (MouseParams(MPROTO_XTERM)) {
  850.         if (doMouseExtend()) {
  851.             DelKillRing();
  852.             CopyRegion();
  853.         }
  854.     }
  855. }
  856.  
  857. /* undo effect of preceding MouseCopyCut, if any */
  858. private void
  859. MouseUndo()
  860. {
  861.     if (last_mouse_act & LMA_PASTE) {
  862.         ObeyProc(DelReg);    /* at old curwind/line/char */
  863.         this_cmd = MOUSE_CMD;
  864.         PopMark();
  865.     }
  866.     SelectWind((Window *)NULL);
  867.     switch (last_mouse_act & ~LMA_PASTE) {
  868.     case LMA_CUT:
  869.         ToMark(CurMark());
  870.         Yank();
  871.         this_cmd = MOUSE_CMD;
  872.         PopMark();
  873.         /*FALLTHROUGH*/
  874.     case LMA_COPY:
  875.         /* remove the entry we previously put in the kill-ring */
  876.         ToMark(CurMark());
  877.         DelKillRing();
  878.     }
  879.     PopMark();
  880.     last_mouse_act = LMA_NONE;
  881. }
  882.  
  883. void
  884. xjMouseWord()
  885. {
  886.     if (MouseParams(MPROTO_JOVETOOL)) {
  887.         MouseUndo();
  888.         doMouseWord();
  889.     }
  890. }
  891.  
  892. void
  893. xjMouseLine()
  894. {
  895.     if (MouseParams(MPROTO_JOVETOOL)) {
  896.         MouseUndo();
  897.         SetCursor();
  898.         doMouseLine();
  899.     }
  900. }
  901.  
  902. void
  903. xjMouseYank()
  904. {
  905.     last_mouse_act = LMA_NONE;
  906.     if (MouseParams(MPROTO_JOVETOOL)) {
  907.         SetCursor();
  908.         if (but_state & JT_CONTROL)
  909.             ObeyProc(Yank);
  910.     }
  911. }
  912.  
  913. void
  914. xjMouseCopyCut()
  915. {
  916.     register Mark    *mp = curmark;
  917.  
  918.     if (MouseParams(MPROTO_JOVETOOL)) {
  919.         if (mp!=NULL
  920.         && (mp->m_line != curline || mp->m_char != curchar))
  921.         {
  922.             switch(but_state & (JT_CSMASK | JT_PASTEMASK)) {
  923.             case JT_CONTROL:
  924.                 CopyRegion();
  925.                 last_mouse_act = LMA_COPY;
  926.                 break;
  927.             case JT_CONTROL | JT_SHIFT:
  928.                 last_mouse_act = ObeyProc(DelReg)? LMA_CUT : LMA_NONE;
  929.                 break;
  930.             case JT_PASTE:
  931.                 CopyRegion();
  932.                 SetWind(oldwind);
  933.                 ToMark(oldpos);
  934.                 ObeyProc(Yank);
  935.                 DelMark(oldpos);
  936.                 oldpos = NULL;
  937.                 last_mouse_act = LMA_COPY | LMA_PASTE;
  938.                 break;
  939.             case JT_CUT:
  940.                 ObeyProc(DelReg);
  941.                 SetWind(oldwind);
  942.                 ToMark(oldpos);
  943.                 ObeyProc(Yank);
  944.                 DelMark(oldpos);
  945.                 oldpos = NULL;
  946.                 last_mouse_act = LMA_CUT | LMA_PASTE;
  947.                 break;
  948.             /* default: ignore */
  949.             }
  950.         } else if (but_state & JT_PASTEMASK) {
  951.             SetWind(oldwind);
  952.             ToMark(oldpos);
  953.             DelMark(oldpos);
  954.             oldpos = NULL;
  955.         }
  956.     }
  957. }
  958.  
  959. #endif /* MOUSE */
  960.