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