1 /* propfont.c - editor text drawing for proportional fonts.
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 #define WIDE_CHAR
21 
22 #include <config.h>
23 #include "edit.h"
24 #if defined (HAVE_MAD) && ! defined (MIDNIGHT) && ! defined (GTK)
25 #include "mad.h"
26 #endif
27 #ifdef HAVE_WCHAR_H
28 #include <wchar.h>
29 #endif
30 
31 /* this file definatively relies on int being 32 bits or more */
32 
33 int option_long_whitespace = 0;
34 int cache_width = 0;
35 int cache_height = 0;
36 
37 /* must be a multiple of 8 */
38 #define MAX_LINE_LEN 8192
39 
40 
41 /* background colors: marked is refers to mouse highlighting, highlighted refers to a found string. */
42 extern unsigned long edit_abnormal_color, edit_marked_abnormal_color;
43 extern unsigned long edit_highlighted_color, edit_marked_color;
44 extern unsigned long edit_normal_background_color;
45 
46 /* foreground colors */
47 extern unsigned long edit_normal_foreground_color, edit_bold_color;
48 extern unsigned long edit_italic_color;
49 
50 /* cursor color */
51 extern unsigned long edit_cursor_color;
52 
53 extern int EditExposeRedraw;
54 extern int EditClear;
55 
set_style_color(Window win,cache_type s,unsigned long * fg,unsigned long * bg)56 int set_style_color (
57 #ifdef GTK
58 			 Window win,
59 #endif
60 		      cache_type s, unsigned long *fg, unsigned long *bg)
61 {
62     int fgp, bgp, underlined = 0;
63     fgp = s.c.fg;
64 /* NO_COLOR would give fgp == 255 */
65     if (fgp < 0xFF)
66 	*fg = color_palette (fgp);
67     else
68 	*fg = edit_normal_foreground_color;
69     bgp = s.c.bg;
70     if (bgp == 0xFE)
71 	underlined = 1;
72     if (bgp < 0xFD)
73 	*bg = color_palette (bgp);
74     else
75 	*bg = edit_normal_background_color;
76     if (!(s.c.style | s.c.fg | s.c.fg))	/* check this first as an optimization */
77 	return underlined;
78     if (s.c.style & MOD_ABNORMAL) {
79 	*bg = edit_abnormal_color;
80 	if (s.c.style & MOD_MARKED)
81 	    *bg = edit_marked_abnormal_color;
82     } else if (s.c.style & MOD_HIGHLIGHTED) {
83 	*bg = edit_highlighted_color;
84     } else if (s.c.style & MOD_MARKED) {
85 	*bg = edit_marked_color;
86     }
87     if (s.c.style & MOD_BOLD)
88 	*fg = edit_bold_color;
89     if (s.c.style & MOD_ITALIC)
90 	*fg = edit_italic_color;
91     if (s.c.style & MOD_INVERSE) {
92 	unsigned long t;
93 	t = *fg;
94 	*fg = *bg;
95 	*bg = t;
96 	if (*bg == COLOR_BLACK)
97 	    *bg = color_palette (1);
98     }
99     return underlined;
100 }
101 
102 #ifdef GTK
103 #define set_style_color(s,f,b) set_style_color(win,s,f,b)
104 #endif
105 
106 int tab_width = 1;
107 
next_tab_pos(int x)108 static inline int next_tab_pos (int x)
109 {
110     return x += tab_width - x % tab_width;
111 }
112 
113 /* this now properly uses ctypes */
convert_to_long_printable(wchar_t c,wchar_t * t)114 static inline int convert_to_long_printable (wchar_t c, wchar_t * t)
115 {
116     c &= 0x7FFFFFFFUL;
117     if (wc_isgraph (c)) {
118 	t[0] = c;
119 	t[1] = 0;
120 	return FONT_PER_CHAR_256 (c);
121     }
122     if (c == ' ') {
123 	if (option_long_whitespace) {
124 	    t[0] = ' ';
125 	    t[1] = ' ';
126 	    t[2] = 0;
127 	    return FONT_PER_CHAR (' ') + FONT_PER_CHAR (' ');
128 	} else {
129 	    t[0] = ' ';
130 	    t[1] = 0;
131 	    return FONT_PER_CHAR (' ');
132 	}
133     }
134     if (option_international_characters && FONT_PER_CHAR (c)) {
135 	t[0] = c;
136 	t[1] = 0;
137 	return FONT_PER_CHAR (c);
138     }
139     if (c > 0xFFFF) {
140 	t[0] = ("0123456789ABCDEF")[(c >> 28) & 0xF];
141 	t[1] = ("0123456789ABCDEF")[(c >> 24) & 0xF];
142 	t[2] = ("0123456789ABCDEF")[(c >> 20) & 0xF];
143 	t[3] = ("0123456789ABCDEF")[(c >> 16) & 0xF];
144 	t[4] = ("0123456789ABCDEF")[(c >> 12) & 0xF];
145 	t[5] = ("0123456789ABCDEF")[(c >> 8) & 0xF];
146 	t[6] = ("0123456789ABCDEF")[(c >> 4) & 0xF];
147 	t[7] = ("0123456789ABCDEF")[c & 0xF];
148 	t[8] = 'h';
149 	t[9] = 0;
150 	return FONT_PER_CHAR (t[0]) + FONT_PER_CHAR (t[1]) + FONT_PER_CHAR (t[2]) + FONT_PER_CHAR (t[3]) +
151 	    FONT_PER_CHAR (t[4]);
152     }
153     if (c > 0xFF) {
154 	t[0] = ("0123456789ABCDEF")[(c >> 12) & 0xF];
155 	t[1] = ("0123456789ABCDEF")[(c >> 8) & 0xF];
156 	t[2] = ("0123456789ABCDEF")[(c >> 4) & 0xF];
157 	t[3] = ("0123456789ABCDEF")[c & 0xF];
158 	t[4] = 'h';
159 	t[5] = 0;
160 	return FONT_PER_CHAR (t[0]) + FONT_PER_CHAR (t[1]) + FONT_PER_CHAR (t[2]) + FONT_PER_CHAR (t[3]) +
161 	    FONT_PER_CHAR (t[4]);
162     }
163     if (c > '~') {
164 	t[0] = ("0123456789ABCDEF")[c >> 4];
165 	t[1] = ("0123456789ABCDEF")[c & 0xF];
166 	t[2] = 'h';
167 	t[3] = 0;
168 	return FONT_PER_CHAR (t[0]) + FONT_PER_CHAR (t[1]) + FONT_PER_CHAR (t[2]);
169     }
170     t[0] = '^';
171     t[1] = c + '@';
172     t[2] = 0;
173     return FONT_PER_CHAR (t[0]) + FONT_PER_CHAR (t[1]);
174 }
175 
176 /* same as above but just gets the length */
width_of_long_printable(wchar_t c)177 static inline int width_of_long_printable (wchar_t c)
178 {
179     c &= 0x7FFFFFFFUL;
180     if (wc_isgraph (c))
181 	return FONT_PER_CHAR (c);
182     if (c == ' ') {
183 	if (option_long_whitespace)
184 	    return FONT_PER_CHAR (' ') + FONT_PER_CHAR (' ');
185 	else
186 	    return FONT_PER_CHAR (' ');
187     }
188     if (option_international_characters && FONT_PER_CHAR (c))
189 	return FONT_PER_CHAR (c);
190     if (c > 0xFF)
191 	return FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 12) & 0xF]) +
192 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 8) & 0xF]) +
193 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 4) & 0xF]) +
194 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[c & 0xF]) + FONT_PER_CHAR ((unsigned char) 'h');
195     if (c > 0xFFFF)
196 	return FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 28) & 0xF]) +
197 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 24) & 0xF]) +
198 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 20) & 0xF]) +
199 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 16) & 0xF]) +
200 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 12) & 0xF]) +
201 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 8) & 0xF]) +
202 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 4) & 0xF]) +
203 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[c & 0xF]) + FONT_PER_CHAR ((unsigned char) 'h');
204     if (c > '~')
205 	return FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[c >> 4]) +
206 	    FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[c & 0xF]) + FONT_PER_CHAR ((unsigned char) 'h');
207     return FONT_PER_CHAR ('^') + FONT_PER_CHAR (c + '@');
208 }
209 
edit_width_of_long_printable(wchar_t c)210 int edit_width_of_long_printable (wchar_t c)
211 {
212     return width_of_long_printable (c);
213 }
214 
215 /* returns x pixel pos of char at offset *q with x not more than l */
calc_text_pos(WEdit * edit,long b,long * q,int l)216 static int calc_text_pos (WEdit * edit, long b, long *q, int l)
217 {
218     int x = 0, xn = 0;
219     wchar_t c;
220     for (;;) {
221 	c = edit_get_wide_byte (edit, b);
222 	switch (c) {
223 	case -1:
224 /* no character since used up by a multi-byte sequence */
225 	    break;
226 	case '\n':
227 	    *q = b;
228 	    if (x > edit->max_column)
229 		edit->max_column = x;
230 	    return x;
231 	case '\t':
232 	    xn = next_tab_pos (x);
233 	    break;
234 	default:
235 	    xn = x + width_of_long_printable (c);
236 	    break;
237 	}
238 	if (xn > l)
239 	    break;
240 	x = xn;
241 	b++;
242     }
243     *q = b;
244     if (x > edit->max_column)
245 	edit->max_column = x;
246     return x;
247 }
248 
249 
250 /* calcs pixel length of the line beginning at b up to upto */
calc_text_len(WEdit * edit,long b,long upto)251 static int calc_text_len (WEdit * edit, long b, long upto)
252 {
253     int x = 0;
254     wchar_t c;
255     for (;;) {
256 	if (b == upto) {
257 	    if (x > edit->max_column)
258 		edit->max_column = x;
259 	    return x;
260 	}
261 	c = edit_get_wide_byte (edit, b);
262 	switch (c) {
263 	case -1:
264 /* no character since used up by a multi-byte sequence */
265 	    break;
266 	case '\n':{
267 		if (x > edit->max_column)
268 		    edit->max_column = x;
269 		return x;
270 	    }
271 	case '\t':
272 	    x = next_tab_pos (x);
273 	    break;
274 	default:
275 	    x += width_of_long_printable (c);
276 	    break;
277 	}
278 	b++;
279     }
280 }
281 
282 /* If pixels is zero this returns the count of pixels from current to upto. */
283 /* If upto is zero returns index of pixels across from current. */
edit_move_forward3(WEdit * edit,long current,int pixels,long upto)284 long edit_move_forward3 (WEdit * edit, long current, int pixels, long upto)
285 {
286     CPushFont ("editor", 0);
287     if (upto) {
288 	current = calc_text_len (edit, current, upto);
289     } else if (pixels) {
290 	long q;
291 	calc_text_pos (edit, current, &q, pixels);
292 	current = q;
293     }
294     CPopFont ();
295     return current;
296 }
297 
298 extern int column_highlighting;
299 
300 /* gets the characters style (eg marked, highlighted) from its position in the edit buffer */
get_style_fast(WEdit * edit,long q,wchar_t c)301 static inline cache_type get_style_fast (WEdit * edit, long q, wchar_t c)
302 {
303     cache_type s;
304     unsigned int fg, bg;
305     s.c.ch = s._style = 0;
306     if (!(wc_isprint (c) || (option_international_characters && FONT_PER_CHAR(c))))
307 	if (c != '\n' && c != '\t')
308 	    s.c.style = MOD_ABNORMAL;
309     edit_get_syntax_color (edit, q, (int *) &fg, (int *) &bg);
310     s.c.fg = fg;
311     s.c.bg = bg;
312     return s;
313 }
314 
315 /* gets the characters style (eg marked, highlighted) from its position in the edit buffer */
get_style(WEdit * edit,long q,wchar_t c,long m1,long m2,int x)316 static inline cache_type get_style (WEdit * edit, long q, wchar_t c, long m1, long m2, int x)
317 {
318     cache_type s;
319     unsigned int fg, bg;
320     s.c.ch = s._style = 0;
321     if (q == edit->curs1)
322 	s.c.style |= MOD_CURSOR;
323     if (q >= m1 && q < m2) {
324 	if (column_highlighting) {
325 	    if ((x >= edit->column1 && x < edit->column2)
326 		|| (x >= edit->column2 && x < edit->column1))
327 		s.c.style |= MOD_INVERSE;
328 	} else {
329 	    s.c.style |= MOD_MARKED;
330 	}
331     }
332     if (q == edit->bracket)
333 	s.c.style |= MOD_BOLD;
334     if (q >= edit->found_start && q < edit->found_start + edit->found_len)
335 	s.c.style |= MOD_HIGHLIGHTED;
336     if (!(wc_isprint (c) || (option_international_characters && FONT_PER_CHAR(c))))
337 	if (c != '\n' && c != '\t')
338 	    s.c.style |= MOD_ABNORMAL;
339     edit_get_syntax_color (edit, q, (int *) &fg, (int *) &bg);
340     s.c.fg = fg;
341     s.c.bg = bg;
342     return s;
343 }
344 
convert_text(WEdit * edit,long q,cache_type * p,cache_type * eol,int x,int x_max,int row)345 static void convert_text (WEdit * edit, long q, cache_type * p, cache_type * eol, int x, int x_max, int row)
346 {
347     wchar_t c;
348     cache_type s;
349     long m1, m2, last;
350     wchar_t *r, text[12];
351     int book_mark_colors[10], book_mark;
352     eval_marks (edit, &m1, &m2);
353     book_mark = book_mark_query_all (edit, edit->start_line + row, book_mark_colors);
354     last = q + (x_max - x) / 2 + 2;	/* for optimization, we say that the last character
355 					   of this line cannot have an offset greater than this.
356 					   This can be used to rule out uncommon text styles,
357 					   like a character with a cursor, or selected text */
358 
359     if (book_mark) {
360 	int the_end = 0, book_mark_cycle = 0;
361 	for (;;) {
362 	    c = edit_get_wide_byte (edit, q);
363 	    if (!the_end)
364 		*p = get_style (edit, q, c, m1, m2, x);
365 	    if (the_end)
366 		p->c.ch = p->_style = 0;
367 	    book_mark_cycle = (book_mark_cycle + 1) % book_mark;
368 	    p->c.fg = book_mark_colors[book_mark_cycle] << 8;
369 	    p->c.bg = book_mark_colors[book_mark_cycle];
370 	    switch (c) {
371 	    case -1:
372 /* no character since used up by a multi-byte sequence */
373 		break;
374 	    case '\n':
375 		the_end = 1;
376 		c = ' ';
377 		q--;
378 		goto the_default;
379 	    case '\t':
380 		if (FIXED_FONT) {
381 		    int t;
382 		    t = next_tab_pos (x);
383 		    t = min (t, x_max);
384 		    s = *p;
385 		    s.c.ch = ' ';
386 		    while (x < t) {
387 			x += FONT_PER_CHAR(' ');
388 			*p++ = s;
389 			if (p >= eol)
390 			    goto end_loop;
391 		    }
392 		} else {
393 		    (p++)->c.ch = '\t';
394 		    if (p >= eol)
395 			goto end_loop;
396 		    x = next_tab_pos (x);
397 		}
398 		break;
399 	    default:
400 	      the_default:
401 		x += convert_to_long_printable (c, text);
402 		r = text;
403 		s = *p;
404 		s.c.ch = *r++;
405 		*p++ = s;
406 		if (!*r)
407 		    break;
408 		s.c.ch = *r++;
409 		*p++ = s;
410 		if (!*r)
411 		    break;
412 		s.c.ch = *r++;
413 		*p++ = s;
414 		if (!*r)
415 		    break;
416 		s.c.ch = *r++;
417 		*p++ = s;
418 		if (!*r)
419 		    break;
420 		s.c.ch = *r++;
421 		*p++ = s;
422 		if (!*r)
423 		    break;
424 		s.c.ch = *r++;
425 		*p++ = s;
426 		if (!*r)
427 		    break;
428 		s.c.ch = *r++;
429 		*p++ = s;
430 		if (!*r)
431 		    break;
432 		s.c.ch = *r++;
433 		*p++ = s;
434 		if (!*r)
435 		    break;
436 		s.c.ch = *r++;
437 		*p++ = s;
438 		if (p >= eol)
439 		    goto end_loop;
440 		break;
441 	    }
442 	    if (x >= x_max)
443 		break;
444 	    q++;
445 	}
446     } else if ((m2 < q || m1 > last) && (edit->curs1 < q || edit->curs1 > last) && \
447 	       (edit->found_start + edit->found_len < q || edit->found_start > last) &&
448 	       (edit->bracket < q || edit->bracket > last)) {
449 	for (;;) {
450 	    c = edit_get_wide_byte (edit, q);
451 	    *p = get_style_fast (edit, q, c);
452 	    switch (c) {
453 	    case -1:
454 /* no character since used up by a multi-byte sequence */
455 		break;
456 	    case '\n':
457 		(p++)->c.ch = ' ';
458 		if (p >= eol)
459 		    goto end_loop;
460 		p->c.ch = p->_style = 0;
461 		if (x > edit->max_column)
462 		    edit->max_column = x;
463 		return;
464 	    case '\t':
465 		if (FIXED_FONT) {
466 		    int t;
467 		    t = next_tab_pos (x);
468 		    t = min (t, x_max);
469 		    s = *p;
470 		    s.c.ch = ' ';
471 		    while (x < t) {
472 			x += FONT_PER_CHAR(' ');
473 			*p++ = s;
474 			if (p >= eol)
475 			    goto end_loop;
476 		    }
477 		} else {
478 		    (p++)->c.ch = '\t';
479 		    if (p >= eol)
480 			goto end_loop;
481 		    x = next_tab_pos (x);
482 		}
483 		break;
484 	    default:
485 		x += convert_to_long_printable (c, text);
486 		r = text;
487 		s = *p;
488 		s.c.ch = *r++;
489 		*p++ = s;
490 		if (!*r)
491 		    break;
492 		s.c.ch = *r++;
493 		*p++ = s;
494 		if (!*r)
495 		    break;
496 		s.c.ch = *r++;
497 		*p++ = s;
498 		if (!*r)
499 		    break;
500 		s.c.ch = *r++;
501 		*p++ = s;
502 		if (!*r)
503 		    break;
504 		s.c.ch = *r++;
505 		*p++ = s;
506 		if (!*r)
507 		    break;
508 		s.c.ch = *r++;
509 		*p++ = s;
510 		if (!*r)
511 		    break;
512 		s.c.ch = *r++;
513 		*p++ = s;
514 		if (!*r)
515 		    break;
516 		s.c.ch = *r++;
517 		*p++ = s;
518 		if (!*r)
519 		    break;
520 		s.c.ch = *r++;
521 		*p++ = s;
522 		if (p >= eol)
523 		    goto end_loop;
524 		break;
525 	    }
526 	    if (x >= x_max)
527 		break;
528 	    q++;
529 	}
530     } else {
531 	for (;;) {
532 	    c = edit_get_wide_byte (edit, q);
533 
534 #if 0
535 	    if (wc_isgraph(c))
536 	        printf ("(%d)%c", q, c);
537 	    else
538 	        printf ("(%d)%Xh", q, c);
539 #endif
540 
541 	    *p = get_style (edit, q, c, m1, m2, x);
542 	    switch (c) {
543 	    case -1:
544 /* no character since used up by a multi-byte sequence */
545 		break;
546 	    case '\n':
547 		(p++)->c.ch = ' ';
548 		if (p >= eol)
549 		    goto end_loop;
550 		p->c.ch = p->_style = 0;
551 		if (x > edit->max_column)
552 		    edit->max_column = x;
553 		return;
554 	    case '\t':
555 		if (FIXED_FONT) {
556 		    int t;
557 		    t = next_tab_pos (x);
558 		    t = min (t, x_max);
559 		    s = *p;
560 		    s.c.ch = ' ';
561 		    while (x < t) {
562 			x += FONT_PER_CHAR(' ');
563 			*p++ = s;
564 			if (p >= eol)
565 			    goto end_loop;
566 		    }
567 		} else {
568 		    (p++)->c.ch = '\t';
569 		    if (p >= eol)
570 			goto end_loop;
571 		    x = next_tab_pos (x);
572 		}
573 		break;
574 	    default:
575 		x += convert_to_long_printable (c, text);
576 		r = text;
577 		s = *p;
578 		s.c.ch = *r++;
579 		*p++ = s;
580 		if (!*r)
581 		    break;
582 		s.c.ch = *r++;
583 		*p++ = s;
584 		if (!*r)
585 		    break;
586 		s.c.ch = *r++;
587 		*p++ = s;
588 		if (!*r)
589 		    break;
590 		s.c.ch = *r++;
591 		*p++ = s;
592 		if (!*r)
593 		    break;
594 		s.c.ch = *r++;
595 		*p++ = s;
596 		if (!*r)
597 		    break;
598 		s.c.ch = *r++;
599 		*p++ = s;
600 		if (!*r)
601 		    break;
602 		s.c.ch = *r++;
603 		*p++ = s;
604 		if (!*r)
605 		    break;
606 		s.c.ch = *r++;
607 		*p++ = s;
608 		if (!*r)
609 		    break;
610 		s.c.ch = *r++;
611 		*p++ = s;
612 		if (p >= eol)
613 		    goto end_loop;
614 		break;
615 	    }
616 	    if (x >= x_max)
617 		break;
618 	    q++;
619 	}
620     }
621   end_loop:
622     if (x > edit->max_column)
623 	edit->max_column = x;
624     p->c.ch = p->_style = 0;
625 }
626 
edit_set_cursor(Window win,int x,int y,int bg,int fg,int width,wchar_t t,int style)627 static void edit_set_cursor (Window win, int x, int y, int bg, int fg, int width, wchar_t t, int style)
628 {
629 #ifdef GTK
630     gdk_gc_set_foreground (win->gc, &win->color[18]);
631     gdk_draw_rectangle (win->text_area, win->gc, 0, x, y + FONT_OVERHEAD, width - 1, FONT_HEIGHT - 1);
632 #else
633     CSetColor (edit_cursor_color);
634     if (style & MOD_REVERSE)
635 	CLine (win, x + width - 1, y + FONT_OVERHEAD, x + width - 1, y + FONT_HEIGHT - 1);	/* non focussed cursor form */
636     else
637 	CLine (win, x, y + FONT_OVERHEAD, x, y + FONT_HEIGHT - 1);	/* non focussed cursor form */
638     CLine (win, x, y + FONT_OVERHEAD, x + width - 1, y + FONT_OVERHEAD);
639     set_cursor_position (win, x, y, width, FONT_HEIGHT, CURSOR_TYPE_EDITOR, t, bg, fg, style);	/* widget library's flashing cursor */
640 #endif
641 }
642 
next_tab(int x,int scroll_right)643 static inline int next_tab (int x, int scroll_right)
644 {
645     return next_tab_pos (x - scroll_right - EDIT_TEXT_HORIZONTAL_OFFSET) - x + scroll_right + EDIT_TEXT_HORIZONTAL_OFFSET;
646 }
647 
draw_tab(Window win,int x,int y,cache_type s,int scroll_right)648 static int draw_tab (Window win, int x, int y, cache_type s, int scroll_right)
649 {
650     int l;
651 #ifdef GTK
652     GdkColor fg, bg;
653 #else
654     unsigned long fg, bg;
655 #endif
656     l = next_tab (x, scroll_right);
657 #ifdef GTK
658     set_style_color (s, &fg.pixel, &bg.pixel);
659     gdk_gc_set_foreground (win->gc, &bg);
660     gdk_draw_rectangle (win->text_area, win->gc, 1, x, y + FONT_OVERHEAD, l, FONT_HEIGHT);
661 /* if we printed a cursor: */
662     if (s.c.style & MOD_CURSOR)
663 	edit_set_cursor (win, x, y, bg.pixel, fg.pixel, FONT_PER_CHAR(' '), ' ', s.c.style);
664 #else
665     set_style_color (s, &fg, &bg);
666     CSetColor (bg);
667     CRectangle (win, x, y + FONT_OVERHEAD, l, FONT_HEIGHT);
668 /* if we printed a cursor: */
669     if (s.c.style & MOD_CURSOR)
670 	edit_set_cursor (win, x, y, bg, fg, FONT_PER_CHAR(' '), ' ', s.c.style);
671 #endif
672     return x + l;
673 }
674 
675 #ifdef GTK
676 #include <gdk/gdk.h>
677 #endif
678 
draw_space(Window win,int x,int y,cache_type s,int l)679 static inline void draw_space (Window win, int x, int y, cache_type s, int l)
680 {
681 #ifdef GTK
682     GdkColor fg, bg;
683 #else
684     unsigned long fg, bg;
685 #endif
686 #ifdef GTK
687     set_style_color (s, &fg.pixel, &bg.pixel);
688     gdk_gc_set_foreground (win->gc, &bg);
689     gdk_draw_rectangle (win->text_area, win->gc, 1, x, y + FONT_OVERHEAD, l, FONT_HEIGHT);
690 #else
691     set_style_color (s, &fg, &bg);
692     CSetColor (bg);
693     CRectangle (win, x, y + FONT_OVERHEAD, l, FONT_HEIGHT);
694 /* if we printed a cursor: */
695     if (s.c.style & MOD_CURSOR)
696 	edit_set_cursor (win, x, y, bg, fg, FONT_PER_CHAR(' '), ' ', s.c.style);
697 #endif
698 }
699 
700 #ifdef GTK
701 
702 void
gdk_draw_image_text(GdkDrawable * drawable,GdkFont * font,GdkGC * gc,gint x,gint y,const gchar * text,gint text_length)703 gdk_draw_image_text (GdkDrawable *drawable,
704 	       GdkFont     *font,
705 	       GdkGC       *gc,
706 	       gint         x,
707 	       gint         y,
708 	       const gchar *text,
709 	       gint         text_length)
710 {
711   GdkWindowPrivate *drawable_private;
712   GdkFontPrivate *font_private;
713   GdkGCPrivate *gc_private;
714 
715   g_return_if_fail (drawable != NULL);
716   g_return_if_fail (font != NULL);
717   g_return_if_fail (gc != NULL);
718   g_return_if_fail (text != NULL);
719 
720   drawable_private = (GdkWindowPrivate*) drawable;
721   if (drawable_private->destroyed)
722     return;
723   gc_private = (GdkGCPrivate*) gc;
724   font_private = (GdkFontPrivate*) font;
725 
726   if (font->type == GDK_FONT_FONT)
727     {
728       XFontStruct *xfont = (XFontStruct *) font_private->xfont;
729       XSetFont(drawable_private->xdisplay, gc_private->xgc, xfont->fid);
730       if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0))
731 	{
732 	  XDrawImageString (drawable_private->xdisplay, drawable_private->xwindow,
733 		       gc_private->xgc, x, y, text, text_length);
734 	}
735       else
736 	{
737 	  XDrawImageString16 (drawable_private->xdisplay, drawable_private->xwindow,
738 			 gc_private->xgc, x, y, (XChar2b *) text, text_length / 2);
739 	}
740     }
741   else if (font->type == GDK_FONT_FONTSET)
742     {
743       XFontSet fontset = (XFontSet) font_private->xfont;
744       XmbDrawImageString (drawable_private->xdisplay, drawable_private->xwindow,
745 		     fontset, gc_private->xgc, x, y, text, text_length);
746     }
747   else
748     g_error("undefined font type\n");
749 }
750 
751 
752 #endif
753 
draw_string(Window win,int x,int y,cache_type s,XChar2b * text,wchar_t * textwc,int length)754 static int draw_string (Window win, int x, int y, cache_type s, XChar2b *text, wchar_t *textwc, int length)
755 {
756 #ifdef GTK
757     GdkColor fg, bg;
758 #else
759     unsigned long fg, bg;
760     int underlined, l;
761 #endif
762 #ifdef GTK
763     set_style_color (s, &fg.pixel, &bg.pixel);
764     gdk_gc_set_background (win->gc, &bg);
765     gdk_gc_set_foreground (win->gc, &fg);
766     gdk_draw_image_text (win->text_area, GTK_WIDGET (win)->style->font, win->gc, x + FONT_OFFSET_X, y + FONT_OFFSET_Y, text, length);
767 #else
768     underlined = set_style_color (s, &fg, &bg);
769     CSetBackgroundColor (bg);
770     CSetColor (fg);
771     CImageTextWC (win, x + FONT_OFFSET_X, y + FONT_OFFSET_Y, text, textwc, length);
772     l = CImageTextWidthWC (text, textwc, length);
773     if (underlined) {
774 	int i, h, inc;
775 	inc = FONT_MEAN_WIDTH * 2 / 3;
776 	CSetColor (color_palette (18));
777 	h = (x / inc) & 1;
778 	CLine (win, x, y + FONT_HEIGHT + FONT_OVERHEAD - 1 - h, x + min (l, inc - (x % inc) - 1), y + FONT_HEIGHT + FONT_OVERHEAD - 1 - h);
779 	h = h ^ 1;
780 	for (i = inc - min (l, (x % inc)); i < l; i += inc) {
781 	    CLine (win, x + i, y + FONT_HEIGHT + FONT_OVERHEAD - 1 - h, x + min (l, i + inc - 1), y + FONT_HEIGHT + FONT_OVERHEAD - 1 - h);
782 	    h = h ^ 1;
783 	}
784     }
785 #endif
786 /* if we printed a cursor: */
787 #ifdef GTK
788     if (s.c.style & MOD_CURSOR)
789 	edit_set_cursor (win, x, y, bg.pixel, fg.pixel, FONT_PER_CHAR(*textwc), *textwc, s.c.style);
790     return x + gdk_text_width (GTK_WIDGET (win)->style->font, text, length);
791 #else
792     if (s.c.style & MOD_CURSOR)
793 	edit_set_cursor (win, x, y, bg, fg, FONT_PER_CHAR(*textwc), *textwc, s.c.style);
794     return x + l;
795 #endif
796 }
797 
798 #define STYLE_DIFF (cache->_style != line->_style || cache->c.ch != line->c.ch \
799 	    || ((cache->c.style | line->c.style) & MOD_CURSOR) \
800 	    || !(cache->c.ch | cache->_style) || !(line->c.ch | line->_style))
801 
get_ignore_length(cache_type * cache,cache_type * line)802 int get_ignore_length (cache_type *cache, cache_type *line)
803 {
804     int i;
805     for (i = 0; i < cache_width; i++, line++, cache++) {
806 	if (STYLE_DIFF)
807 	    return i;
808     }
809     return cache_width;
810 }
811 
lwstrnlen(const cache_type * s,size_t count)812 static inline size_t lwstrnlen (const cache_type * s, size_t count)
813 {
814     const cache_type *sc;
815     for (sc = s; count-- && (sc->c.ch | sc->_style) != 0; ++sc);
816     return sc - s;
817 }
818 
lwstrlen(const cache_type * s)819 static inline size_t lwstrlen (const cache_type * s)
820 {
821     const cache_type *sc;
822     for (sc = s; (sc->c.ch | sc->_style) != 0; ++sc);
823     return sc - s;
824 }
825 
get_ignore_trailer(cache_type * cache,cache_type * line,int length)826 int get_ignore_trailer (cache_type *cache, cache_type *line, int length)
827 {
828     int i;
829     int cache_len, line_len;
830     cache_len = lwstrnlen (cache, cache_width);
831     line_len = lwstrlen (line);
832 
833     if (line_len > cache_len)
834 	for (i = line_len - 1; i >= cache_len && i >= length; i--) {
835 	    if (line[i].c.ch == ' ' && !(line[i].c.style | line[i].c.fg | line[i].c.bg))
836 		continue;
837 	    return i + 1;
838 	}
839     for (i = cache_len - 1, line = line + i, cache = cache + i; i > length; i--, line--, cache--)
840 	if (STYLE_DIFF)
841 	    return i + 1;
842 
843     return length + 1;
844 }
845 
846 /* erases trailing bit of old line if a new line is printed over a longer old line */
cover_trail(Window win,int x_start,int x_new,int x_old,int y)847 static void cover_trail (Window win, int x_start, int x_new, int x_old, int y)
848 {
849     if (x_new < EDIT_TEXT_HORIZONTAL_OFFSET)
850 	x_new = EDIT_TEXT_HORIZONTAL_OFFSET;
851     if (x_new < x_old) {	/* no need to print */
852 #ifdef GTK
853 	gdk_gc_set_foreground (win->gc, &win->color[1]);
854 	gdk_draw_rectangle (win->text_area, win->gc, 1, x_new, y + FONT_OVERHEAD, x_old - x_new, FONT_HEIGHT);
855 #else
856 	CSetColor (edit_normal_background_color);
857 	CRectangle (win, x_new, y + FONT_OVERHEAD, x_old - x_new, FONT_HEIGHT + (FONT_OVERHEAD != 0 && !FIXED_FONT));
858 #endif
859     } else {
860 #ifdef GTK
861 	gdk_gc_set_foreground (win->gc, &win->color[1]);
862 #else
863 	CSetColor (edit_normal_background_color);
864 #endif
865     }
866 /* true type fonts print stuff out of the bounding box (aaaaaaaaarrrgh!!) */
867     if (!FIXED_FONT)
868 	if (FONT_OVERHEAD && x_new > EDIT_TEXT_HORIZONTAL_OFFSET)
869 #ifdef GTK
870 	    gdk_draw_line (win->text_area, win->gc, max (x_start, EDIT_TEXT_HORIZONTAL_OFFSET), y + FONT_HEIGHT + FONT_OVERHEAD, x_new - 1, y + FONT_HEIGHT + FONT_OVERHEAD);
871 #else
872 	    CLine (win, max (x_start, EDIT_TEXT_HORIZONTAL_OFFSET), y + FONT_HEIGHT + FONT_OVERHEAD, x_new - 1, y + FONT_HEIGHT + FONT_OVERHEAD);
873 #endif
874 }
875 
876 #define NOT_VALID (-2000000000)
877 
878 struct cache_line {
879     int x0, x1;
880     cache_type *data;
881 };
882 
883 struct cache_line *cache_lines = 0;
884 
edit_free_cache_lines(void)885 void edit_free_cache_lines (void)
886 {
887     int i;
888     if (cache_lines) {
889 	for (i = 0; i < cache_height; i++)
890 	    free (cache_lines[i].data);
891 	free (cache_lines);
892 	cache_lines = 0;
893     }
894 }
895 
edit_realloc_cache_lines(int width,int height)896 static void edit_realloc_cache_lines (int width, int height)
897 {
898     if (width > cache_width || height > cache_height) {
899 	int i;
900 	edit_free_cache_lines ();
901 	cache_width = max (width + 10, cache_width);
902 	cache_height = max (height + 10, cache_height);
903 	cache_lines = malloc (sizeof (struct cache_line) * cache_height);
904 	memset (cache_lines, 0, sizeof (struct cache_line) * cache_height);
905 	for (i = 0; i < cache_height; i++) {
906 	    cache_lines[i].data = malloc (sizeof (cache_type) * (cache_width + 1));
907 	    memset (cache_lines[i].data, 0, sizeof (cache_type) * (cache_width + 1));
908 	    cache_lines[i].x0 = NOT_VALID;
909 	    cache_lines[i].x1 = 10000;
910 	}
911     }
912 }
913 
914 #define IS_HEBREW(c) (((c) >= 0x0590UL && (c) <= 0x05FFUL))
915 
916 int option_reverse_hebrew = 1;
917 
reverse_text(cache_type * line)918 static void reverse_text (cache_type * line)
919 {
920     int i, n;
921     if (option_reverse_hebrew) {
922 	while (line->c.ch | line->_style) {
923 	    while (!IS_HEBREW (line->c.ch) && (line->c.ch | line->_style))
924 		line++;
925 	    for (n = 0; (IS_HEBREW (line[n].c.ch) || (line[n].c.ch == ' ')) && (line[n].c.ch | line[n]._style); n++);
926 	    while (n && !IS_HEBREW (line[n - 1].c.ch))
927 		n--;
928 	    n--;
929 	    for (i = 0; i < (n + 1) / 2; i++) {
930 		cache_type t;
931 		t = line[i];
932 		line[i] = line[n - i];
933 		line[i].c.style |= MOD_REVERSE;
934 		line[n - i] = t;
935 		line[n - i].c.style |= MOD_REVERSE;
936 	    }
937 	    line += n + 1;
938 	}
939     }
940 }
941 
edit_draw_proportional(void * data,void (* converttext)(void *,long,cache_type *,cache_type *,int,int,int),int calctextpos (void *,long,long *,int),int scroll_right,Window win,int x_max,long b,int row,int y,int x_offset,int tabwidth)942 void edit_draw_proportional (void *data,
943 	   void (*converttext) (void *, long, cache_type *, cache_type *, int, int, int),
944 	   int calctextpos (void *, long, long *, int),
945 				int scroll_right,
946 				Window win,
947 				int x_max,
948 				long b,
949 				int row,
950 				int y,
951 				int x_offset,
952 				int tabwidth)
953 {
954     static Window last = 0;
955     cache_type style, line[MAX_LINE_LEN], *p, *eol;
956     XChar2b text[128];
957     wchar_t textwc[128];
958     int x0, x, ignore_text = 0, ignore_trailer = 2000000000, j, i;
959     long q;
960 
961     tab_width = tabwidth;
962     if (option_long_whitespace)
963 	tab_width = tabwidth *= 2;
964 
965     x_max -= 3;
966 
967     edit_realloc_cache_lines (x_max / 3, row + 1);
968 
969 /* if its not the same window, reset the screen rememberer */
970     if (last != win || !win) {
971 	last = win;
972 	for (i = 0; i < cache_height; i++) {
973 	    cache_lines[i].x0 = NOT_VALID;
974 	    cache_lines[i].x1 = x_max;
975 	}
976 	if (!win)
977 	    return;
978     }
979 
980 /* get point to start drawing */
981     x0 = (*calctextpos) (data, b, &q, -scroll_right + x_offset);
982 /* q contains the offset in the edit buffer */
983 
984 /* translate this line into printable characters with a style (=color) high byte */
985     for (i = 0; i < MAX_LINE_LEN; i += 8) {
986 	line[i]._style = line[i].c.ch = 0;
987 	line[i + 1]._style = line[i + 1].c.ch = 0;
988 	line[i + 2]._style = line[i + 2].c.ch = 0;
989 	line[i + 3]._style = line[i + 3].c.ch = 0;
990 	line[i + 4]._style = line[i + 4].c.ch = 0;
991 	line[i + 5]._style = line[i + 5].c.ch = 0;
992 	line[i + 6]._style = line[i + 6].c.ch = 0;
993 	line[i + 7]._style = line[i + 7].c.ch = 0;
994     }
995     (*converttext) (data, q, line, &line[MAX_LINE_LEN - 16], x0, x_max - scroll_right - EDIT_TEXT_HORIZONTAL_OFFSET, row);
996     reverse_text (line);
997 
998 /* adjust for the horizontal scroll and border */
999     x0 += scroll_right + EDIT_TEXT_HORIZONTAL_OFFSET;
1000     x = x0;
1001 
1002 /* is some of the line identical to that already printed so that we can ignore it? */
1003     if (!EditExposeRedraw) {
1004 	if (cache_lines[row].x0 == x0 && row < cache_height) {	/* i.e. also  && cache_lines[row].x0 != NOT_VALID */
1005 	    ignore_text = get_ignore_length (cache_lines[row].data, line);
1006 	    if (FIXED_FONT)
1007 		ignore_trailer = get_ignore_trailer (cache_lines[row].data, line, ignore_text);
1008 	}
1009     }
1010     p = line;
1011     eol = &line[MAX_LINE_LEN - 1];
1012     j = 0;
1013     while (p->c.ch | p->_style) {
1014 	if (p->c.style & MOD_PIXMAP) {
1015 #ifdef STILL_TO_BE_SUPPORTED
1016 	    x += edit_insert_pixmap (win, x, y, p->c.ch);
1017 		/* the pixmap will be clipped, if it's taller than the
1018 		   current font, else centred top to bottom */
1019 #endif
1020 	    j++;
1021 	    if (p++ == eol)
1022 		goto done_loop;
1023 	    continue;
1024 	}
1025 	if (p->c.style & MOD_TAB) {
1026 	    draw_space (win, x, y, *p, p->c.ch);
1027 	    x += p->c.ch;
1028 	    j++;
1029 	    if (p++ == eol)
1030 		goto done_loop;
1031 	} else if (p->c.ch == '\t') {
1032 	    j++;
1033 	    if (j > ignore_text && j < ignore_trailer + 1)
1034 		x = draw_tab (win, x, y, *p, scroll_right);
1035 	    else
1036 		x += next_tab (x, scroll_right);
1037 	    if (p++ == eol)
1038 		goto done_loop;
1039 	} else {
1040 	    style = *p;
1041 	    style.c.ch = 0;
1042 	    i = 0;
1043 	    do {
1044 		text[i].byte2 = (unsigned char) p->c.ch;
1045 		text[i].byte1 = (unsigned char) (p->c.ch >> 8);
1046 		textwc[i++] = (p++)->c.ch;
1047 		if (p == eol)
1048 		    goto done_loop;
1049 		j++;
1050 		if (j == ignore_text || j == ignore_trailer)
1051 		    break;
1052 	    } while (i < 128 && p->c.ch && style._style == p->_style && p->c.ch != '\t');
1053 
1054 	    if (j > ignore_text && j < ignore_trailer + 1) {
1055 		x = draw_string (win, x, y, style, text, textwc, i);
1056 	    } else {
1057 #ifdef GTK
1058 		x += gdk_text_width (GTK_WIDGET(win)->style->font, text, i);
1059 #else
1060 		x += CImageTextWidthWC (text, textwc, i);
1061 #endif
1062 	    }
1063 	}
1064     }
1065   done_loop:
1066 
1067     x = min (x, x_max);
1068     if (!EditExposeRedraw || EditClear)
1069 	cover_trail (win, x0, x, cache_lines[row].x1, y);
1070     memcpy (&(cache_lines[row].data[ignore_text]),
1071 	    &(line[ignore_text]),
1072 	 (min (j, cache_width) - ignore_text) * sizeof (cache_type));
1073 
1074     cache_lines[row].data[min (j, cache_width)].c.ch = 0;
1075     cache_lines[row].data[min (j, cache_width)]._style = 0;
1076 
1077     cache_lines[row].x0 = x0;
1078     cache_lines[row].x1 = x;
1079     if (EditExposeRedraw)
1080 	last = 0;
1081     else
1082 	last = win;
1083 }
1084 
1085 
edit_draw_this_line_proportional(WEdit * edit,long b,int row,int start_column,int end_column)1086 void edit_draw_this_line_proportional (WEdit * edit, long b, int row, int start_column, int end_column)
1087 {
1088     int fg, bg;
1089     if (row < 0 || row >= edit->num_widget_lines)
1090 	return;
1091 
1092     if (row + edit->start_line > edit->total_lines)
1093 	b = edit->last_byte + 1;		/* force b out of range of the edit buffer for blanks lines */
1094 
1095     if (end_column > CWidthOf (edit->widget))
1096 	end_column = CWidthOf (edit->widget);
1097 
1098     edit_get_syntax_color (edit, b - 1, &fg, &bg);
1099 
1100     edit_draw_proportional (edit,
1101 			    (void (*) (void *, long, cache_type *, cache_type *, int, int, int)) convert_text,
1102 			    (int (*) (void *, long, long *, int)) calc_text_pos,
1103 			    edit->start_col, CWindowOf (edit->widget),
1104 			    end_column, b, row, row * FONT_PIX_PER_LINE + EDIT_TEXT_VERTICAL_OFFSET,
1105 			    EditExposeRedraw ? start_column : 0, FONT_PER_CHAR(' ') * TAB_SIZE);
1106 }
1107 
1108 
1109 /*********************************************************************************/
1110 /*         The remainder is for the text box widget                              */
1111 /*********************************************************************************/
1112 
calc_text_pos_str(unsigned char * text,long b,long * q,int l)1113 static int calc_text_pos_str (unsigned char *text, long b, long *q, int l)
1114 {
1115     int x = 0, c = 0, xn = 0, d;
1116     for (;;) {
1117 	d = c;
1118 	c = (unsigned char) text[b];
1119 	switch (c) {
1120 	case '\0':
1121 	case '\n':
1122 	    *q = b;
1123 	    return x;
1124 	case '\t':
1125 	    xn = next_tab_pos (x);
1126 	    break;
1127 	case '\r':
1128 	    break;
1129 	case '\b':
1130 	    if (d)
1131 		xn = x - FONT_PER_CHAR(d);
1132 	    break;
1133 	default:
1134 	    if (!FONT_PER_CHAR (c))
1135 		c = ' ';
1136 	    xn = x + FONT_PER_CHAR(c);
1137 	    break;
1138 	}
1139 	if (xn > l)
1140 	    break;
1141 	x = xn;
1142 	b++;
1143     }
1144     *q = b;
1145     return x;
1146 }
1147 
prop_font_strcolmove(unsigned char * str,int i,int column)1148 int prop_font_strcolmove (unsigned char *str, int i, int column)
1149 {
1150     long q;
1151     CPushFont ("editor", 0);
1152     calc_text_pos_str (str, i, &q, column * FONT_MEAN_WIDTH);
1153     CPopFont ();
1154     return q;
1155 }
1156 
1157 #ifndef GTK
1158 
1159 /* b is the beginning of the line. l is the length in pixels up to a point
1160    on some character which is unknown. The character pos is returned in
1161    *q and the characters pixel x pos from b is return'ed. */
calc_text_pos2(CWidget * w,long b,long * q,int l)1162 int calc_text_pos2 (CWidget * w, long b, long *q, int l)
1163 {
1164     int r;
1165     CPushFont ("editor", 0);
1166     r = calc_text_pos_str ((unsigned char *) w->text, b, q, l);
1167     CPopFont ();
1168     return r;
1169 }
1170 
1171 int highlight_this_line;
1172 
1173 /* this is for the text widget (i.e. nroff formatting) */
convert_text2(CWidget * w,long q,cache_type * line,cache_type * eol,int x,int x_max,int row)1174 void convert_text2 (CWidget * w, long q, cache_type * line, cache_type * eol, int x, int x_max, int row)
1175 {
1176     int c = 0, d;
1177     cache_type s, *p, *bol;
1178     long m1, m2;
1179 
1180     m1 = min (w->mark1, w->mark2);
1181     m2 = max (w->mark1, w->mark2);
1182 
1183     p = line;
1184     bol = line;
1185     p->c.ch = p->_style = 0;
1186     for (;;) {
1187 	d = c;
1188 	c = (unsigned char) w->text[q];
1189 	p[1].c.ch = p[1]._style = 0;
1190 	p->c.fg = p->c.bg = 0xFF;	/* default background colors */
1191 	if (highlight_this_line)
1192 	    p->c.style |= MOD_HIGHLIGHTED;
1193 	if (q >= m1 && q < m2)
1194 	    p->c.style |= MOD_MARKED;
1195 	switch (c) {
1196 	case '\0':
1197 	case '\n':
1198 	    (p++)->c.ch |= ' ';
1199 	    if (p >= eol)
1200 		goto end_loop;
1201 	    if (highlight_this_line) {
1202 		q--;
1203 		x += FONT_PER_CHAR (' ');
1204 	    } else
1205 		return;
1206 	    break;
1207 	case '\t':
1208 	    if (FIXED_FONT) {
1209 		int i;
1210 		i = next_tab_pos (x) - x;
1211 		x += i;
1212 		s = *p;
1213 		while (i > 0) {
1214 		    i -= FONT_PER_CHAR (' ');
1215 		    (p++)->c.ch = s.c.ch | ' ';
1216 		    if (p >= eol)
1217 			goto end_loop;
1218 		    p->c.ch = p->_style = 0;
1219 		}
1220 	    } else {
1221 		(p++)->c.ch |= '\t';
1222 		if (p >= eol)
1223 		    goto end_loop;
1224 		x = next_tab_pos (x);
1225 	    }
1226 	    break;
1227 	case '\r':
1228 	    break;
1229 	case '\b':
1230 	    if (d) {
1231 		--p;
1232 		if (p < bol)
1233 		    goto end_loop;
1234 		x -= FONT_PER_CHAR (d);
1235 		if (d == '_')
1236 		    p->c.style |= MOD_ITALIC;
1237 		else
1238 		    p->c.style |= MOD_BOLD;
1239 	    }
1240 	    break;
1241 	default:
1242 	    if (!FONT_PER_CHAR (c)) {
1243 		c = ' ';
1244 		p->c.style |= MOD_ABNORMAL;
1245 	    }
1246 	    x += FONT_PER_CHAR (c);
1247 	    (p++)->c.ch = c;
1248 	    if (p >= eol)
1249 		goto end_loop;
1250 	    break;
1251 	}
1252 	if (x > x_max)
1253 	    break;
1254 	q++;
1255     }
1256   end_loop:
1257     p->c.ch = p->_style = 0;
1258 }
1259 
1260 
1261 #endif
1262 
1263 
1264