1 /*
2  *  Copyright (C) 1995, 1996  Karl-Johan Johnsson.
3  */
4 
5 #include <X11/IntrinsicP.h>
6 #include <X11/StringDefs.h>
7 #include <X11/Xatom.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 
13 #include "Compat.h"
14 #include "Util.h"
15 #include "TextFieldP.h"
16 
17 static XtResource resources[] = {
18     {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
19      XtOffsetOf(TextFieldRec, core.border_width), XtRImmediate, (XtPointer)2},
20 #define offset(field) XtOffsetOf(TextFieldRec, textfield.field)
21     {XtNbuffer, XtCBuffer, XtRString, sizeof(String),
22      offset(buffer), XtRImmediate, (XtPointer)""},
23     {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
24      offset(fg_pixel), XtRString, XtDefaultForeground},
25     {XtNfocusColor, XtCForeground, XtRPixel, sizeof(Pixel),
26      offset(focus_pixel), XtRString, XtDefaultForeground},
27     {XtNhighlightForeground, XtCBackground, XtRPixel, sizeof(Pixel),
28      offset(highlight_fg), XtRString, XtDefaultBackground},
29     {XtNhighlightBackground, XtCForeground, XtRPixel, sizeof(Pixel),
30      offset(highlight_bg), XtRString, XtDefaultForeground},
31     {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
32      offset(font), XtRString, XtDefaultFont},
33     {XtNinternalWidth, XtCInternalWidth, XtRDimension, sizeof(Dimension),
34      offset(internal_width), XtRImmediate, (XtPointer)8},
35     {XtNinternalHeight, XtCInternalHeight, XtRDimension, sizeof(Dimension),
36      offset(internal_height), XtRImmediate, (XtPointer)3},
37     {XtNpreferredChars, XtCPreferredChars, XtRInt, sizeof(int),
38      offset(pref_chars), XtRImmediate, (XtPointer)20},
39     {XtNpreferredLines, XtCPreferredLines, XtRInt, sizeof(int),
40      offset(pref_lines), XtRImmediate, (XtPointer)1},
41     {XtNsingleLine, XtCSingleLine, XtRBoolean, sizeof(Boolean),
42      offset(single_line), XtRImmediate, (XtPointer)True},
43     {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
44      offset(callback), XtRCallback, (XtPointer)NULL},
45     {XtNtabCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
46      offset(tab_callback), XtRCallback, (XtPointer)NULL},
47     {XtNfocusCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
48      offset(focus_callback), XtRCallback, (XtPointer)NULL},
49     {XtNborderIn, XtCBorderIn, XtRBoolean, sizeof(Boolean),
50      offset(border_in), XtRImmediate, (XtPointer)True},
51     {XtNdisplayCaret, XtCDisplayCaret, XtRBoolean, sizeof(Boolean),
52      offset(display_caret), XtRImmediate, (XtPointer)False},
53     {XtNfocusRoot, XtCFocusRoot, XtRWidget, sizeof(Widget),
54      offset(focus_root), XtRImmediate, (XtPointer)NULL},
55     {XtNfocusHack, XtCHack, XtRBoolean, sizeof(Boolean),
56      offset(focus_hack), XtRImmediate, (XtPointer)True},
57     {XtNprintFocus, XtCDebug, XtRBoolean, sizeof(Boolean),
58      offset(print_focus), XtRImmediate, (XtPointer)False},
59     {XtNechoOff, XtCEchoOff, XtRBoolean, sizeof(Boolean),
60      offset(echo_off), XtRImmediate, (XtPointer)False},
61 #undef offset
62 };
63 
64 static void	Initialize(Widget, Widget, ArgList, Cardinal*);
65 static void	Destroy(Widget);
66 static void	Redisplay(Widget, XEvent*, Region);
67 static void	Resize(Widget);
68 static void	Realize(Widget, XtValueMask*, XSetWindowAttributes*);
69 static void	SetHPos(ScrollableWidget, long);
70 static void	SetVPos(ScrollableWidget, long);
71 static Boolean	SetValues(Widget, Widget, Widget, ArgList, Cardinal*);
72 static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*,
73 				      XtWidgetGeometry*);
74 
75 static void aquire_focus(Widget, XEvent*, String*, Cardinal*);
76 static void nop(Widget, XEvent*, String*, Cardinal*);
77 static void multiply(Widget, XEvent*, String*, Cardinal*);
78 static void beginning_of_line(Widget, XEvent*, String*, Cardinal*);
79 static void end_of_line(Widget, XEvent*, String*, Cardinal*);
80 static void home(Widget, XEvent*, String*, Cardinal*);
81 static void end(Widget, XEvent*, String*, Cardinal*);
82 static void left(Widget, XEvent*, String*, Cardinal*);
83 static void right(Widget, XEvent*, String*, Cardinal*);
84 static void up(Widget, XEvent*, String*, Cardinal*);
85 static void down(Widget, XEvent*, String*, Cardinal*);
86 static void page(Widget, XEvent*, String*, Cardinal*);
87 static void delete_next(Widget, XEvent*, String*, Cardinal*);
88 static void delete_previous(Widget, XEvent*, String*, Cardinal*);
89 static void kill_action(Widget, XEvent*, String*, Cardinal*);
90 static void redraw(Widget, XEvent*, String*, Cardinal*);
91 static void enter(Widget, XEvent*, String*, Cardinal*);
92 static void tab(Widget, XEvent*, String*, Cardinal*);
93 static void transpose(Widget, XEvent*, String*, Cardinal*);
94 static void set_border_color(Widget, XEvent*, String*, Cardinal*);
95 static void insert(Widget, XEvent*, String*, Cardinal*);
96 static void insert_string(Widget, XEvent*, String*, Cardinal*);
97 static void swap_select(Widget, XEvent*, String*, Cardinal*);
98 static void select_start(Widget, XEvent*, String*, Cardinal*);
99 static void extend_start(Widget, XEvent*, String*, Cardinal*);
100 static void select_extend(Widget, XEvent*, String*, Cardinal*);
101 static void select_end(Widget, XEvent*, String*, Cardinal*);
102 static void kill_selection(Widget, XEvent*, String*, Cardinal*);
103 static void insert_selection(Widget, XEvent*, String*, Cardinal*);
104 static void disown_selection(Widget, XEvent*, String*, Cardinal*);
105 static void display_caret(Widget, XEvent*, String*, Cardinal*);
106 
107 static XtActionsRec actions[] = {
108     {"aquire-focus",		aquire_focus},
109     {"nop",			nop},
110     {"multiply",		multiply},
111     {"beginning-of-line",	beginning_of_line},
112     {"end-of-line",		end_of_line},
113     {"home",			home},
114     {"end",			end},
115     {"left",			left},
116     {"right",			right},
117     {"up",			up},
118     {"down",			down},
119     {"page",			page},
120     {"delete-next",		delete_next},
121     {"delete-previous",		delete_previous},
122     {"kill",			kill_action},
123     {"redraw",			redraw},
124     {"enter",			enter},
125     {"tab",			tab},
126     {"transpose",		transpose},
127     {"set-border-color",	set_border_color},
128     {"insert",			insert},
129     {"insert-string",		insert_string},
130     {"swap-select",		swap_select},
131     {"select-start",		select_start},
132     {"extend-start",		extend_start},
133     {"select-extend",		select_extend},
134     {"select-end",		select_end},
135     {"kill-selection",		kill_selection},
136     {"insert-selection",	insert_selection},
137     {"disown-selection",	disown_selection},
138     {"display-caret",		display_caret},
139 };
140 
141 static char translations[] =
142 "Ctrl<Key>A:		beginning-of-line() \n"
143 "Ctrl<Key>B:		left() \n"
144 "Ctrl<Key>C:		nop() \n"
145 "Ctrl<Key>D:		delete-next() \n"
146 "Ctrl<Key>E:		end-of-line() \n"
147 "Ctrl<Key>F:		right() \n"
148 "Ctrl<Key>G:		multiply(0) disown-selection() \n"
149 "Ctrl<Key>H:		delete-previous() \n"
150 "Ctrl<Key>I:		tab() \n"
151 "Ctrl<Key>J:		enter() \n"
152 "Ctrl<Key>K:		kill() \n"
153 "Ctrl<Key>L:		redraw() \n"
154 "Ctrl<Key>M:		enter() \n"
155 "Ctrl<Key>N:		down() \n"
156 "Ctrl<Key>O:		enter() left() \n"
157 "Ctrl<Key>P:		up() \n"
158 "Ctrl<Key>T:		transpose() \n"
159 "Ctrl<Key>U:		multiply(4) \n"
160 "Ctrl<Key>V:		page(+1.0) \n"
161 "Ctrl<Key>W:		kill-selection(PRIMARY) \n"
162 "Ctrl<Key>X,Ctrl<Key>X:	swap-select(PRIMARY) \n"
163 "Ctrl<Key>space:	select-start() \n"
164 "<Key>Left:		left() \n"
165 "<Key>Right:		right() \n"
166 "<Key>Up:		up() \n"
167 "<Key>Down:		down() \n"
168 "<Key>Return:		enter() \n"
169 "<Key>BackSpace:	delete-previous() \n"
170 "<Key>Delete:		delete-previous() \n"
171 "<Key>Tab:		tab() \n"
172 "<Key>Home:		home() \n"
173 "<Key>End:		end() \n"
174 "<Key>Prior:		page(-1.0) \n"
175 "<Key>Next:		page(+1.0) \n"
176 "<FocusIn>:		set-border-color(focusColor) display-caret(on) \n"
177 "<FocusOut>:		set-border-color(background) display-caret(off) \n"
178 "<Btn1Down>:		aquire-focus() select-start() \n"
179 "<Btn1Motion>:		select-extend() \n"
180 "<Btn1Up>:		select-end(PRIMARY) \n"
181 "<Btn2Down>:		insert-selection(PRIMARY) \n"
182 "<Btn3Down>:		extend-start(PRIMARY) \n"
183 "<Btn3Motion>:		select-extend() \n"
184 "<Btn3Up>:		select-end(PRIMARY) \n"
185 "Ctrl<Key>0:		multiply(0) \n"
186 "Ctrl<Key>1:		multiply(1) \n"
187 "Ctrl<Key>2:		multiply(2) \n"
188 "Ctrl<Key>3:		multiply(3) \n"
189 "Ctrl<Key>4:		multiply(4) \n"
190 "Ctrl<Key>5:		multiply(5) \n"
191 "Ctrl<Key>6:		multiply(6) \n"
192 "Ctrl<Key>7:		multiply(7) \n"
193 "Ctrl<Key>8:		multiply(8) \n"
194 "Ctrl<Key>9:		multiply(9) \n"
195 "<Key>:			insert() \n";
196 
197 TextFieldClassRec textFieldClassRec = {
198     {                                   /* core fields			*/
199         (WidgetClass) &scrollableClassRec,  /* superclass		*/
200         "TextField",                    /* class_name                   */
201         sizeof(TextFieldRec),	        /* widget_size                  */
202         NULL,                           /* class_initialize             */
203         NULL,                           /* class_part_initialize        */
204         FALSE,                          /* class_inited                 */
205         Initialize,                     /* initialize                   */
206         NULL,                           /* initialize_hook              */
207         Realize,			/* realize                      */
208         actions,                        /* actions                      */
209         XtNumber(actions),              /* num_actions                  */
210         resources,                      /* resources                    */
211         XtNumber(resources),            /* num_resources                */
212         NULLQUARK,                      /* xrm_class                    */
213         TRUE,                           /* compress_motion              */
214 #if (XtSpecificationRelease < 4)
215 	True,				/* compress_exposure		*/
216 #elif (XtSpecificationRelease < 6)
217         XtExposeCompressMaximal | XtExposeGraphicsExposeMerged,
218 					/* compress_exposure		*/
219 #else
220         XtExposeCompressMaximal |  XtExposeGraphicsExposeMerged |
221 	XtExposeNoRegion,		/* compress_exposure*/
222 #endif
223         FALSE,                          /* compress_enterleave          */
224         FALSE,                          /* visible_interest             */
225         Destroy,                        /* destroy                      */
226         Resize,                         /* resize                       */
227         Redisplay,                      /* expose                       */
228         SetValues,                      /* set_values                   */
229         NULL,                           /* set_values_hook              */
230         XtInheritSetValuesAlmost,       /* set_values_almost            */
231         NULL,                           /* get_values_hook              */
232         NULL,				/* accept_focus                 */
233         XtVersion,                      /* version                      */
234         NULL,                           /* callback_private             */
235         translations,                   /* tm_table                     */
236         QueryGeometry,                  /* query_geometry               */
237         XtInheritDisplayAccelerator,    /* display_accelerator          */
238         NULL                            /* extension                    */
239     },
240     {					/* shadow fields		*/
241 	XtInheritPixelOffset,		/* pixel_offset			*/
242 	True,				/* use_arm_for_background	*/
243 	XtInheritAllocShadowColors,	/* alloc_shadow_colors		*/
244 	XtInheritAllocShadowPixmaps,	/* alloc_shadow_pixmaps		*/
245 	XtInheritAllocArmColor,		/* alloc_arm_color		*/
246 	NULL,				/* alloc_arm_pixmap		*/
247 	XtInheritAllocGCs,		/* alloc_gcs			*/
248 	NULL,				/* extension			*/
249     },
250     {					/* scrollable fields		*/
251 	SetHPos,			/* set_hpos			*/
252 	SetVPos,			/* set_vpos			*/
253 	NULL,				/* suspend_hook			*/
254 	NULL				/* extension			*/
255     },
256     {                                   /* textfield fields             */
257         0                               /* extension                    */
258     }
259 };
260 
261 WidgetClass textFieldWidgetClass = (WidgetClass)&textFieldClassRec;
262 
263 /*************************************************************************/
264 
265 static void	delete_sel_callback(Widget, XtPointer, Atom*, Atom*,
266 				    XtPointer, unsigned long*, int*);
267 static void	insert_sel_callback(Widget, XtPointer, Atom*, Atom*,
268 				    XtPointer, unsigned long*, int*);
269 static Boolean	convert_sel_proc(Widget, Atom*, Atom*, Atom*,
270 				 XtPointer*, unsigned long*, int*);
271 static void	lose_sel_proc(Widget, Atom*);
272 
init_gcs(TextFieldWidget w)273 static void init_gcs(TextFieldWidget w)
274 {
275     XGCValues	values;
276 
277     values.foreground = w->textfield.fg_pixel;
278     values.background = w->core.background_pixel;
279     values.font = w->textfield.font->fid;
280     w->textfield.gc =
281 	XtGetGC((Widget)w, GCForeground | GCBackground | GCFont, &values);
282 
283     values.foreground = w->textfield.highlight_fg;
284     values.background = w->textfield.highlight_bg;
285     values.font = w->textfield.font->fid;
286     w->textfield.h_gc =
287 	XtGetGC((Widget)w, GCForeground | GCBackground | GCFont, &values);
288 }
289 
free_gcs(TextFieldWidget w)290 static void free_gcs(TextFieldWidget w)
291 {
292     XtReleaseGC((Widget)w, w->textfield.gc);
293     XtReleaseGC((Widget)w, w->textfield.h_gc);
294 }
295 
call_callbacks(TextFieldWidget w,XtCallbackList c_list)296 static void call_callbacks(TextFieldWidget w, XtCallbackList c_list)
297 {
298     char	*buffer;
299 
300     if (!c_list)
301 	return;
302 
303     buffer = TextFieldGetBuffer((Widget)w);
304     XtCallCallbackList((Widget)w, c_list, (XtPointer)buffer);
305     XtFree(buffer);
306 }
307 
free_lines(TextFieldWidget w)308 static void free_lines(TextFieldWidget w)
309 {
310     long	i;
311 
312     for (i = 0 ; i < w->scrollable.height ; i++)
313 	XtFree(w->textfield.lines[i].buf);
314     XtFree((char *)w->textfield.lines);
315     w->textfield.lines = NULL;
316     w->textfield.n_lines = 0;
317     w->scrollable.height = 0;
318     w->scrollable.pos_y = 0;
319     w->scrollable.width = 0;
320     w->scrollable.pos_x = 0;
321 }
322 
lines_from_buffer(TextFieldWidget w)323 static void lines_from_buffer(TextFieldWidget w)
324 {
325     char	*c = w->textfield.buffer;
326 
327     w->textfield.buffer = NULL;
328     free_lines(w);
329 
330     if (w->textfield.single_line)
331 	w->textfield.n_lines = 1;
332     else
333 	w->textfield.n_lines = 16;
334     w->textfield.lines = (LineBuf *)XtMalloc(w->textfield.n_lines *
335 					     sizeof w->textfield.lines[0]);
336 
337     if (!c)
338 	c = "";
339 
340     if (w->textfield.single_line) {
341 	char	*p = strchr(c, '\n');
342 	long	len = p ? p - c : strlen(c);
343 
344 	w->textfield.lines[0].len = len + 1;
345 	w->textfield.lines[0].buf = strcpy(XtMalloc(len + 1), c);
346 	w->textfield.lines[0].buf[len] = '\0';
347 	w->scrollable.width = len + 1;
348 	w->scrollable.height = 1;
349 	if (len < w->scrollable.shown_x)
350 	    w->scrollable.pos_x = 0;
351 	else
352 	    w->scrollable.pos_x = len - w->scrollable.shown_x + 1;
353 	w->scrollable.pos_y = 0;
354 	w->textfield.caret_x = len;
355 	w->textfield.caret_y = 0;
356     } else {
357 	long	n = 0;
358 	long	max = 0;
359 
360 	for (;;) {
361 	    char	*p = strchr(c, '\n');
362 	    long	len = p ? p - c : strlen(c);
363 
364 	    if (n + 8 > w->textfield.n_lines)
365 		w->textfield.lines =
366 		    (LineBuf *)XtRealloc((char *)w->textfield.lines,
367 					 (w->textfield.n_lines = 2 * n) *
368 					 sizeof w->textfield.lines[0]);
369 
370 	    w->textfield.lines[n].buf = memcpy(XtMalloc(len + 1), c, len);
371 	    w->textfield.lines[n].buf[len] = '\0';
372 	    w->textfield.lines[n++].len = len + 1;
373 
374 	    if (max < len)
375 		max = len;
376 	    if (!p)
377 		break;
378 	    c = p + 1;
379 	}
380 
381 	w->scrollable.width = max + 1;
382 	w->scrollable.height = n;
383 	w->scrollable.pos_x = 0;
384 	w->scrollable.pos_y = 0;
385 	w->textfield.caret_x = 0;
386 	w->textfield.caret_y = 0;
387 
388 	while (n < w->textfield.n_lines) {
389 	    w->textfield.lines[n].buf = NULL;
390 	    w->textfield.lines[n].len = 0;
391 	    n++;
392 	}
393     }
394 }
395 
open_lines(TextFieldWidget w,long at,long size)396 static void open_lines(TextFieldWidget w, long at, long size)
397 {
398     long	n = w->textfield.n_lines;
399 
400     if (w->scrollable.height + size + 8 < n) {
401 	w->textfield.lines =
402 	    (LineBuf *)XtRealloc((char *)w->textfield.lines,
403 				 (w->textfield.n_lines =
404 				  2 * (w->scrollable.height + size + 8)) *
405 				 sizeof w->textfield.lines[0]);
406 	while (n < w->textfield.n_lines) {
407 	    w->textfield.lines[n].buf = NULL;
408 	    w->textfield.lines[n].len = 0;
409 	    n++;
410 	}
411     }
412 
413     n = w->scrollable.height;
414     w->scrollable.height += size;
415     ScrollableFitVBar((ScrollableWidget)w);
416 
417     if (at < n)
418 	memmove(w->textfield.lines + at + size,
419 		w->textfield.lines + at,
420 		(n - at) * sizeof w->textfield.lines[0]);
421     n = at + size;
422     while (at < n) {
423 	w->textfield.lines[at].buf = XtMalloc(1);
424 	w->textfield.lines[at].buf[0] = '\0';
425 	w->textfield.lines[at].len = 1;
426 	at++;
427     }
428 }
429 
get_char_sizes(TextFieldWidget w)430 static void get_char_sizes(TextFieldWidget w)
431 {
432     XFontStruct	*font = w->textfield.font;
433 
434     w->textfield.char_w = font->max_bounds.width;
435     if (w->textfield.char_w <= 0)
436 	w->textfield.char_w = 1;
437     w->textfield.char_h = font->ascent + font->descent;
438 
439     if (w->textfield.char_w != font->min_bounds.width)
440 	fputs("Warning: The TextFieldWidget "
441 	      "only works with fixed width fonts.\n", stderr);
442 }
443 
get_preferred_sizes(TextFieldWidget w,Dimension * width,Dimension * height)444 static void get_preferred_sizes(TextFieldWidget  w,
445 				Dimension       *width,
446 				Dimension       *height)
447 {
448     if (w->textfield.pref_chars <= 0)
449 	w->textfield.pref_chars = 1;
450     if (w->textfield.pref_lines <= 0)
451 	w->textfield.pref_lines = 1;
452 
453     *width =
454 	2 * (w->textfield.internal_width + w->shadow.shadow_width) +
455 	w->textfield.pref_chars * w->textfield.char_w;
456 
457     *height =
458 	2 * (w->textfield.internal_height + w->shadow.shadow_width) +
459 	w->textfield.pref_lines * w->textfield.char_h;
460 }
461 
event_to_pos(TextFieldWidget w,XEvent * event,long * x,long * y)462 static int event_to_pos(TextFieldWidget w, XEvent *event, long *x, long *y)
463 {
464     int	e_x, e_y;
465 
466     if (!get_event_xy(event, &e_x, &e_y))
467 	return False;
468 
469     e_x -= w->shadow.shadow_width + w->textfield.internal_width;
470     e_y -= w->shadow.shadow_width + w->textfield.internal_height;
471     *x = w->scrollable.pos_x + e_x / w->textfield.char_w;
472     *y = w->scrollable.pos_y + e_y / w->textfield.char_h;
473     if (*x >= w->scrollable.width)
474 	*x = w->scrollable.width - 1;
475     if (*x < 0)
476 	*x = 0;
477     if (*y >= w->scrollable.height)
478 	*y = w->scrollable.height - 1;
479     if (*y < 0)
480 	*y = 0;
481 
482     return True;
483 }
484 
calc_shown(TextFieldWidget w)485 static void calc_shown(TextFieldWidget w)
486 {
487     long	tmp;
488 
489     tmp = w->core.width;
490     tmp -= 2 * (w->shadow.shadow_width + w->textfield.internal_width);
491     if (tmp < 0)
492 	tmp = 0;
493     w->scrollable.shown_x = tmp / w->textfield.char_w;
494 
495     tmp = w->core.height;
496     tmp -= 2 * (w->shadow.shadow_width + w->textfield.internal_height);
497     if (tmp < 0)
498 	tmp = 0;
499     w->scrollable.shown_y = tmp / w->textfield.char_h;
500 }
501 
max_width(TextFieldWidget w)502 static long max_width(TextFieldWidget w)
503 {
504     long	n, max = 0;
505 
506     for (n = 0 ; n < w->scrollable.height ; n++) {
507 	long	tmp = strlen(w->textfield.lines[n].buf);
508 
509 	if (max < tmp)
510 	    max = tmp;
511     }
512 
513     return max;
514 }
515 
sync_width(TextFieldWidget w,long old_w,long new_w)516 static void sync_width(TextFieldWidget w, long old_w, long new_w)
517 {
518     long	max, width = w->scrollable.width;
519 
520     old_w++;
521     new_w++;
522     if (new_w < width && old_w < width)
523 	return;
524 
525     max = 1;
526     if (old_w < width || new_w <= old_w)
527 	max = max_width(w) + 1;
528     if (max < new_w)
529 	max = new_w;
530 
531     if (max == width)
532 	return;
533 
534     w->scrollable.width = max;
535     ScrollableFitHBar((ScrollableWidget)w);
536 }
537 
clear_segment(TextFieldWidget w,long line,long pos,long width)538 static void clear_segment(TextFieldWidget w, long line, long pos, long width)
539 {
540     long	x, y;
541 
542     line -= w->scrollable.pos_y;
543     if (line < 0 || line >= w->scrollable.shown_y)
544 	return;
545     pos -= w->scrollable.pos_x;
546     if (pos >= w->scrollable.shown_x)
547 	return;
548     if (pos < 0)
549 	width += pos, pos = 0;
550     if (width > w->scrollable.shown_x - pos)
551 	width = w->scrollable.shown_x - pos;
552     if (pos + width < 0)
553 	return;
554 
555     x = w->shadow.shadow_width + w->textfield.internal_width;
556     y = w->shadow.shadow_width + w->textfield.internal_height;
557 
558     x += pos * w->textfield.char_w;
559     y += line * w->textfield.char_h;
560     width *= w->textfield.char_w;
561     XClearArea(XtDisplay(w), XtWindow(w), x, y,
562 	       width, w->textfield.char_h, False);
563 }
564 
draw_stars(Display * disp,Window win,GC gc,long x,long y,long len)565 static void draw_stars(Display *disp, Window win, GC gc,
566 		       long x, long y, long len)
567 {
568     char	buffer[256];
569     char	*c = buffer;
570 
571     if (len > sizeof buffer)
572 	c = XtMalloc(len);
573     memset(c, '*', len);
574     XDrawString(disp, win, gc, x, y, c, len);
575     if (c != buffer)
576 	XtFree(c);
577 }
578 
draw_rectangle(TextFieldWidget w,long x0,long y0,long width,long height,int clear)579 static void draw_rectangle(TextFieldWidget w, long x0, long y0,
580 			   long width, long height, int clear)
581 {
582     Display	*disp = XtDisplay(w);
583     Window	win = XtWindow(w);
584     GC		gc = w->textfield.gc;
585     GC		h_gc = w->textfield.h_gc;
586     long	x, y, i, tmp;
587 
588     tmp = x0 - w->scrollable.pos_x;
589     if (tmp < 0) {
590 	x0 = w->scrollable.pos_x;
591 	width += tmp;
592     }
593     tmp = w->scrollable.pos_x + w->scrollable.shown_x - x0;
594     if (width > tmp)
595 	width = tmp;
596     if (width <= 0)
597 	return;
598 
599     tmp = y0 - w->scrollable.pos_y;
600     if (tmp < 0) {
601 	y0 = w->scrollable.pos_y;
602 	height += tmp;
603     }
604     tmp = w->scrollable.pos_y + w->scrollable.shown_y - y0;
605     if (height > tmp)
606 	height = tmp;
607     tmp = w->scrollable.height - y0;
608     if (height > tmp)
609 	height = tmp;
610 
611     x = w->shadow.shadow_width + w->textfield.internal_width;
612     y = w->shadow.shadow_width + w->textfield.internal_height;
613     x += (x0 - w->scrollable.pos_x) * w->textfield.char_w;
614     y += (y0 - w->scrollable.pos_y) * w->textfield.char_h;
615 
616     if (clear) {
617 	long	tmp;
618 
619 	if (y0 + height < w->scrollable.height)
620 	    tmp = height;
621 	else
622 	    tmp = w->scrollable.shown_y + w->scrollable.pos_y - y0;
623 	if (tmp > 0)
624 	    XClearArea(disp, win, x, y, width * w->textfield.char_w,
625 		       tmp * w->textfield.char_h, False);
626     }
627 
628     y += w->textfield.font->ascent;
629     for (i = y0 ; i < y0 + height ; i++, y += w->textfield.char_h) {
630 	char	*str = w->textfield.lines[i].buf;
631 	long	 len = strlen(str);
632 
633 	len -= x0;
634 	if (len <= 0)
635 	    continue;
636 	if (len > width)
637 	    len = width;
638 	str += x0;
639 
640 	if (w->textfield.echo_off)
641 	    draw_stars(disp, win, gc, x, y, len);
642 	else if (!w->textfield.sel_set ||
643 		 i < w->textfield.sel_start_y ||
644 		 i > w->textfield.sel_stop_y)
645 	    XDrawString(disp, win, gc, x, y, str, len);
646 	else if (i > w->textfield.sel_start_y &&
647 		 i < w->textfield.sel_stop_y)
648 	    XDrawImageString(disp, win, h_gc, x, y, str, len);
649 	else {
650 	    long	n, pos = x0;
651 	    long	x_tmp = x;
652 
653 	    if (i == w->textfield.sel_start_y &&
654 		(n = w->textfield.sel_start_x - pos) > 0) {
655 		if (n > len)
656 		    n = len;
657 		XDrawString(disp, win, gc, x_tmp, y, str, n);
658 		x_tmp += n * w->textfield.char_w;
659 		str += n;
660 		pos += n;
661 		len -= n;
662 	    }
663 
664 	    if (i == w->textfield.sel_stop_y)
665 		n = w->textfield.sel_stop_x - pos;
666 	    else
667 		n = len;
668 
669 	    if (n > 0) {
670 		if (n > len)
671 		    n = len;
672 		XDrawImageString(disp, win, h_gc, x_tmp, y, str, n);
673 		x_tmp += n * w->textfield.char_w;
674 		str += n;
675 		pos += n;
676 		len -= n;
677 	    }
678 
679 	    if (len > 0)
680 		XDrawString(disp, win, gc, x_tmp, y, str, len);
681 	}
682     }
683 }
684 
draw_pixels(TextFieldWidget w,long x0,long y0,long width,long height,int clear)685 static void draw_pixels(TextFieldWidget w, long x0, long y0,
686 			long width, long height, int clear)
687 {
688     long	tmp;
689 
690     tmp = w->shadow.shadow_width + w->textfield.internal_width;
691     x0 -= tmp;
692     width += x0 + w->textfield.char_w - 1;
693     if (x0 < 0)
694 	x0 = 0;
695     else
696 	x0 /= w->textfield.char_w;
697     width /= w->textfield.char_w;
698     width -= x0;
699     if (width <= 0)
700 	return;
701 
702     tmp = w->shadow.shadow_width + w->textfield.internal_height;
703     y0 -= tmp;
704     height += y0 + w->textfield.char_h - 1;
705     if (y0 < 0)
706 	y0 = 0;
707     else
708 	y0 /= w->textfield.char_h;
709     height /= w->textfield.char_h;
710     height -= y0;
711     if (height <= 0)
712 	return;
713 
714     x0 += w->scrollable.pos_x;
715     y0 += w->scrollable.pos_y;
716     draw_rectangle(w, x0, y0, width, height, clear);
717 }
718 
draw_caret(TextFieldWidget w)719 static void draw_caret(TextFieldWidget w)
720 {
721     long	x, y, tmp;
722 
723     if (!w->textfield.display_caret ||
724 	(w->textfield.sel_set &&
725 	 (w->textfield.sel_start_x != w->textfield.sel_stop_x ||
726 	  w->textfield.sel_start_y != w->textfield.sel_stop_y)))
727 	return;
728 
729     x = w->shadow.shadow_width + w->textfield.internal_width;
730     y = w->shadow.shadow_width + w->textfield.internal_height;
731 
732     tmp = w->textfield.caret_x - w->scrollable.pos_x;
733     if (tmp < 0 || tmp >= w->scrollable.shown_x)
734 	return;
735     x += tmp * w->textfield.char_w;
736 
737     tmp = w->textfield.caret_y - w->scrollable.pos_y;
738     if (tmp < 0 || tmp >= w->scrollable.shown_y)
739 	return;
740     y += tmp * w->textfield.char_h;
741 
742     XDrawLine(XtDisplay(w), XtWindow(w), w->textfield.gc,
743 	      x, y, x, y + w->textfield.char_h - 1);
744 }
745 
undraw_caret(TextFieldWidget w)746 static void undraw_caret(TextFieldWidget w)
747 {
748     if (!w->textfield.display_caret ||
749 	(w->textfield.sel_set &&
750 	 (w->textfield.sel_start_x != w->textfield.sel_stop_x ||
751 	  w->textfield.sel_start_y != w->textfield.sel_stop_y)))
752 	return;
753 
754     draw_rectangle(w, w->textfield.caret_x,  w->textfield.caret_y, 1, 1, True);
755 }
756 
make_visible(TextFieldWidget w,long x,long y)757 static void make_visible(TextFieldWidget w, long x, long y)
758 {
759     if (x < w->scrollable.pos_x)
760 	ScrollableSetHPos((Widget)w, x);
761     else if (x >= w->scrollable.pos_x + w->scrollable.shown_x)
762 	ScrollableSetHPos((Widget)w, x - w->scrollable.shown_x + 1);
763     if (y < w->scrollable.pos_y)
764 	ScrollableSetVPos((Widget)w, y);
765     else if (y >= w->scrollable.pos_y + w->scrollable.shown_y)
766 	ScrollableSetVPos((Widget)w, y - w->scrollable.shown_y + 1);
767 }
768 
order_sel(TextFieldWidget w)769 static void order_sel(TextFieldWidget w)
770 {
771     long	tmp;
772 
773 #define SWAP(a, b) (tmp = (a), (a) = (b), (b) = tmp)
774     if (w->textfield.sel_start_y == w->textfield.sel_stop_y &&
775 	w->textfield.sel_start_x > w->textfield.sel_stop_x)
776 	SWAP(w->textfield.sel_start_x, w->textfield.sel_stop_x);
777     else if (w->textfield.sel_start_y > w->textfield.sel_stop_y) {
778 	SWAP(w->textfield.sel_start_x, w->textfield.sel_stop_x);
779 	SWAP(w->textfield.sel_start_y, w->textfield.sel_stop_y);
780     }
781 #undef SWAP
782 }
783 
invalidate_selection(TextFieldWidget w)784 static void invalidate_selection(TextFieldWidget w)
785 {
786     long	start = w->textfield.sel_start_y;
787     long	stop  = w->textfield.sel_stop_y;
788     long	width = w->scrollable.width;
789 
790     if (w->textfield.sel_set) {
791 	w->textfield.sel_set = False;
792 	draw_rectangle(w, 0, start, width, stop - start + 1, True);
793     }
794     if (w->textfield.curr_sel != None) {
795         XtDisownSelection((Widget)w,
796 			  w->textfield.curr_sel,
797 			  w->textfield.sel_time);
798 	w->textfield.curr_sel = None;
799     }
800 }
801 
change_pos(TextFieldWidget w,long caret_x,long caret_y)802 static void change_pos(TextFieldWidget w, long caret_x, long caret_y)
803 {
804     long	n;
805 
806     w->textfield.waiting_for_sel = False;
807     w->textfield.multiply = 1;
808 
809     if (w->scrollable.height <= 0)
810 	return;
811 
812     if (caret_y < 0)
813 	caret_y = 0;
814     if (caret_y >= w->scrollable.height)
815 	caret_y = w->scrollable.height - 1;
816 
817     if (caret_x < 0)
818 	caret_x = 0;
819     n = strlen(w->textfield.lines[caret_y].buf);
820     if (caret_x > n)
821 	caret_x = n;
822 
823     if (w->textfield.sel_set) {
824 	long	start_x = w->textfield.sel_start_x;
825 	long	start_y = w->textfield.sel_start_y;
826 	long	stop_x  = w->textfield.sel_stop_x;
827 	long	stop_y  = w->textfield.sel_stop_y;
828 	long	o_x     = w->textfield.caret_x;
829 	long	o_y     = w->textfield.caret_y;
830 	int	extend_end;
831 
832 	extend_end = (o_x == stop_x && o_y == stop_y);
833 	if (!extend_end && (o_x != start_x || o_y != start_y)) /* wierd... */
834 	    invalidate_selection(w);
835 	else if (caret_y == o_y) {
836 	    if (extend_end)
837 		w->textfield.sel_stop_x  = caret_x;
838 	    else
839 		w->textfield.sel_start_x = caret_x;
840 	    order_sel(w);
841 
842 	    if (w->textfield.sel_start_x == start_x) {
843 		long	diff = w->textfield.sel_stop_x - stop_x;
844 
845 		if (diff < 0)
846 		    draw_rectangle(w, w->textfield.sel_stop_x, o_y,
847 				   - diff, 1, True);
848 		else if (diff > 0)
849 		    draw_rectangle(w, stop_x, o_y, diff, 1, False);
850 	    } else if (w->textfield.sel_stop_x == stop_x) {
851 		long	diff = w->textfield.sel_start_x - start_x;
852 
853 		if (diff > 0)
854 		    draw_rectangle(w, start_x, o_y, diff, 1, True);
855 		else if (diff < 0)
856 		    draw_rectangle(w, w->textfield.sel_start_x, o_y,
857 				   - diff, 1, False);
858 	    } else {
859 		if (start_x > w->textfield.sel_start_x)
860 		    start_x = w->textfield.sel_start_x;
861 		if (stop_x < w->textfield.sel_stop_x)
862 		    stop_x = w->textfield.sel_stop_x;
863 		draw_rectangle(w, start_x, o_y, stop_x - start_x + 1, 1, True);
864 	    }
865 	} else {
866 	    long	width = w->scrollable.width;
867 
868 	    if (extend_end) {
869 		w->textfield.sel_stop_x = caret_x;
870 		w->textfield.sel_stop_y = caret_y;
871 	    } else {
872 		w->textfield.sel_start_x = caret_x;
873 		w->textfield.sel_start_y = caret_y;
874 	    }
875 	    order_sel(w);
876 
877 	    if (w->textfield.sel_start_x == start_x &&
878 		w->textfield.sel_start_y == start_y) {
879 		long	diff = w->textfield.sel_stop_y - stop_y;
880 
881 		if (diff < 0)
882 		    draw_rectangle(w, 0, w->textfield.sel_stop_y,
883 				   width, - diff + 1, True);
884 		else if (diff > 0)
885 		    draw_rectangle(w, 0, stop_y, width, diff + 1, False);
886 	    } else if (w->textfield.sel_stop_x == stop_x &&
887 		       w->textfield.sel_stop_y == stop_y) {
888 		long	diff = w->textfield.sel_start_y - start_y;
889 
890 		if (diff > 0)
891 		    draw_rectangle(w, 0, start_y, width, diff + 1, True);
892 		else if (diff < 0)
893 		    draw_rectangle(w, 0, w->textfield.sel_start_y,
894 				   width, - diff + 1, False);
895 	    } else {
896 		if (start_y > w->textfield.sel_start_y)
897 		    start_y = w->textfield.sel_start_y;
898 		if (stop_y < w->textfield.sel_stop_y)
899 		    stop_y = w->textfield.sel_stop_y;
900 		draw_rectangle(w, 0, start_y, w->scrollable.width,
901 			       stop_y - start_y + 1, True);
902 	    }
903 	}
904     }
905 
906     undraw_caret(w);
907     w->textfield.caret_x = caret_x;
908     w->textfield.caret_y = caret_y;
909     draw_caret(w);
910     make_visible(w, caret_x, caret_y);
911 }
912 
merge_line_with_next(TextFieldWidget w,long line)913 static void merge_line_with_next(TextFieldWidget w, long line)
914 {
915     char	*c;
916     long	pos, len, n;
917 
918     if (line < 0 || line >= w->scrollable.height - 1)
919 	return;
920 
921     n = --w->scrollable.height;
922     c = w->textfield.lines[line + 1].buf;
923     if (line < n)
924 	memmove(w->textfield.lines + line + 1,
925 		w->textfield.lines + line + 2,
926 		(n - line) * sizeof w->textfield.lines[0]);
927     ScrollableFitVBar((ScrollableWidget)w);
928     pos = strlen(w->textfield.lines[line].buf);
929     len = strlen(c);
930     sync_width(w, pos, pos + len);
931 
932     if (pos + len + 8 < w->textfield.lines[line].len)
933 	w->textfield.lines[line].buf =
934 	    XtRealloc(w->textfield.lines[line].buf,
935 		      (w->textfield.lines[line].len = pos + len + 8));
936 
937     strcpy(w->textfield.lines[line].buf + pos, c);
938     XtFree(c);
939     draw_rectangle(w, pos, line, len, 1, False);
940     draw_rectangle(w, 0, line + 1, w->scrollable.width,
941 		   w->scrollable.height, True);
942     change_pos(w, pos, line);
943 }
944 
945 /*************************************************************************/
946 
aquire_focus(Widget gw,XEvent * event,String * params,Cardinal * no_params)947 static void aquire_focus(Widget gw, XEvent *event,
948 			 String *params, Cardinal *no_params)
949 {
950     TextFieldWidget	w = (TextFieldWidget)gw;
951     XtCallbackList	c_list = w->textfield.focus_callback;
952 
953     if (!w->textfield.focus_root || !w->textfield.active)
954 	return;
955 
956     XtSetKeyboardFocus(w->textfield.focus_root, (Widget)w);
957     if (c_list)
958 	XtCallCallbackList((Widget)w, c_list, NULL);
959 }
960 
nop(Widget gw,XEvent * event,String * params,Cardinal * no_params)961 static void nop(Widget gw, XEvent *event, String *params, Cardinal *no_params)
962 {
963     TextFieldWidget	w = (TextFieldWidget)gw;
964 
965     w->textfield.multiply = 1;
966     w->textfield.waiting_for_sel = False;
967 }
968 
multiply(Widget gw,XEvent * event,String * params,Cardinal * no_params)969 static void multiply(Widget gw, XEvent *event,
970 		     String *params, Cardinal *no_params)
971 {
972     TextFieldWidget	w = (TextFieldWidget)gw;
973     long		factor = *no_params == 1 ? atol(params[0]) : 4;
974     long		new_mult = w->textfield.multiply * factor;
975 
976     if (new_mult <= 0 || new_mult > 16384)
977 	new_mult = 1;
978     w->textfield.multiply = new_mult;
979     w->textfield.waiting_for_sel = False;
980 }
981 
beginning_of_line(Widget gw,XEvent * event,String * params,Cardinal * no_params)982 static void beginning_of_line(Widget gw, XEvent *event,
983 			      String *params, Cardinal *no_params)
984 {
985     TextFieldWidget	w = (TextFieldWidget)gw;
986 
987     change_pos(w, 0, w->textfield.caret_y);
988 }
989 
end_of_line(Widget gw,XEvent * event,String * params,Cardinal * no_params)990 static void end_of_line(Widget gw, XEvent *event,
991 			String *params, Cardinal *no_params)
992 {
993     TextFieldWidget	w = (TextFieldWidget)gw;
994 
995     change_pos(w, w->scrollable.width, w->textfield.caret_y);
996 }
997 
home(Widget gw,XEvent * event,String * params,Cardinal * no_params)998 static void home(Widget gw, XEvent *event, String *params, Cardinal *no_params)
999 {
1000     TextFieldWidget	w = (TextFieldWidget)gw;
1001 
1002     change_pos(w, 0, 0);
1003 }
1004 
end(Widget gw,XEvent * event,String * params,Cardinal * no_params)1005 static void end(Widget gw, XEvent *event, String *params, Cardinal *no_params)
1006 {
1007     TextFieldWidget	w = (TextFieldWidget)gw;
1008 
1009     change_pos(w, w->scrollable.width, w->scrollable.height);
1010 }
1011 
left(Widget gw,XEvent * event,String * params,Cardinal * no_params)1012 static void left(Widget gw, XEvent *event, String *params, Cardinal *no_params)
1013 {
1014     TextFieldWidget	w = (TextFieldWidget)gw;
1015 
1016     change_pos(w, w->textfield.caret_x - w->textfield.multiply,
1017 	       w->textfield.caret_y);
1018 }
1019 
right(Widget gw,XEvent * event,String * params,Cardinal * no_params)1020 static void right(Widget gw, XEvent *event,
1021 		  String *params, Cardinal *no_params)
1022 {
1023     TextFieldWidget	w = (TextFieldWidget)gw;
1024 
1025     change_pos(w, w->textfield.caret_x + w->textfield.multiply,
1026 	       w->textfield.caret_y);
1027 }
1028 
up(Widget gw,XEvent * event,String * params,Cardinal * no_params)1029 static void up(Widget gw, XEvent *event, String *params, Cardinal *no_params)
1030 {
1031     TextFieldWidget	w = (TextFieldWidget)gw;
1032 
1033     change_pos(w, w->textfield.caret_x,
1034 	       w->textfield.caret_y - w->textfield.multiply);
1035 }
1036 
down(Widget gw,XEvent * event,String * params,Cardinal * no_params)1037 static void down(Widget gw, XEvent *event, String *params, Cardinal *no_params)
1038 {
1039     TextFieldWidget	w = (TextFieldWidget)gw;
1040 
1041     change_pos(w, w->textfield.caret_x,
1042 	       w->textfield.caret_y + w->textfield.multiply);
1043 }
1044 
page(Widget gw,XEvent * event,String * params,Cardinal * no_params)1045 static void page(Widget gw, XEvent *event, String *params, Cardinal *no_params)
1046 {
1047     TextFieldWidget	w = (TextFieldWidget)gw;
1048     float		amount;
1049 
1050     if (*no_params == 1 && sscanf(params[0], "%f", &amount) == 1)
1051 	change_pos(w, w->textfield.caret_x,
1052 		   w->textfield.caret_y +
1053 		   w->scrollable.shown_y * amount * w->textfield.multiply);
1054 }
1055 
delete_next(Widget gw,XEvent * event,String * params,Cardinal * no_params)1056 static void delete_next(Widget gw, XEvent *event,
1057 			String *params, Cardinal *no_params)
1058 {
1059     TextFieldWidget	w = (TextFieldWidget)gw;
1060     long		m = w->textfield.multiply;
1061     long		line = w->textfield.caret_y;
1062     long		pos  = w->textfield.caret_x;
1063     char		*c;
1064     long		n;
1065 
1066     w->textfield.waiting_for_sel = False;
1067     w->textfield.multiply = 1;
1068     if (w->textfield.sel_set)
1069 	invalidate_selection(w);
1070     if (pos < 0 || line < 0 || line >= w->scrollable.height)
1071 	return;
1072 
1073     c = w->textfield.lines[line].buf;
1074     n = strlen(c);
1075     if (pos >= n)
1076 	merge_line_with_next(w, line);
1077     else {
1078 	clear_segment(w, line, pos, n);
1079 	if (m > n - pos)
1080 	    m = n - pos;
1081 	n -= m;
1082 	memmove(c + pos, c + pos + m, n - pos + 1);
1083 	draw_rectangle(w, pos, line, n - pos, 1, False);
1084 	draw_caret(w);
1085 	sync_width(w, n + m, n);
1086     }
1087 }
1088 
delete_previous(Widget gw,XEvent * event,String * params,Cardinal * no_params)1089 static void delete_previous(Widget gw, XEvent *event,
1090 			    String *params, Cardinal *no_params)
1091 {
1092     TextFieldWidget	w = (TextFieldWidget)gw;
1093     long		m = w->textfield.multiply;
1094     long		line = w->textfield.caret_y;
1095     long		pos  = w->textfield.caret_x;
1096     char		*c;
1097     long		n;
1098 
1099     w->textfield.waiting_for_sel = False;
1100     w->textfield.multiply = 1;
1101     if (w->textfield.sel_set)
1102 	invalidate_selection(w);
1103     if (pos < 0 || line < 0 || line >= w->scrollable.height)
1104 	return;
1105 
1106     c = w->textfield.lines[line].buf;
1107     n = strlen(c);
1108     if (pos > n)
1109 	return;
1110 
1111     if (pos == 0)
1112 	merge_line_with_next(w, line - 1);
1113     else {
1114 	if (m > pos)
1115 	    m = pos;
1116 	clear_segment(w, line, pos - m, n);
1117 	memmove(c + pos - m, c + pos, n - pos);
1118 	c[n - m] = '\0';
1119 	draw_rectangle(w, pos - m, line, n - pos + 1, 1, False);
1120 	change_pos(w, pos - m, w->textfield.caret_y);
1121 	sync_width(w, n, n - m);
1122     }
1123 }
1124 
kill_action(Widget gw,XEvent * event,String * params,Cardinal * no_params)1125 static void kill_action(Widget gw, XEvent *event,
1126 			String *params, Cardinal *no_params)
1127 {
1128     TextFieldWidget	w = (TextFieldWidget)gw;
1129     long		line = w->textfield.caret_y;
1130     long		pos = w->textfield.caret_x;
1131     char		*c;
1132     long		n;
1133 
1134     w->textfield.waiting_for_sel = False;
1135     w->textfield.multiply = 1;
1136     if (w->textfield.sel_set)
1137 	invalidate_selection(w);
1138     if (line < 0 || line >= w->scrollable.height)
1139 	return;
1140 
1141     c = w->textfield.lines[line].buf;
1142     n = strlen(c);
1143     if (pos >= n)
1144 	merge_line_with_next(w, line);
1145     else {
1146 	c[pos] = '\0';
1147 	clear_segment(w, line, pos, n - pos);
1148 	draw_caret(w);
1149 	sync_width(w, n, pos);
1150     }
1151 }
1152 
redraw(Widget gw,XEvent * event,String * params,Cardinal * no_params)1153 static void redraw(Widget gw, XEvent *event,
1154 		   String *params, Cardinal *no_params)
1155 {
1156     TextFieldWidget	w = (TextFieldWidget)gw;
1157 
1158     XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True);
1159     w->textfield.waiting_for_sel = False;
1160     w->textfield.multiply = 1;
1161 }
1162 
enter(Widget gw,XEvent * event,String * params,Cardinal * no_params)1163 static void enter(Widget gw, XEvent *event,
1164 		  String *params, Cardinal *no_params)
1165 {
1166     TextFieldWidget	w = (TextFieldWidget)gw;
1167     long		m = w->textfield.multiply;
1168     long		line = w->textfield.caret_y;
1169     long		pos  = w->textfield.caret_x;
1170     char		*c;
1171     long		len, n;
1172 
1173     w->textfield.waiting_for_sel = False;
1174     w->textfield.multiply = 1;
1175     if (w->textfield.sel_set)
1176 	invalidate_selection(w);
1177 
1178     call_callbacks(w, w->textfield.callback);
1179     if (w->textfield.single_line || line < 0 || line >= w->scrollable.height)
1180 	return;
1181 
1182     open_lines(w, line, m);
1183 
1184     c = w->textfield.lines[line + m].buf;
1185     len = strlen(c);
1186     if (pos > len)
1187 	pos = len;
1188     w->textfield.lines[line].len = pos + 1;
1189     XtFree(w->textfield.lines[line].buf);
1190     w->textfield.lines[line].buf = memcpy(XtMalloc(pos + 1), c, pos);
1191     w->textfield.lines[line].buf[pos] = '\0';
1192 
1193     n = len - pos;
1194     if (n > 0)
1195 	memmove(c, c + pos, n);
1196     c[n] = '\0';
1197     if (n < pos)
1198 	n = pos;
1199 
1200     draw_rectangle(w, 0, line, w->scrollable.width,
1201 		   w->scrollable.height, True);
1202     sync_width(w, len, n);  /* Don't sync before clear... */
1203     change_pos(w, 0, line + m);
1204 }
1205 
tab(Widget gw,XEvent * event,String * params,Cardinal * no_params)1206 static void tab(Widget gw, XEvent *event, String *params, Cardinal *no_params)
1207 {
1208     TextFieldWidget	w = (TextFieldWidget)gw;
1209 
1210     w->textfield.waiting_for_sel = False;
1211     w->textfield.multiply = 1;
1212     call_callbacks(w, w->textfield.tab_callback);
1213 }
1214 
transpose(Widget gw,XEvent * event,String * params,Cardinal * no_params)1215 static void transpose(Widget gw, XEvent *event,
1216 		      String *params, Cardinal *no_params)
1217 {
1218     TextFieldWidget	w = (TextFieldWidget)gw;
1219     long		line = w->textfield.caret_y;
1220     long		pos = w->textfield.caret_x;
1221     long		m = w->textfield.multiply;
1222     char		*c;
1223     long		len;
1224     int			ch;
1225 
1226     w->textfield.waiting_for_sel = False;
1227     w->textfield.multiply = 1;
1228     if (w->textfield.sel_set)
1229 	invalidate_selection(w);
1230     if (line < 0 || line >= w->scrollable.height)
1231 	return;
1232 
1233     c = w->textfield.lines[line].buf;
1234     len = strlen(c);
1235     if (pos <= 0 || pos >= len)
1236 	return;
1237 
1238     if (pos + m >= len)
1239 	m = len - pos;
1240 
1241     ch = c[pos - 1];
1242     memmove(c + pos - 1, c + pos, m);
1243     c[pos - 1 + m] = ch;
1244     draw_rectangle(w, pos - 1, line, m + 1, 1, True);
1245     change_pos(w, pos + m, line);
1246 }
1247 
set_border_color(Widget gw,XEvent * event,String * params,Cardinal * no_params)1248 static void set_border_color(Widget gw, XEvent *event,
1249 			     String *params, Cardinal *no_params)
1250 {
1251     TextFieldWidget	w = (TextFieldWidget)gw;
1252     Arg			arg;
1253 
1254     if (*no_params != 1) {
1255         XBell(XtDisplay(w), 0);
1256         return;
1257     }
1258 
1259     if (w->textfield.print_focus && (event->type == FocusIn ||
1260 				     event->type == FocusOut)) {
1261 	fprintf(stderr,
1262 		"Focus%s : %s\n"
1263 		"          serial:     %ld\n"
1264 		"          send_event: %s\n"
1265 		"          window:     0x%lx\n"
1266 		"          mode:       ",
1267 		event->type == FocusIn ? "In " : "Out",
1268 		XtName((Widget)w),
1269 		event->xfocus.serial,
1270                 event->xfocus.send_event ? "True" : "False",
1271 		event->xfocus.window);
1272         switch (event->xfocus.mode) {
1273         case NotifyNormal:
1274             fputs("NotifyNormal\n", stderr);
1275             break;
1276         case NotifyGrab:
1277             fputs("NotifyGrab\n", stderr);
1278             break;
1279         case NotifyUngrab:
1280             fputs("NotifyUngrab\n", stderr);
1281             break;
1282         default:
1283             fputc('\n', stderr);
1284             break;
1285         }
1286         fputs("          detail:     ", stderr);
1287         switch (event->xfocus.detail) {
1288         case NotifyAncestor:
1289             fputs("NotifyAncestor\n", stderr);
1290             break;
1291         case NotifyVirtual:
1292             fputs("NotifyVirtual\n", stderr);
1293             break;
1294         case NotifyInferior:
1295             fputs("NotifyInferior\n", stderr);
1296             break;
1297         case NotifyNonlinear:
1298             fputs("NotifyNonlinear\n", stderr);
1299             break;
1300         case NotifyNonlinearVirtual:
1301             fputs("NotifyNonlinearVirtual\n", stderr);
1302             break;
1303         case NotifyPointer:
1304             fputs("NotifyPointer\n", stderr);
1305             break;
1306         case NotifyPointerRoot:
1307             fputs("NotifyPointerRoot\n", stderr);
1308             break;
1309         case NotifyDetailNone:
1310             fputs("NotifyDetailNone\n", stderr);
1311             break;
1312         default:
1313             fputc('\n', stderr);
1314             break;
1315         }
1316     }
1317 
1318     if (w->textfield.focus_hack &&
1319 	event->type == FocusIn &&
1320 	!event->xfocus.send_event)
1321         return;
1322 
1323     if (strlen(params[0]) < 15) {
1324         char	buffer[16];
1325         char	*c;
1326         long	i;
1327 
1328         c = params[0];
1329         i = 0;
1330         do {
1331             buffer[i++] =
1332                 isupper((unsigned char)*c) ? tolower((unsigned char)*c) : *c;
1333         } while (*c++ != '\0');
1334 
1335         if (strcmp(buffer, XtNbackground) == 0) {
1336             XtSetArg(arg, XtNborderColor, w->core.background_pixel);
1337             XtSetValues((Widget)w, &arg, 1);
1338             return;
1339         } else if (strcmp(buffer, XtNforeground) == 0) {
1340             XtSetArg(arg, XtNborderColor, w->textfield.fg_pixel);
1341             XtSetValues((Widget)w, &arg, 1);
1342             return;
1343         } else if (strcmp(buffer, "focuscolor") == 0) {
1344             XtSetArg(arg, XtNborderColor, w->textfield.focus_pixel);
1345             XtSetValues((Widget)w, &arg, 1);
1346             return;
1347         }
1348     }
1349 
1350     XBell(XtDisplay(w), 0);
1351 }
1352 
insert(Widget gw,XEvent * event,String * params,Cardinal * no_params)1353 static void insert(Widget gw, XEvent *event,
1354 		   String *params, Cardinal *no_params)
1355 {
1356     TextFieldWidget	w = (TextFieldWidget)gw;
1357     char		buffer[16];
1358     String		param;
1359     Cardinal		n_params;
1360     KeySym		keysym;
1361     long		len;
1362 
1363     if (!event || (event->type != KeyPress && event->type != KeyRelease))
1364 	return;
1365 
1366     len = XLookupString(&event->xkey, buffer, sizeof buffer, &keysym, NULL);
1367     if (len <= 0)
1368 	return;
1369     buffer[len] = '\0';
1370     param = buffer;
1371     n_params = 1;
1372 
1373     insert_string((Widget)w, event, &param, &n_params);
1374 }
1375 
insert_string(Widget gw,XEvent * event,String * params,Cardinal * no_params)1376 static void insert_string(Widget gw, XEvent *event,
1377 			  String *params, Cardinal *no_params)
1378 {
1379     TextFieldWidget	w = (TextFieldWidget)gw;
1380     char		*c;
1381     long		m = w->textfield.multiply;
1382     long		line = w->textfield.caret_y;
1383     long		pos = w->textfield.caret_x;
1384     long		n_pos, n, len;
1385 
1386     w->textfield.waiting_for_sel = False;
1387     w->textfield.multiply = 1;
1388     if (w->textfield.sel_set)
1389 	invalidate_selection(w);
1390 
1391     if (*no_params != 1 || line < 0 || line >= w->scrollable.height)
1392 	return;
1393 
1394     n = strlen(params[0]);
1395     if (n <= 0 || XTextWidth(w->textfield.font, params[0], n) <= 0)
1396 	return;
1397 
1398     len = strlen(w->textfield.lines[line].buf);
1399     if (pos > len)
1400 	pos = len;
1401     n_pos = pos + m * n;
1402     if (len + m * n + 8 > w->textfield.lines[line].len)
1403 	w->textfield.lines[line].buf =
1404 	    XtRealloc(w->textfield.lines[line].buf,
1405 		      (w->textfield.lines[line].len = len + m * n + 8));
1406     sync_width(w, len, len + m * n);
1407     c = w->textfield.lines[line].buf + pos;
1408     memmove(c + m * n, c, len - pos + 1);
1409     while (m-- > 0) {
1410 	memcpy(c, params[0], n);
1411 	c += n;
1412     }
1413     draw_rectangle(w, pos, line, w->scrollable.width, 1, True);
1414     change_pos(w, n_pos, w->textfield.caret_y);
1415 }
1416 
swap_select(Widget gw,XEvent * event,String * params,Cardinal * no_params)1417 static void swap_select(Widget gw, XEvent *event,
1418 			String *params, Cardinal *no_params)
1419 {
1420     TextFieldWidget	w = (TextFieldWidget)gw;
1421     Atom		atom = XA_PRIMARY;
1422     Time		time = get_event_time(event);
1423     long		o_caret_x = w->textfield.caret_x;
1424     long		o_caret_y = w->textfield.caret_y;
1425 
1426     w->textfield.waiting_for_sel = False;
1427     w->textfield.multiply = 1;
1428     if (w->textfield.sel_set)
1429 	invalidate_selection(w);
1430 
1431     change_pos(w, w->textfield.mark_x, w->textfield.mark_y);
1432     w->textfield.sel_start_x = w->textfield.caret_x;
1433     w->textfield.sel_start_y = w->textfield.caret_y;
1434     w->textfield.mark_x = w->textfield.sel_stop_x = o_caret_x;
1435     w->textfield.mark_y = w->textfield.sel_stop_y = o_caret_y;
1436     order_sel(w);
1437 
1438     if (*no_params > 0 && strcmp(params[0], "PRIMARY") != 0)
1439 	atom = intern_atom(XtDisplay(w), params[0]);
1440 
1441     if (XtOwnSelection((Widget)w, atom, time,
1442 		       convert_sel_proc, lose_sel_proc, NULL)) {
1443 	long	y, height;
1444 
1445 	w->textfield.curr_sel = atom;
1446 	w->textfield.sel_time = time;
1447 	w->textfield.sel_set = True;
1448 	height = w->textfield.sel_stop_y - w->textfield.sel_start_y + 1;
1449 	y = w->textfield.sel_start_y;
1450 	undraw_caret(w);
1451 	draw_rectangle(w, 0, y, w->scrollable.width, height, False);
1452     }
1453 }
1454 
select_start(Widget gw,XEvent * event,String * params,Cardinal * no_params)1455 static void select_start(Widget gw, XEvent *event,
1456 			 String *params, Cardinal *no_params)
1457 {
1458     TextFieldWidget	w = (TextFieldWidget)gw;
1459     long		x, y;
1460 
1461     w->textfield.waiting_for_sel = False;
1462     w->textfield.multiply = 1;
1463     if (w->textfield.sel_set)
1464 	invalidate_selection(w);
1465 
1466     if (event->type == ButtonPress || event->type == ButtonRelease) {
1467 	if (!event_to_pos(w, event, &x, &y))
1468 	    return;
1469 	change_pos(w, x, y);
1470     }
1471 
1472     w->textfield.sel_start_x = w->textfield.sel_stop_x =
1473 	w->textfield.mark_x = w->textfield.caret_x;
1474     w->textfield.sel_start_y = w->textfield.sel_stop_y =
1475 	w->textfield.mark_y = w->textfield.caret_y;
1476     w->textfield.sel_set = True;
1477 }
1478 
extend_start(Widget gw,XEvent * event,String * params,Cardinal * no_params)1479 static void extend_start(Widget gw, XEvent *event,
1480 			 String *params, Cardinal *no_params)
1481 {
1482     TextFieldWidget	w = (TextFieldWidget)gw;
1483     long		x, y, n;
1484     int			extend_end;
1485 
1486     if (w->scrollable.height <= 0)
1487 	return;
1488 
1489     w->textfield.waiting_for_sel = False;
1490     w->textfield.multiply = 1;
1491 
1492     if (!w->textfield.sel_set) {
1493 	select_start((Widget)w, event, params, no_params);
1494 	return;
1495     }
1496 
1497     if (!event_to_pos(w, event, &x, &y))
1498 	return;
1499 
1500     if (w->textfield.sel_start_y == w->textfield.sel_stop_y)
1501 	if (y < w->textfield.sel_start_y)
1502 	    extend_end = False;
1503 	else if (y > w->textfield.sel_start_y)
1504 	    extend_end = True;
1505 	else if (2 * x < w->textfield.sel_start_x + w->textfield.sel_stop_x)
1506 	    extend_end = False;
1507 	else
1508 	    extend_end = True;
1509     else if (y <= w->textfield.sel_start_y)
1510 	extend_end = False;
1511     else if (y >= w->textfield.sel_stop_y)
1512 	extend_end = True;
1513     else if (2 * y < w->textfield.sel_start_y + w->textfield.sel_stop_y)
1514 	extend_end = False;
1515     else
1516 	extend_end = True;
1517 
1518     if (y >= w->scrollable.height)
1519 	y = w->scrollable.height - 1;
1520     if (y < 0)
1521 	y = 0;
1522     n = strlen(w->textfield.lines[y].buf);
1523     if (x > n)
1524 	x = n;
1525     if (x < 0)
1526 	x = 0;
1527 
1528     invalidate_selection(w);
1529     undraw_caret(w);
1530     if (extend_end) {
1531 	w->textfield.sel_stop_x = x;
1532 	w->textfield.sel_stop_y = y;
1533     } else {
1534 	w->textfield.sel_start_x = x;
1535 	w->textfield.sel_start_y = y;
1536     }
1537     w->textfield.sel_set = True;
1538     w->textfield.caret_x = x;
1539     w->textfield.caret_y = y;
1540     y = w->textfield.sel_start_y;
1541     n = w->textfield.sel_stop_y - y + 1;
1542     draw_rectangle(w, 0, y, w->scrollable.width, n, True);
1543     draw_caret(w);
1544 }
1545 
select_extend(Widget gw,XEvent * event,String * params,Cardinal * no_params)1546 static void select_extend(Widget gw, XEvent *event,
1547 			  String *params, Cardinal *no_params)
1548 {
1549     TextFieldWidget	w = (TextFieldWidget)gw;
1550     long		x, y;
1551 
1552     w->textfield.waiting_for_sel = False;
1553     w->textfield.multiply = 1;
1554     if (!w->textfield.sel_set)
1555 	return;
1556 
1557     if (!event_to_pos(w, event, &x, &y))
1558 	return;
1559 
1560     change_pos(w, x, y);
1561 }
1562 
select_end(Widget gw,XEvent * event,String * params,Cardinal * no_params)1563 static void select_end(Widget gw, XEvent *event,
1564 		       String *params, Cardinal *no_params)
1565 {
1566     TextFieldWidget	w = (TextFieldWidget)gw;
1567     Atom		atom = XA_PRIMARY;
1568     Time		time = get_event_time(event);
1569 
1570     w->textfield.waiting_for_sel = False;
1571     w->textfield.multiply = 1;
1572     if (!w->textfield.sel_set)
1573 	return;
1574 
1575     if (w->textfield.curr_sel != None)
1576 	XtDisownSelection((Widget)w, w->textfield.curr_sel,
1577 			  w->textfield.sel_time);
1578     w->textfield.curr_sel = None;
1579 
1580     if (w->textfield.sel_start_y == w->textfield.sel_stop_y &&
1581 	w->textfield.sel_start_x == w->textfield.sel_stop_x)
1582 	return;
1583 
1584     if (*no_params > 0 && strcmp(params[0], "PRIMARY") != 0)
1585 	atom = intern_atom(XtDisplay(w), params[0]);
1586 
1587     if (!XtOwnSelection((Widget)w, atom, time,
1588 			convert_sel_proc, lose_sel_proc, NULL)) {
1589 	invalidate_selection(w);
1590 	return;
1591     }
1592 
1593     w->textfield.curr_sel = atom;
1594     w->textfield.sel_time = time;
1595 }
1596 
kill_selection(Widget gw,XEvent * event,String * params,Cardinal * no_params)1597 static void kill_selection(Widget gw, XEvent *event,
1598 			   String *params, Cardinal *no_params)
1599 {
1600     TextFieldWidget	w = (TextFieldWidget)gw;
1601     Display		*disp = XtDisplay(w);
1602     Atom		atom = XA_PRIMARY;
1603     Time		time = get_event_time(event);
1604 
1605     w->textfield.multiply = 1;
1606 
1607     if (*no_params > 0 && strcmp(params[0], "PRIMARY") != 0)
1608 	atom = intern_atom(disp, params[0]);
1609 
1610     w->textfield.waiting_for_sel = True;
1611     XtGetSelectionValue((Widget)w, atom, intern_atom(disp, "DELETE"),
1612 			delete_sel_callback, NULL, time);
1613 }
1614 
insert_selection(Widget gw,XEvent * event,String * params,Cardinal * no_params)1615 static void insert_selection(Widget gw, XEvent *event,
1616 			     String *params, Cardinal *no_params)
1617 {
1618     TextFieldWidget	w = (TextFieldWidget)gw;
1619     Display		*disp = XtDisplay(w);
1620     Atom		atom = XA_PRIMARY;
1621     Time		time = get_event_time(event);
1622     long		x, y;
1623 
1624     w->textfield.waiting_for_sel = False;
1625     w->textfield.multiply = 1;
1626 
1627     if (!event_to_pos(w, event, &x, &y))
1628 	return;
1629 
1630     change_pos(w, x, y);
1631     w->textfield.waiting_for_sel = True;
1632     if (*no_params > 0 && strcmp(params[0], "PRIMARY") != 0)
1633 	atom = intern_atom(disp, params[0]);
1634     XtGetSelectionValue((Widget)w, atom, XA_STRING,
1635 			insert_sel_callback, NULL, time);
1636 }
1637 
disown_selection(Widget gw,XEvent * event,String * params,Cardinal * no_params)1638 static void disown_selection(Widget gw, XEvent *event,
1639 			     String *params, Cardinal *no_params)
1640 {
1641     TextFieldWidget	w = (TextFieldWidget)gw;
1642 
1643     w->textfield.waiting_for_sel = False;
1644     w->textfield.multiply = 1;
1645     if (w->textfield.sel_set)
1646 	invalidate_selection(w);
1647     draw_rectangle(w, w->scrollable.pos_x, w->scrollable.pos_y,
1648 		   w->scrollable.shown_x, w->scrollable.shown_y, True);
1649     draw_caret(w);
1650 }
1651 
display_caret(Widget gw,XEvent * event,String * params,Cardinal * no_params)1652 static void display_caret(Widget gw, XEvent *event,
1653 			  String *params, Cardinal *no_params)
1654 {
1655     TextFieldWidget	w = (TextFieldWidget)gw;
1656 
1657     w->textfield.waiting_for_sel = False;
1658     w->textfield.multiply = 1;
1659 
1660     if (w->textfield.focus_hack &&
1661         event->type == FocusIn &&
1662         !event->xfocus.send_event)
1663         return;
1664 
1665     if (*no_params == 1 && strlen(params[0]) < 15) {
1666         char	buffer[16];
1667         char	*c;
1668         long	i;
1669 
1670         c = params[0];
1671         i = 0;
1672         do {
1673             buffer[i++] =
1674                 isupper((unsigned char)*c) ? tolower((unsigned char)*c) : *c;
1675         } while (*c++ != '\0');
1676 
1677         if (strcmp(buffer, "on") == 0 ||
1678 	    strcmp(buffer, "true") == 0) {
1679             w->textfield.display_caret = True;
1680             draw_caret(w);
1681             return;
1682         } else if (strcmp(buffer, "off") == 0 ||
1683                    strcmp(buffer, "false") == 0) {
1684             undraw_caret(w);
1685             w->textfield.display_caret = False;
1686             return;
1687         }
1688     }
1689 
1690     XBell(XtDisplay(w), 0);
1691 }
1692 
1693 /*************************************************************************/
1694 
Initialize(Widget grequest,Widget gnew,ArgList args,Cardinal * no_args)1695 static void Initialize(Widget grequest, Widget gnew,
1696 		       ArgList args, Cardinal *no_args)
1697 {
1698     TextFieldWidget	new = (TextFieldWidget)gnew;
1699     Dimension		width, height;
1700 
1701     init_gcs(new);
1702 
1703     get_char_sizes(new);
1704     get_preferred_sizes(new, &width, &height);
1705     if (new->core.width == 0)
1706 	new->core.width  = width;
1707     if (new->core.height == 0)
1708 	new->core.height = height;
1709     calc_shown(new);
1710 
1711     new->textfield.waiting_for_sel = False;
1712     new->textfield.active          = True;
1713     new->textfield.sel_set         = False;
1714     new->textfield.curr_sel        = None;
1715     new->textfield.sel_time        = 0;
1716     new->textfield.sel_start_x     = 0;
1717     new->textfield.sel_start_y     = 0;
1718     new->textfield.sel_stop_x      = 0;
1719     new->textfield.sel_stop_y      = 0;
1720     new->textfield.multiply        = 1;
1721     new->textfield.caret_x         = 0;
1722     new->textfield.caret_y         = 0;
1723     new->textfield.mark_x          = 0;
1724     new->textfield.mark_y          = 0;
1725 
1726     new->textfield.lines = NULL;
1727     new->textfield.n_lines = 0;
1728     lines_from_buffer(new);
1729 
1730     if (new->textfield.single_line)
1731 	new->textfield.pref_lines = 1;
1732 
1733     new->core.border_pixel = new->core.background_pixel;
1734 }
1735 
Destroy(Widget gw)1736 static void Destroy(Widget gw)
1737 {
1738     TextFieldWidget	w = (TextFieldWidget)gw;
1739 
1740     if (w->textfield.curr_sel != None)
1741 	XtDisownSelection((Widget)w, w->textfield.curr_sel,
1742 			  w->textfield.sel_time);
1743     w->textfield.curr_sel = None;
1744     free_lines(w);
1745     free_gcs(w);
1746 }
1747 
Resize(Widget gw)1748 static void Resize(Widget gw)
1749 {
1750     TextFieldWidget	w = (TextFieldWidget)gw;
1751 
1752     calc_shown(w);
1753     if (!XtIsRealized((Widget)w))
1754 	return;
1755     make_visible(w, w->textfield.caret_x, w->textfield.caret_y);
1756     ScrollableFitHBar((ScrollableWidget)w);
1757     ScrollableFitVBar((ScrollableWidget)w);
1758 }
1759 
SetHPos(ScrollableWidget gw,long pos_x)1760 static void SetHPos(ScrollableWidget gw, long pos_x)
1761 {
1762     TextFieldWidget	w       = (TextFieldWidget)gw;
1763     Display		*disp   = XtDisplay(w);
1764     Window		win     = XtWindow(w);
1765     long		old     = w->scrollable.pos_x;
1766     long		pos_y   = w->scrollable.pos_y;
1767     long		shown_x = w->scrollable.shown_x;
1768     long		shown_y = w->scrollable.shown_y;
1769     long		diff;
1770 
1771     if (pos_x > w->scrollable.width - shown_x)
1772 	pos_x = w->scrollable.width - shown_x;
1773     if (pos_x < 0)
1774 	pos_x = 0;
1775 
1776     diff = pos_x - old;
1777     if (diff == 0)
1778 	return;
1779 
1780     undraw_caret(w);
1781     w->scrollable.pos_x = pos_x;
1782     if (diff <= - shown_x || diff >= shown_x)
1783 	draw_rectangle(w, pos_x, pos_y, shown_x, shown_y, True);
1784     else {
1785 	long	y0 = w->shadow.shadow_width + w->textfield.internal_height;
1786 	long	x0 = w->shadow.shadow_width + w->textfield.internal_width;
1787 	long	height = (long)w->core.height - 2 * y0;
1788 	long	width, xdiff;
1789 	GC	gc = w->textfield.gc;
1790 
1791 	if (height <= 0)
1792 	    return;
1793 
1794 	if (diff < 0) {
1795 	    diff = -diff;
1796 	    width = (shown_x - diff) * w->textfield.char_w;
1797 	    xdiff = diff * w->textfield.char_w;
1798 	    XCopyArea(disp, win, win, gc,
1799 		      x0, y0, width, height, x0 + xdiff, y0);
1800 	    XClearArea(disp, win, x0, y0, xdiff, height, False);
1801 	    draw_rectangle(w, pos_x, pos_y, diff, shown_y, False);
1802 	} else {
1803 	    width = (shown_x - diff) * w->textfield.char_w;
1804 	    xdiff = diff * w->textfield.char_w;
1805 	    XCopyArea(disp, win, win, gc,
1806 		      x0 + xdiff, y0, width, height, x0, y0);
1807 	    XClearArea(disp, win, x0 + width, y0, xdiff, height, False);
1808 	    draw_rectangle(w, pos_x + shown_x - diff, pos_y,
1809 			   diff, shown_y, False);
1810 	}
1811     }
1812     draw_caret(w);
1813 }
1814 
SetVPos(ScrollableWidget gw,long pos_y)1815 static void SetVPos(ScrollableWidget gw, long pos_y)
1816 {
1817     TextFieldWidget	w       = (TextFieldWidget)gw;
1818     Display		*disp   = XtDisplay(w);
1819     Window		win     = XtWindow(w);
1820     long		old     = w->scrollable.pos_y;
1821     long		pos_x   = w->scrollable.pos_x;
1822     long		shown_x = w->scrollable.shown_x;
1823     long		shown_y = w->scrollable.shown_y;
1824     long		diff;
1825 
1826     if (pos_y > w->scrollable.height - shown_y)
1827 	pos_y = w->scrollable.height - shown_y;
1828     if (pos_y < 0)
1829 	pos_y = 0;
1830 
1831     diff = pos_y - old;
1832     if (diff == 0)
1833 	return;
1834 
1835     undraw_caret(w);
1836     w->scrollable.pos_y = pos_y;
1837     if (diff <= - shown_y || diff >= shown_y)
1838 	draw_rectangle(w, pos_x, pos_y, shown_x, shown_y, True);
1839     else {
1840 	long	y0 = w->shadow.shadow_width + w->textfield.internal_height;
1841 	long	x0 = w->shadow.shadow_width + w->textfield.internal_width;
1842 	long	width = (long)w->core.width - 2 * x0;
1843 	long	height, ydiff;
1844 	GC	gc = w->textfield.gc;
1845 
1846 	if (width <= 0)
1847 	    return;
1848 
1849 	if (diff < 0) {
1850 	    diff = -diff;
1851 	    height = (shown_y - diff) * w->textfield.char_h;
1852 	    ydiff = diff * w->textfield.char_h;
1853 	    XCopyArea(disp, win, win, gc,
1854 		      x0, y0, width, height, x0, y0 + ydiff);
1855 	    XClearArea(disp, win, x0, y0, width, ydiff, False);
1856 	    draw_rectangle(w, pos_x, pos_y, shown_x, diff, False);
1857 	} else {
1858 	    height = (shown_y - diff) * w->textfield.char_h;
1859 	    ydiff = diff * w->textfield.char_h;
1860 	    XCopyArea(disp, win, win, gc,
1861 		      x0, y0 + ydiff, width, height, x0, y0);
1862 	    XClearArea(disp, win, x0, y0 + height, width, ydiff, False);
1863 	    draw_rectangle(w, pos_x, pos_y + shown_y - diff,
1864 			   shown_x, diff, False);
1865 	}
1866     }
1867     draw_caret(w);
1868 }
1869 
Realize(Widget gw,XtValueMask * mask,XSetWindowAttributes * attributes)1870 static void Realize(Widget gw, XtValueMask *mask,
1871 		    XSetWindowAttributes *attributes)
1872 {
1873     TextFieldWidget	w = (TextFieldWidget)gw;
1874 
1875     calc_shown(w);
1876     if (w->textfield.single_line) {
1877 	w->scrollable.pos_x = w->scrollable.width - w->scrollable.shown_x;
1878 	if (w->scrollable.pos_x < 0)
1879 	    w->scrollable.pos_x = 0;
1880     }
1881     scrollableWidgetClass->core_class.realize((Widget)w, mask, attributes);
1882 }
1883 
Redisplay(Widget gw,XEvent * event,Region region)1884 static void Redisplay(Widget gw, XEvent *event, Region region)
1885 {
1886     TextFieldWidget	w = (TextFieldWidget)gw;
1887     int			x, y, width, height;
1888 
1889     if (event)
1890 	switch (event->type) {
1891 	case Expose:
1892 	    x = event->xexpose.x;
1893 	    y = event->xexpose.y;
1894 	    width = event->xexpose.width;
1895 	    height = event->xexpose.height;
1896 	    break;
1897 	case GraphicsExpose:
1898 	    x = event->xgraphicsexpose.x;
1899 	    y = event->xgraphicsexpose.y;
1900 	    width = event->xgraphicsexpose.width;
1901 	    height = event->xgraphicsexpose.height;
1902 	    break;
1903 	default:
1904 	    return;
1905 	}
1906     else {
1907 	x = 0;
1908 	y = 0;
1909 	width = w->core.width;
1910 	height = w->core.height;
1911     }
1912 
1913     ShadowDrawShadows((ShadowWidget)w, 0, 0, w->core.width,
1914 		      w->core.height, w->textfield.border_in);
1915     draw_pixels(w, x, y, width, height, False);
1916     draw_caret(w);
1917 }
1918 
SetValues(Widget gcurrent,Widget grequest,Widget gnew,ArgList args,Cardinal * num_args)1919 static Boolean SetValues(Widget gcurrent,
1920 			 Widget grequest,
1921 			 Widget gnew,
1922 			 ArgList args,
1923 			 Cardinal *num_args)
1924 {
1925     TextFieldWidget	new = (TextFieldWidget)gnew;
1926     TextFieldWidget	current = (TextFieldWidget)gcurrent;
1927     Boolean		redisplay = False;
1928 
1929     if (new->textfield.single_line)
1930 	new->textfield.pref_lines = 1;
1931 
1932     if (new->textfield.font           != current->textfield.font         ||
1933 	new->core.background_pixel    != current->core.background_pixel  ||
1934 	new->textfield.fg_pixel       != current->textfield.fg_pixel     ||
1935 	new->textfield.highlight_fg   != current->textfield.highlight_fg ||
1936 	new->textfield.highlight_bg   != current->textfield.highlight_bg) {
1937 	free_gcs(new);
1938 	init_gcs(new);
1939 	redisplay = True;
1940     }
1941 
1942     if (new->textfield.buffer) {
1943 	if (new->textfield.sel_set)
1944 	    invalidate_selection(new);
1945 	lines_from_buffer(new);
1946 	new->textfield.curr_sel = None;
1947 	new->textfield.waiting_for_sel = False;
1948 	ScrollableFitHBar((ScrollableWidget)new);
1949 	ScrollableFitVBar((ScrollableWidget)new);
1950 	redisplay = True;
1951     }
1952 
1953     if (new->textfield.font != current->textfield.font)
1954 	get_char_sizes(new);
1955 
1956     if (new->textfield.font            != current->textfield.font           ||
1957 	new->textfield.internal_width  != current->textfield.internal_width ||
1958 	new->textfield.internal_height != current->textfield.internal_height) {
1959 	Dimension	width, height;
1960 
1961 	get_preferred_sizes(new, &width, &height);
1962 	new->core.width = width;
1963 	new->core.height = height;
1964 	redisplay = True;
1965     }
1966 
1967     return redisplay;
1968 }
1969 
QueryGeometry(Widget gw,XtWidgetGeometry * intended,XtWidgetGeometry * preferred)1970 static XtGeometryResult QueryGeometry(Widget gw,
1971 				      XtWidgetGeometry *intended,
1972 				      XtWidgetGeometry *preferred)
1973 {
1974     TextFieldWidget	w = (TextFieldWidget)gw;
1975     Dimension		width, height;
1976     Dimension		intended_width, intended_height;
1977 
1978     get_preferred_sizes(w, &width, &height);
1979     preferred->request_mode = CWWidth | CWHeight;
1980     preferred->width = width;
1981     preferred->height = height;
1982 
1983     if (intended->request_mode & CWWidth)
1984 	intended_width = intended->width;
1985     else
1986 	intended_width = w->core.width;
1987     if (intended->request_mode & CWHeight)
1988 	intended_height = intended->height;
1989     else
1990 	intended_height = w->core.height;
1991 
1992     if (width == w->core.width && height == w->core.height)
1993 	return XtGeometryNo;
1994     if (width == intended_width && height == intended_height)
1995 	return XtGeometryYes;
1996     return XtGeometryAlmost;
1997 }
1998 
1999 /*************************************************************************/
2000 
TextFieldSetActive(Widget gw,int active)2001 void TextFieldSetActive(Widget gw, int active)
2002 {
2003     TextFieldWidget	w = (TextFieldWidget)gw;
2004 
2005     w->textfield.active = active;
2006 }
2007 
TextFieldSetBuffer(Widget gw,char * buffer)2008 void TextFieldSetBuffer(Widget gw, char *buffer)
2009 {
2010     TextFieldWidget	w = (TextFieldWidget)gw;
2011 
2012     if (w->textfield.sel_set)
2013 	invalidate_selection(w);
2014     w->textfield.buffer = buffer;
2015     lines_from_buffer(w);
2016     ScrollableFitHBar((ScrollableWidget)w);
2017     ScrollableFitVBar((ScrollableWidget)w);
2018     XClearWindow(XtDisplay(w), XtWindow(w));
2019     Redisplay((Widget)w, NULL, NULL);
2020 }
2021 
TextFieldGetBuffer(Widget gw)2022 char *TextFieldGetBuffer(Widget gw)
2023 {
2024     TextFieldWidget	w = (TextFieldWidget)gw;
2025     char		*buffer;
2026     long		i, n;
2027 
2028     if (w->scrollable.height <= 0)
2029 	return XtNewString("");
2030 
2031     n = w->scrollable.height + 3;
2032     for (i = 0 ; i < w->scrollable.height ; i++)
2033 	n += strlen(w->textfield.lines[i].buf);
2034     buffer = XtMalloc(n);
2035 
2036     strcpy(buffer, w->textfield.lines[0].buf);
2037     n = strlen(buffer);
2038     for (i = 1 ; i < w->scrollable.height ; i++) {
2039 	buffer[n++] = '\n';
2040 	strcpy(buffer + n, w->textfield.lines[i].buf);
2041 	n += strlen(buffer + n);
2042     }
2043     buffer[n] = '\0';
2044 
2045     return buffer;
2046 }
2047 
2048 /*************************************************************************/
2049 
get_sel_len(TextFieldWidget w)2050 static long get_sel_len(TextFieldWidget w)
2051 {
2052     long	start = w->textfield.sel_start_y;
2053     long	stop  = w->textfield.sel_stop_y;
2054     long	len, n;
2055 
2056     if (w->textfield.curr_sel == None)
2057 	return 0;
2058 
2059     if (start < 0 || start >= w->scrollable.height ||
2060 	stop < 0 || stop >= w->scrollable.height)
2061 	return -1;
2062 
2063     if (start == stop)
2064 	return w->textfield.sel_stop_x - w->textfield.sel_start_x;
2065 
2066     len = strlen(w->textfield.lines[start].buf) - w->textfield.sel_start_x + 1;
2067     for (n = start + 1 ; n < stop ; n++)
2068 	len += strlen(w->textfield.lines[n].buf) + 1;
2069     len += w->textfield.sel_stop_x;
2070 
2071     return len;
2072 }
2073 
get_sel(TextFieldWidget w,char * buffer)2074 static long get_sel(TextFieldWidget w, char *buffer)
2075 {
2076     long	start = w->textfield.sel_start_y;
2077     long	stop  = w->textfield.sel_stop_y;
2078     long	n, len;
2079     char	*c;
2080 
2081     if (w->textfield.curr_sel == None || start < 0 || stop < 0 ||
2082 	start >= w->scrollable.height || stop >= w->scrollable.height)
2083 	return -1;
2084 
2085     c = w->textfield.lines[start].buf;
2086     n = strlen(c);
2087 
2088     if (start == stop) {
2089 	start = w->textfield.sel_start_x;
2090 	stop = w->textfield.sel_stop_x;
2091 	if (start > n)
2092 	    return -1;
2093 	if (stop > n)
2094 	    stop = n;
2095 	len = stop - start;
2096 	if (len > 0)
2097 	    memcpy(buffer, c + start, len);
2098 	buffer[len] = '\0';
2099 	return len;
2100     }
2101 
2102     if (w->textfield.sel_start_x > n)
2103 	len = 0;
2104     else {
2105 	strcpy(buffer, c + w->textfield.sel_start_x);
2106 	len = strlen(buffer);
2107     }
2108     buffer[len++] = '\n';
2109 
2110     for (n = start + 1 ; n < stop ; n++) {
2111 	strcpy(buffer + len, w->textfield.lines[n].buf);
2112 	len += strlen(buffer + len);
2113 	buffer[len++] = '\n';
2114     }
2115 
2116     c = w->textfield.lines[stop].buf;
2117     n = strlen(c);
2118     stop = w->textfield.sel_stop_x;
2119     if (stop > n)
2120 	stop = n;
2121     if (stop > 0)
2122 	memcpy(buffer + len, c, stop);
2123     len += stop;
2124     buffer[len] = '\0';
2125 
2126     return len;
2127 }
2128 
2129 /*************************************************************************/
2130 
delete_sel_callback(Widget gw,XtPointer client_data,Atom * selection,Atom * type,XtPointer value,unsigned long * len,int * format)2131 static void delete_sel_callback(Widget		 gw,
2132 				XtPointer	 client_data,
2133 				Atom		*selection,
2134 				Atom		*type,
2135 				XtPointer	 value,
2136 				unsigned long	*len,
2137 				int		*format)
2138 {
2139     TextFieldWidget	w = (TextFieldWidget)gw;
2140 
2141     w->textfield.waiting_for_sel = False;
2142     XtFree((char *)value);
2143 }
2144 
insert_sel_callback(Widget gw,XtPointer client_data,Atom * selection,Atom * type,XtPointer val,unsigned long * lenp,int * format)2145 static void insert_sel_callback(Widget		 gw,
2146 				XtPointer	 client_data,
2147 				Atom		*selection,
2148 				Atom		*type,
2149 				XtPointer	 val,
2150 				unsigned long	*lenp,
2151 				int		*format)
2152 {
2153     TextFieldWidget	w = (TextFieldWidget)gw;
2154     char		*value = (char *)val;
2155     char		*c, *p;
2156     long		pos = w->textfield.caret_x;
2157     long		line = w->textfield.caret_y;
2158     long		len = *lenp;
2159     long		m, n, i;
2160     int			was_waiting = w->textfield.waiting_for_sel;
2161 
2162     if (w->textfield.sel_set)
2163 	invalidate_selection(w);
2164 
2165     c = memchr(value, '\n', len);
2166     w->textfield.waiting_for_sel = False;
2167     if (!was_waiting || *type == XT_CONVERT_FAIL || !value ||
2168 	(c && w->textfield.single_line) ||
2169 	pos < 0 || line < 0 || line >= w->scrollable.height) {
2170 	XtFree(value);
2171 	return;
2172     }
2173 
2174     m = 0;
2175     p = c;
2176     while (p) {
2177 	m++;
2178 	p++;
2179 	p = memchr(p, '\n', value + len - p);
2180     }
2181 
2182     if (m == 0) {
2183 	n = strlen(w->textfield.lines[line].buf);
2184 	if (n + len + 8 > w->textfield.lines[line].len)
2185 	    w->textfield.lines[line].buf =
2186 		XtRealloc(w->textfield.lines[line].buf,
2187 			  (w->textfield.lines[line].len = n + len + 8));
2188 	if (pos > n)
2189 	    pos = n;
2190 	p = w->textfield.lines[line].buf;
2191 	memmove(p + pos + len, p + pos, n - pos + 1);
2192 	memcpy(p + pos, value, len);
2193 	sync_width(w, n, n + len);
2194 	draw_rectangle(w, pos, line, w->scrollable.width, 1, True);
2195 	change_pos(w, pos + len, line);
2196     } else {
2197 	n = strlen(w->textfield.lines[line].buf);
2198 	if (pos > n)
2199 	    pos = n;
2200 
2201 	open_lines(w, line, m);
2202 
2203 	n = c - value;
2204 	XtFree(w->textfield.lines[line].buf);
2205 	p = w->textfield.lines[line].buf =
2206 	    XtMalloc(w->textfield.lines[line].len = pos + n + 1);
2207 	memcpy(p, w->textfield.lines[line + m].buf, pos);
2208 	memcpy(p + pos, value, n);
2209 	p[pos + n] = '\0';
2210 
2211 	c++;
2212 	for (i = line + 1 ; i < line + m ; i++) {
2213 	    p = memchr(c, '\n', value + len - c);
2214 	    /* p != NULL */
2215 	    n = p - c;
2216 	    XtFree(w->textfield.lines[i].buf);
2217 	    w->textfield.lines[i].buf =
2218 		memcpy(XtMalloc(w->textfield.lines[i].len = n + 1), c, n);
2219 	    w->textfield.lines[i].buf[n] = '\0';
2220 	    c = p + 1;
2221 	}
2222 
2223 	len = value + len - c;
2224 	n = strlen(w->textfield.lines[line + m].buf) - pos;
2225 	p = XtMalloc(w->textfield.lines[line + m].len = len + n + 1);
2226 	memcpy(p, c, len);
2227 	memcpy(p + len, w->textfield.lines[line + m].buf + pos, n);
2228 	p[n + len] = '\0';
2229 	XtFree(w->textfield.lines[line + m].buf);
2230 	w->textfield.lines[line + m].buf = p;
2231 
2232 	draw_rectangle(w, 0, line, w->scrollable.width,
2233 		       w->scrollable.height, True);
2234 
2235 	w->scrollable.width = max_width(w);
2236 	ScrollableFitHBar((ScrollableWidget)w);
2237 	change_pos(w, pos, line + m);
2238     }
2239 
2240     XtFree(value);
2241 }
2242 
delete_selection(TextFieldWidget w)2243 static int delete_selection(TextFieldWidget w)
2244 {
2245     long	start = w->textfield.sel_start_y;
2246     long	stop  = w->textfield.sel_stop_y;
2247     long	pos, len, n;
2248     char	*c;
2249 
2250     if (!w->textfield.sel_set || w->textfield.curr_sel == None ||
2251 	(unsigned long)start >= w->scrollable.height ||
2252 	(unsigned long)stop >= w->scrollable.height)
2253 	return False;
2254 
2255     invalidate_selection(w);
2256 
2257     if (start == stop) {
2258 	pos = w->textfield.sel_start_x;
2259 	c = w->textfield.lines[start].buf;
2260 	n = strlen(c);
2261 	if (pos > n)
2262 	    pos = n;
2263 	len = w->textfield.sel_stop_x - pos;
2264 	if (len > n - pos)
2265 	    len = n - pos;
2266 	memmove(c + pos, c + pos + len, n - pos - len + 1);
2267 	draw_rectangle(w, pos, start, w->scrollable.width, 1, True);
2268 	sync_width(w, n, n - len);
2269 	change_pos(w, pos, start);
2270     } else {
2271 	/*
2272 	 * FIXME... (knews doesn't need it)
2273 	 */
2274 	fputs("TextField: Multiline DELETE not yet implemented.\n", stderr);
2275 	return False;
2276     }
2277 
2278     return True;
2279 }
2280 
convert_sel_proc(Widget gw,Atom * sel,Atom * target,Atom * type,XtPointer * value,unsigned long * len,int * format)2281 static Boolean convert_sel_proc(Widget		 gw,
2282 				Atom		*sel,
2283 				Atom		*target,
2284 				Atom		*type,
2285 				XtPointer	*value,
2286 				unsigned long	*len,
2287 				int		*format)
2288 {
2289     TextFieldWidget	w = (TextFieldWidget)gw;
2290     Display		*disp = XtDisplay(w);
2291 
2292     if (w->textfield.curr_sel == None || *sel != w->textfield.curr_sel)
2293 	return False;
2294 
2295     if (*target == XA_STRING || *target == intern_atom(disp, "TEXT")) {
2296 	long	length;
2297 	char	*buffer;
2298 
2299 	length = get_sel_len(w);
2300 	if (length < 0)
2301 	    return False;
2302 	buffer = XtMalloc(length + 16);
2303 	length = get_sel(w, buffer);
2304 	if (length < 0) {
2305 	    XtFree(buffer);
2306 	    return False;
2307 	}
2308 	buffer[length] = '\0';
2309 
2310 	*len = length;
2311 	*value = (XtPointer)buffer;
2312 	*type = XA_STRING;
2313 	*format = 8;
2314 
2315 	return True;
2316     }
2317 
2318     if (*target == intern_atom(disp, "DELETE")) {
2319 	if (!delete_selection(w))
2320 	    return False;
2321 
2322 	*value = NULL;
2323 	*type = intern_atom(disp, "NULL");
2324 	*len = 0;
2325 	*format = 32;
2326 
2327 	return True;
2328     }
2329 
2330     if (*target == intern_atom(disp, "LENGTH")) {
2331 	long	*length = (long *)XtMalloc(sizeof(long));
2332 
2333 	*length = get_sel_len(w);
2334 	*value = (XPointer)length;
2335 	*type = XA_INTEGER;
2336 	*len = 1;
2337 	*format = 32;
2338 
2339 	return True;
2340     }
2341 
2342     if (*target == intern_atom(disp, "TARGETS")) {
2343 	Atom		*std_targets, *atom;
2344 	unsigned long	std_length, n;
2345 
2346 	if (!cvt_std_sel((Widget)w, w->textfield.sel_time,
2347 			 sel, target, type,
2348 			 (XPointer *)&std_targets, &std_length, format))
2349 	    return False;
2350 
2351 	*value = (XtPointer)XtMalloc((std_length + 8) * sizeof(Atom));
2352 	atom = (Atom *)*value;
2353 	n = std_length;
2354 
2355 	n++; *atom++ = XA_STRING;
2356 	n++; *atom++ = intern_atom(disp, "TEXT");
2357 	n++; *atom++ = intern_atom(disp, "LENGTH");
2358 	n++; *atom++ = intern_atom(disp, "LIST_LENGTH");
2359 	n++; *atom++ = intern_atom(disp, "DELETE");
2360 
2361 	memcpy(atom, std_targets, std_length * sizeof(Atom));
2362 	XtFree((char *)std_targets);
2363 
2364 	*len = n;
2365 	*type = XA_ATOM;
2366 	*format = 32;
2367 
2368 	return True;
2369     }
2370 
2371     if (*target == intern_atom(disp, "LIST_LENGTH")) {
2372 	long	*length = (long *)XtMalloc(sizeof(long));
2373 
2374 	*length = 1;
2375 	*value = (XPointer)length;
2376 	*type = XA_INTEGER;
2377 	*format = 32;
2378 
2379 	return True;
2380     }
2381 
2382     return cvt_std_sel((Widget)w, w->textfield.sel_time,
2383 		       sel, target, type, (XPointer *)value, len, format);
2384 }
2385 
lose_sel_proc(Widget gw,Atom * sel)2386 static void lose_sel_proc(Widget gw, Atom *sel)
2387 {
2388     TextFieldWidget	w = (TextFieldWidget)gw;
2389 
2390     w->textfield.curr_sel = None;
2391     if (w->textfield.sel_set)
2392 	invalidate_selection(w);
2393 }
2394