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