1 /* fieldedtextbox.c - for drawing a scrollable text window widget
2 Copyright (C) 1996-2017 Paul Sheer
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307, USA.
18 */
19
20
21 /*
22 * format:
23 *
24 * the printed fields can have the following:
25 *
26 * '\t': can only occur at the begining and end, or at the beginning.
27 * At the beginning signals right justification. At the beginning
28 * and the end signals centering. eg "\tcentred\t" or "\tleft"
29 * "\v%d": insert pixmap %d at the point. Pixmaps are a special variety
30 * defined by the create_text_pixmap() function. The value passed
31 * to this function must appear after the \v and must be less
32 * than 128.
33 * "\f%d": advance forward %d pixels. %d must be less than 128.
34 * "\r%c": puts %c in 'italic' color, default: green.
35 * "\b%c": puts %c in 'bold' color, default: blue.
36 */
37
38
39 #define BDR 8
40
41 #include <config.h>
42 #include <stdio.h>
43 #include <my_string.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46
47 #include <X11/Intrinsic.h>
48 #include <X11/Xatom.h>
49 #include "lkeysym.h"
50
51 #include "stringtools.h"
52 #include "app_glob.c"
53 #include "edit.h"
54 #include "editcmddef.h"
55
56 #include "coolwidget.h"
57 #include "coollocal.h"
58 #include "mousemark.h"
59 #include "pool.h"
60
61 #include "mad.h"
62
63 extern struct look *look;
64
65 extern int option_text_fg_normal;
66 extern int option_text_fg_bold;
67 extern int option_text_fg_italic;
68
69 extern int option_text_bg_normal;
70 extern int option_text_bg_marked;
71 extern int option_text_bg_highlighted;
72
73 /* for this widget
74 ->column holds the pixel width of the longest line
75 */
76
77 /* char ** (*get_line) (void *data, int line_number, int *num_fields, int *tagged); */
78
79 #define pixmap_width(x) 0
80
81 int eh_fielded_textbox (CWidget * w, XEvent * xevent, CEvent * cwevent);
82 static int calc_text_pos_fielded_textbox (CWidget * w, long b, long *q, int l);
83
84 /* these are not printable in this module */
85 #define this_is_printable(c) (!strchr ("\r\b\t", c))
86
this_text_width(char * s)87 static int this_text_width (char *s)
88 {
89 int l = 0;
90 char *p;
91 for (p = s; *p; p++) {
92 if (*p == '\v')
93 l += pixmap_width (++p);
94 else if (*p == '\f')
95 l += *(++p);
96 else if (this_is_printable (*p))
97 l += FONT_PER_CHAR((unsigned char) *p);
98 }
99 return l;
100 }
101
102 #define INTER_FIELD_SPACING 6
103 #define LINE_OFFSET 0
104
105 /* result must be free'd */
get_field_sizes(void * data,int * num_lines,int * max_width,char ** (* get_line)(void *,int,int *,int *))106 static int *get_field_sizes (void *data, int *num_lines, int *max_width,
107 char **(*get_line) (void *, int, int *, int *))
108 {
109 char **fields;
110 int tagged, i, tab[256], *result, num_fields, max_num_fields = 0, line_number;
111
112 memset (tab, 0, sizeof (tab));
113
114 *num_lines = 0;
115
116 for (line_number = 0;; line_number++) {
117 fields = (*get_line) (data, line_number, &num_fields, &tagged);
118 if (!fields)
119 break;
120 (*num_lines)++;
121 if (max_num_fields < num_fields)
122 max_num_fields = num_fields;
123 for (i = 0; i < num_fields; i++) {
124 int l;
125 if (!fields[i])
126 break;
127 l = this_text_width (fields[i]);
128 if (tab[i] < l)
129 tab[i] = l;
130 }
131 }
132 *max_width = 0;
133 for (i = 0; i < max_num_fields; i++)
134 tab[i] += INTER_FIELD_SPACING;
135 for (i = 0; i < max_num_fields; i++)
136 *max_width += tab[i];
137
138 result = CMalloc ((max_num_fields + 1) * sizeof (int));
139 memcpy (result, tab, max_num_fields * sizeof (int));
140 result[max_num_fields] = 0;
141 return result;
142 }
143
compose_line(void * data,int line_number,unsigned char * line,int * tab,char ** (* get_line)(void *,int,int *,int *),int * tagged)144 static void compose_line (void *data, int line_number, unsigned char *line, int *tab,
145 char **(*get_line) (void *, int, int *, int *), int *tagged)
146 {
147 char **fields;
148 int i, num_fields;
149
150 *line = 0;
151 *tagged = 0;
152
153 if (!data)
154 return;
155
156 fields = (*get_line) (data, line_number, &num_fields, tagged);
157
158 if (!fields)
159 return;
160
161 for (i = 0; i < num_fields; i++) {
162 int l = 0, t, centred = 0;
163 char *p;
164 p = fields[i];
165 t = tab[i] - this_text_width (p) - INTER_FIELD_SPACING;
166 if (t < 0)
167 t = 0;
168 if (p[0] == '\t') {
169 p++;
170 if (p[strlen (p) - 1] == '\t') {
171 l = t - (t / 2);
172 t /= 2;
173 centred = 1;
174 } else {
175 l = t;
176 t = 0;
177 }
178 }
179 for (;;) {
180 l -= 127;
181 if (l >= 0) {
182 *line++ = '\f';
183 *line++ = (unsigned char) 127;
184 } else {
185 l += 127;
186 if (l) {
187 *line++ = '\f';
188 *line++ = (unsigned char) l;
189 }
190 break;
191 }
192 }
193 strcpy ((char *) line, p);
194 line += strlen (p) - centred;
195 if (!fields[i + 1])
196 break;
197 t += INTER_FIELD_SPACING;
198 for (;;) {
199 t -= 127;
200 if (t >= 0) {
201 *line++ = '\f';
202 *line++ = (unsigned char) 127;
203 } else {
204 t += 127;
205 if (t) {
206 *line++ = '\f';
207 *line++ = (unsigned char) t;
208 }
209 break;
210 }
211 }
212 }
213 *line = 0;
214 }
215
compose_line_cached(void * data,int l,int * tab,char ** (* get_line)(void *,int,int *,int *),int * tagged)216 static unsigned char *compose_line_cached (void *data, int l, int *tab, char **(*get_line) (void *, int, int *, int *), int *tagged)
217 {
218 static unsigned char line[4096];
219 static int c_tagged, c_l = -1;
220 if (c_l == l) {
221 *tagged = c_tagged;
222 return line;
223 }
224 compose_line (data, l, line, tab, get_line, tagged);
225 c_l = l;
226 c_tagged = *tagged;
227 return line;
228 }
229
230 static long count_fielded_textbox_lines (CWidget * wdt);
231 void render_fielded_textbox (CWidget * w, int redrawall);
232
link_scrollbar_to_fielded_textbox(CWidget * scrollbar,CWidget * textbox,XEvent * xevent,CEvent * cwevent,int whichscrbutton)233 void link_scrollbar_to_fielded_textbox (CWidget * scrollbar, CWidget * textbox, XEvent * xevent, CEvent * cwevent, int whichscrbutton)
234 {
235 int redrawtext = 0, count, c;
236 static int r = 0;
237 if ((xevent->type == ButtonRelease || xevent->type == MotionNotify) && whichscrbutton == 3) {
238 redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, (double) scrollbar->firstline * textbox->numlines / 65535.0);
239 } else if (xevent->type == ButtonPress && (cwevent->button == Button1 || cwevent->button == Button2)) {
240 switch (whichscrbutton) {
241 case 1:
242 redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, textbox->firstline - (textbox->height / FONT_PIX_PER_LINE - 2));
243 break;
244 case 2:
245 redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, textbox->firstline - 1);
246 break;
247 case 5:
248 redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, textbox->firstline + 1);
249 break;
250 case 4:
251 redrawtext = CSetTextboxPos (textbox, TEXT_SET_LINE, textbox->firstline + (textbox->height / FONT_PIX_PER_LINE - 2));
252 break;
253 }
254 }
255 if (xevent->type == ButtonRelease)
256 render_fielded_textbox (textbox, 0);
257 else {
258 c = CCheckWindowEvent (xevent->xany.window, ButtonReleaseMask | ButtonMotionMask, 0);
259 if (redrawtext) {
260 if (!c) {
261 render_fielded_textbox (textbox, 0);
262 r = 0;
263 } else {
264 r = 1;
265 }
266 } else if (c && r) {
267 render_fielded_textbox (textbox, 0);
268 r = 0;
269 }
270 }
271 count = count_fielded_textbox_lines (textbox);
272 if (!count)
273 count = 1;
274 scrollbar->firstline = (double) 65535.0 *textbox->firstline / (textbox->numlines ? textbox->numlines : 1);
275 scrollbar->numlines = (double) 65535.0 *count / (textbox->numlines ? textbox->numlines : 1);
276 }
277
link_h_scrollbar_to_fielded_textbox(CWidget * scrollbar,CWidget * textbox,XEvent * xevent,CEvent * cwevent,int whichscrbutton)278 void link_h_scrollbar_to_fielded_textbox (CWidget * scrollbar, CWidget * textbox, XEvent * xevent, CEvent * cwevent, int whichscrbutton)
279 {
280 int redrawtext = 0, c;
281 static int r = 0;
282 if ((xevent->type == ButtonRelease || xevent->type == MotionNotify) && whichscrbutton == 3) {
283 redrawtext = CSetTextboxPos (textbox, TEXT_SET_COLUMN, (double) scrollbar->firstline * (textbox->column / FONT_MEAN_WIDTH) / 65535.0);
284 } else if (xevent->type == ButtonPress && (cwevent->button == Button1 || cwevent->button == Button2)) {
285 switch (whichscrbutton) {
286 case 1:
287 redrawtext = CSetTextboxPos (textbox, TEXT_SET_COLUMN, textbox->firstcolumn - (textbox->width / FONT_MEAN_WIDTH - 2));
288 break;
289 case 2:
290 redrawtext = CSetTextboxPos (textbox, TEXT_SET_COLUMN, textbox->firstcolumn - 1);
291 break;
292 case 5:
293 redrawtext = CSetTextboxPos (textbox, TEXT_SET_COLUMN, textbox->firstcolumn + 1);
294 break;
295 case 4:
296 redrawtext = CSetTextboxPos (textbox, TEXT_SET_COLUMN, textbox->firstcolumn + (textbox->width / FONT_MEAN_WIDTH - 2));
297 break;
298 }
299 }
300 if (xevent->type == ButtonRelease)
301 render_fielded_textbox (textbox, 0);
302 else {
303 c = CCheckWindowEvent (xevent->xany.window, ButtonReleaseMask | ButtonMotionMask, 0);
304 if (redrawtext) {
305 if (!c) {
306 render_fielded_textbox (textbox, 0);
307 r = 0;
308 } else {
309 r = 1;
310 }
311 } else if (c && r) {
312 render_fielded_textbox (textbox, 0);
313 r = 0;
314 }
315 }
316 scrollbar->firstline = (double) 65535.0 *(textbox->firstcolumn * FONT_MEAN_WIDTH) / textbox->column;
317 scrollbar->numlines = (double) 65535.0 *(textbox->width - 6) / textbox->column;
318 }
319
320 void edit_translate_xy (int xs, int ys, int *x, int *y);
321 void selection_clear (void);
322 static long current;
323
xy(int x,int y,int * x_return,int * y_return)324 static void xy (int x, int y, int *x_return, int *y_return)
325 {
326 edit_translate_xy (x, y, x_return, y_return);
327 }
328
cp(CWidget * w,int x,int y)329 static long cp (CWidget * w, int x, int y)
330 {
331 long q;
332 y = (y + w->firstline - 1) << 16;
333 if (y < 0)
334 x = y = 0;
335 if (w->options & TEXTBOX_MARK_WHOLE_LINES)
336 x = 0;
337 calc_text_pos_fielded_textbox (w, y, &q, --x);
338 return q;
339 }
340
341 /* return 1 if not marked */
marks(CWidget * w,long * start,long * end)342 static int marks (CWidget * w, long *start, long *end)
343 {
344 if (w->mark1 == w->mark2)
345 return 1;
346 *start = min (w->mark1, w->mark2);
347 *end = max (w->mark1, w->mark2);
348 return 0;
349 }
350
351 extern int range (CWidget * w, long start, long end, int click);
352
move_mark(CWidget * w)353 static void move_mark (CWidget * w)
354 {
355 w->mark2 = w->mark1 = current;
356 }
357
fin_mark(CWidget * w)358 static void fin_mark (CWidget * w)
359 {
360 w->mark2 = w->mark1 = -1;
361 }
362
release_mark(CWidget * w,XEvent * event)363 static void release_mark (CWidget * w, XEvent * event)
364 {
365 w->mark2 = current;
366 if (w->mark2 != w->mark1 && event) {
367 selection_clear ();
368 XSetSelectionOwner (CDisplay, XA_PRIMARY, w->winid, event->xbutton.time);
369 XSetSelectionOwner (CDisplay, ATOM_ICCCM_P2P_CLIPBOARD, w->winid, event->xbutton.time);
370 }
371 }
372
373 /* result must be free'd */
get_block(CWidget * w,long start_mark,long end_mark,int * type,int * l)374 static char *get_block (CWidget * w, long start_mark, long end_mark, int *type, int *l)
375 {
376 POOL *p;
377 int tagged, i;
378 unsigned char c, *t;
379 long x, y, a, b;
380 void *data;
381 CPushFont ("editor", 0);
382
383 a = min (w->mark2, w->mark1);
384 b = max (w->mark2, w->mark1);
385 x = a & 0xFFFFL;
386 y = a >> 16;
387
388 p = pool_init ();
389
390 for (;; y++) {
391 unsigned char *text;
392 if (y < w->numlines)
393 data = w->hook;
394 else
395 data = 0;
396 text = compose_line_cached (data, y, w->tab, w->get_line, &tagged);
397 for (;; x++) {
398 if (y == (b >> 16))
399 if (x >= (b & 0xFFFFL))
400 goto done;
401 if (y > (b >> 16))
402 goto done;
403 c = text[x];
404 if (!c) {
405 c = '\n';
406 pool_write (p, (unsigned char *) &c, 1);
407 break;
408 }
409 if (c == '\f') {
410 int j;
411 #ifdef HAVE_DND
412 if (w->options & TEXTBOX_FILE_LIST) { /* this is a filelist, so only get the first field: i.e. the file name */
413 #else
414 if (*type == DndFiles || *type == DndFile) { /* this is a filelist, so only get the first field: i.e. the file name */
415 #endif
416 c = '\n';
417 pool_write (p, (unsigned char *) "\n", 1);
418 break;
419 }
420 j = text[++x];
421 while ((j -= FONT_PER_CHAR(' ')) > 0)
422 pool_write (p, (unsigned char *) " ", 1);
423 pool_write (p, (unsigned char *) " ", 1);
424 continue;
425 }
426 if (c == '\v') {
427 int j;
428 j = pixmap_width (text[++x]);
429 while ((j -= FONT_PER_CHAR(' ')) > 0)
430 pool_write (p, (unsigned char *) " ", 1);
431 continue;
432 }
433 if (this_is_printable (c))
434 pool_write (p, (unsigned char *) &c, 1);
435 }
436 x = 0;
437 }
438 done:
439 CPopFont ();
440 #ifdef HAVE_DND
441 *type = DndText;
442 #endif
443 *l = pool_length (p);
444 pool_null (p);
445 #ifdef HAVE_DND
446 if (!(w->options & TEXTBOX_FILE_LIST))
447 #else
448 if (!(*type == DndFiles || *type == DndFile))
449 #endif
450 return (char *) pool_break (p);
451 t = (unsigned char *) CDndFileList ((char *) pool_start (p), l, &i);
452 pool_free (p);
453 if (i == 1)
454 *type = DndFile;
455 else
456 *type = DndFiles;
457 return (char *) t;
458 }
459
460 static void move (CWidget * w, long click, int row)
461 {
462 int h;
463 current = click;
464 if (w->mark2 == -1)
465 w->mark1 = current;
466 h = (w->height - BDR) / FONT_PIX_PER_LINE;
467 if (row > h && w->firstline < w->numlines - h - 2)
468 CSetTextboxPos (w, TEXT_SET_LINE, w->firstline + row - h);
469 if (row < 1)
470 CSetTextboxPos (w, TEXT_SET_LINE, w->firstline + row - 1);
471 w->mark2 = click;
472 }
473
474 static void motion (CWidget * w, long click)
475 {
476 w->mark2 = click;
477 }
478
479 struct mouse_funcs fielded_mouse_funcs = {
480 0,
481 (void (*)(int, int, int *, int *)) xy,
482 (long (*)(void *, int, int)) cp,
483 (int (*)(void *, long *, long *)) marks,
484 (int (*)(void *, long, long, long)) range,
485 (void (*)(void *)) fin_mark,
486 (void (*)(void *)) move_mark,
487 (void (*)(void *, XEvent *)) release_mark,
488 (char *(*)(void *, long, long, int *, int *)) get_block,
489 (void (*)(void *, long, int)) move,
490 (void (*)(void *, long)) motion,
491 0,
492 0,
493 0,
494 0,
495 DndFile
496 };
497
498 CWidget *CDrawFieldedTextbox (const char *identifier, Window parent, int x, int y,
499 int width, int height, int line, int column,
500 char **(*get_line) (void *, int, int *, int *),
501 long options, void *data)
502 {
503 char *scroll;
504 int numlines;
505 CWidget *wdt;
506 int *tab, wf;
507
508 int w, h, x_hint, y_hint;
509 CPushFont ("editor", 0);
510
511 tab = get_field_sizes (data, &numlines, &wf, get_line);
512
513 if (width == AUTO_WIDTH)
514 w = wf + 6;
515 else
516 w = width;
517 if (height == AUTO_HEIGHT)
518 h = (max (1, numlines)) * FONT_PIX_PER_LINE + 6;
519 else
520 h = height;
521
522 wdt = CSetupWidget (identifier, parent, x, y,
523 w, h, C_FIELDED_TEXTBOX_WIDGET, INPUT_KEY,
524 color_palette (option_text_bg_normal), 1);
525
526 xdnd_set_type_list (CDndClass, wdt->winid, xdnd_typelist_send[DndText]);
527
528 wdt->eh = eh_fielded_textbox;
529 wdt->options = options | WIDGET_TAKES_SELECTION;
530
531 wdt->firstline = line;
532 wdt->firstcolumn = column;
533 wdt->column = wf;
534 wdt->cursor = 0;
535 wdt->numlines = numlines;
536 wdt->tab = tab;
537 wdt->get_line = get_line;
538 wdt->hook = data;
539 wdt->funcs = mouse_funcs_new (wdt, &fielded_mouse_funcs);
540
541 if (h > 80 && height != AUTO_HEIGHT) {
542 wdt->vert_scrollbar = CDrawVerticalScrollbar (scroll = catstrs (identifier, ".vsc", NULL), parent,
543 x + w + WIDGET_SPACING, y, h, AUTO_WIDTH, 0, 0);
544 CSetScrollbarCallback (wdt->vert_scrollbar->ident, wdt->ident, link_scrollbar_to_fielded_textbox);
545 CGetHintPos (&x_hint, 0);
546 } else {
547 x_hint = x + w + WIDGET_SPACING;
548 }
549
550 if (w > 80 && width != AUTO_WIDTH) {
551 wdt->hori_scrollbar = CDrawHorizontalScrollbar (scroll = catstrs (identifier, ".hsc", NULL), parent,
552 x, y + h + WIDGET_SPACING, w, (*look->get_fielded_textbox_hscrollbar_width) (), 0, 65535);
553 CSetScrollbarCallback (wdt->hori_scrollbar->ident, wdt->ident, link_h_scrollbar_to_fielded_textbox);
554 CGetHintPos (0, &y_hint);
555 } else {
556 y_hint = y + h + WIDGET_SPACING;
557 }
558
559 set_hint_pos (x_hint, y_hint);
560 CPopFont ();
561 return wdt;
562 }
563
564
565 CWidget *CRedrawFieldedTextbox (const char *identifier, int preserve)
566 {
567 int numlines;
568 CWidget *wdt;
569 int *tab, w;
570
571 CPushFont ("editor", 0);
572 wdt = CIdent (identifier);
573 tab = get_field_sizes (wdt->hook, &numlines, &w, wdt->get_line);
574
575 if (!preserve) {
576 wdt->firstline = 0;
577 wdt->firstcolumn = 0;
578 wdt->cursor = 0;
579 }
580 wdt->column = w;
581 wdt->numlines = numlines;
582 if (wdt->tab)
583 free (wdt->tab);
584 wdt->tab = tab;
585 wdt->mark1 = wdt->mark2 = -1;
586
587 CSetColor (color_palette (option_text_bg_normal));
588 CRectangle (wdt->winid, 3, 3, wdt->width - 3 - 1, wdt->height - 3 - 1);
589 CExpose (identifier);
590
591 CPopFont ();
592 return wdt;
593 }
594
595 static int calc_text_pos_fielded_textbox (CWidget *w, long b, long *q, int l)
596 {
597 int x = 0, c, xn = 0, tagged;
598 unsigned char *text;
599 long k;
600 void *data = 0;
601
602 if ((b >> 16) < w->numlines)
603 data = w->hook;
604 text = compose_line_cached (data, b >> 16, w->tab, w->get_line, &tagged);
605
606 k = b & 0xFFFFL;
607 if (k == 0xFFFFL)
608 k = 0;
609 for (;;) {
610 c = text[k];
611 switch (c) {
612 case '\0':
613 case '\n':
614 *q = b;
615 return x;
616 case '\f':
617 xn = x + text[k + 1];
618 if (xn > l) {
619 *q = b;
620 return x;
621 break;
622 }
623 k++;
624 b++;
625 break;
626 case '\b':
627 case '\r':
628 xn = x + FONT_PER_CHAR(text[k + 1]);
629 if (xn > l) {
630 *q = b;
631 return x;
632 break;
633 }
634 k++;
635 b++;
636 break;
637 case '\v':
638 xn = x + pixmap_width (text[k + 1]);
639 if (xn > l) {
640 *q = b;
641 return x;
642 break;
643 }
644 k++;
645 b++;
646 break;
647 default:
648 xn = x + FONT_PER_CHAR(c);
649 break;
650 }
651 if (xn > l)
652 break;
653 x = xn;
654 b++;
655 k++;
656 }
657 *q = b;
658 return x;
659 }
660
661 extern int highlight_this_line;
662
663 /* here upper 16 bits of q are the line, lowe 16, the column */
664 static void convert_text_fielded_textbox (CWidget * w, long q, cache_type *line, cache_type *eol, int x, int x_max, int row)
665 {
666 unsigned char *text;
667 int c, tagged, bold = 0, italic = 0;
668 cache_type *p;
669 long m1, m2, k;
670 void *data = 0;
671
672 m1 = min (w->mark1, w->mark2);
673 m2 = max (w->mark1, w->mark2);
674
675 if ((q >> 16) < w->numlines)
676 data = w->hook;
677 text = compose_line_cached (data, q >> 16, w->tab, w->get_line, &tagged);
678
679 k = q & 0xFFFFL;
680 if (k == 0xFFFFL)
681 k = 0;
682
683 p = line;
684 p->c.ch = p->_style = 0;
685 for (;;) {
686 c = text[k];
687 p[1]._style = p[1].c.ch = 0;
688 p->c.fg = p->c.bg = 0xFF; /* default background colors */
689 if (highlight_this_line)
690 p->c.style |= MOD_HIGHLIGHTED;
691 if (tagged)
692 p->c.style |= MOD_INVERSE;
693 if (q >= m1 && q < m2)
694 p->c.style |= MOD_MARKED;
695 switch (c) {
696 case '\0':
697 case '\n':
698 (p++)->c.ch = ' ';
699 if (p >= eol)
700 goto end_loop;
701 if (highlight_this_line || tagged) {
702 q--;
703 k--;
704 x += FONT_PER_CHAR(' ');
705 } else
706 return;
707 break;
708 case '\f':
709 k++;
710 q++;
711 p->c.style |= MOD_TAB;
712 (p++)->c.ch = text[k];
713 if (p >= eol)
714 goto end_loop;
715 x += text[k];
716 break;
717 case '\b':
718 bold = 2;
719 break;
720 case '\r':
721 italic = 2;
722 break;
723 case '\v':
724 k++;
725 q++;
726 p->c.style |= MOD_TAB;
727 (p++)->c.ch = text[k];
728 if (p >= eol)
729 goto end_loop;
730 x += pixmap_width (text[k]);
731 break;
732 default:
733 x += FONT_PER_CHAR(c);
734 p->c.ch = c;
735 if (italic > 0)
736 p->c.style |= MOD_ITALIC;
737 if (bold > 0)
738 p->c.style |= MOD_BOLD;
739 p++;
740 if (p >= eol)
741 goto end_loop;
742 break;
743 }
744 italic--;
745 bold--;
746 if (x > x_max)
747 break;
748 q++;
749 k++;
750 }
751 end_loop:
752 p->c.ch = p->_style = 0;
753 }
754
755
756 static void fielded_text_print_line (CWidget * w, long b, int row)
757 {
758 edit_draw_proportional (w,
759 (void (*)(void *, long, cache_type *, cache_type *, int, int, int)) convert_text_fielded_textbox,
760 (int (*)(void *, long, long *, int)) calc_text_pos_fielded_textbox,
761 -w->firstcolumn * FONT_MEAN_WIDTH, w->winid,
762 w->width, b, row, row * FONT_PIX_PER_LINE + EDIT_TEXT_VERTICAL_OFFSET, 0,
763 1);
764 }
765
766
767
768 /*
769 ->firstline is line number of the top line in the window.
770 ->firstcolumn is column shift (positive).
771 ->current is actual char position of first line in display.
772 ->numlines is the total number of lines.
773 ->cursor is the number of the highlighted line.
774 ->textlength is the length of text excluding trailing NULL.
775 First three must be initialised to proper values (e.g. 0, 0 and 0).
776 */
777
778 extern int EditExposeRedraw;
779 extern int EditClear;
780 extern unsigned long edit_normal_background_color;
781
782 void render_fielded_textbox (CWidget * w, int redrawall)
783 {
784 int row, height, isfocussed, curs, i, x;
785 static Window last_win = 0;
786 static int last_firstcolumn = 0;
787 CPushFont ("editor", 0);
788 if (redrawall) {
789 EditExposeRedraw = 1;
790 EditClear = 1;
791 }
792 if (last_win == w->winid && last_firstcolumn != w->firstcolumn) {
793 x = 0;
794 CSetColor (color_palette (option_text_bg_normal));
795 for (i = 0;; i++) {
796 x += w->tab[i];
797 if (x >= w->column)
798 break;
799 CLine (w->winid, x + LINE_OFFSET - (last_firstcolumn * FONT_MEAN_WIDTH), 3, x + LINE_OFFSET - (last_firstcolumn * FONT_MEAN_WIDTH), (w->numlines - w->firstline) * FONT_PIX_PER_LINE);
800 }
801 }
802 last_firstcolumn = w->firstcolumn;
803 last_win = w->winid;
804
805 height = w->height / FONT_PIX_PER_LINE;
806
807 isfocussed = (w->winid == CGetFocus ());
808 curs = !(w->options & TEXTBOX_NO_CURSOR || w->mark1 != w->mark2); /* don't draw the cursor line */
809
810 edit_set_foreground_colors (color_palette (option_text_fg_normal), color_palette (option_text_fg_bold), color_palette (option_text_fg_italic));
811 edit_set_background_colors (color_palette (option_text_bg_normal), color_palette (0), color_palette (option_text_bg_marked), color_palette (9), color_palette (option_text_bg_highlighted));
812
813 for (row = 0; row < height; row++) {
814 if (row + w->firstline == w->cursor && isfocussed && curs)
815 highlight_this_line = 1;
816 else
817 highlight_this_line = 0;
818 fielded_text_print_line (w, (row + w->firstline) << 16, row);
819 }
820
821 x = 0;
822 CSetColor (COLOR_FLAT);
823 for (i = 0;; i++) {
824 if (!w->tab[i])
825 break;
826 x += w->tab[i];
827 if (x >= w->column)
828 break;
829 CLine (w->winid, x + LINE_OFFSET - (w->firstcolumn * FONT_MEAN_WIDTH), 3, x + LINE_OFFSET - (w->firstcolumn * FONT_MEAN_WIDTH), (w->numlines - w->firstline) * FONT_PIX_PER_LINE + 3);
830 }
831 if ((w->numlines - w->firstline) * FONT_PIX_PER_LINE < w->height) {
832 x = 0;
833 CSetColor (color_palette (option_text_bg_normal));
834 for (i = 0;; i++) {
835 if (!w->tab[i])
836 break;
837 x += w->tab[i];
838 if (x >= w->column)
839 break;
840 CLine (w->winid, x + LINE_OFFSET - (w->firstcolumn * FONT_MEAN_WIDTH), (w->numlines - w->firstline) * FONT_PIX_PER_LINE + 3, x + LINE_OFFSET - (w->firstcolumn * FONT_MEAN_WIDTH), w->height - 3);
841 }
842 }
843 EditExposeRedraw = 0;
844 EditClear = 0;
845
846 (*look->render_fielded_textbox_tidbits) (w, isfocussed);
847
848 CPopFont ();
849 return;
850 }
851
852 /*
853 Count the number of lines that would be printed
854 by the above routine, but don't print anything.
855 If all is non-zero then count all the lines.
856 */
857 static long count_fielded_textbox_lines (CWidget * wdt)
858 {
859 long height;
860 height = wdt->height / FONT_PIX_PER_LINE;
861 if (wdt->numlines - wdt->firstline < height)
862 return wdt->numlines - wdt->firstline;
863 return height;
864 }
865
866 static void fielded_text_mouse_mark (CWidget * w, XEvent * event, CEvent * ce)
867 {
868 CPushFont ("editor", 0);
869 mouse_mark (event, ce->double_click, w->funcs);
870 CPopFont ();
871 }
872
873 static struct selection fieldedtext_selection = {0, 0};
874
875 /* gets selected text into selection structure, stripping nroff */
876 void fielded_text_get_selection (CWidget * w)
877 {
878 int type;
879 if (fieldedtext_selection.text)
880 free (fieldedtext_selection.text);
881 fieldedtext_selection.text = (unsigned char *) get_block (w, 0, 0, &type, &fieldedtext_selection.len);
882 selection = fieldedtext_selection;
883 }
884
885
886 void selection_send (XSelectionRequestEvent * rq);
887
888 int eh_fielded_textbox (CWidget * w, XEvent * xevent, CEvent * cwevent)
889 {
890 int handled = 0, redrawall = 0, count;
891
892 switch (xevent->type) {
893 case SelectionRequest:
894 fielded_text_get_selection (w);
895 selection_send (&(xevent->xselectionrequest));
896 return 1;
897 case Expose:
898 if (!xevent->xexpose.count)
899 redrawall = 1;
900 break;
901 case ClientMessage:
902 w->mark1 = w->mark2 = 0;
903 break;
904 case ButtonPress:
905 CPushFont ("editor", 0);
906 CFocus (w);
907 if (xevent->xbutton.button == Button1)
908 w->cursor = (xevent->xbutton.y - BDR) / FONT_PIX_PER_LINE + w->firstline;
909 if (w->cursor > w->numlines - 1)
910 w->cursor = w->numlines - 1;
911 if (w->cursor < 0)
912 w->cursor = 0;
913 cwevent->ident = w->ident;
914 cwevent->xt = (xevent->xbutton.x - 7) / FONT_MEAN_WIDTH + w->firstcolumn;
915 cwevent->yt = w->cursor;
916 CPopFont ();
917 case ButtonRelease:
918 case MotionNotify:
919 if (!xevent->xmotion.state && xevent->type == MotionNotify)
920 return 0;
921 resolve_button (xevent, cwevent);
922 fielded_text_mouse_mark (w, xevent, cwevent);
923 break;
924 case FocusIn:
925 case FocusOut:
926 break;
927 case KeyPress:
928 cwevent->ident = w->ident;
929 if (!(TEXTBOX_NO_KEYS & w->options)) {
930 if (w->options & TEXTBOX_FILE_LIST && w->hook) {
931 if (cwevent->key == XK_Insert || cwevent->key == XK_KP_Insert) {
932 if (w->mark1 == w->mark2) {
933 struct file_entry *f;
934 f = (struct file_entry *) w->hook;
935 if (f[w->cursor].options & FILELIST_TAGGED_ENTRY)
936 f[w->cursor].options &= (0xFFFFFFFFUL - FILELIST_TAGGED_ENTRY);
937 else
938 f[w->cursor].options |= FILELIST_TAGGED_ENTRY;
939 CTextboxCursorMove (w, CK_Down);
940 handled = 1;
941 break;
942 }
943 }
944 }
945 handled = CTextboxCursorMove (w, cwevent->command);
946 }
947 break;
948 default:
949 return 0;
950 }
951
952 /* Now draw the changed text box, count will contain
953 the number of textlines displayed */
954 render_fielded_textbox (w, redrawall);
955 count = count_fielded_textbox_lines (w);
956
957 /* now update the scrollbar position */
958 if (w->vert_scrollbar && w->numlines) {
959 w->vert_scrollbar->firstline = (double) 65535.0 *w->firstline / (w->numlines ? w->numlines : 1);
960 w->vert_scrollbar->numlines = (double) 65535.0 *count / (w->numlines ? w->numlines : 1);
961 w->vert_scrollbar->options = 0;
962 render_scrollbar (w->vert_scrollbar);
963 }
964 if (w->hori_scrollbar && w->column) {
965 w->hori_scrollbar->firstline = (double) 65535.0 *(w->firstcolumn * FONT_MEAN_WIDTH) / w->column;
966 w->hori_scrollbar->numlines = (double) 65535.0 *(w->width - 6) / w->column;
967 w->hori_scrollbar->options = 0;
968 render_scrollbar (w->hori_scrollbar);
969 }
970 return handled;
971 }
972
973