1 /*
2 * Copyright (c) 1993-2009, 2014-2016, 2018-2020 Paul Mattes.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Paul Mattes nor his contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * Portions of this code were taken from xterm/button.c:
32 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
33 *
34 * All Rights Reserved
35 *
36 * Permission to use, copy, modify, and distribute this software and its
37 * documentation for any purpose and without fee is hereby granted,
38 * provided that the above copyright notice appear in all copies and that
39 * both that copyright notice and this permission notice appear in
40 * supporting documentation, and that the name of Digital Equipment
41 * Corporation not be used in advertising or publicity pertaining to
42 * distribution of the software without specific, written prior permission.
43 *
44 *
45 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
46 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
47 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
48 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
49 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
50 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
51 * SOFTWARE.
52 */
53
54 /*
55 * select.c
56 * This module handles selections.
57 */
58
59 #include "globals.h"
60 #include "xglobals.h"
61
62 #include <X11/Xatom.h>
63 #include <X11/Xmu/Atoms.h>
64 #include <X11/Xmu/StdSel.h>
65 #include "3270ds.h"
66 #include "appres.h"
67 #include "ctlr.h"
68 #include "resources.h"
69 #include "toggles.h"
70
71 #include "codepage.h"
72 #include "ctlrc.h"
73 #include "kybd.h"
74 #include "popups.h"
75 #include "screen.h"
76 #include "selectc.h"
77 #include "tables.h"
78 #include "unicodec.h"
79 #include "utf8.h"
80 #include "utils.h"
81 #include "xactions.h"
82 #include "xscreen.h"
83
84 #define Max(x, y) (((x) > (y))? (x): (y))
85 #define Min(x, y) (((x) < (y))? (x): (y))
86
87 /*
88 * Mouse side.
89 */
90
91 /* A button click establishes the boundaries of the 'fixed' area. */
92 static int f_start = 0; /* 'fixed' area */
93 static int f_end = 0;
94
95 /* Mouse motion moves the boundaries of the 'varying' area. */
96 static int v_start = 0; /* 'varying' area */
97 static int v_end = 0;
98
99 static unsigned long down_time = 0;
100 static unsigned long down1_time = 0;
101 static Dimension down1_x, down1_y;
102 static unsigned long up_time = 0;
103 static int saw_motion = 0;
104 static int num_clicks = 0;
105 static void grab_sel(int start, int end, bool really, Time t);
106 #define NS 5
107 static Atom want_sel[NS];
108 static struct { /* owned selections */
109 Atom atom; /* atom */
110 char *buffer; /* buffer contents (UTF-8) */
111 } own_sel[NS];
112 static bool cursor_moved = false;
113 static int saved_cursor_addr;
114 static void own_sels(Time t);
115 static int n_owned = -1;
116 static bool any_selected = false;
117
118 #define CLICK_INTERVAL 300
119
120 #define event_x(event) event->xbutton.x
121 #define event_y(event) event->xbutton.y
122 #define event_time(event) event->xbutton.time
123
124 #define BOUNDED_XY(event, x, y) { \
125 x = X_TO_COL(event_x(event)); \
126 if (x < 0) { \
127 x = 0; \
128 } \
129 if (x >= COLS) { \
130 x = COLS - 1; \
131 } \
132 if (flipped) { \
133 x = (COLS - x) - 1; \
134 } \
135 y = Y_TO_ROW(event_y(event) - *descent); \
136 if (y <= 0) { \
137 y = 0; \
138 } \
139 if (y >= ROWS) { \
140 y = ROWS - 1; \
141 } \
142 }
143
144 static int char_class[256] = {
145 /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
146 32, 1, 1, 1, 1, 1, 1, 1, 1, 32, 1, 1, 1, 1, 1, 1,
147 /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
148 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
149 /* sp ! " # $ % & ' ( ) * + , - . / */
150 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
151 /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
152 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 58, 59, 60, 61, 62, 63,
153 /* @ A B C D E F G H I J K L M N O */
154 64, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
155 /* P Q R S T U V W X Y Z [ \ ] ^ _ */
156 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 91, 92, 93, 94, 48,
157 /* ` a b c d e f g h i j k l m n o */
158 96, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
159 /* p q r s t u v w x y z { | } ~ del */
160 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,123,124,125,126, 1,
161 /* ---,---,---,---,---,---,---,---,---,---,---,---,---,---,---,--- */
162 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
163 /* ---,---,---,---,---,---,---,---,---,---,---,---,---,---,---,--- */
164 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
165 /* nob exc cen ste cur yen bro sec dia cop ord gui not hyp reg mac */
166 32,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
167 /* deg plu two thr acu mu par per ce one mas gui one one thr que */
168 176,177,178,179,180,181,182,183,184,185,186,178,188,189,190,191,
169 /* Agr Aac Aci Ati Adi Ari AE Cce Egr Eac Eci Edi Igr Iac Ici Idi */
170 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
171 /* ETH Nti Ogr Oac Oci Oti Odi mul Oob Ugr Uac Uci Udi Yac THO ssh */
172 48, 48, 48, 48, 48, 48, 48,215, 48, 48, 48, 48, 48, 48, 48, 48,
173 /* agr aac aci ati adi ari ae cce egr eac eci edi igr iac ici idi */
174 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
175 /* eth nti ogr oac oci oti odi div osl ugr uac uci udi yac tho ydi */
176 48, 48, 48, 48, 48, 48, 48,247, 48, 48, 48, 48, 48, 48, 48, 48
177 };
178
179 /* Parse a charClass string: [low-]high:value[,...] */
180 void
reclass(char * s)181 reclass(char *s)
182 {
183 int n;
184 int low, high, value;
185 int i;
186 char c;
187
188 n = -1;
189 low = -1;
190 high = -1;
191 for (;;) {
192 c = *s++;
193 if (isdigit((unsigned char)c)) {
194 if (n == -1) {
195 n = 0;
196 }
197 n = (n * 10) + (c - '0');
198 if (n > 255) {
199 goto fail;
200 }
201 } else if (c == '-') {
202 if (n == -1 || low != -1) {
203 goto fail;
204 }
205 low = n;
206 n = -1;
207 } else if (c == ':') {
208 if (n == -1) {
209 goto fail;
210 }
211 high = n;
212 n = -1;
213 } else if (c == ',' || c == '\0') {
214 if (n == -1) {
215 goto fail;
216 }
217 value = n;
218 n = -1;
219 if (high == -1) {
220 goto fail;
221 }
222 if (low == -1) {
223 low = high;
224 }
225 if (high < low) {
226 goto fail;
227 }
228 for (i = low; i <= high; i++) {
229 char_class[i] = value;
230 }
231 low = -1;
232 high = -1;
233 if (c == '\0') {
234 return;
235 }
236 } else {
237 goto fail;
238 }
239 }
240
241 fail:
242 popup_an_error("Error in %s string", ResCharClass);
243 }
244
245 static int
ucs4_class(ucs4_t u)246 ucs4_class(ucs4_t u)
247 {
248 return (u < 0x100)? char_class[u]: (int)u;
249 }
250
251 static void
select_word(int baddr,Time t)252 select_word(int baddr, Time t)
253 {
254 unsigned char fa = get_field_attribute(baddr);
255 unsigned char ch;
256 int class;
257
258 /* Find the initial character class */
259 if (ea_buf[baddr].ucs4) {
260 class = ucs4_class(ea_buf[baddr].ucs4);
261 } else {
262 if (FA_IS_ZERO(fa)) {
263 ch = EBC_space;
264 } else {
265 ch = ea_buf[baddr].ec;
266 }
267 class = char_class[ebc2asc0[ch]];
268 }
269
270 /* Find the beginning */
271 for (f_start = baddr; f_start % COLS; f_start--) {
272 int xclass;
273
274 if (ea_buf[f_start].ucs4) {
275 xclass = ucs4_class(ea_buf[f_start].ucs4);
276 } else {
277 fa = get_field_attribute(f_start);
278 if (FA_IS_ZERO(fa)) {
279 ch = EBC_space;
280 } else {
281 ch = ea_buf[f_start].ec;
282 }
283 xclass = char_class[ebc2asc0[ch]];
284 }
285 if (xclass != class) {
286 f_start++;
287 break;
288 }
289 }
290
291 /* Find the end */
292 for (f_end = baddr; (f_end+1) % COLS; f_end++) {
293 int xclass;
294
295 if (ea_buf[f_start].ucs4) {
296 xclass = ucs4_class(ea_buf[f_end].ucs4);
297 } else {
298 fa = get_field_attribute(f_end);
299 if (FA_IS_ZERO(fa)) {
300 ch = EBC_space;
301 } else {
302 ch = ea_buf[f_end].ec;
303 }
304 xclass = char_class[ebc2asc0[ch]];
305 }
306 if (xclass != class) {
307 f_end--;
308 break;
309 }
310 }
311
312 v_start = f_start;
313 v_end = f_end;
314 grab_sel(f_start, f_end, true, t);
315 }
316
317 static void
select_line(int baddr,Time t)318 select_line(int baddr, Time t)
319 {
320 f_start = baddr - (baddr % COLS);
321 f_end = f_start + COLS - 1;
322 v_start = f_start;
323 v_end = f_end;
324 grab_sel(f_start, f_end, true, t);
325 }
326
327
328 /*
329 * Start a new selection.
330 * Usually bound to <Btn1Down>.
331 */
332 void
select_start_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)333 select_start_xaction(Widget w, XEvent *event, String *params,
334 Cardinal *num_params)
335 {
336 int x, y;
337 int baddr;
338
339 xaction_debug(select_start_xaction, event, params, num_params);
340 if (w != *screen) {
341 return;
342 }
343 BOUNDED_XY(event, x, y);
344 baddr = ROWCOL_TO_BA(y, x);
345 f_start = f_end = v_start = v_end = baddr;
346 down1_time = down_time = event_time(event);
347 down1_x = event_x(event);
348 down1_y = event_y(event);
349 if (down_time - up_time > CLICK_INTERVAL) {
350 num_clicks = 0;
351 /* Commit any previous cursor move. */
352 cursor_moved = false;
353 }
354 if (num_clicks == 0) {
355 unselect(0, ROWS*COLS);
356 }
357 }
358
359 /*
360 * Alternate form of select_start, which combines cursor motion with selection.
361 * Usually bound to <Btn1Down> in a user-specified keymap.
362 */
363 void
move_select_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)364 move_select_xaction(Widget w, XEvent *event, String *params,
365 Cardinal *num_params)
366 {
367 int x, y;
368 int baddr;
369
370 xaction_debug(move_select_xaction, event, params, num_params);
371 if (w != *screen) {
372 return;
373 }
374 BOUNDED_XY(event, x, y);
375 baddr = ROWCOL_TO_BA(y, x);
376
377 f_start = f_end = v_start = v_end = baddr;
378 down1_time = down_time = event_time(event);
379 down1_x = event_x(event);
380 down1_y = event_y(event);
381
382 if (down_time - up_time > CLICK_INTERVAL) {
383 num_clicks = 0;
384 /* Commit any previous cursor move. */
385 cursor_moved = false;
386 }
387 if (num_clicks == 0) {
388 if (any_selected) {
389 unselect(0, ROWS*COLS);
390 } else {
391 cursor_moved = true;
392 saved_cursor_addr = cursor_addr;
393 cursor_move(baddr);
394 }
395 }
396 }
397
398 /*
399 * Begin extending the current selection.
400 * Usually bound to <Btn3Down>.
401 */
402 void
start_extend_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)403 start_extend_xaction(Widget w, XEvent *event, String *params,
404 Cardinal *num_params)
405 {
406 int x, y;
407 int baddr;
408 bool continuous = (!ever_3270 && !toggled(RECTANGLE_SELECT));
409
410 xaction_debug(start_extend_xaction, event, params, num_params);
411 if (w != *screen) {
412 return;
413 }
414
415 down1_time = 0L;
416
417 BOUNDED_XY(event, x, y);
418 baddr = ROWCOL_TO_BA(y, x);
419
420 if (continuous) {
421 /* Think linearly. */
422 if (baddr < f_start) {
423 v_start = baddr;
424 } else if (baddr > f_end) {
425 v_end = baddr;
426 } else if (baddr - f_start > f_end - baddr) {
427 v_end = baddr;
428 } else {
429 v_start = baddr;
430 }
431 } else {
432 /* Think rectangularly. */
433 int nrow = baddr / COLS;
434 int ncol = baddr % COLS;
435 int vrow_ul = v_start / COLS;
436 int vrow_lr = v_end / COLS;
437 int vcol_ul = Min(v_start % COLS, v_end % COLS);
438 int vcol_lr = Max(v_start % COLS, v_end % COLS);
439
440 /* Set up the row. */
441 if (nrow <= vrow_ul) {
442 vrow_ul = nrow;
443 } else if (nrow >= vrow_lr) {
444 vrow_lr = nrow;
445 } else if (nrow - vrow_ul > vrow_lr - nrow) {
446 vrow_lr = nrow;
447 } else {
448 vrow_ul = nrow;
449 }
450
451 /* Set up the column. */
452 if (ncol <= vcol_ul) {
453 vcol_ul = ncol;
454 } else if (ncol >= vcol_lr) {
455 vcol_lr = ncol;
456 } else if (ncol - vcol_ul > vcol_lr - ncol) {
457 vcol_lr = ncol;
458 } else {
459 vcol_ul = ncol;
460 }
461
462 v_start = (vrow_ul * COLS) + vcol_ul;
463 v_end = (vrow_lr * COLS) + vcol_lr;
464 }
465
466 grab_sel(v_start, v_end, true, event_time(event));
467 saw_motion = 1;
468 num_clicks = 0;
469 }
470
471 /*
472 * Continuously extend the current selection.
473 * Usually bound to <Btn1Motion> and <Btn3Motion>.
474 */
475 void
select_extend_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)476 select_extend_xaction(Widget w, XEvent *event, String *params,
477 Cardinal *num_params)
478 {
479 int x, y;
480 int baddr;
481
482 xaction_debug(select_extend_xaction, event, params, num_params);
483 if (w != *screen) {
484 return;
485 }
486
487 /* Ignore initial drag events if are too near. */
488 if (down1_time != 0L &&
489 abs((int) event_x(event) - (int) down1_x) < *char_width &&
490 abs((int) event_y(event) - (int) down1_y) < *char_height) {
491 return;
492 } else {
493 down1_time = 0L;
494 }
495
496 /* If we moved the 3270 cursor on the first click, put it back. */
497 if (cursor_moved) {
498 cursor_move(saved_cursor_addr);
499 cursor_moved = false;
500 }
501
502 BOUNDED_XY(event, x, y);
503 baddr = ROWCOL_TO_BA(y, x);
504
505 /*
506 * If baddr falls outside if the v range, open up the v range. In
507 * addition, if we are extending one end of the v range, make sure the
508 * other end at least covers the f range.
509 */
510 if (baddr <= v_start) {
511 v_start = baddr;
512 v_end = f_end;
513 }
514 if (baddr >= v_end) {
515 v_end = baddr;
516 v_start = f_start;
517 }
518
519 /*
520 * If baddr falls within the v range, narrow up the nearer end of the
521 * v range.
522 */
523 if (baddr > v_start && baddr < v_end) {
524 if (baddr - v_start < v_end - baddr) {
525 v_start = baddr;
526 } else {
527 v_end = baddr;
528 }
529 }
530
531 num_clicks = 0;
532 saw_motion = 1;
533 grab_sel(v_start, v_end, false, event_time(event));
534 }
535
536 /*
537 * End the selection.
538 * Usually bound to <BtnUp>.
539 */
540 void
select_end_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)541 select_end_xaction(Widget w _is_unused, XEvent *event, String *params,
542 Cardinal *num_params)
543 {
544 Cardinal i;
545 int x, y;
546
547 xaction_debug(select_end_xaction, event, params, num_params);
548 if (w != *screen) {
549 return;
550 }
551
552 if (n_owned == -1) {
553 for (i = 0; i < NS; i++) {
554 own_sel[i].atom = None;
555 }
556 n_owned = 0;
557 }
558 for (i = 0; i < NS; i++) {
559 if (i < *num_params) {
560 want_sel[i] = XInternAtom(display, params[i], false);
561 } else {
562 want_sel[i] = None;
563 }
564 }
565 if (*num_params == 0) {
566 want_sel[0] = XA_PRIMARY;
567 }
568
569 BOUNDED_XY(event, x, y);
570 up_time = event_time(event);
571
572 if (up_time - down_time > CLICK_INTERVAL) {
573 num_clicks = 0;
574 }
575
576 if (++num_clicks > 3) {
577 num_clicks = 1;
578 }
579
580 switch (num_clicks) {
581 case 1:
582 if (saw_motion) {
583 f_start = v_start;
584 f_end = v_end;
585 grab_sel(f_start, f_end, true, event_time(event));
586 }
587 break;
588 case 2:
589 /*
590 * If we moved the 3270 cursor on the first click, put it back.
591 */
592 if (cursor_moved) {
593 cursor_move(saved_cursor_addr);
594 cursor_moved = false;
595 }
596 select_word(f_start, event_time(event));
597 break;
598 case 3:
599 select_line(f_start, event_time(event));
600 break;
601 }
602 saw_motion = 0;
603 }
604
605 /*
606 * New-style selection actions.
607 *
608 * These actions work a bit more intuitively in 3270 mode than the historic
609 * ones.
610 * SelectDown is usually bound to Btn1Down.
611 * SelectMotion is usually bound to Btn1Motion.
612 * SelectUp is usually bound to Btn1Up.
613 *
614 * SelectDown deselects on the first click, and remembers the screen position.
615 *
616 * SelectMotion extends the selection from the location remembered by
617 * SelectDown to the current location.
618 *
619 * SelectUp moves the cursor on the first click. On clicks two and three, it
620 * selects a word or line. On click four, it deselects and resets the click
621 * counter to one.
622 */
623
624 void
SelectDown_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)625 SelectDown_xaction(Widget w _is_unused, XEvent *event, String *params,
626 Cardinal *num_params)
627 {
628 int x, y;
629 int baddr;
630
631 xaction_debug(SelectDown_xaction, event, params, num_params);
632 if (w != *screen) {
633 return;
634 }
635 BOUNDED_XY(event, x, y);
636 baddr = ROWCOL_TO_BA(y, x);
637
638 if (event_time(event) - down_time > CLICK_INTERVAL) {
639 num_clicks = 0;
640 }
641
642 down_time = event_time(event);
643 if (num_clicks == 0) {
644 f_start = f_end = v_start = v_end = baddr;
645 down1_time = down_time;
646 if (any_selected) {
647 unselect(0, ROWS*COLS);
648 }
649 }
650 }
651
652 void
SelectMotion_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)653 SelectMotion_xaction(Widget w _is_unused, XEvent *event, String *params,
654 Cardinal *num_params)
655 {
656 int x, y;
657 int baddr;
658
659 xaction_debug(SelectMotion_xaction, event, params, num_params);
660 if (w != *screen) {
661 return;
662 }
663
664 /* Ignore initial drag events if are too near. */
665 if (down1_time != 0L &&
666 abs((int) event_x(event) - (int) down1_x) < *char_width &&
667 abs((int) event_y(event) - (int) down1_y) < *char_height) {
668 return;
669 } else {
670 down1_time = 0L;
671 }
672
673 BOUNDED_XY(event, x, y);
674 baddr = ROWCOL_TO_BA(y, x);
675
676 /*
677 * If baddr falls outside if the v range, open up the v range. In
678 * addition, if we are extending one end of the v range, make sure the
679 * other end at least covers the f range.
680 */
681 if (baddr <= v_start) {
682 v_start = baddr;
683 v_end = f_end;
684 }
685 if (baddr >= v_end) {
686 v_end = baddr;
687 v_start = f_start;
688 }
689
690 /*
691 * If baddr falls within the v range, narrow up the nearer end of the
692 * v range.
693 */
694 if (baddr > v_start && baddr < v_end) {
695 if (baddr - v_start < v_end - baddr) {
696 v_start = baddr;
697 } else {
698 v_end = baddr;
699 }
700 }
701
702 num_clicks = 0;
703 saw_motion = 1;
704 grab_sel(v_start, v_end, false, event_time(event));
705 }
706
707 void
SelectUp_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)708 SelectUp_xaction(Widget w _is_unused, XEvent *event, String *params,
709 Cardinal *num_params)
710 {
711 int x, y;
712 int baddr;
713 Cardinal i;
714
715 xaction_debug(SelectUp_xaction, event, params, num_params);
716 if (w != *screen) {
717 return;
718 }
719
720 if (n_owned == -1) {
721 for (i = 0; i < NS; i++) {
722 own_sel[i].atom = None;
723 }
724 n_owned = 0;
725 }
726 for (i = 0; i < NS; i++) {
727 if (i < *num_params) {
728 want_sel[i] = XInternAtom(display, params[i], false);
729 } else {
730 want_sel[i] = None;
731 }
732 }
733 if (*num_params == 0) {
734 want_sel[0] = XA_PRIMARY;
735 }
736
737 BOUNDED_XY(event, x, y);
738 baddr = ROWCOL_TO_BA(y, x);
739
740 if (event_time(event) - up_time > CLICK_INTERVAL) {
741 #if defined(DEBUG_CLICKS) /*[*/
742 printf("too long, reset\n");
743 #endif /*]*/
744 num_clicks = 0;
745 }
746 up_time = event_time(event);
747
748 if (++num_clicks > 3) {
749 #if defined(DEBUG_CLICKS) /*[*/
750 printf("wrap\n");
751 #endif /*]*/
752 num_clicks = 1;
753 }
754
755 #if defined(DEBUG_CLICKS) /*[*/
756 printf("%d clicks\n", num_clicks);
757 #endif /*]*/
758 switch (num_clicks) {
759 case 1:
760 /*
761 * If we saw motion, then take the selection.
762 * Otherwise, if we're in 3270 mode, move the cursor.
763 */
764 if (saw_motion) {
765 f_start = v_start;
766 f_end = v_end;
767 grab_sel(f_start, f_end, true, event_time(event));
768 } else if (IN_3270) {
769 cursor_move(baddr);
770 }
771 break;
772 case 2:
773 /*
774 * If we moved the 3270 cursor on the first click, put it back.
775 */
776 select_word(f_start, event_time(event));
777 break;
778 case 3:
779 select_line(f_start, event_time(event));
780 break;
781 }
782 saw_motion = 0;
783 }
784
785 static void
set_select(XEvent * event,String * params,Cardinal * num_params)786 set_select(XEvent *event, String *params, Cardinal *num_params)
787 {
788 Cardinal i;
789
790 if (!any_selected) {
791 return;
792 }
793 if (n_owned == -1) {
794 for (i = 0; i < NS; i++) {
795 own_sel[i].atom = None;
796 }
797 n_owned = 0;
798 }
799 for (i = 0; i < NS; i++)
800 if (i < *num_params) {
801 want_sel[i] = XInternAtom(display, params[i], false);
802 } else {
803 want_sel[i] = None;
804 }
805 if (*num_params == 0) {
806 want_sel[0] = XA_PRIMARY;
807 }
808 own_sels(event_time(event));
809 }
810
811 /*
812 * Set the selection.
813 * Usually bound to the Copy key.
814 */
815 void
set_select_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)816 set_select_xaction(Widget w _is_unused, XEvent *event, String *params,
817 Cardinal *num_params)
818 {
819 xaction_debug(set_select_xaction, event, params, num_params);
820
821 set_select(event, params, num_params);
822 }
823
824 /*
825 * Translate the mouse position to a buffer address.
826 */
827 int
mouse_baddr(Widget w,XEvent * event)828 mouse_baddr(Widget w, XEvent *event)
829 {
830 int x, y;
831
832 if (w != *screen) {
833 return 0;
834 }
835 BOUNDED_XY(event, x, y);
836 return ROWCOL_TO_BA(y, x);
837 }
838
839 /*
840 * Cut action.
841 */
842 #define ULS sizeof(unsigned long)
843 #define ULBS (ULS * 8)
844
845 void
Cut_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)846 Cut_xaction(Widget w _is_unused, XEvent *event, String *params,
847 Cardinal *num_params)
848 {
849 int baddr;
850 int ba2;
851 unsigned char fa = get_field_attribute(0);
852 unsigned long *target;
853
854 xaction_debug(Cut_xaction, event, params, num_params);
855
856 if (!any_selected) {
857 return;
858 }
859
860 set_select(event, params, num_params);
861
862 target = (unsigned long *)XtCalloc(ULS, ((ROWS*COLS)+(ULBS-1)) / ULBS);
863
864 /* Identify the positions to empty. */
865 for (baddr = 0; baddr < ROWS*COLS; baddr++) {
866 if (ea_buf[baddr].fa) {
867 fa = ea_buf[baddr].fa;
868 } else if ((IN_NVT || !FA_IS_PROTECTED(fa)) && screen_selected(baddr)) {
869 target[baddr/ULBS] |= 1L << (baddr%ULBS);
870 }
871 }
872
873 /* Erase them. */
874 for (baddr = 0; baddr < ROWS*COLS; baddr++) {
875 if ((target[baddr/ULBS] & (1L << (baddr%ULBS)))
876 && ea_buf[baddr].ec != EBC_so
877 && ea_buf[baddr].ec != EBC_si) {
878 switch (ctlr_dbcs_state(baddr)) {
879 case DBCS_NONE:
880 case DBCS_SB:
881 ctlr_add(baddr, EBC_space, ea_buf[baddr].cs);
882 break;
883 case DBCS_LEFT:
884 ctlr_add(baddr, EBC_space, ea_buf[baddr].cs);
885 ba2 = baddr;
886 INC_BA(ba2);
887 ctlr_add(ba2, EBC_space, ea_buf[baddr].cs);
888 break;
889 case DBCS_RIGHT:
890 ba2 = baddr;
891 DEC_BA(ba2);
892 ctlr_add(ba2, EBC_space, ea_buf[baddr].cs);
893 ctlr_add(baddr, EBC_space, ea_buf[baddr].cs);
894 break;
895 default:
896 break;
897 }
898 mdt_set(baddr);
899 }
900 }
901
902 Free(target);
903 }
904
905 /*
906 * KybdSelect action. Extends the selection area in the indicated direction.
907 */
908 void
KybdSelect_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)909 KybdSelect_xaction(Widget w _is_unused, XEvent *event, String *params,
910 Cardinal *num_params)
911 {
912 enum { UP, DOWN, LEFT, RIGHT } direction;
913 int x_start, x_end;
914 Cardinal i;
915
916 xaction_debug(KybdSelect_xaction, event, params, num_params);
917 if (w != *screen) {
918 return;
919 }
920
921 if (*num_params < 1) {
922 popup_an_error("%s(): Requires one argument",
923 action_name(KybdSelect_xaction));
924 return;
925 }
926 if (!strcasecmp(params[0], "Up")) {
927 direction = UP;
928 } else if (!strcasecmp(params[0], "Down")) {
929 direction = DOWN;
930 } else if (!strcasecmp(params[0], "Left")) {
931 direction = LEFT;
932 } else if (!strcasecmp(params[0], "Right")) {
933 direction = RIGHT;
934 } else {
935 popup_an_error("%s(): First argument must be Up, Down, Left, or "
936 "Right", action_name(KybdSelect_xaction));
937 return;
938 }
939
940 if (!any_selected) {
941 x_start = x_end = cursor_addr;
942 } else {
943 if (f_start < f_end) {
944 x_start = f_start;
945 x_end = f_end;
946 } else {
947 x_start = f_end;
948 x_end = f_start;
949 }
950 }
951
952 switch (direction) {
953 case UP:
954 if (!(x_start / COLS)) {
955 return;
956 }
957 x_start -= COLS;
958 break;
959 case DOWN:
960 if ((x_end / COLS) == ROWS - 1) {
961 return;
962 }
963 x_end += COLS;
964 break;
965 case LEFT:
966 if (!(x_start % COLS)) {
967 return;
968 }
969 x_start--;
970 break;
971 case RIGHT:
972 if ((x_end % COLS) == COLS - 1) {
973 return;
974 }
975 x_end++;
976 break;
977 }
978
979 /* Figure out the atoms they want. */
980 if (n_owned == -1) {
981 for (i = 0; i < NS; i++) {
982 own_sel[i].atom = None;
983 }
984 n_owned = 0;
985 }
986 for (i = 1; i < NS; i++) {
987 if (i < *num_params) {
988 want_sel[i] = XInternAtom(display, params[i], false);
989 } else {
990 want_sel[i] = None;
991 }
992 }
993 if (*num_params == 1) {
994 want_sel[0] = XA_PRIMARY;
995 }
996
997 /* Grab the selection. */
998 f_start = v_start = x_start;
999 f_end = v_end = x_end;
1000 grab_sel(f_start, f_end, true, event_time(event));
1001 }
1002
1003 /*
1004 * unselect action. Removes a selection.
1005 */
1006 void
Unselect_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)1007 Unselect_xaction(Widget w _is_unused, XEvent *event, String *params,
1008 Cardinal *num_params)
1009 {
1010 xaction_debug(Unselect_xaction, event, params, num_params);
1011
1012 /* It's just cosmetic. */
1013 unselect(0, ROWS*COLS);
1014 }
1015
1016 void
SelectAll_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)1017 SelectAll_xaction(Widget w _is_unused, XEvent *event, String *params,
1018 Cardinal *num_params)
1019 {
1020 Cardinal i;
1021
1022 xaction_debug(SelectUp_xaction, event, params, num_params);
1023 if (w != *screen) {
1024 return;
1025 }
1026
1027 if (n_owned == -1) {
1028 for (i = 0; i < NS; i++) {
1029 own_sel[i].atom = None;
1030 }
1031 n_owned = 0;
1032 }
1033 for (i = 0; i < NS; i++) {
1034 if (i < *num_params) {
1035 want_sel[i] = XInternAtom(display, params[i], false);
1036 } else {
1037 want_sel[i] = None;
1038 }
1039 }
1040 if (*num_params == 0) {
1041 want_sel[0] = XA_PRIMARY;
1042 }
1043
1044 grab_sel(0, (ROWS * COLS) - 1, true, event_time(event));
1045 }
1046
1047 /*
1048 * Screen side.
1049 */
1050
1051 static char *select_buf = NULL;
1052 static char *sb_ptr = NULL;
1053 static int sb_size = 0;
1054 #define SB_CHUNK 1024
1055
1056 static Time sel_time;
1057
1058 static void
init_select_buf(void)1059 init_select_buf(void)
1060 {
1061 if (select_buf == NULL) {
1062 select_buf = XtMalloc(sb_size = SB_CHUNK);
1063 }
1064 sb_ptr = select_buf;
1065 }
1066
1067 static void
store_sel(char c)1068 store_sel(char c)
1069 {
1070 if (sb_ptr - select_buf >= sb_size) {
1071 sb_size += SB_CHUNK;
1072 select_buf = XtRealloc(select_buf, sb_size);
1073 sb_ptr = select_buf + sb_size - SB_CHUNK;
1074 }
1075 *(sb_ptr++) = c;
1076 }
1077
1078 /*
1079 * Convert the UTF-8 string to an ICCCM-defined STRING (ISO 8859-1 printable
1080 * plus tab and newline).
1081 *
1082 * Returns the length of the stored string.
1083 */
1084 static unsigned long
store_icccm_string(XtPointer value,const char * buf)1085 store_icccm_string(XtPointer value, const char *buf)
1086 {
1087 char *dst = (char *)value;
1088 unsigned long len = 0;
1089 bool skip = false;
1090
1091 while (*buf) {
1092 int nw;
1093 ucs4_t ucs;
1094
1095 if (*buf == '\033') {
1096 /* Funky GE sequence. Skip it. */
1097 *dst++ = ' ';
1098 len++;
1099 buf++;
1100 skip = true;
1101 continue;
1102 }
1103 nw = utf8_to_unicode(buf, strlen(buf), &ucs);
1104 if (nw <= 0) {
1105 return len;
1106 }
1107 if (skip) {
1108 skip = false;
1109 continue;
1110 }
1111 if (ucs == '\n' ||
1112 (ucs >= 0x20 && ucs <= 0x7f) ||
1113 (ucs >= 0xa0 && ucs <= 0xff)) {
1114 *dst++ = ucs & 0xff;
1115 len++;
1116 }
1117 buf += nw;
1118 }
1119 return len;
1120 }
1121
1122 static Boolean
convert_sel(Widget w,Atom * selection,Atom * target,Atom * type,XtPointer * value,unsigned long * length,int * format)1123 convert_sel(Widget w, Atom *selection, Atom *target, Atom *type,
1124 XtPointer *value, unsigned long *length, int *format)
1125 {
1126 int i;
1127
1128 /* Find the right selection. */
1129 for (i = 0; i < NS; i++) {
1130 if (own_sel[i].atom == *selection) {
1131 break;
1132 }
1133 }
1134 if (i >= NS) { /* not my selection */
1135 return False;
1136 }
1137
1138 if (*target == XA_TARGETS(display)) {
1139 Atom* targetP;
1140 Atom* std_targets;
1141 unsigned long std_length;
1142
1143 XmuConvertStandardSelection(w, sel_time, selection,
1144 target, type, (caddr_t*) &std_targets, &std_length, format);
1145 #if defined(XA_UTF8_STRING) /*[*/
1146 *length = std_length + 6;
1147 #else /*][*/
1148 *length = std_length + 5;
1149 #endif /*]*/
1150 *value = (XtPointer) XtMalloc(sizeof(Atom) * (*length));
1151 targetP = *(Atom**)value;
1152 *targetP++ = XA_STRING;
1153 *targetP++ = XA_TEXT(display);
1154 *targetP++ = XA_COMPOUND_TEXT(display);
1155 #if defined(XA_UTF8_STRING) /*[*/
1156 *targetP++ = XA_UTF8_STRING(display);
1157 #endif /*]*/
1158 *targetP++ = XA_LENGTH(display);
1159 *targetP++ = XA_LIST_LENGTH(display);
1160 memmove(targetP, std_targets,
1161 (int) (sizeof(Atom) * std_length));
1162 XtFree((char *) std_targets);
1163 *type = XA_ATOM;
1164 *format = 32;
1165 return True;
1166 }
1167
1168 if (*target == XA_STRING ||
1169 *target == XA_TEXT(display) ||
1170 *target == XA_COMPOUND_TEXT(display)
1171 #if defined(XA_UTF8_STRING) /*[*/
1172 || *target == XA_UTF8_STRING(display)
1173 #endif /*]*/
1174 ) {
1175 if (*target == XA_COMPOUND_TEXT(display)
1176 #if defined(XA_UTF8_STRING) /*[*/
1177 || *target == XA_UTF8_STRING(display)
1178 #endif /*]*/
1179 ) {
1180 *type = *target;
1181 } else {
1182 *type = XA_STRING;
1183 }
1184 *length = strlen(own_sel[i].buffer);
1185 *value = XtMalloc(*length);
1186 #if defined(XA_UTF8_STRING) /*[*/
1187 if (*target == XA_UTF8_STRING(display)) {
1188 memmove(*value, own_sel[i].buffer, (int) *length);
1189 } else {
1190 #endif /*]*/
1191 /*
1192 * N.B.: We return a STRING for COMPOUND_TEXT.
1193 * Someday we may do real ISO 2022, but not today.
1194 */
1195 *length = store_icccm_string(*value, own_sel[i].buffer);
1196 #if defined(XA_UTF8_STRING) /*[*/
1197 }
1198 #endif /*]*/
1199 *format = 8;
1200 return True;
1201 }
1202 if (*target == XA_LIST_LENGTH(display)) {
1203 *value = XtMalloc(4);
1204 if (sizeof(long) == 4) {
1205 *(long *)*value = 1;
1206 } else {
1207 long temp = 1;
1208 memmove(*value, ((char*) &temp) + sizeof(long) - 4, 4);
1209 }
1210 *type = XA_INTEGER;
1211 *length = 1;
1212 *format = 32;
1213 return True;
1214 }
1215 if (*target == XA_LENGTH(display)) {
1216 *value = XtMalloc(4);
1217 if (sizeof(long) == 4) {
1218 *(long*)*value = strlen(own_sel[i].buffer);
1219 } else {
1220 long temp = strlen(own_sel[i].buffer);
1221 memmove(*value, ((char *) &temp) + sizeof(long) - 4, 4);
1222 }
1223 *type = XA_INTEGER;
1224 *length = 1;
1225 *format = 32;
1226 return True;
1227 }
1228
1229 if (XmuConvertStandardSelection(w, sel_time, selection,
1230 target, type, (caddr_t *)value, length, format)) {
1231 return True;
1232 }
1233
1234 return False;
1235 }
1236
1237 static void
lose_sel(Widget w _is_unused,Atom * selection)1238 lose_sel(Widget w _is_unused, Atom *selection)
1239 {
1240 int i;
1241
1242 for (i = 0; i < NS; i++) {
1243 if (own_sel[i].atom != None && own_sel[i].atom == *selection) {
1244 own_sel[i].atom = None;
1245 XtFree(own_sel[i].buffer);
1246 own_sel[i].buffer = NULL;
1247 n_owned--;
1248 break;
1249 }
1250 }
1251 if (!n_owned) {
1252 unselect(0, ROWS*COLS);
1253 }
1254 }
1255
1256 /*
1257 * Somewhat convoluted logic to return an ASCII character for a given screen
1258 * position.
1259 *
1260 * The character has to be found indirectly from ea_buf and the field
1261 * attirbutes, so that zero-intensity fields become blanks.
1262 */
1263 static bool osc_valid = false;
1264
1265 static void
osc_start(void)1266 osc_start(void)
1267 {
1268 osc_valid = false;
1269 }
1270
1271 /*
1272 * Return a 'selection' version of a given character on the screen.
1273 * Returns a printable ASCII character, or 0 if the character is a NULL and
1274 * shouldn't be included in the selection.
1275 */
1276 static void
onscreen_char(int baddr,unsigned char * r,int * rlen)1277 onscreen_char(int baddr, unsigned char *r, int *rlen)
1278 {
1279 static int osc_baddr;
1280 static unsigned char fa;
1281 ucs4_t uc;
1282 int baddr2;
1283
1284 *rlen = 1;
1285
1286 /* If we aren't moving forward, all bets are off. */
1287 if (osc_valid && baddr < osc_baddr) {
1288 osc_valid = false;
1289 }
1290
1291 if (osc_valid) {
1292 /*
1293 * Search for a new field attribute between the address we
1294 * want and the last address we searched. If we found a new
1295 * field attribute, save the address for next time.
1296 */
1297 get_bounded_field_attribute(baddr, osc_baddr, &fa);
1298 osc_baddr = baddr;
1299 } else {
1300 /*
1301 * Find the attribute the old way.
1302 */
1303 fa = get_field_attribute(baddr);
1304 osc_baddr = baddr;
1305 osc_valid = true;
1306 }
1307
1308 /* If it isn't visible, then make it a blank. */
1309 if (FA_IS_ZERO(fa)) {
1310 *r = ' ';
1311 return;
1312 }
1313
1314 /* Handle DBCS. */
1315 switch (ctlr_dbcs_state(baddr)) {
1316 case DBCS_LEFT:
1317 if (ea_buf[baddr].ucs4) {
1318 *rlen = unicode_to_utf8(ea_buf[baddr].ucs4, (char *)r);
1319 } else {
1320 baddr2 = baddr;
1321 INC_BA(baddr2);
1322 uc = ebcdic_to_unicode((ea_buf[baddr].ec << 8) |
1323 ea_buf[baddr2].ec,
1324 CS_BASE, EUO_NONE);
1325 *rlen = unicode_to_utf8(uc, (char *)r);
1326 }
1327 return;
1328 case DBCS_RIGHT:
1329 /* Returned the entire character when the left half was read. */
1330 *rlen = 0;
1331 return;
1332 case DBCS_SI:
1333 /* Suppress SI's altogether. They'll expand back on paste. */
1334 *rlen = 0;
1335 return;
1336 case DBCS_SB:
1337 /* Treat SB's as normal SBCS characters. */
1338 break;
1339 default:
1340 break;
1341 }
1342
1343 switch (ea_buf[baddr].cs) {
1344 case CS_BASE:
1345 default:
1346 if (ea_buf[baddr].ucs4) {
1347 *rlen = unicode_to_utf8(ea_buf[baddr].ucs4, (char *)r);
1348 } else {
1349 switch (ea_buf[baddr].ec) {
1350 case EBC_so:
1351 /*
1352 * Suppress SO's altogether. They'll expand back on
1353 * paste.
1354 */
1355 *rlen = 0;
1356 return;
1357 case EBC_null:
1358 *r = 0;
1359 return;
1360 default:
1361 /*
1362 * Note that we use the 'for_display' flavor of
1363 * ebcdic_base_to_unicode here, so DUP and FM are
1364 * translated to special private-use Unicode values.
1365 * These will (hopefully) be ignored by other
1366 * applications, but translated back to DUP and FM if
1367 * pasted back into x3270.
1368 */
1369 uc = ebcdic_base_to_unicode(ea_buf[baddr].ec,
1370 EUO_BLANK_UNDEF | EUO_UPRIV);
1371 *rlen = unicode_to_utf8(uc, (char *)r);
1372 if (*rlen < 0) {
1373 *rlen = 0;
1374 }
1375 }
1376 }
1377 return;
1378 case CS_GE:
1379 /* Translate APL to Unicode. */
1380 uc = apl_to_unicode(ea_buf[baddr].ec, EUO_NONE);
1381 if (uc == (ucs4_t)-1) {
1382 /* No translation. */
1383 uc = UPRIV_GE_00 + ea_buf[baddr].ec;
1384 }
1385 *rlen = unicode_to_utf8(uc, (char *)r);
1386 if (*rlen < 0) {
1387 *rlen = 0;
1388 }
1389 return;
1390 case CS_LINEDRAW:
1391 /* VT100 line-drawing character. */
1392 *rlen = unicode_to_utf8(
1393 linedraw_to_unicode(ea_buf[baddr].ucs4, false), (char *)r);
1394 return;
1395 }
1396 }
1397
1398 /*
1399 * Attempt to own the selections in want_sel[].
1400 */
1401 static void
own_sels(Time t)1402 own_sels(Time t)
1403 {
1404 int i, j;
1405
1406 /*
1407 * Try to grab any new selections we may want.
1408 */
1409 for (i = 0; i < NS; i++) {
1410 bool already_own = false;
1411
1412 if (want_sel[i] == None) {
1413 continue;
1414 }
1415
1416 /* Check if we already own it. */
1417 for (j = 0; j < NS; j++) {
1418 if (own_sel[j].atom == want_sel[i]) {
1419 already_own = true;
1420 break;
1421 }
1422 }
1423
1424 /* Find the slot for it. */
1425 if (!already_own) {
1426 for (j = 0; j < NS; j++) {
1427 if (own_sel[j].atom == None) {
1428 break;
1429 }
1430 }
1431 if (j >= NS) {
1432 continue;
1433 }
1434 }
1435
1436 if (XtOwnSelection(*screen, want_sel[i], t, convert_sel, lose_sel,
1437 NULL)) {
1438 if (!already_own) {
1439 n_owned++;
1440 own_sel[j].atom = want_sel[i];
1441 }
1442 Replace(own_sel[j].buffer, XtMalloc(strlen(select_buf) + 1));
1443 memmove(own_sel[j].buffer, select_buf, strlen(select_buf) + 1);
1444 } else {
1445 XtWarning("Could not get selection");
1446 if (own_sel[j].atom != None) {
1447 XtFree(own_sel[j].buffer);
1448 own_sel[j].buffer = NULL;
1449 own_sel[j].atom = None;
1450 n_owned--;
1451 }
1452 }
1453 }
1454 if (!n_owned) {
1455 unselect(0, ROWS*COLS);
1456 }
1457 sel_time = t;
1458 }
1459
1460 /*
1461 * Copy the selected area on the screen into a buffer and attempt to
1462 * own the selections in want_sel[].
1463 */
1464 #define VISUAL_LEFT(d) ((IS_LEFT(d)) || ((d) == DBCS_SI))
1465 static void
grab_sel(int start,int end,bool really,Time t)1466 grab_sel(int start, int end, bool really, Time t)
1467 {
1468 int i, j;
1469 int start_row, end_row;
1470 int nulls = 0;
1471 unsigned char osc[16];
1472 int len;
1473
1474 unselect(0, ROWS*COLS);
1475
1476 if (start > end) {
1477 int exch = end;
1478
1479 end = start;
1480 start = exch;
1481 }
1482
1483 start_row = start / COLS;
1484 end_row = end / COLS;
1485
1486 init_select_buf(); /* prime the store_sel() routine */
1487 osc_start(); /* prime the onscreen_char() routine */
1488
1489 if (!ever_3270 && !toggled(RECTANGLE_SELECT)) {
1490 /* Continuous selections */
1491 if (IS_RIGHT(ctlr_dbcs_state(start))) {
1492 DEC_BA(start);
1493 }
1494 if (VISUAL_LEFT(ctlr_dbcs_state(end))) {
1495 INC_BA(end);
1496 }
1497 for (i = start; i <= end; i++) {
1498 screen_set_select(i);
1499 if (really) {
1500 if (i != start && !(i % COLS)) {
1501 nulls = 0;
1502 store_sel('\n');
1503 }
1504 onscreen_char(i, osc, &len);
1505 for (j = 0; j < len; j++) {
1506 if (osc[j]) {
1507 while (nulls) {
1508 store_sel(' ');
1509 nulls--;
1510 }
1511 store_sel((char)osc[j]);
1512 } else {
1513 nulls++;
1514 }
1515 }
1516 }
1517 }
1518 /* Check for newline extension on the last line. */
1519 if ((end % COLS) != (COLS - 1)) {
1520 bool all_blank = true;
1521
1522 for (i = end; i < end + (COLS - (end % COLS)); i++) {
1523 onscreen_char(i, osc, &len);
1524 for (j = 0; j < len; j++) {
1525 if (osc[j]) {
1526 all_blank = false;
1527 break;
1528 }
1529 }
1530 }
1531 if (all_blank) {
1532 for (i = end; i < end + (COLS - (end % COLS)); i++) {
1533 screen_set_select(i);
1534 }
1535 if (really) {
1536 store_sel('\n');
1537 }
1538 }
1539 }
1540 } else {
1541 /* Rectangular selections */
1542 if (start_row == end_row) {
1543 if (IS_RIGHT(ctlr_dbcs_state(start))) {
1544 DEC_BA(start);
1545 }
1546 if (VISUAL_LEFT(ctlr_dbcs_state(end))) {
1547 INC_BA(end);
1548 }
1549 for (i = start; i <= end; i++) {
1550 screen_set_select(i);
1551 if (really) {
1552 onscreen_char(i, osc, &len);
1553 for (j = 0; j < len; j++) {
1554 if (osc[j]) {
1555 while (nulls) {
1556 store_sel(' ');
1557 nulls--;
1558 }
1559 store_sel((char)osc[j]);
1560 } else {
1561 nulls++;
1562 }
1563 }
1564 }
1565 }
1566 } else {
1567 int row, col;
1568 int start_col = start % COLS;
1569 int end_col = end % COLS;
1570
1571 if (start_col > end_col) {
1572 int exch = end_col;
1573
1574 end_col = start_col;
1575 start_col = exch;
1576 }
1577
1578 for (row = start_row; row <= end_row; row++) {
1579 int sc = start_col;
1580 int ec = end_col;
1581
1582 if (sc && IS_RIGHT(ctlr_dbcs_state(row*COLS + sc))) {
1583 sc = sc - 1;
1584 }
1585 if (ec < COLS-1 &&
1586 VISUAL_LEFT(ctlr_dbcs_state(row*COLS + ec))) {
1587 ec = ec + 1;
1588 }
1589
1590 for (col = sc; col <= ec; col++) {
1591 screen_set_select(row*COLS + col);
1592 if (really) {
1593 onscreen_char(row*COLS + col, osc, &len);
1594 for (j = 0; j < len; j++) {
1595 if (osc[j]) {
1596 while (nulls) {
1597 store_sel(' ');
1598 nulls--;
1599 }
1600 store_sel((char)osc[j]);
1601 } else {
1602 nulls++;
1603 }
1604 }
1605 }
1606 }
1607 nulls = 0;
1608 if (really && row < end_row) {
1609 store_sel('\n');
1610 }
1611 }
1612 }
1613 }
1614
1615 /* Terminate the result. */
1616 if (really) {
1617 store_sel('\0');
1618 }
1619
1620 any_selected = true;
1621 ctlr_changed(0, ROWS*COLS);
1622 if (really) {
1623 own_sels(t);
1624 }
1625 }
1626
1627 /*
1628 * Check if any character in a given region is selected.
1629 */
1630 bool
area_is_selected(int baddr,int len)1631 area_is_selected(int baddr, int len)
1632 {
1633 int i;
1634
1635 for (i = 0; i < len; i++) {
1636 if (screen_selected(baddr+i)) {
1637 return true;
1638 }
1639 }
1640 return false;
1641 }
1642
1643 /*
1644 * Unhighlight the region of selected text -- but don't give up the selection.
1645 */
1646 void
unselect(int baddr _is_unused,int len _is_unused)1647 unselect(int baddr _is_unused, int len _is_unused)
1648 {
1649 if (any_selected) {
1650 screen_unselect_all();
1651 ctlr_changed(0, ROWS*COLS);
1652 any_selected = false;
1653 }
1654 }
1655
1656 /* Selection insertion. */
1657 #define NP 5
1658 static Atom paste_atom[NP];
1659 static int n_pasting = 0;
1660 static int pix = 0;
1661 static Time paste_time;
1662 #if defined(XA_UTF8_STRING) /*[*/
1663 static bool paste_utf8;
1664 #endif /*]*/
1665
1666 static void
paste_callback(Widget w,XtPointer client_data _is_unused,Atom * selection _is_unused,Atom * type _is_unused,XtPointer value,unsigned long * length,int * format _is_unused)1667 paste_callback(Widget w, XtPointer client_data _is_unused,
1668 Atom *selection _is_unused, Atom *type _is_unused, XtPointer value,
1669 unsigned long *length, int *format _is_unused)
1670 {
1671 char *s;
1672 unsigned long s_len;
1673 ucs4_t *u_buf;
1674 size_t u_len;
1675
1676 if ((value == NULL) || (*length == 0)) {
1677 XtFree(value);
1678
1679 /* Try the next one. */
1680 #if defined(XA_UTF8_STRING) /*[*/
1681 if (paste_utf8) {
1682 paste_utf8 = false;
1683 XtGetSelectionValue(w, paste_atom[(pix - 1)], XA_STRING,
1684 paste_callback, NULL, paste_time);
1685 } else
1686 #endif /*]*/
1687 if (n_pasting > pix) {
1688 #if defined(XA_UTF8_STRING) /*[*/
1689 paste_utf8 = true;
1690 #endif /*]*/
1691 XtGetSelectionValue(w, paste_atom[pix++],
1692 #if defined(XA_UTF8_STRING) /*[*/
1693 XA_UTF8_STRING(display),
1694 #else /*][*/
1695 XA_STRING,
1696 #endif /*]*/
1697 paste_callback, NULL,
1698 paste_time);
1699 }
1700 return;
1701 }
1702
1703 /* Convert the selection to Unicode. */
1704 s_len = *length;
1705 s = (char *)value;
1706 u_buf = (ucs4_t *)Malloc(*length * sizeof(ucs4_t));
1707 u_len = 0;
1708
1709 while (s_len) {
1710 ucs4_t uc;
1711
1712 #if defined(XA_UTF8_STRING) /*[*/
1713 if (paste_utf8) {
1714 int nu;
1715
1716 nu = utf8_to_unicode(s, s_len, &uc);
1717 if (nu <= 0) {
1718 break;
1719 }
1720 s += nu;
1721 s_len -= nu;
1722 } else
1723 #endif /*]*/
1724 {
1725 /* Assume it's ISO 8859-1. */
1726 uc = *s & 0xff;
1727 s++;
1728 s_len--;
1729 }
1730 u_buf[u_len++] = uc;
1731 }
1732 emulate_uinput(u_buf, u_len, true);
1733
1734 Free(u_buf);
1735 XtFree(value);
1736
1737 n_pasting = 0;
1738 }
1739
1740 void
insert_selection_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)1741 insert_selection_xaction(Widget w, XEvent *event, String *params,
1742 Cardinal *num_params)
1743 {
1744 Cardinal i;
1745 Atom a;
1746 XButtonEvent *be = (XButtonEvent *)event;
1747
1748 xaction_debug(insert_selection_xaction, event, params, num_params);
1749
1750 n_pasting = 0;
1751 for (i = 0; i < *num_params; i++) {
1752 a = XInternAtom(display, params[i], true);
1753 if (a == None) {
1754 popup_an_error("%s(): No atom for selection",
1755 action_name(insert_selection_xaction));
1756 continue;
1757 }
1758 if (n_pasting < NP) {
1759 paste_atom[n_pasting++] = a;
1760 }
1761 }
1762 pix = 0;
1763 #if defined(XA_UTF8_STRING) /*[*/
1764 paste_utf8 = true;
1765 #endif /*]*/
1766 if (n_pasting > pix) {
1767 paste_time = be->time;
1768 XtGetSelectionValue(w, paste_atom[pix++],
1769 #if defined(XA_UTF8_STRING) /*[*/
1770 XA_UTF8_STRING(display),
1771 #else /*][*/
1772 XA_STRING,
1773 #endif /*]*/
1774 paste_callback, NULL,
1775 paste_time);
1776 }
1777 }
1778
1779 /**
1780 * Select module registration.
1781 */
1782 void
select_register(void)1783 select_register(void)
1784 {
1785 static toggle_register_t toggles[] = {
1786 { RECTANGLE_SELECT, NULL, 0 }
1787 };
1788
1789 register_toggles(toggles, array_count(toggles));
1790 }
1791