1 /* textwidget.c - for drawing a scrollable text window widget
2    Copyright (C) 1996-2017 Paul Sheer
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307, USA.
18  */
19 
20 
21 #include <config.h>
22 #include <stdio.h>
23 #include <my_string.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 
27 #include <X11/Intrinsic.h>
28 #include <X11/Xatom.h>
29 #include "lkeysym.h"
30 
31 #include "stringtools.h"
32 #include "app_glob.c"
33 #include "edit.h"
34 #include "editcmddef.h"
35 
36 #include "coolwidget.h"
37 #include "coollocal.h"
38 #include "mousemark.h"
39 
40 #include "mad.h"
41 
42 extern struct look *look;
43 
44 int option_text_fg_normal = 0;
45 int option_text_fg_bold = 1;
46 int option_text_fg_italic = 18;
47 
48 int option_text_bg_normal = 26;
49 int option_text_bg_marked = 25;
50 int option_text_bg_highlighted = 12;
51 
52 void selection_clear (void);
53 
54 static long current;
55 
56 extern int calc_text_pos2 (CWidget * w, long b, long *q, int l);
57 extern void convert_text2 (CWidget * w, long from, cache_type *line, cache_type *eol, int x, int x_max, int row);
58 void edit_translate_xy (int xs, int ys, int *x, int *y);
59 
60 /* returns the position in the edit buffer of a window click */
text_get_click_pos(CWidget * w,int x,int y)61 long text_get_click_pos (CWidget * w, int x, int y)
62 {
63     long click, c, q;
64     int width;
65     width = w->options & TEXTBOX_WRAP ? (w->width - TEXTBOX_BDR) / FONT_MEAN_WIDTH : 32000;
66     if (y > 1)
67 	c = strmovelines (w->text, w->current, y - 1, width);
68     else
69 	c = w->current;
70     if (y > 0)
71 	click = strmovelines (w->text, c, 1, width);
72     else
73 	click = w->current;
74     if (w->options & TEXTBOX_MARK_WHOLE_LINES) {
75 	if (click == c && y > 0) {
76 	    calc_text_pos2 (w, click, &q, 32000);	/* this is to get the last line */
77 	    return q;
78 	}
79 	return click;
80     } else
81 	calc_text_pos2 (w, click, &q, x);
82     return q;
83 }
84 
xy(int x,int y,int * x_return,int * y_return)85 static void xy (int x, int y, int *x_return, int *y_return)
86 {
87     edit_translate_xy (x, y, x_return, y_return);
88 }
89 
cp(CWidget * w,int x,int y)90 static long cp (CWidget * w, int x, int y)
91 {
92     return text_get_click_pos (w, --x, --y);
93 }
94 
95 /* return 1 if not marked */
marks(CWidget * w,long * start,long * end)96 static int marks (CWidget * w, long *start, long *end)
97 {
98     if (w->mark1 ==  w->mark2)
99 	return 1;
100     *start = min (w->mark1, w->mark2);
101     *end = max (w->mark1, w->mark2);
102     return 0;
103 }
104 
range(CWidget * w,long start,long end,int click)105 int range (CWidget * w, long start, long end, int click)
106 {
107     return (start <= click && click < end);
108 }
109 
move_mark(CWidget * w)110 static void move_mark (CWidget * w)
111 {
112     w->mark2 = w->mark1 = current;
113 }
114 
fin_mark(CWidget * w)115 static void fin_mark (CWidget * w)
116 {
117     w->mark2 = w->mark1 = -1;
118 }
119 
release_mark(CWidget * w,XEvent * event)120 static void release_mark (CWidget * w, XEvent * event)
121 {
122     w->mark2 = current;
123     if (w->mark2 != w->mark1 && event) {
124 	selection_clear ();
125 	XSetSelectionOwner (CDisplay, XA_PRIMARY, w->winid, event->xbutton.time);
126 	XSetSelectionOwner (CDisplay, ATOM_ICCCM_P2P_CLIPBOARD, w->winid, event->xbutton.time);
127     }
128 }
129 
get_block(CWidget * w,long start_mark,long end_mark,int * type,int * l)130 static char *get_block (CWidget * w, long start_mark, long end_mark, int *type, int *l)
131 {
132     char *t, *t2;
133     *l = abs (w->mark2 - w->mark1);
134     t = CMalloc (*l + 1);
135     memcpy (t, w->text + min (w->mark1, w->mark2), *l);
136     t[*l] = 0;
137 
138     t2 = str_strip_nroff ((char *) t, l);
139 
140     free (t);
141     t2[*l] = 0;
142     if (w->options & TEXTBOX_FILE_LIST) {
143 #ifdef HAVE_DND
144 	char *p;
145 	int i;
146 	p = CDndFileList (t2, l, &i);
147 	if (!p) {
148 	    free (t2);
149 	    return 0;
150 	}
151 	free (t2);
152 	t2 = p;
153 	if (i == 1)
154 	    *type = DndFile;
155 	else
156 #endif
157 	    *type = DndFiles;
158     } else {
159 	*type = DndText;
160     }
161 
162     return t2;
163 }
164 
move(CWidget * w,long click,int row)165 static void move (CWidget * w, long click, int row)
166 {
167     int h;
168     current = click;
169     if (w->mark2 == -1)
170 	w->mark1 = current;
171     h = (w->height - TEXTBOX_BDR) / FONT_PIX_PER_LINE;
172     if (row > h && w->firstline < w->numlines - h - 2)
173 	CSetTextboxPos (w, TEXT_SET_LINE, w->firstline + row - h);
174     if (row < 1)
175 	CSetTextboxPos (w, TEXT_SET_LINE, w->firstline + row - 1);
176     w->mark2 = click;
177 }
178 
motion(CWidget * w,long click)179 static void motion (CWidget * w, long click)
180 {
181     w->mark2 = click;
182 }
183 
184 struct mouse_funcs textbox_mouse_mark = {
185     0,
186     (void (*)(int, int, int *, int *)) xy,
187     (long (*)(void *, int, int)) cp,
188     (int (*)(void *, long *, long *)) marks,
189     (int (*)(void *, long, long, long)) range,
190     (void (*)(void *)) fin_mark,
191     (void (*)(void *)) move_mark,
192     (void (*)(void *, XEvent *)) release_mark,
193     (char *(*)(void *, long, long, int *, int *)) get_block,
194     (void (*)(void *, long, int)) move,
195     (void (*)(void *, long)) motion,
196     0,
197     0,
198     0,
199     0,
200     DndText
201 };
202 
203 
204 /*
205    If options & TEXTBOX_NO_STRDUP then text must be passed as an malloced string
206    which will be free'd automatically when the text box is destroyed.
207    If !options & TEXTBOX_NO_STRDUP then text will be strdup'ed (creating its own
208    internal copy of the text), like other widgets.
209    CRedrawTextbox will do the same as CDrawTextbox: it will not free old text
210    or make its own copy of new text.
211  */
CDrawTextbox(const char * identifier,Window parent,int x,int y,int width,int height,int line,int column,const char * text,long options)212 CWidget *CDrawTextbox (const char *identifier, Window parent, int x, int y,
213 		       int width, int height, int line, int column, const char *text, long options)
214 {
215     char *scroll;
216     int numlines;
217     CWidget *wdt;
218 
219     int w, h;
220     CPushFont ("editor", 0);
221     if (width == AUTO_WIDTH || height == AUTO_HEIGHT)
222 	CTextSize (&w, &h, text);
223     if (width == AUTO_WIDTH)
224 	width = w + 6;
225     if (height == AUTO_HEIGHT)
226 	height = h + 6;
227 
228     wdt = CSetupWidget (identifier, parent, x, y,
229 			width, height, C_TEXTBOX_WIDGET, INPUT_KEY,
230 			color_palette (option_text_bg_normal), 1);
231     wdt->funcs = mouse_funcs_new (wdt, &textbox_mouse_mark);
232 
233     xdnd_set_type_list (CDndClass, wdt->winid, xdnd_typelist_send[DndText]);
234 
235     wdt->options = options | WIDGET_TAKES_SELECTION;
236     if (options & TEXTBOX_NO_STRDUP)
237 	wdt->text = (char *) text;
238     else
239 	wdt->text = (char *) strdup (text);
240     numlines = strcountlines (text, 0, 1000000000, options & TEXTBOX_WRAP ? (wdt->width - TEXTBOX_BDR) / FONT_MEAN_WIDTH : 32000) + 1;
241 
242     wdt->firstline = 0;
243     wdt->firstcolumn = 0;
244     wdt->cursor = 0;
245     wdt->current = 0;
246     wdt->numlines = numlines;
247     wdt->textlength = strlen (wdt->text);
248 
249     CSetTextboxPos (wdt, TEXT_SET_LINE, line);
250     CSetTextboxPos (wdt, TEXT_SET_COLUMN, column);
251 
252     if (height > 80) {
253 /* this will also set the hint position, set_hint_pos() */
254 	wdt->vert_scrollbar = CDrawVerticalScrollbar (scroll = catstrs (identifier, ".vsc", NULL), parent,
255 		x + width + WIDGET_SPACING, y, height, AUTO_WIDTH, 0, 0);
256 	CSetScrollbarCallback (wdt->vert_scrollbar->ident, wdt->ident, link_scrollbar_to_textbox);
257     } else {
258 	set_hint_pos (x + width + WIDGET_SPACING, y + height + WIDGET_SPACING);
259     }
260     CPopFont ();
261     return wdt;
262 }
263 
264 
265 int CSetTextboxPos (CWidget * w, int which, long p);
266 
267 /* redraws the text box. If preserve is 0 then view position is reset to 0 */
CRedrawTextbox(const char * identifier,const char * text,int preserve)268 CWidget *CRedrawTextbox (const char *identifier, const char *text, int preserve)
269 {
270     CWidget *w = CIdent (identifier);
271     int numlines, firstline, firstcolumn, cursor;
272 
273     if (!w)
274 	return 0;
275     if (w->options & TEXTBOX_NO_STRDUP)
276 	w->text = (char *) text;
277     else {
278 	if (w->text)
279 	    free (w->text);
280 	w->text = (char *) strdup (text);
281     }
282     CPushFont ("editor", 0);
283     w->textlength = strlen (w->text);
284     numlines = strcountlines (text, 0, 1000000000, w->options & TEXTBOX_WRAP ? (w->width - TEXTBOX_BDR) / FONT_MEAN_WIDTH : 32000) + 1;
285     w->numlines = numlines;
286     firstline = w->firstline;
287     firstcolumn = w->firstcolumn;
288     cursor = w->cursor;
289 
290     w->firstline = 0;
291     w->current = 0;
292     w->firstcolumn = 0;
293     w->cursor = 0;
294     w->mark1 = w->mark2 = -1;
295 
296     if (preserve) {
297 	CSetTextboxPos (w, TEXT_SET_LINE, firstline);
298 	CSetTextboxPos (w, TEXT_SET_COLUMN, firstcolumn);
299 	CSetTextboxPos (w, TEXT_SET_CURSOR_LINE, cursor);
300     }
301     CExpose (identifier);
302     CPopFont ();
303 
304     return w;
305 }
306 
307 /* result must not be free'd, and must be used immediately */
CGetTextBoxLine(CWidget * w,int i)308 char *CGetTextBoxLine (CWidget * w, int i)
309 {
310     int width;
311     char *r;
312     CPushFont ("editor", 0);
313     width = w->options & TEXTBOX_WRAP ? (w->width - TEXTBOX_BDR) / FONT_MEAN_WIDTH : 32000;
314     r = strline (w->text, strmovelines (w->text, w->current, i - w->firstline, width));
315     CPopFont ();
316     return r;
317 }
318 
319 /* clears the text box */
CClearTextbox(const char * identifier)320 CWidget *CClearTextbox (const char *identifier)
321 {
322     CWidget *w;
323     w = CIdent (identifier);
324     if (w) {
325 	if (!(w->options & TEXTBOX_NO_STRDUP))
326 	    if (w->text)
327 		free (w->text);
328 	w->text = (char *) strdup ("");
329 	w->textlength = w->numlines = 0;
330 	w->firstline = w->firstcolumn = 0;
331 	w->mark1 = w->mark2 = 0;
332 	CExpose (identifier);
333     }
334     return w;
335 }
336 
CDrawManPage(const char * identifier,Window parent,int x,int y,int width,int height,int line,int column,const char * text)337 CWidget *CDrawManPage (const char *identifier, Window parent, int x, int y,
338 	   int width, int height, int line, int column, const char *text)
339 {
340     CWidget *w;
341     w = CDrawTextbox (identifier, parent, x, y, width, height, line, column, text, TEXTBOX_MAN_PAGE);
342     return w;
343 }
344 
345 
346 
347 /*
348    If which is TEXT_SET_POS the current offset of the top right
349    corner is set to p.
350    returns non-zero if anything actually changed.
351  */
CSetTextboxPos(CWidget * wdt,int which,long p)352 int CSetTextboxPos (CWidget * wdt, int which, long p)
353 {
354     long q;
355     int width, i, j;
356     if (p < 0)
357 	p = 0;
358     CPushFont ("editor", 0);
359 
360     width = wdt->options & TEXTBOX_WRAP ? (wdt->width - TEXTBOX_BDR) / FONT_MEAN_WIDTH : 32000;
361 
362     switch (which) {
363     case TEXT_SET_COLUMN:
364 	i = wdt->firstcolumn;
365 	wdt->firstcolumn = p;
366 	CPopFont ();
367 	return (i != wdt->firstcolumn);
368     case TEXT_SET_LINE:
369 	i = wdt->firstline;
370 	if (p >= wdt->numlines)
371 	    p = wdt->numlines - 1;
372 	if (p < 0)
373 	    p = 0;
374 	if (wdt->kind == C_FIELDED_TEXTBOX_WIDGET) {
375 	    wdt->firstline = p;
376 	} else {
377 	    q = strmovelines (wdt->text, wdt->current, p - wdt->firstline, width);
378 	    wdt->firstline += strcountlines (wdt->text, wdt->current, q - wdt->current, width);
379 	    wdt->current = q;
380 	}
381 	CPopFont ();
382 	return (i != wdt->firstline);
383     case TEXT_SET_POS:
384 	i = wdt->firstline;
385 	if (wdt->kind == C_FIELDED_TEXTBOX_WIDGET) {
386 	    break;
387 	} else {
388 	    wdt->firstline += strcountlines (wdt->text, wdt->current, p - wdt->current, width);
389 	    wdt->current = p;
390 	}
391 	CPopFont ();
392 	return (i != wdt->firstline);
393     case TEXT_SET_CURSOR_LINE:
394 	i = wdt->firstline;
395 	j = wdt->cursor;
396 	if (p < 0)
397 	    p = 0;
398 	if (p >= wdt->numlines)
399 	    p = wdt->numlines - 1;
400 	wdt->cursor = p;
401 	if (p < wdt->firstline)
402 	    CSetTextboxPos (wdt, TEXT_SET_LINE, p);
403 	else if (p > wdt->firstline + (wdt->height - FONT_PIX_PER_LINE - 6) / FONT_PIX_PER_LINE)
404 	    CSetTextboxPos (wdt, TEXT_SET_LINE, p - (wdt->height - FONT_PIX_PER_LINE - 6) / FONT_PIX_PER_LINE);
405 	CPopFont ();
406 	return (i != wdt->firstline || j != wdt->cursor);
407     }
408 /* NLS ? */
409     CError ("settextpos: command not found.\n");
410     CPopFont ();
411     return 0;
412 }
413 
414 
text_print_line(CWidget * w,long b,int row)415 static void text_print_line (CWidget * w, long b, int row)
416 {
417     edit_draw_proportional (w,
418       (void (*)(void *, long, cache_type *, cache_type *, int, int, int)) convert_text2,
419 		     (int (*)(void *, long, long *, int)) calc_text_pos2,
420 			    -w->firstcolumn * FONT_MEAN_WIDTH, w->winid,
421 			    w->width, b, row, row * FONT_PIX_PER_LINE + EDIT_TEXT_VERTICAL_OFFSET, 0,
422 			    FONT_PER_CHAR(' ') * TAB_SIZE);
423 }
424 
425 
426 
427 /*
428    ->firstline   is line number of the top line in the window.
429    ->firstcolumn is column shift (positive).
430    ->current     is actual char position of first line in display.
431    ->numlines    is the total number of lines.
432    ->cursor      is the number of the highlighted line.
433    ->textlength  is the length of text excluding trailing NULL.
434    First three must be initialised to proper values (e.g. 0, 0 and 0).
435  */
436 
437 extern int EditExposeRedraw;
438 extern int EditClear;
439 extern int highlight_this_line;
440 extern unsigned long edit_normal_background_color;
441 
render_textbox(CWidget * w,int redrawall)442 long render_textbox (CWidget * w, int redrawall)
443 {
444     long b;
445     int c = 0, r = 0, row, height, isfocussed, wrap_width = 32000,
446      curs, lines_drawn = 0;
447     CPushFont ("editor", 0);
448     if (w->options & TEXTBOX_WRAP) {
449 	wrap_width = (w->width - TEXTBOX_BDR) / FONT_MEAN_WIDTH;
450 	if (w->resized) {	/* a resize will change the number lines if text is wrapping */
451 	    int firstline = w->firstline;
452 	    w->numlines = strcountlines (w->text, 0, 1000000000, wrap_width) + 1;
453 	    w->firstline = 0;
454 	    w->current = w->cursor = 0;
455 	    CSetTextboxPos (w, TEXT_SET_LINE, firstline);
456 	    w->resized = 0;	/* done */
457 	}
458     }
459     if (redrawall) {
460 	EditExposeRedraw = 1;
461 	EditClear = 1;
462     }
463     b = w->current;
464     height = w->height / FONT_PIX_PER_LINE + 1;
465 
466     isfocussed = (w->winid == CGetFocus ());
467     curs = !(w->options & TEXTBOX_NO_CURSOR || w->mark1 != w->mark2);	/* don't draw the cursor line */
468 
469     edit_set_foreground_colors (color_palette (option_text_fg_normal), color_palette (option_text_fg_bold), color_palette (option_text_fg_italic));
470     edit_set_background_colors (color_palette (option_text_bg_normal), color_palette (0), color_palette (option_text_bg_marked), color_palette (9), color_palette (option_text_bg_highlighted));
471 
472     for (row = 0; row < height; row++) {
473 	if (row + w->firstline == w->cursor && isfocussed && curs)
474 	    highlight_this_line = 1;
475 	else
476 	    highlight_this_line = 0;
477 	if (row + w->firstline < w->numlines) {
478 	    c = strmovelines (w->text, b, 1, wrap_width);
479 	    if (c != b) {	/* at last line strmovelines cannot move */
480 		r = w->text[c];
481 		w->text[c] = 0;	/* mark where line wraps */
482 	    }
483 	    lines_drawn++;
484 	    text_print_line (w, b, row);
485 	    if (c != b)
486 		w->text[c] = r;	/* remove mark */
487 	    b = c;
488 	} else {
489 	    text_print_line (w, w->textlength, row);	/* print blank lines */
490 	}
491     }
492 
493     EditExposeRedraw = 0;
494     EditClear = 0;
495     (*look->render_textbox_tidbits) (w, isfocussed);
496     CSetColor (edit_normal_background_color);
497     CLine (w->winid, 3, 3, 3, w->height - 4);
498     CPopFont ();
499     return lines_drawn;
500 }
501 
502 /*
503    Count the number of lines that would be printed
504    by the above routine, but don't print anything.
505    If all is non-zero then count all the lines.
506  */
count_textbox_lines(CWidget * wdt,int all)507 long count_textbox_lines (CWidget * wdt, int all)
508 {
509     int nroff, col = 0, row = 0, height, width;
510     long from;
511     unsigned char c;
512     int wrap_mode;
513     unsigned char *text;
514 
515     CPushFont ("editor", 0);
516     nroff = (wdt->options & TEXTBOX_MAN_PAGE);
517     wrap_mode = (wdt->options & TEXTBOX_WRAP);
518     if (nroff)
519 	wrap_mode = 0;
520 
521     text = (unsigned char *) wdt->text;
522     height = wdt->height / FONT_PIX_PER_LINE;
523     width = (wdt->width - TEXTBOX_BDR) / FONT_MEAN_WIDTH;
524     if (all)
525 	from = 0;
526     else
527 	from = wdt->current;
528 
529     for (; row < height || all; from++) {
530 	c = text[from];
531 	if (!c)
532 	    break;
533 	if ((c == '\n') || (col == width && wrap_mode)) {
534 	    col = 0;
535 	    row++;
536 	    if (c == '\n' || row >= height)
537 		continue;
538 	}
539 	if (c == '\r')
540 	    continue;
541 	if (c == '\t') {
542 	    col = (col / 8) * 8 + 8;
543 	    continue;
544 	}
545 	col++;
546     }
547     CPopFont ();
548     return row + 1;
549 }
550 
551 /* move the text box cursor or the text window if there isn't one */
CTextboxCursorMove(CWidget * wdt,KeySym key)552 int CTextboxCursorMove (CWidget * wdt, KeySym key)
553 {
554     int handled = 0;
555     CPushFont ("editor", 0);
556 /* when text is highlighted, the cursor must be off */
557     if (wdt->options & TEXTBOX_NO_CURSOR || wdt->mark1 != wdt->mark2) {
558 	int to_move = 0;
559 	switch ((int) key) {
560 	case CK_Up:
561 	    handled = 1;
562 	    to_move = -1;
563 	    break;
564 	case CK_Down:
565 	    handled = 1;
566 	    to_move = 1;
567 	    break;
568 	case CK_Page_Up:
569 	    handled = 1;
570 	    to_move = 1 - wdt->height / FONT_PIX_PER_LINE;
571 	    break;
572 	case CK_Page_Down:
573 	    handled = 1;
574 	    to_move = wdt->height / FONT_PIX_PER_LINE - 1;
575 	    break;
576 	case CK_Home:
577 	    handled = 1;
578 	    to_move = -32000;
579 	    break;
580 	case CK_End:
581 	    handled = 1;
582 	    to_move = 32000;
583 	    break;
584 	case CK_Left:
585 	    handled = 1;
586 	    if (wdt->firstcolumn > 0)
587 		wdt->firstcolumn--;
588 	    break;
589 	case CK_Right:
590 	    handled = 1;
591 	    wdt->firstcolumn++;
592 	    break;
593 	}
594 	CSetTextboxPos (wdt, TEXT_SET_LINE, wdt->firstline + to_move);
595     } else {
596 	switch ((int) key) {
597 	case CK_Up:
598 	    handled = 1;
599 	    wdt->cursor--;
600 	    break;
601 	case CK_Down:
602 	    handled = 1;
603 	    wdt->cursor++;
604 	    break;
605 	case CK_Page_Up:
606 	    handled = 1;
607 	    wdt->cursor -= (wdt->height / FONT_PIX_PER_LINE - 1);
608 	    break;
609 	case CK_Page_Down:
610 	    handled = 1;
611 	    wdt->cursor += (wdt->height / FONT_PIX_PER_LINE - 1);
612 	    break;
613 	case CK_Home:
614 	    handled = 1;
615 	    wdt->cursor = 0;
616 	    break;
617 	case CK_End:
618 	    handled = 1;
619 	    wdt->cursor = wdt->numlines;
620 	    break;
621 	case CK_Left:
622 	    handled = 1;
623 	    if (wdt->firstcolumn > 0) {
624 		wdt->firstcolumn--;
625 	    }
626 	    break;
627 	case CK_Right:
628 	    handled = 1;
629 	    wdt->firstcolumn++;
630 	    break;
631 	}
632 	CSetTextboxPos (wdt, TEXT_SET_CURSOR_LINE, wdt->cursor);	/* just does some checks */
633     }
634     CPopFont ();
635     return handled;
636 }
637 
text_mouse_mark(CWidget * w,XEvent * event,CEvent * ce)638 static void text_mouse_mark (CWidget * w, XEvent * event, CEvent * ce)
639 {
640     CPushFont ("editor", 0);
641     mouse_mark (event, ce->double_click, w->funcs);
642     CPopFont ();
643 }
644 
645 static struct selection text_selection = {0, 0};
646 
647 /* gets selected text into selection structure, stripping nroff */
text_get_selection(CWidget * w)648 void text_get_selection (CWidget * w)
649 {
650     char *t;
651     int len;
652     len = abs (w->mark2 - w->mark1);
653     t = CMalloc (len + 1);
654     memcpy (t, w->text + min (w->mark1, w->mark2), len);
655     t[len] = 0;
656     if (text_selection.text)
657 	free (text_selection.text);
658     text_selection.text = (unsigned char *) str_strip_nroff ((char *) t, &text_selection.len);
659     free (t);
660     if (!text_selection.text) {
661 	text_selection.text = CMalloc (1);
662 	text_selection.len = 0;
663     }
664     text_selection.text[text_selection.len] = 0;
665     selection_clear ();
666     selection = text_selection;
667 }
668 
669 void selection_send (XSelectionRequestEvent * rq);
670 
eh_textbox(CWidget * w,XEvent * xevent,CEvent * cwevent)671 int eh_textbox (CWidget * w, XEvent * xevent, CEvent * cwevent)
672 {
673     int handled = 0, redrawall, count;
674 
675     redrawall = 0;
676     switch (xevent->type) {
677     case SelectionRequest:
678 	text_get_selection (w);
679 	selection_send (&(xevent->xselectionrequest));
680 	return 1;
681     case Expose:
682 	if (!xevent->xexpose.count)
683 	    redrawall = 1;
684 	break;
685     case ClientMessage:
686 	w->mark1 = w->mark2 = 0;
687 	break;
688     case ButtonPress:
689 	CFocus (w);
690 	CPushFont ("editor", 0);
691 	if (xevent->xbutton.button == Button1)
692 	    w->cursor = (xevent->xbutton.y - TEXTBOX_BDR) / FONT_PIX_PER_LINE + w->firstline;
693 	if (w->cursor > w->numlines - 1)
694 	    w->cursor = w->numlines - 1;
695 	if (w->cursor < 0)
696 	    w->cursor = 0;
697 	cwevent->ident = w->ident;
698 	cwevent->xt = (xevent->xbutton.x - 7) / FONT_MEAN_WIDTH + w->firstcolumn;
699 	cwevent->yt = w->cursor;
700 	CPopFont ();
701     case ButtonRelease:
702     case MotionNotify:
703 	if (!xevent->xmotion.state && xevent->type == MotionNotify)
704 	    return 0;
705 	resolve_button (xevent, cwevent);
706 	text_mouse_mark (w, xevent, cwevent);
707 	break;
708     case FocusIn:
709     case FocusOut:
710 	break;
711     case KeyPress:
712 	cwevent->ident = w->ident;
713 	if (!(TEXTBOX_NO_KEYS & w->options))
714 	    handled = CTextboxCursorMove (w, cwevent->command);
715 	break;
716     default:
717 	return 0;
718     }
719 
720 /* Now draw the changed text box, count will contain
721    the number of textlines displayed */
722     count = render_textbox (w, redrawall);
723 
724 /* now update the scrollbar position */
725     if (w->vert_scrollbar) {
726 	w->vert_scrollbar->firstline = (double) 65535.0 *w->firstline / w->numlines;
727 	w->vert_scrollbar->numlines = (double) 65535.0 *count / w->numlines;
728 	w->vert_scrollbar->options = 0;
729 	render_scrollbar (w->vert_scrollbar);
730     }
731 
732     return handled;
733 }
734 
link_scrollbar_to_textbox(CWidget * scrollbar,CWidget * textbox,XEvent * xevent,CEvent * cwevent,int whichscrbutton)735 void link_scrollbar_to_textbox (CWidget * scrollbar, CWidget * textbox, XEvent * xevent, CEvent * cwevent, int whichscrbutton)
736 {
737     int redrawtext = 0, count = -1, c;
738     static int r = 0;
739     CPushFont ("editor", 0);
740     if ((xevent->type == ButtonRelease || xevent->type == MotionNotify) && whichscrbutton == 3) {
741 	redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, (double) scrollbar->firstline * textbox->numlines / 65535.0);
742     } else if (xevent->type == ButtonPress && (cwevent->button == Button1 || cwevent->button == Button2)) {
743 	switch (whichscrbutton) {
744 	case 1:
745 	    redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, textbox->firstline - (textbox->height / FONT_PIX_PER_LINE - 2));
746 	    break;
747 	case 2:
748 	    redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, textbox->firstline - 1);
749 	    break;
750 	case 5:
751 	    redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, textbox->firstline + 1);
752 	    break;
753 	case 4:
754 	    redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, textbox->firstline + (textbox->height / FONT_PIX_PER_LINE - 2));
755 	    break;
756 	}
757     }
758     if (xevent->type == ButtonRelease)
759 	count = render_textbox (textbox, 0);
760     else {
761 	c = CCheckWindowEvent (xevent->xany.window, ButtonReleaseMask | ButtonMotionMask, 0);
762 	if (redrawtext) {
763 	    if (!c) {
764 		render_textbox (textbox, 0);
765 		r = 0;
766 	    } else {
767 		r = 1;
768 	    }
769 	} else if (c && r) {
770 	    render_textbox (textbox, 0);
771 	    r = 0;
772 	}
773     }
774     if (count < 0)
775 	count = count_textbox_lines (textbox, 0);
776     if (!count)
777 	count = 1;
778     scrollbar->firstline = (double) 65535.0 *textbox->firstline / textbox->numlines;
779     scrollbar->numlines = (double) 65535.0 *count / textbox->numlines;
780     CPopFont ();
781     return;
782 }
783 
784 
785 
786 
787