1 /* propfont.c - editor text drawing for proportional fonts.
2 Copyright (C) 1997 Paul Sheer
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
21 /* this file definatively relies on int being 32 bits or more */
23 int option_long_whitespace
= 0;
25 #define MAX_LINE_LEN 1024
26 #define CACHE_WIDTH 256
27 #define CACHE_HEIGHT 128
32 cache_type data
[CACHE_WIDTH
];
35 extern unsigned char per_char
[256];
37 /* background colors: marked is refers to mouse highlighting, highlighted refers to a found string. */
38 extern unsigned long edit_abnormal_color
, edit_marked_abnormal_color
;
39 extern unsigned long edit_highlighted_color
, edit_marked_color
;
40 extern unsigned long edit_normal_background_color
;
42 /* foreground colors */
43 extern unsigned long edit_normal_foreground_color
, edit_bold_color
;
44 extern unsigned long edit_italic_color
;
47 extern unsigned long edit_cursor_color
;
49 extern int EditExposeRedraw
;
52 void set_style_color (
56 cache_type s
, unsigned long *fg
, unsigned long *bg
)
59 fgp
= (s
& 0xFF000000UL
) >> 24;
61 *fg
= color_palette (fgp
);
63 *fg
= edit_normal_foreground_color
;
64 bgp
= (s
& 0x00FF0000) >> 16;
66 *bg
= color_palette (bgp
);
68 *bg
= edit_normal_background_color
;
69 if (!(s
& 0xFFFFFF00UL
)) /* check this first as an optimization */
71 if (s
& (MOD_ABNORMAL
* 256)) {
72 *bg
= edit_abnormal_color
;
73 if (s
& (MOD_MARKED
* 256))
74 *bg
= edit_marked_abnormal_color
;
75 } else if (s
& (MOD_HIGHLIGHTED
* 256)) {
76 *bg
= edit_highlighted_color
;
77 } else if (s
& (MOD_MARKED
* 256)) {
78 *bg
= edit_marked_color
;
80 if (s
& (MOD_BOLD
* 256))
81 *fg
= edit_bold_color
;
82 if (s
& (MOD_ITALIC
* 256))
83 *fg
= edit_italic_color
;
84 if (s
& (MOD_INVERSE
* 256)) {
89 if (*bg
== COLOR_BLACK
)
90 *bg
= color_palette (1);
95 #define set_style_color(s,f,b) set_style_color(win,s,f,b)
100 static inline int next_tab_pos (int x
)
102 return x
+= tab_width
- x
% tab_width
;
106 /* converts a possibly unprintable character to a string,
107 returning the string's width in pixels, t must have space for 4 chars */
108 static inline int convert_to_long_printable (int c
, unsigned char *t
)
117 if (option_international_characters
) {
123 t
[0] = ("0123456789ABCDEF")[c
>> 4];
124 t
[1] = ("0123456789ABCDEF")[c
& 0xF];
127 return per_char
[t
[0]] + per_char
[t
[1]] + per_char
[t
[2]];
131 if (option_long_whitespace
) {
135 return per_char
[' '] + per_char
[' '];
139 return per_char
[' '];
145 return per_char
[t
[0]] + per_char
[t
[1]];
148 /* same as above but just gets the length */
149 static inline int width_of_long_printable (int c
)
155 if (option_international_characters
)
158 return per_char
[(unsigned char) ("0123456789ABCDEF")[c
>> 4]] + per_char
[(unsigned char) ("0123456789ABCDEF")[c
& 0xF]] + per_char
['h'];
161 if (option_long_whitespace
)
162 return per_char
[' '] + per_char
[' '];
164 return per_char
[' '];
166 return per_char
['^'] + per_char
[c
+ '@'];
169 int edit_width_of_long_printable (int c
)
171 return width_of_long_printable (c
);
174 /* returns x pixel pos of char at offset *q with x not more than l */
175 int calc_text_pos (WEdit
* edit
, long b
, long *q
, int l
)
179 c
= edit_get_byte (edit
, b
);
183 if (x
> edit
->max_column
)
184 edit
->max_column
= x
;
187 xn
= next_tab_pos (x
);
190 xn
= x
+ width_of_long_printable (c
);
199 if (x
> edit
->max_column
)
200 edit
->max_column
= x
;
205 /* calcs pixel length of the line beginning at b up to upto */
206 int calc_text_len (WEdit
* edit
, long b
, long upto
)
211 if (x
> edit
->max_column
)
212 edit
->max_column
= x
;
215 c
= edit_get_byte (edit
, b
);
218 if (x
> edit
->max_column
)
219 edit
->max_column
= x
;
223 x
= next_tab_pos (x
);
226 x
+= width_of_long_printable (c
);
233 /* If pixels is zero this returns the count of pixels from current to upto. */
234 /* If upto is zero returns index of pixels across from current. */
235 long edit_move_forward3 (WEdit
* edit
, long current
, int pixels
, long upto
)
238 return calc_text_len (edit
, current
, upto
);
241 calc_text_pos (edit
, current
, &q
, pixels
);
247 extern int column_highlighting
;
249 /* gets the characters style (eg marked, highlighted) from its position in the edit buffer */
250 static inline cache_type
get_style (WEdit
* edit
, long q
, int c
, long m1
, long m2
, int x
)
254 if (q
== edit
->curs1
)
255 s
|= MOD_CURSOR
* 256;
256 if (q
>= m1
&& q
< m2
) {
257 if (column_highlighting
) {
258 if ((x
>= edit
->column1
&& x
< edit
->column2
)
259 || (x
>= edit
->column2
&& x
< edit
->column1
))
260 s
|= MOD_INVERSE
* 256;
262 s
|= MOD_MARKED
* 256;
265 if (q
== edit
->bracket
)
267 if (q
>= edit
->found_start
&& q
< edit
->found_start
+ edit
->found_len
)
268 s
|= MOD_HIGHLIGHTED
* 256;
269 if (option_international_characters
) {
270 if ((c
< ' ' || (c
> '~' && c
< 160)) && c
!= '\t' && c
!= '\n')
271 s
|= MOD_ABNORMAL
* 256;
273 if ((c
< ' ' || c
> '~') && c
!= '\t' && c
!= '\n')
274 s
|= MOD_ABNORMAL
* 256;
276 edit_get_syntax_color (edit
, q
, (int *) &fg
, (int *) &bg
);
277 return s
| ((fg
& 0xFF) << 24) | ((bg
& 0xFF) << 16);
280 void convert_text (WEdit
* edit
, long q
, cache_type
*p
, int x
, int x_max
)
285 unsigned char *r
, text
[4];
286 eval_marks (edit
, &m1
, &m2
);
288 c
= edit_get_byte (edit
, q
);
289 *p
= get_style (edit
, q
, c
, m1
, m2
, x
);
294 if (x
> edit
->max_column
)
295 edit
->max_column
= x
;
300 t
= next_tab_pos (x
);
309 x
= next_tab_pos (x
);
313 x
+= convert_to_long_printable (c
, text
);
328 if (x
> edit
->max_column
)
329 edit
->max_column
= x
;
333 void edit_set_cursor (Window win
, int x
, int y
, int bg
, int fg
, int width
, char t
)
336 gdk_gc_set_foreground (win
->gc
, &win
->color
[18]);
337 gdk_draw_rectangle (win
->text_area
, win
->gc
, 0, x
, y
+ FONT_OVERHEAD
, width
- 1, FONT_HEIGHT
- 1);
339 CSetColor (edit_cursor_color
);
340 CLine (win
, x
, y
+ FONT_OVERHEAD
,
341 x
, y
+ FONT_HEIGHT
- 1); /* non focussed cursor form */
342 CLine (win
, x
+ 1, y
+ FONT_OVERHEAD
,
343 x
+ width
- 1, y
+ FONT_OVERHEAD
);
344 set_cursor_position (win
, x
, y
, width
, FONT_HEIGHT
, CURSOR_TYPE_EDITOR
, t
, bg
, fg
); /* widget library's flashing cursor */
348 static inline int next_tab (int x
, int scroll_right
)
350 return next_tab_pos (x
- scroll_right
- EDIT_TEXT_HORIZONTAL_OFFSET
) - x
+ scroll_right
+ EDIT_TEXT_HORIZONTAL_OFFSET
;
353 int draw_tab (Window win
, int x
, int y
, cache_type s
, int scroll_right
)
359 unsigned long fg
, bg
;
361 l
= next_tab (x
, scroll_right
);
363 set_style_color (s
, &fg
.pixel
, &bg
.pixel
);
364 gdk_gc_set_foreground (win
->gc
, &bg
);
365 gdk_draw_rectangle (win
->text_area
, win
->gc
, 1, x
, y
+ FONT_OVERHEAD
, l
, FONT_HEIGHT
);
366 /* if we printed a cursor: */
367 if (s
& (MOD_CURSOR
* 256))
368 edit_set_cursor (win
, x
, y
, bg
.pixel
, fg
.pixel
, per_char
[' '], ' ');
370 set_style_color (s
, &fg
, &bg
);
372 CRectangle (win
, x
, y
+ FONT_OVERHEAD
, l
, FONT_HEIGHT
);
373 /* if we printed a cursor: */
374 if (s
& (MOD_CURSOR
* 256))
375 edit_set_cursor (win
, x
, y
, bg
, fg
, per_char
[' '], ' ');
384 static inline void draw_space (Window win
, int x
, int y
, cache_type s
, int l
)
389 unsigned long fg
, bg
;
392 set_style_color (s
, &fg
.pixel
, &bg
.pixel
);
393 gdk_gc_set_foreground (win
->gc
, &bg
);
394 gdk_draw_rectangle (win
->text_area
, win
->gc
, 1, x
, y
+ FONT_OVERHEAD
, l
, FONT_HEIGHT
);
396 set_style_color (s
, &fg
, &bg
);
398 CRectangle (win
, x
, y
+ FONT_OVERHEAD
, l
, FONT_HEIGHT
);
399 /* if we printed a cursor: */
400 if (s
& (MOD_CURSOR
* 256))
401 edit_set_cursor (win
, x
, y
, bg
, fg
, per_char
[' '], ' ');
408 gdk_draw_image_text (GdkDrawable
*drawable
,
416 GdkWindowPrivate
*drawable_private
;
417 GdkFontPrivate
*font_private
;
418 GdkGCPrivate
*gc_private
;
420 g_return_if_fail (drawable
!= NULL
);
421 g_return_if_fail (font
!= NULL
);
422 g_return_if_fail (gc
!= NULL
);
423 g_return_if_fail (text
!= NULL
);
425 drawable_private
= (GdkWindowPrivate
*) drawable
;
426 if (drawable_private
->destroyed
)
428 gc_private
= (GdkGCPrivate
*) gc
;
429 font_private
= (GdkFontPrivate
*) font
;
431 if (font
->type
== GDK_FONT_FONT
)
433 XFontStruct
*xfont
= (XFontStruct
*) font_private
->xfont
;
434 XSetFont(drawable_private
->xdisplay
, gc_private
->xgc
, xfont
->fid
);
435 if ((xfont
->min_byte1
== 0) && (xfont
->max_byte1
== 0))
437 XDrawImageString (drawable_private
->xdisplay
, drawable_private
->xwindow
,
438 gc_private
->xgc
, x
, y
, text
, text_length
);
442 XDrawImageString16 (drawable_private
->xdisplay
, drawable_private
->xwindow
,
443 gc_private
->xgc
, x
, y
, (XChar2b
*) text
, text_length
/ 2);
446 else if (font
->type
== GDK_FONT_FONTSET
)
448 XFontSet fontset
= (XFontSet
) font_private
->xfont
;
449 XmbDrawImageString (drawable_private
->xdisplay
, drawable_private
->xwindow
,
450 fontset
, gc_private
->xgc
, x
, y
, text
, text_length
);
453 g_error("undefined font type\n");
459 int draw_string (Window win
, int x
, int y
, cache_type s
, unsigned char *text
, int length
)
464 unsigned long fg
, bg
;
467 set_style_color (s
, &fg
.pixel
, &bg
.pixel
);
468 gdk_gc_set_background (win
->gc
, &bg
);
469 gdk_gc_set_foreground (win
->gc
, &fg
);
470 gdk_draw_image_text (win
->text_area
, GTK_WIDGET (win
)->style
->font
, win
->gc
, x
+ FONT_OFFSET_X
, y
+ FONT_OFFSET_Y
, text
, length
);
472 set_style_color (s
, &fg
, &bg
);
473 CSetBackgroundColor (bg
);
475 CImageString (win
, x
+ FONT_OFFSET_X
, y
+ FONT_OFFSET_Y
, (char *) text
, length
);
478 /* if we printed a cursor: */
480 if (s
& (MOD_CURSOR
* 256))
481 edit_set_cursor (win
, x
, y
, bg
.pixel
, fg
.pixel
, per_char
[*text
], *text
);
482 return x
+ gdk_text_width (GTK_WIDGET (win
)->style
->font
, text
, length
);
484 if (s
& (MOD_CURSOR
* 256))
485 edit_set_cursor (win
, x
, y
, bg
, fg
, per_char
[*text
], *text
);
486 return x
+ CTextWidth (win
, (char *) text
, length
);
490 #define STYLE_DIFF (cache[i] != line[i] \
491 || ((cache[i] | line[i]) & (MOD_CURSOR * 256)) \
492 || !cache[i] || !line[i] \
495 int get_ignore_length (cache_type
*cache
, cache_type
*line
)
498 for (i
= 0; i
< CACHE_WIDTH
; i
++) {
505 static inline size_t lwstrnlen (const cache_type
*s
, size_t count
)
507 const cache_type
*sc
;
508 for (sc
= s
; count
-- && *sc
!= 0; ++sc
);
512 static inline size_t lwstrlen (const cache_type
*s
)
514 const cache_type
*sc
;
515 for (sc
= s
; *sc
!= 0; ++sc
);
519 int get_ignore_trailer (cache_type
*cache
, cache_type
*line
, int length
)
522 int cache_len
, line_len
;
523 cache_len
= lwstrnlen (cache
, CACHE_WIDTH
);
524 line_len
= lwstrlen (line
);
526 if (line_len
> cache_len
)
527 for (i
= line_len
- 1; i
>= cache_len
&& i
>= length
; i
--)
531 for (i
= cache_len
- 1; i
> length
; i
--)
538 /* erases trailing bit of old line if a new line is printed over a longer old line */
539 void cover_trail (Window win
, int x_new
, int x_old
, int y
)
541 if (x_new
< EDIT_TEXT_HORIZONTAL_OFFSET
)
542 x_new
= EDIT_TEXT_HORIZONTAL_OFFSET
;
543 if (x_new
>= x_old
) /* no need to print */
546 gdk_gc_set_foreground (win
->gc
, &win
->color
[1]);
547 gdk_draw_rectangle (win
->text_area
, win
->gc
, 1, x_new
, y
+ FONT_OVERHEAD
, x_old
- x_new
, FONT_HEIGHT
);
549 CSetColor (edit_normal_background_color
);
550 /* CSetColor (color_palette(12)); */
551 CRectangle (win
, x_new
, y
+ FONT_OVERHEAD
, x_old
- x_new
, FONT_HEIGHT
);
555 cache_type mode_spacing
= 0;
557 #define NOT_VALID (-2000000000)
559 void edit_draw_proportional (void *data
,
560 void (*converttext
) (void *, long, cache_type
*, int, int),
561 int calctextpos (void *, long, long *, int),
571 static struct cache_line lines
[CACHE_HEIGHT
];
572 static Window last
= 0;
573 cache_type style
, line
[MAX_LINE_LEN
], *p
;
574 unsigned char text
[128];
575 int x0
, x
, ignore_text
= 0, ignore_trailer
= 2000000000, j
, i
;
578 tab_width
= tabwidth
;
579 if (option_long_whitespace
)
580 tab_width
= tabwidth
*= 2;
584 /* if its not the same window, reset the screen rememberer */
587 for (i
= 0; i
< CACHE_HEIGHT
; i
++) {
588 lines
[i
].x0
= NOT_VALID
;
592 /* get point to start drawing */
593 x0
= (*calctextpos
) (data
, b
, &q
, -scroll_right
+ x_offset
);
594 /* q contains the offset in the edit buffer */
596 /* translate this line into printable characters with a style (=color) high byte */
597 (*converttext
) (data
, q
, line
, x0
, x_max
- scroll_right
- EDIT_TEXT_HORIZONTAL_OFFSET
);
599 /* adjust for the horizontal scroll and border */
600 x0
+= scroll_right
+ EDIT_TEXT_HORIZONTAL_OFFSET
;
603 /* is some of the line identical to that already printed so that we can ignore it? */
604 if (!EditExposeRedraw
) {
605 if (lines
[row
].x0
== x0
&& row
< CACHE_HEIGHT
) { /* i.e. also && lines[row].x0 != NOT_VALID */
606 ignore_text
= get_ignore_length (lines
[row
].data
, line
);
608 ignore_trailer
= get_ignore_trailer (lines
[row
].data
, line
, ignore_text
);
615 if ((*p
& 0x80) && (*p
& mode_spacing
)) {
616 #ifdef STILL_TO_BE_SUPPORTED
617 x
+= edit_insert_pixmap (win
, x
, y
, *p
& 0x7F);
618 /* the pixmap will be clipped, if it's taller than the
619 current font, else centred top to bottom */
626 if ((*p
& 0xFF) == '\t') {
628 if (j
> ignore_text
&& j
< ignore_trailer
+ 1)
629 x
= draw_tab (win
, x
, y
, *p
, scroll_right
);
631 x
+= next_tab (x
, scroll_right
);
635 style
= *p
& 0xFFFFFF00UL
;
638 text
[i
++] = (unsigned char) *p
++;
640 if (j
== ignore_text
|| j
== ignore_trailer
)
642 } while (i
< 128 && *p
&& style
== (*p
& 0xFFFFFF00UL
) && (*p
& 0xFF) != '\t');
644 if (style
& mode_spacing
) {
646 for (k
= 0; k
< i
; k
++) {
647 draw_space (win
, x
, y
, (0xFFFFFF00UL
- mode_spacing
) & style
, text
[k
]);
651 if (j
> ignore_text
&& j
< ignore_trailer
+ 1)
652 x
= draw_string (win
, x
, y
, style
, text
, i
);
655 x
+= gdk_text_width (GTK_WIDGET(win
)->style
->font
, text
, i
);
657 x
+= CTextWidth (win
, (char *) text
, i
);
664 if (!EditExposeRedraw
|| EditClear
)
665 cover_trail (win
, x
, lines
[row
].x1
, y
);
666 memcpy (&(lines
[row
].data
[ignore_text
]),
667 &(line
[ignore_text
]),
668 (min (j
, CACHE_WIDTH
) - ignore_text
) * sizeof (cache_type
));
670 lines
[row
].data
[min (j
, CACHE_WIDTH
)] = 0;
674 if (EditExposeRedraw
)
681 void edit_draw_this_line_proportional (WEdit
* edit
, long b
, int row
, int start_column
, int end_column
)
684 if (row
< 0 || row
>= edit
->num_widget_lines
)
687 if (row
+ edit
->start_line
> edit
->total_lines
)
688 b
= 2000000000; /* force b out of range of the edit buffer for blanks lines */
690 if (end_column
> CWidthOf (edit
->widget
))
691 end_column
= CWidthOf (edit
->widget
);
693 edit_get_syntax_color (edit
, b
- 1, &fg
, &bg
);
695 edit_draw_proportional (edit
,
696 (void (*) (void *, long, cache_type
*, int, int)) convert_text
,
697 (int (*) (void *, long, long *, int)) calc_text_pos
,
698 edit
->start_col
, CWindowOf (edit
->widget
),
699 end_column
, b
, row
, row
* FONT_PIX_PER_LINE
+ EDIT_TEXT_VERTICAL_OFFSET
,
700 EditExposeRedraw
? start_column
: 0, per_char
[' '] * TAB_SIZE
);
704 /*********************************************************************************/
705 /* The remainder is for the text box widget */
706 /*********************************************************************************/
708 static inline int nroff_printable (int c
)
710 return ((c
>= ' ' && c
<= '~') || c
>= 160);
714 int calc_text_pos_str (unsigned char *text
, long b
, long *q
, int l
)
716 int x
= 0, c
= 0, xn
= 0, d
;
726 xn
= next_tab_pos (x
);
732 xn
= x
- per_char
[d
];
735 if (!nroff_printable (c
))
737 xn
= x
+ per_char
[c
];
749 int prop_font_strcolmove (unsigned char *str
, int i
, int column
)
752 calc_text_pos_str (str
, i
, &q
, column
* FONT_MEAN_WIDTH
);
758 /* b is the beginning of the line. l is the length in pixels up to a point
759 on some character which is unknown. The character pos is returned in
760 *q and the characters pixel x pos from b is return'ed. */
761 int calc_text_pos2 (CWidget
* w
, long b
, long *q
, int l
)
763 return calc_text_pos_str ((unsigned char *) w
->text
, b
, q
, l
);
766 /* calcs pixel length of the line beginning at b up to upto */
767 int calc_text_len2 (CWidget
*w
, long b
, long upto
)
780 x
= next_tab_pos (x
);
789 if (!nroff_printable (c
))
799 int highlight_this_line
;
801 /* this is for the text widget (i.e. nroff formatting) */
802 void convert_text2 (CWidget
* w
, long q
, cache_type
*line
, int x
, int x_max
)
808 m1
= min (w
->mark1
, w
->mark2
);
809 m2
= max (w
->mark1
, w
->mark2
);
817 *p
|= 0xFFFF0000UL
; /* default background colors */
818 if (highlight_this_line
)
819 *p
|= MOD_HIGHLIGHTED
* 256;
820 if (q
>= m1
&& q
< m2
)
821 *p
|= MOD_MARKED
* 256;
826 if (highlight_this_line
) {
835 i
= next_tab_pos (x
) - x
;
845 x
= next_tab_pos (x
);
855 *p
|= MOD_ITALIC
* 256;
857 *p
|= MOD_BOLD
* 256;
861 if (!nroff_printable (c
)) {
863 *p
|= MOD_ABNORMAL
* 256;