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, ¶m, &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