1 /* textinput.c - draw an editable text widget
2 Copyright (C) 1996-2017 Paul Sheer
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307, USA.
18 */
19
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <my_string.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26
27 #include <X11/Intrinsic.h>
28 #include <X11/Xatom.h>
29 #include "lkeysym.h"
30
31 #include "stringtools.h"
32 #include "app_glob.c"
33
34 #include "coolwidget.h"
35 #include "coollocal.h"
36
37 #include "edit.h"
38 #include "editcmddef.h"
39 #include "mousemark.h"
40
41 #include "mad.h"
42
43
44 int eh_textinput (CWidget * w, XEvent * xevent, CEvent * cwevent);
45 void input_mouse_mark (CWidget * w, XEvent * event, CEvent * ce);
46
47 extern struct look *look;
48
49
50 /* {{{ history stuff: draws a history of inputs a widget of the same ident */
51
52
53 #define MAX_HIST_LEN 64
54 #define MAX_HIST_WIDGETS 128
55
56 struct textinput_history {
57 char ident[32];
58 int text_len; /* length of a newline separate list of all 'input' strings */
59 int last;
60 char *input[MAX_HIST_LEN];
61 };
62
63 static struct textinput_history *history_widgets[MAX_HIST_WIDGETS] =
64 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
65 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
66 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
68 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
69 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
71 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
72 static int last = 0;
73
add_to_history(struct textinput_history * h,const char * text,int allow_blank_lines)74 static void add_to_history (struct textinput_history *h, const char *text, int allow_blank_lines)
75 {
76 int i, j;
77 char *t, *p;
78 if (!text)
79 return;
80 if (!*text && !allow_blank_lines)
81 return;
82 t = (char *) strdup (text);
83 if ((p = strchr (t, '\n')))
84 *p = '\0'; /* no newlines allowed in the history */
85 if (h->last)
86 for (i = h->last - 1; i >= 0; i--)
87 if (!strcmp (h->input[i], text)) { /* avoid adding duplicates */
88 text = h->input[i];
89 if (i < h->last - 1)
90 for (j = i; j < h->last - 1; j++) /* shift all entries up one place */
91 h->input[j] = h->input[j + 1];
92 h->input[h->last - 1] = (char *) text; /* move new entry to top */
93 free (t);
94 return;
95 }
96 h->input[h->last++] = t;
97 if (h->last == MAX_HIST_LEN) {
98 h->text_len -= strlen (h->input[0]) + 1;
99 free (h->input[0]);
100 memmove (h->input, h->input + 1, (MAX_HIST_LEN - 1) * sizeof (char *)); /* shift up to make space */
101 h->last--;
102 }
103 h->text_len += strlen (text) + 1; /* maintain total list length */
104 }
105
add_to_widget_history(const char * ident,const char * text)106 static void add_to_widget_history (const char *ident, const char *text)
107 {
108 int i;
109 int allow_blank_lines = 0;
110 allow_blank_lines = (strchr (ident, '+') != 0);
111 for (i = 0; i < MAX_HIST_WIDGETS; i++) {
112 if (!history_widgets[i])
113 break;
114 if (!strcmp (history_widgets[i]->ident, ident)) {
115 add_to_history (history_widgets[i], text, allow_blank_lines);
116 return;
117 }
118 }
119 /* create a new history */
120 history_widgets[last] = CMalloc (sizeof (struct textinput_history));
121 memset (history_widgets[last], 0, sizeof (struct textinput_history));
122 strcpy (history_widgets[last]->ident, ident);
123 add_to_history (history_widgets[last], text, allow_blank_lines);
124 last++;
125
126 if (last == MAX_HIST_WIDGETS) { /* shift up one */
127 for (i=0;i<history_widgets[0]->last;i++) {
128 if (!history_widgets[0]->input[i])
129 break;
130 free (history_widgets[0]->input[i]);
131 }
132 free (history_widgets[0]);
133 memmove (history_widgets, history_widgets + 1, (MAX_HIST_WIDGETS - 1) * sizeof (struct textinput_history *));
134 last--;
135 }
136 }
137
CAddToTextInputHistory(const char * ident,const char * text)138 void CAddToTextInputHistory (const char *ident, const char *text)
139 {
140 add_to_widget_history (ident, text);
141 }
142
143 /*
144 Returns a newline separate list of all 'input' in the history.
145 Result must be free'd .
146 */
get_history_list(const char * ident,int reverse,int * num_lines)147 static char *get_history_list (const char *ident, int reverse, int *num_lines)
148 {
149 char *s, *r;
150 int i, j;
151 for (i = 0; i < MAX_HIST_WIDGETS; i++) {
152 if (!history_widgets[i])
153 break;
154 if (!strcmp (history_widgets[i]->ident, ident)) {
155 r = s = CMalloc (history_widgets[i]->text_len);
156 if (!(*num_lines = history_widgets[i]->last))
157 break;
158 if (reverse) {
159 for (j = 0; j < history_widgets[i]->last; j++) {
160 strcpy (s, history_widgets[i]->input[j]);
161 s += strlen (s);
162 *s++ = '\n';
163 }
164 } else {
165 for (j = history_widgets[i]->last - 1; j >= 0; j--) { /* most recent at top */
166 strcpy (s, history_widgets[i]->input[j]);
167 s += strlen (s);
168 *s++ = '\n';
169 }
170 }
171 *(--s) = 0;
172 return r;
173 }
174 }
175 *num_lines = 1;
176 return (char *) strdup ("");
177 }
178
179 /* result must not be free'd */
180 /* returns the last text inputted in the input widget named ident */
CLastInput(const char * ident)181 char *CLastInput (const char *ident)
182 {
183 int i;
184 for (i = 0; i < MAX_HIST_WIDGETS; i++) {
185 if (!history_widgets[i])
186 break;
187 if (!strcmp (history_widgets[i]->ident, ident)) {
188 if (!history_widgets[i]->last)
189 return "";
190 return history_widgets[i]->input[history_widgets[i]->last - 1];
191 }
192 }
193 return "";
194 }
195
196 #define HISTORY_LINES 10
197
clip_lines(int lines,int num_lines)198 static int clip_lines (int lines, int num_lines)
199 {
200 if (lines > num_lines)
201 lines = num_lines;
202 if (lines > HISTORY_LINES)
203 lines = HISTORY_LINES;
204 if (lines < 1)
205 lines = 1;
206 return lines;
207 }
208
209 /* gets a list of all input histories and all widget for use in saving
210 to an options file (if you want to save the state of an application,
211 for example. result must be free'd */
get_all_lists(void)212 char *get_all_lists (void)
213 {
214 char *s, *r;
215 int i, j;
216 int tot_len;
217
218 tot_len = 0;
219
220 /* calc length to alloc */
221 for (i = 0; i < MAX_HIST_WIDGETS; i++) {
222 if (!history_widgets[i])
223 break;
224 tot_len += strlen (history_widgets[i]->ident) + 1;
225 tot_len += history_widgets[i]->text_len + history_widgets[i]->last;
226 }
227
228 r = s = CMalloc (tot_len + 1);
229
230 for (i = 0; i < MAX_HIST_WIDGETS; i++) {
231 if (!history_widgets[i])
232 break;
233 strcpy (s, history_widgets[i]->ident);
234 s += strlen (s);
235 *s++ = '\n';
236 for (j = 0; j < history_widgets[i]->last; j++) {
237 *s++ = '\t';
238 strcpy (s, history_widgets[i]->input[j]);
239 s += strlen (s);
240 *s++ = '\n';
241 }
242 }
243 *s = 0;
244 return r;
245 }
246
free_all_lists(void)247 void free_all_lists (void)
248 {
249 int i, j;
250 for (i = 0; i < MAX_HIST_WIDGETS; i++) {
251 if (!history_widgets[i])
252 break;
253 for (j = 0; j < history_widgets[i]->last; j++) {
254 if (!history_widgets[i]->input[j])
255 break;
256 free (history_widgets[i]->input[j]);
257 history_widgets[i]->input[j] = 0;
258 }
259 free (history_widgets[i]);
260 history_widgets[i] = 0;
261 }
262 }
263
put_all_lists(char * list)264 void put_all_lists (char *list)
265 {
266 char *p;
267 char ident[33];
268 char input[1024];
269
270 ident[32] = 0;
271 input[1023] = 0;
272
273 if (!list)
274 return;
275 while (*list) {
276 p = strchr (list, '\n');
277 if (!p)
278 return;
279 *p++ = 0;
280 strncpy (ident, list, 32);
281 list = p;
282 while (*list == '\t') {
283 list++;
284 p = strchr (list, '\n');
285 if (!p)
286 return;
287 *p++ = 0;
288 strncpy (input, list, 1023);
289 list = p;
290 add_to_widget_history (ident, input);
291 }
292 }
293 }
294
draw_text_input_history(CWidget * text_input)295 static char *draw_text_input_history (CWidget * text_input)
296 {
297 char *p, *r;
298 int num_lines;
299 CWidget *w;
300 int x, y;
301 int columns, lines;
302
303 if (text_input->options & TEXTINPUT_PASSWORD) /* password lines, not allowed a history! */
304 return 0;
305
306 x = text_input->x;
307 CPushFont ("editor", 0);
308 columns = (text_input->width - WIDGET_SPACING * 3 - 4 - 6 - 20) / FONT_MEAN_WIDTH;
309 w = CWidgetOfWindow (text_input->parentid);
310
311 if (!w) {
312 CPopFont ();
313 return 0;
314 }
315 if (text_input->y > w->height / 2) {
316 p = get_history_list (text_input->ident, 1, &num_lines);
317 lines = (text_input->y - 2 - WIDGET_SPACING * 2 - 4 - 6) / FONT_PIX_PER_LINE;
318 lines = clip_lines (lines, num_lines);
319 y = text_input->y - lines * FONT_PIX_PER_LINE - WIDGET_SPACING * 2 - 4 - 6;
320 r = CTrivialSelectionDialog (w->winid, x, y, columns, lines, p, max (0, num_lines - lines), num_lines - 1);
321 } else {
322 p = get_history_list (text_input->ident, 0, &num_lines);
323 lines = (w->height - text_input->height - text_input->y - 2 - WIDGET_SPACING * 2 - 4 - 6) / FONT_PIX_PER_LINE;
324 lines = clip_lines (lines, num_lines);
325 y = text_input->y + text_input->height;
326 r = CTrivialSelectionDialog (w->winid, x, y, columns, lines, p, 0, 0);
327 }
328 free (p);
329 CPopFont ();
330 return r;
331 }
332
draw_selection_history(CWidget * text_input)333 static char *draw_selection_history (CWidget * text_input)
334 {
335 CWidget *w;
336 char *p, *r;
337 int x, y;
338 long len;
339 int columns, lines;
340 x = text_input->x;
341 CPushFont ("editor", 0);
342 columns = (text_input->width - WIDGET_SPACING * 3 - 4 - 6 - 20) / FONT_MEAN_WIDTH;
343 w = CWidgetOfWindow (text_input->parentid);
344 if (!w) {
345 CPopFont ();
346 return 0;
347 }
348 if (text_input->y > w->height / 2) {
349 lines = (text_input->y - 2 - WIDGET_SPACING * 2 - 4 - 6) / FONT_PIX_PER_LINE;
350 y = text_input->y - lines * FONT_PIX_PER_LINE - WIDGET_SPACING * 2 - 4 - 6;
351 } else {
352 lines =
353 (w->height - text_input->height - text_input->y - 2 - WIDGET_SPACING * 2 - 4 -
354 6) / FONT_PIX_PER_LINE;
355 y = text_input->y + text_input->height;
356 }
357 p = edit_get_text_from_selection_history (w->winid, x, y, columns, lines, &len);
358 if (p) {
359 r = (char *) CMalloc (len + 1);
360 strncpy (r, p, len + 1);
361 } else {
362 r = NULL;
363 }
364 CPopFont ();
365 return r;
366 }
367
draw_selection_completion(CWidget * text_input)368 static char *draw_selection_completion (CWidget * text_input)
369 {
370 CWidget *w;
371 char *r;
372 int x, y;
373 int columns, lines;
374 x = text_input->x;
375 CPushFont ("editor", 0);
376 columns = (text_input->width - WIDGET_SPACING * 3 - 4 - 6 - 20) / FONT_MEAN_WIDTH;
377 w = CWidgetOfWindow (text_input->parentid);
378 if (!w) {
379 CPopFont ();
380 return 0;
381 }
382 if (text_input->y > w->height / 2) {
383 lines = (text_input->y - 2 - WIDGET_SPACING * 2 - 4 - 6) / FONT_PIX_PER_LINE;
384 y = text_input->y - lines * FONT_PIX_PER_LINE - WIDGET_SPACING * 2 - 4 - 6;
385 } else {
386 lines =
387 (w->height - text_input->height - text_input->y - 2 - WIDGET_SPACING * 2 - 4 -
388 6) / FONT_PIX_PER_LINE;
389 y = text_input->y + text_input->height;
390 }
391 r = user_file_list_complete (w->winid, x, y, columns, lines, text_input->text);
392 CPopFont ();
393 return r;
394 }
395
396 #if 0
397 wchar_t *mbstowcs_dup (unsigned char *s);
398 int wchar_t_strlen (wchar_t * s);
399
400 int wchar_t_columns (char *t, int x)
401 {
402 wchar_t *s;
403 t = (char *) strdup ((char *) t);
404 t[x] = '\0';
405 s = mbstowcs_dup ((unsigned char *) t);
406 x = wchar_t_strlen (s);
407 free (t);
408 free (s);
409 return x;
410 }
411 #endif
412
render_passwordinput(CWidget * wdt)413 void render_passwordinput (CWidget * wdt)
414 {
415 int wc, k, l, w = wdt->width, h = wdt->height;
416 Window win;
417 char *password;
418
419 CPushFont ("editor", 0);
420
421 win = wdt->winid;
422
423 CSetBackgroundColor (COLOR_WHITE);
424 CSetColor (COLOR_BLACK);
425 password = (char *) strdup (wdt->text);
426 memset (password, '*', strlen (wdt->text));
427 CImageString (win, FONT_OFFSET_X + 3 + TEXTINPUT_RELIEF,
428 FONT_OFFSET_Y + 3 + TEXTINPUT_RELIEF,
429 password);
430 CSetColor (COLOR_WHITE);
431 l = CImageStringWidth (password);
432 k = min (l, w - 6);
433 memset (password, 0, strlen (password));
434 free (password);
435 CRectangle (win, 3, 3, k, option_text_line_spacing + 1);
436 CLine (win, 3, 4, 3, h - 5);
437 CLine (win, 3, h - 4, k + 3, h - 4);
438 CRectangle (win, k + 3, 3, w - 6 - k, h - 6);
439 (*look->render_passwordinput_tidbits) (wdt, win == CGetFocus ());
440 wc = 3 + TEXTINPUT_RELIEF + 1 + CImageTextWidth (password, wdt->cursor);
441 set_cursor_position (win, wc, 5, 0, h - 5, CURSOR_TYPE_TEXTINPUT, 0, 0, 0, 0);
442 CPopFont ();
443 return;
444 }
445
render_textinput(CWidget * wdt)446 void render_textinput (CWidget * wdt)
447 {
448 int wc, isfocussed = 0;
449 int f, k, l;
450 int x, m1, m2;
451 int w = wdt->width, h = wdt->height;
452 Window win;
453 char *s;
454
455 if (wdt->options & TEXTINPUT_PASSWORD) {
456 render_passwordinput (wdt);
457 return;
458 }
459
460 CPushFont ("editor", 0);
461 win = wdt->winid;
462 isfocussed = (win == CGetFocus ());
463
464 /*This is a little untidy, but it will account for uneven font widths
465 without having to think to hard */
466
467 do {
468 f = 0;
469 /*wc is the position of the cursor from the left of the input window */
470 wc = 3 + TEXTINPUT_RELIEF + 1 +
471 CImageTextWidth (wdt->text + wdt->firstcolumn, wdt->cursor - wdt->firstcolumn);
472
473 /*now lets make sure the cursor is well within the view */
474
475 /*except for when the cursor is at the end of the line */
476 if (wdt->cursor == strlen (wdt->text)) {
477 if (wc > w - 3 - h) {
478 wdt->firstcolumn++;
479 f = 1;
480 }
481 } else if (wc > max (w - FONT_MEAN_WIDTH - h, w * 3 / 4 - h)) {
482 wdt->firstcolumn++;
483 f = 1;
484 }
485 if (wc < min (FONT_MEAN_WIDTH, w / 4)) {
486 wdt->firstcolumn--;
487 f = 1;
488 /*Unless of course we are at the beginning of the string */
489 if (wdt->firstcolumn <= 0) {
490 wdt->firstcolumn = 0;
491 f = 0;
492 }
493 }
494 } while (f); /*recalculate if firstcolumn has changed */
495
496 s = wdt->text + wdt->firstcolumn;
497 l = strlen (s);
498 CSetColor (COLOR_WHITE);
499 k = min (CImageTextWidth (s, l), w - h - 6);
500 CRectangle (win, 3, 3, k, option_text_line_spacing + 1);
501 CLine (win, 3, 4, 3, h - 5);
502 CLine (win, 3, h - 4, k + 3, h - 4);
503 CRectangle (win, k + 3, 3, w - h - 6 - k, h - 6);
504 /* now draw the visible part of the string */
505 wdt->mark1 = min (wdt->mark1, l + wdt->firstcolumn);
506 wdt->mark2 = min (wdt->mark2, l + wdt->firstcolumn);
507 m1 = min (wdt->mark1, wdt->mark2);
508 m2 = max (wdt->mark1, wdt->mark2);
509 x = 0;
510 if (m1 > wdt->firstcolumn) {
511 CSetBackgroundColor (COLOR_WHITE);
512 CSetColor (COLOR_BLACK);
513 CImageText (win,
514 FONT_OFFSET_X + 3 + TEXTINPUT_RELIEF,
515 FONT_OFFSET_Y + 3 + TEXTINPUT_RELIEF,
516 s, m1 - wdt->firstcolumn);
517 x += CImageTextWidth (s, m1 - wdt->firstcolumn);
518 s += m1 - wdt->firstcolumn;
519 }
520 if (x < w - h && m2 > wdt->firstcolumn) {
521 m1 = max (wdt->firstcolumn, m1);
522 CSetBackgroundColor (COLOR_BLACK);
523 CSetColor (COLOR_WHITE);
524 CImageText (win,
525 FONT_OFFSET_X + 3 + TEXTINPUT_RELIEF + x,
526 FONT_OFFSET_Y + 3 + TEXTINPUT_RELIEF,
527 s, m2 - m1);
528 x += CImageTextWidth (s, m2 - m1);
529 s += m2 - m1;
530 }
531 if (x < w - h) {
532 CSetBackgroundColor (COLOR_WHITE);
533 CSetColor (COLOR_BLACK);
534 CImageString (win,
535 FONT_OFFSET_X + 3 + TEXTINPUT_RELIEF + x,
536 FONT_OFFSET_Y + 3 + TEXTINPUT_RELIEF,
537 s);
538 }
539
540 (*look->render_textinput_tidbits) (wdt, isfocussed);
541
542 set_cursor_position (win, wc, 5, 0, h - 5, CURSOR_TYPE_TEXTINPUT, 0, 0, 0, 0);
543 CPopFont();
544 return;
545 }
546
547
text_input_destroy(CWidget * w)548 void text_input_destroy (CWidget * w)
549 {
550 CAddToTextInputHistory (w->ident, w->text);
551 }
552
xy(int x,int y,int * x_return,int * y_return)553 static void xy (int x, int y, int *x_return, int *y_return)
554 {
555 *x_return = x - (3 + TEXTINPUT_RELIEF + 1);
556 *y_return = 0;
557 }
558
cp(CWidget * wdt,int x,int y)559 static long cp (CWidget * wdt, int x, int y)
560 {
561 int i;
562 i = wdt->firstcolumn;
563 for (;;) {
564 int a;
565 a = CImageTextWidth (wdt->text + wdt->firstcolumn, i - wdt->firstcolumn);
566 if (a > x) {
567 if (i > 0)
568 return i - 1;
569 return 0;
570 }
571 if (!wdt->text[i])
572 break;
573 i++;
574 }
575 return strlen (wdt->text);
576 }
577
578 /* return 1 if not marked */
marks(CWidget * w,long * start,long * end)579 static int marks (CWidget * w, long *start, long *end)
580 {
581 if (w->mark1 == w->mark2)
582 return 1;
583 *start = min (w->mark1, w->mark2);
584 *end = max (w->mark1, w->mark2);
585 return 0;
586 }
587
588 extern int range (CWidget * w, long start, long end, int click);
589
move_mark(CWidget * w)590 static void move_mark (CWidget * w)
591 {
592 w->mark2 = w->mark1 = w->cursor;
593 }
594
fin_mark(CWidget * w)595 static void fin_mark (CWidget * w)
596 {
597 w->mark2 = w->mark1 = -1;
598 }
599
release_mark(CWidget * w,XEvent * event)600 static void release_mark (CWidget * w, XEvent * event)
601 {
602 w->mark2 = w->cursor;
603 if (w->mark2 != w->mark1 && event) {
604 XSetSelectionOwner (CDisplay, XA_PRIMARY, w->winid, event->xbutton.time);
605 XSetSelectionOwner (CDisplay, ATOM_ICCCM_P2P_CLIPBOARD, w->winid, event->xbutton.time);
606 }
607 }
608
get_block(CWidget * w,long start_mark,long end_mark,int * type,int * l)609 static char *get_block (CWidget * w, long start_mark, long end_mark, int *type, int *l)
610 {
611 char *t;
612 if (w->options & TEXTINPUT_PASSWORD) {
613 *type = DndText;
614 *l = 0;
615 return (char *) strdup ("");
616 }
617 *l = abs (w->mark2 - w->mark1);
618 t = CMalloc (*l + 1);
619 memcpy (t, w->text + min (w->mark1, w->mark2), *l);
620 t[*l] = 0;
621 if (*type == DndFile || *type == DndFiles) {
622 char *s;
623 int i;
624 s = CDndFileList (t, l, &i);
625 free (t);
626 t = s;
627 }
628 return t;
629 }
630
move(CWidget * w,long click,int row)631 static void move (CWidget * w, long click, int row)
632 {
633 w->cursor = click;
634 if (w->mark2 == -1)
635 w->mark1 = click;
636 w->mark2 = click;
637 }
638
motion(CWidget * w,long click)639 static void motion (CWidget * w, long click)
640 {
641 w->mark2 = click;
642 }
643
644 void input_insert (CWidget * w, int c);
645 char *filename_from_url (char *data, int size, int i);
646
insert_drop(CWidget * w,Window from,unsigned char * data,int size,int xs,int ys,Atom type,Atom action)647 static int insert_drop (CWidget * w, Window from, unsigned char *data, int size, int xs, int ys, Atom type, Atom action)
648 {
649 int cursor;
650 char *f;
651 int x, y, i;
652 if (xs < 0 || ys < 0 || xs >= w->width || ys >= w->height)
653 return 1;
654 xy (xs, ys, &x, &y);
655 f = filename_from_url ((char *) data, size, 0);
656 data = (unsigned char *) f;
657 cursor = w->cursor = cp (w, x, y);
658 if (type == XInternAtom (CDisplay, "url/url", False) || \
659 type == XInternAtom (CDisplay, "text/uri-list", False))
660 if (!strncmp ((char *) data, "file:/", 6))
661 data += 5;
662 for (i = 0; i < size && data[i] != '\n' && data[i]; i++)
663 input_insert (w, data[i] < ' ' ? ' ' : data[i]);
664 if (cursor > strlen (w->text))
665 cursor = strlen (w->text);
666 w->cursor = cursor;
667 free (f);
668 return 0;
669 }
670
671 static char *mime_majors[3] =
672 {"text", 0};
673
674 static struct mouse_funcs input_mouse_funcs =
675 {
676 0,
677 (void (*)(int, int, int *, int *)) xy,
678 (long (*)(void *, int, int)) cp,
679 (int (*)(void *, long *, long *)) marks,
680 (int (*)(void *, long, long, long)) range,
681 (void (*)(void *)) fin_mark,
682 (void (*)(void *)) move_mark,
683 (void (*)(void *, XEvent *)) release_mark,
684 (char *(*)(void *, long, long, int *, int *)) get_block,
685 (void (*)(void *, long, int)) move,
686 (void (*)(void *, long)) motion,
687 0,
688 0,
689 (int (*)(void *, Window, unsigned char *, int, int, int, Atom, Atom)) insert_drop,
690 0,
691 DndText,
692 mime_majors
693 };
694
695 /*
696 This will reallocate a previous draw of the same identifier.
697 so you can draw the same widget over and over without flicker
698 */
CDrawTextInput(const char * identifier,Window parent,int x,int y,int width,int height,int maxlen,const char * text)699 CWidget *CDrawTextInput (const char *identifier, Window parent, int x, int y,
700 int width, int height, int maxlen, const char *text)
701 {
702 CWidget *wdt;
703
704 if (text == TEXTINPUT_LAST_INPUT)
705 text = CLastInput (identifier);
706
707 CPushFont ("editor", 0);
708 if (!(wdt = CIdent (identifier))) {
709 int w, h;
710 if (width == AUTO_WIDTH || height == AUTO_HEIGHT)
711 CTextSize (&w, &h, text);
712 if (width == AUTO_WIDTH)
713 width = w + 6 + TEXTINPUT_RELIEF * 2;
714 if (height == AUTO_HEIGHT)
715 height = FONT_PIX_PER_LINE + 6 + TEXTINPUT_RELIEF * 2;
716
717 set_hint_pos (x + width + WIDGET_SPACING, y + height + WIDGET_SPACING);
718
719 wdt = CSetupWidget (identifier, parent, x, y,
720 width, height, C_TEXTINPUT_WIDGET, INPUT_KEY, COLOR_FLAT, 1);
721
722 /* For the text input widget we need enough memory allocated to the label
723 for it to grow to maxlen, so reallocate it */
724
725 wdt->text = CMalloc (maxlen + 16);
726 strcpy (wdt->text, text);
727 wdt->cursor = strlen (text);
728 wdt->firstcolumn = 0;
729 wdt->textlength = maxlen;
730 wdt->destroy = text_input_destroy;
731 wdt->options |= WIDGET_TAKES_SELECTION;
732 wdt->funcs = mouse_funcs_new (wdt, &input_mouse_funcs);
733
734 xdnd_set_dnd_aware (CDndClass, wdt->winid, 0);
735 xdnd_set_type_list (CDndClass, wdt->winid, xdnd_typelist_send[DndText]);
736 } else { /*redraw the thing so it doesn't flicker if its redrawn in the same place.
737 Also, this doesn't need an undraw */
738 CSetWidgetSize (identifier, width, height);
739 wdt->x = x;
740 wdt->y = y;
741 XMoveWindow (CDisplay, wdt->winid, x, y);
742 free (wdt->text);
743 wdt->text = CMalloc (maxlen + 16);
744 strcpy (wdt->text, text);
745 wdt->cursor = strlen (text);
746 wdt->firstcolumn = 0;
747 wdt->textlength = maxlen;
748 wdt->keypressed = 0;
749 render_textinput (wdt);
750 }
751 CPopFont();
752
753 return wdt;
754 }
755
756 void paste_prop (void *data, void (*insert) (void *, int), Window win, unsigned prop, int delete);
757
input_insert(CWidget * w,int c)758 void input_insert (CWidget * w, int c)
759 {
760 if (strlen ((char *) w->text) < w->textlength) {
761 if (!w->keypressed) {
762 w->keypressed = 1;
763 w->cursor = 0;
764 w->text[0] = '\0';
765 }
766 memmove ((char *) w->text + w->cursor + 1, w->text + w->cursor, strlen ((char *) w->text) - w->cursor + 1);
767 w->text[w->cursor] = c;
768 w->cursor++;
769 }
770 }
771
772 static void xy (int x, int y, int *x_return, int *y_return);
773 static long cp (CWidget * wdt, int x, int y);
774 void text_get_selection (CWidget * w);
775 void selection_send (XSelectionRequestEvent * rq);
776
eh_textinput(CWidget * w,XEvent * xevent,CEvent * cwevent)777 int eh_textinput (CWidget * w, XEvent * xevent, CEvent * cwevent)
778 {
779 int handled = 0, save_options;
780 int cursor;
781 char *u;
782
783 switch (xevent->type) {
784 case FocusIn:
785 case FocusOut:
786 render_textinput (w);
787 break;
788 case SelectionRequest:
789 text_get_selection (w);
790 selection_send (&(xevent->xselectionrequest));
791 render_textinput (w);
792 return 1;
793 case SelectionNotify:
794 cursor = w->keypressed ? w->cursor : 0;
795 paste_prop ((void *) w, (void (*)(void *, int)) input_insert,
796 xevent->xselection.requestor, xevent->xselection.property, True);
797 w->mark1 = w->mark2 = 0;
798 w->cursor = cursor;
799 render_textinput (w);
800 break;
801 case ButtonPress:
802 resolve_button (xevent, cwevent);
803 if (!(w->options & TEXTINPUT_PASSWORD)) {
804 if (xevent->xbutton.x >= w->width - w->height) {
805 char *p;
806 w->options &= 0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT;
807 w->options |= BUTTON_PRESSED;
808 p = draw_text_input_history (w);
809 if (p) {
810 strncpy (w->text, p, w->textlength);
811 w->keypressed = 1;
812 w->cursor = strlen (w->text);
813 w->firstcolumn = 0;
814 }
815 } else {
816 input_mouse_mark (w, xevent, cwevent);
817 w->options &= 0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT;
818 }
819 }
820 render_textinput (w);
821 CFocus (w);
822 case ButtonRelease:
823 if (!(w->options & TEXTINPUT_PASSWORD))
824 input_mouse_mark (w, xevent, cwevent);
825 render_textinput (w);
826 break;
827 case Expose:
828 if (xevent->xexpose.count)
829 return 0;
830 case EnterNotify:
831 w->options &= 0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT;
832 if (xevent->xbutton.x >= w->width - w->height)
833 w->options |= BUTTON_HIGHLIGHT;
834 render_textinput (w);
835 break;
836 case MotionNotify:
837 save_options = w->options;
838 w->options &= ~(BUTTON_PRESSED | BUTTON_HIGHLIGHT);
839 if (xevent->xmotion.x >= w->width - w->height) {
840 w->options |= BUTTON_HIGHLIGHT;
841 if (save_options != w->options)
842 render_textinput (w);
843 return 0;
844 } else {
845 if (!xevent->xmotion.state) {
846 if (save_options != w->options)
847 render_textinput (w);
848 return 0;
849 }
850 }
851 if (!(w->options & TEXTINPUT_PASSWORD))
852 input_mouse_mark (w, xevent, cwevent);
853 render_textinput (w);
854 break;
855 case LeaveNotify:
856 w->options &= 0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT;
857 render_textinput (w);
858 break;
859 case KeyPress:
860 cwevent->ident = w->ident;
861 cwevent->state = xevent->xkey.state;
862 cursor = w->cursor;
863 if (cwevent->insert > 0) {
864 if (!((w->options & TEXTINPUT_NUMBERS) && !(cwevent->insert >= '0' && cwevent->insert <= '9')))
865 input_insert (w, cwevent->insert);
866 handled = 1;
867 } else {
868 unsigned char *intext;
869 intext = (unsigned char *) w->text;
870 switch (cwevent->command) {
871 case CK_Complete:
872 u = draw_selection_completion (w);
873 if (u) {
874 w->cursor = 0;
875 w->text[0] = '\0';
876 while (*u)
877 input_insert (w, *u++);
878 }
879 handled = 1;
880 break;
881 case CK_Selection_History:
882 u = draw_selection_history (w);
883 if (u)
884 while (*u)
885 input_insert (w, *u++);
886 handled = 1;
887 break;
888 case CK_Insert_Unicode:
889 u = (char *) CGetUnichar (CRoot, "Unicode characters");
890 if (u)
891 while (*u)
892 input_insert (w, *u++);
893 handled = 1;
894 break;
895 case CK_XPaste:
896 if (!XGetSelectionOwner (CDisplay, XA_PRIMARY)) {
897 cursor = w->cursor;
898 paste_prop ((void *) w, (void (*)(void *, int)) input_insert,
899 CRoot, XA_CUT_BUFFER0, False);
900 w->cursor = cursor;
901 } else {
902 XConvertSelection (CDisplay, XA_PRIMARY, XA_STRING,
903 XInternAtom (CDisplay, "VT_SELECTION", False),
904 w->winid, CurrentTime);
905 return 0;
906 }
907 handled = 1;
908 break;
909 case CK_BackSpace:
910 if (w->mark1 != w->mark2) {
911 memmove (intext + min (w->mark1, w->mark2), intext + max (w->mark1, w->mark2), strlen ((char *) intext + max (w->mark1, w->mark2)) + 1);
912 w->cursor = min (w->mark1, w->mark2);
913 } else if (w->cursor > 0) {
914 memmove ((char *) intext + w->cursor - 1, intext + w->cursor, strlen ((char *) intext) - w->cursor + 1);
915 w->cursor--;
916 }
917 handled = 1;
918 break;
919 case CK_Left:
920 if (w->cursor > 0)
921 w->cursor--;
922 handled = 1;
923 break;
924 case CK_Down:
925 case CK_Up:
926 case CK_Down_Highlight:
927 case CK_Up_Highlight:
928 if (cwevent->state & ShiftMask) {
929 char *p;
930 w->options |= BUTTON_PRESSED;
931 p = draw_text_input_history (w);
932 if (p) {
933 strncpy (w->text, p, w->textlength);
934 w->keypressed = 1;
935 w->cursor = strlen (w->text);
936 w->firstcolumn = 0;
937 }
938 w->options &= 0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT;
939 handled = 1;
940 }
941 break;
942 case CK_Right:
943 if (w->cursor < strlen ((char *) intext))
944 w->cursor++;
945 handled = 1;
946 break;
947 case CK_Delete:
948 if (w->mark1 != w->mark2) {
949 memmove (intext + min (w->mark1, w->mark2), intext + max (w->mark1, w->mark2), strlen ((char *) intext + max (w->mark1, w->mark2)) + 1);
950 w->cursor = min (w->mark1, w->mark2);
951 } else if (w->cursor < strlen ((char *) intext))
952 memmove (intext + w->cursor, intext + w->cursor + 1, strlen ((char *) intext) - w->cursor + 1);
953 handled = 1;
954 break;
955 case CK_Home:
956 w->cursor = 0;
957 handled = 1;
958 break;
959 case CK_End:
960 w->cursor = strlen ((char *) intext);
961 handled = 1;
962 break;
963 }
964 w->keypressed |= handled;
965 }
966 if (handled) {
967 w->mark1 = w->mark2 = 0;
968 render_textinput (w);
969 }
970 cwevent->text = w->text;
971 }
972
973 return handled;
974 }
975
input_mouse_mark(CWidget * w,XEvent * event,CEvent * ce)976 void input_mouse_mark (CWidget * w, XEvent * event, CEvent * ce)
977 {
978 CPushFont ("editor", 0);
979 mouse_mark (event, ce->double_click, w->funcs);
980 CPopFont ();
981 }
982
983
984
985
986
987
988
989
990