/* propfont.c - editor text drawing for proportional fonts. Copyright (C) 1996-2000 Paul Sheer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #define WIDE_CHAR #include #include "edit.h" #if defined (HAVE_MAD) && ! defined (MIDNIGHT) && ! defined (GTK) #include "mad.h" #endif #ifdef HAVE_WCHAR_H #include #endif /* this file definatively relies on int being 32 bits or more */ int option_long_whitespace = 0; int cache_width = 0; int cache_height = 0; /* must be a multiple of 8 */ #define MAX_LINE_LEN 1024 /* background colors: marked is refers to mouse highlighting, highlighted refers to a found string. */ extern unsigned long edit_abnormal_color, edit_marked_abnormal_color; extern unsigned long edit_highlighted_color, edit_marked_color; extern unsigned long edit_normal_background_color; /* foreground colors */ extern unsigned long edit_normal_foreground_color, edit_bold_color; extern unsigned long edit_italic_color; /* cursor color */ extern unsigned long edit_cursor_color; extern int EditExposeRedraw; extern int EditClear; int set_style_color ( #ifdef GTK Window win, #endif cache_type s, unsigned long *fg, unsigned long *bg) { int fgp, bgp, underlined = 0; fgp = s.c.fg; /* NO_COLOR would give fgp == 255 */ if (fgp < 0xFF) *fg = color_palette (fgp); else *fg = edit_normal_foreground_color; bgp = s.c.bg; if (bgp == 0xFE) underlined = 1; if (bgp < 0xFD) *bg = color_palette (bgp); else *bg = edit_normal_background_color; if (!(s.c.style | s.c.fg | s.c.fg)) /* check this first as an optimization */ return underlined; if (s.c.style & MOD_ABNORMAL) { *bg = edit_abnormal_color; if (s.c.style & MOD_MARKED) *bg = edit_marked_abnormal_color; } else if (s.c.style & MOD_HIGHLIGHTED) { *bg = edit_highlighted_color; } else if (s.c.style & MOD_MARKED) { *bg = edit_marked_color; } if (s.c.style & MOD_BOLD) *fg = edit_bold_color; if (s.c.style & MOD_ITALIC) *fg = edit_italic_color; if (s.c.style & MOD_INVERSE) { unsigned long t; t = *fg; *fg = *bg; *bg = t; if (*bg == COLOR_BLACK) *bg = color_palette (1); } return underlined; } #ifdef GTK #define set_style_color(s,f,b) set_style_color(win,s,f,b) #endif int tab_width = 1; static inline int next_tab_pos (int x) { return x += tab_width - x % tab_width; } /* this now properly uses ctypes */ static inline int convert_to_long_printable (wchar_t c, wchar_t * t) { c &= 0x7FFFFFFFUL; if (wc_isgraph (c)) { t[0] = c; t[1] = 0; return FONT_PER_CHAR_256 (c); } if (c == ' ') { if (option_long_whitespace) { t[0] = ' '; t[1] = ' '; t[2] = 0; return FONT_PER_CHAR (' ') + FONT_PER_CHAR (' '); } else { t[0] = ' '; t[1] = 0; return FONT_PER_CHAR (' '); } } if (option_international_characters && FONT_PER_CHAR (c)) { t[0] = c; t[1] = 0; return FONT_PER_CHAR (c); } if (c > 0xFFFF) { t[0] = ("0123456789ABCDEF")[(c >> 28) & 0xF]; t[1] = ("0123456789ABCDEF")[(c >> 24) & 0xF]; t[2] = ("0123456789ABCDEF")[(c >> 20) & 0xF]; t[3] = ("0123456789ABCDEF")[(c >> 16) & 0xF]; t[4] = ("0123456789ABCDEF")[(c >> 12) & 0xF]; t[5] = ("0123456789ABCDEF")[(c >> 8) & 0xF]; t[6] = ("0123456789ABCDEF")[(c >> 4) & 0xF]; t[7] = ("0123456789ABCDEF")[c & 0xF]; t[8] = 'h'; t[9] = 0; return FONT_PER_CHAR (t[0]) + FONT_PER_CHAR (t[1]) + FONT_PER_CHAR (t[2]) + FONT_PER_CHAR (t[3]) + FONT_PER_CHAR (t[4]); } if (c > 0xFF) { t[0] = ("0123456789ABCDEF")[(c >> 12) & 0xF]; t[1] = ("0123456789ABCDEF")[(c >> 8) & 0xF]; t[2] = ("0123456789ABCDEF")[(c >> 4) & 0xF]; t[3] = ("0123456789ABCDEF")[c & 0xF]; t[4] = 'h'; t[5] = 0; return FONT_PER_CHAR (t[0]) + FONT_PER_CHAR (t[1]) + FONT_PER_CHAR (t[2]) + FONT_PER_CHAR (t[3]) + FONT_PER_CHAR (t[4]); } if (c > '~') { t[0] = ("0123456789ABCDEF")[c >> 4]; t[1] = ("0123456789ABCDEF")[c & 0xF]; t[2] = 'h'; t[3] = 0; return FONT_PER_CHAR (t[0]) + FONT_PER_CHAR (t[1]) + FONT_PER_CHAR (t[2]); } t[0] = '^'; t[1] = c + '@'; t[2] = 0; return FONT_PER_CHAR (t[0]) + FONT_PER_CHAR (t[1]); } /* same as above but just gets the length */ static inline int width_of_long_printable (wchar_t c) { c &= 0x7FFFFFFFUL; if (wc_isgraph (c)) return FONT_PER_CHAR (c); if (c == ' ') { if (option_long_whitespace) return FONT_PER_CHAR (' ') + FONT_PER_CHAR (' '); else return FONT_PER_CHAR (' '); } if (option_international_characters && FONT_PER_CHAR (c)) return FONT_PER_CHAR (c); if (c > 0xFF) return FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 12) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 8) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 4) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[c & 0xF]) + FONT_PER_CHAR ((unsigned char) 'h'); if (c > 0xFFFF) return FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 28) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 24) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 20) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 16) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 12) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 8) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[(c >> 4) & 0xF]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[c & 0xF]) + FONT_PER_CHAR ((unsigned char) 'h'); if (c > '~') return FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[c >> 4]) + FONT_PER_CHAR ((unsigned char) ("0123456789ABCDEF")[c & 0xF]) + FONT_PER_CHAR ((unsigned char) 'h'); return FONT_PER_CHAR ('^') + FONT_PER_CHAR (c + '@'); } int edit_width_of_long_printable (wchar_t c) { return width_of_long_printable (c); } /* returns x pixel pos of char at offset *q with x not more than l */ static int calc_text_pos (WEdit * edit, long b, long *q, int l) { int x = 0, xn = 0; wchar_t c; for (;;) { c = edit_get_wide_byte (edit, b); switch (c) { case -1: /* no character since used up by a multi-byte sequence */ break; case '\n': *q = b; if (x > edit->max_column) edit->max_column = x; return x; case '\t': xn = next_tab_pos (x); break; default: xn = x + width_of_long_printable (c); break; } if (xn > l) break; x = xn; b++; } *q = b; if (x > edit->max_column) edit->max_column = x; return x; } /* calcs pixel length of the line beginning at b up to upto */ static int calc_text_len (WEdit * edit, long b, long upto) { int x = 0; wchar_t c; for (;;) { if (b == upto) { if (x > edit->max_column) edit->max_column = x; return x; } c = edit_get_wide_byte (edit, b); switch (c) { case -1: /* no character since used up by a multi-byte sequence */ break; case '\n':{ if (x > edit->max_column) edit->max_column = x; return x; } case '\t': x = next_tab_pos (x); break; default: x += width_of_long_printable (c); break; } b++; } } /* If pixels is zero this returns the count of pixels from current to upto. */ /* If upto is zero returns index of pixels across from current. */ long edit_move_forward3 (WEdit * edit, long current, int pixels, long upto) { CPushFont ("editor", 0); if (upto) { current = calc_text_len (edit, current, upto); } else if (pixels) { long q; calc_text_pos (edit, current, &q, pixels); current = q; } CPopFont (); return current; } extern int column_highlighting; /* gets the characters style (eg marked, highlighted) from its position in the edit buffer */ static inline cache_type get_style_fast (WEdit * edit, long q, wchar_t c) { cache_type s; unsigned int fg, bg; s.c.ch = s._style = 0; if (!(wc_isprint (c) || (option_international_characters && FONT_PER_CHAR(c)))) if (c != '\n' && c != '\t') s.c.style = MOD_ABNORMAL; edit_get_syntax_color (edit, q, (int *) &fg, (int *) &bg); s.c.fg = fg; s.c.bg = bg; return s; } /* gets the characters style (eg marked, highlighted) from its position in the edit buffer */ static inline cache_type get_style (WEdit * edit, long q, wchar_t c, long m1, long m2, int x) { cache_type s; unsigned int fg, bg; s.c.ch = s._style = 0; if (q == edit->curs1) s.c.style |= MOD_CURSOR; if (q >= m1 && q < m2) { if (column_highlighting) { if ((x >= edit->column1 && x < edit->column2) || (x >= edit->column2 && x < edit->column1)) s.c.style |= MOD_INVERSE; } else { s.c.style |= MOD_MARKED; } } if (q == edit->bracket) s.c.style |= MOD_BOLD; if (q >= edit->found_start && q < edit->found_start + edit->found_len) s.c.style |= MOD_HIGHLIGHTED; if (!(wc_isprint (c) || (option_international_characters && FONT_PER_CHAR(c)))) if (c != '\n' && c != '\t') s.c.style |= MOD_ABNORMAL; edit_get_syntax_color (edit, q, (int *) &fg, (int *) &bg); s.c.fg = fg; s.c.bg = bg; return s; } static void convert_text (WEdit * edit, long q, cache_type * p, int x, int x_max, int row) { wchar_t c; cache_type s; long m1, m2, last; wchar_t *r, text[12]; int book_mark_colors[10], book_mark; eval_marks (edit, &m1, &m2); book_mark = book_mark_query_all (edit, edit->start_line + row, book_mark_colors); last = q + (x_max - x) / 2 + 2; /* for optimization, we say that the last character of this line cannot have an offset greater than this. This can be used to rule out uncommon text styles, like a character with a cursor, or selected text */ if (book_mark) { int the_end = 0, book_mark_cycle = 0; for (;;) { c = edit_get_wide_byte (edit, q); if (!the_end) *p = get_style (edit, q, c, m1, m2, x); if (the_end) p->c.ch = p->_style = 0; book_mark_cycle = (book_mark_cycle + 1) % book_mark; p->c.fg = book_mark_colors[book_mark_cycle] << 8; p->c.bg = book_mark_colors[book_mark_cycle]; switch (c) { case -1: /* no character since used up by a multi-byte sequence */ break; case '\n': the_end = 1; c = ' '; q--; goto the_default; case '\t': if (FIXED_FONT) { int t; t = next_tab_pos (x); t = min (t, x_max); s = *p; s.c.ch = ' '; while (x < t) { x += FONT_PER_CHAR(' '); *p++ = s; } } else { (p++)->c.ch = '\t'; x = next_tab_pos (x); } break; default: the_default: x += convert_to_long_printable (c, text); r = text; s = *p; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; break; } if (x >= x_max) break; q++; } } else if ((m2 < q || m1 > last) && (edit->curs1 < q || edit->curs1 > last) && \ (edit->found_start + edit->found_len < q || edit->found_start > last) && (edit->bracket < q || edit->bracket > last)) { for (;;) { c = edit_get_wide_byte (edit, q); *p = get_style_fast (edit, q, c); switch (c) { case -1: /* no character since used up by a multi-byte sequence */ break; case '\n': (p++)->c.ch = ' '; p->c.ch = p->_style = 0; if (x > edit->max_column) edit->max_column = x; return; case '\t': if (FIXED_FONT) { int t; t = next_tab_pos (x); t = min (t, x_max); s = *p; s.c.ch = ' '; while (x < t) { x += FONT_PER_CHAR(' '); *p++ = s; } } else { (p++)->c.ch = '\t'; x = next_tab_pos (x); } break; default: x += convert_to_long_printable (c, text); r = text; s = *p; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; break; } if (x >= x_max) break; q++; } } else { for (;;) { c = edit_get_wide_byte (edit, q); #if 0 if (wc_isgraph(c)) printf ("(%d)%c", q, c); else printf ("(%d)%Xh", q, c); #endif *p = get_style (edit, q, c, m1, m2, x); switch (c) { case -1: /* no character since used up by a multi-byte sequence */ break; case '\n': (p++)->c.ch = ' '; p->c.ch = p->_style = 0; if (x > edit->max_column) edit->max_column = x; return; case '\t': if (FIXED_FONT) { int t; t = next_tab_pos (x); t = min (t, x_max); s = *p; s.c.ch = ' '; while (x < t) { x += FONT_PER_CHAR(' '); *p++ = s; } } else { (p++)->c.ch = '\t'; x = next_tab_pos (x); } break; default: x += convert_to_long_printable (c, text); r = text; s = *p; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; if (!*r) break; s.c.ch = *r++; *p++ = s; break; } if (x >= x_max) break; q++; } } if (x > edit->max_column) edit->max_column = x; p->c.ch = p->_style = 0; } static void edit_set_cursor (Window win, int x, int y, int bg, int fg, int width, wchar_t t, int style) { #ifdef GTK gdk_gc_set_foreground (win->gc, &win->color[18]); gdk_draw_rectangle (win->text_area, win->gc, 0, x, y + FONT_OVERHEAD, width - 1, FONT_HEIGHT - 1); #else CSetColor (edit_cursor_color); if (style & MOD_REVERSE) CLine (win, x + width - 1, y + FONT_OVERHEAD, x + width - 1, y + FONT_HEIGHT - 1); /* non focussed cursor form */ else CLine (win, x, y + FONT_OVERHEAD, x, y + FONT_HEIGHT - 1); /* non focussed cursor form */ CLine (win, x, y + FONT_OVERHEAD, x + width - 1, y + FONT_OVERHEAD); set_cursor_position (win, x, y, width, FONT_HEIGHT, CURSOR_TYPE_EDITOR, t, bg, fg, style); /* widget library's flashing cursor */ #endif } static inline int next_tab (int x, int scroll_right) { return next_tab_pos (x - scroll_right - EDIT_TEXT_HORIZONTAL_OFFSET) - x + scroll_right + EDIT_TEXT_HORIZONTAL_OFFSET; } static int draw_tab (Window win, int x, int y, cache_type s, int scroll_right) { int l; #ifdef GTK GdkColor fg, bg; #else unsigned long fg, bg; #endif l = next_tab (x, scroll_right); #ifdef GTK set_style_color (s, &fg.pixel, &bg.pixel); gdk_gc_set_foreground (win->gc, &bg); gdk_draw_rectangle (win->text_area, win->gc, 1, x, y + FONT_OVERHEAD, l, FONT_HEIGHT); /* if we printed a cursor: */ if (s.c.style & MOD_CURSOR) edit_set_cursor (win, x, y, bg.pixel, fg.pixel, FONT_PER_CHAR(' '), ' ', s.c.style); #else set_style_color (s, &fg, &bg); CSetColor (bg); CRectangle (win, x, y + FONT_OVERHEAD, l, FONT_HEIGHT); /* if we printed a cursor: */ if (s.c.style & MOD_CURSOR) edit_set_cursor (win, x, y, bg, fg, FONT_PER_CHAR(' '), ' ', s.c.style); #endif return x + l; } #ifdef GTK #include #endif static inline void draw_space (Window win, int x, int y, cache_type s, int l) { #ifdef GTK GdkColor fg, bg; #else unsigned long fg, bg; #endif #ifdef GTK set_style_color (s, &fg.pixel, &bg.pixel); gdk_gc_set_foreground (win->gc, &bg); gdk_draw_rectangle (win->text_area, win->gc, 1, x, y + FONT_OVERHEAD, l, FONT_HEIGHT); #else set_style_color (s, &fg, &bg); CSetColor (bg); CRectangle (win, x, y + FONT_OVERHEAD, l, FONT_HEIGHT); /* if we printed a cursor: */ if (s.c.style & MOD_CURSOR) edit_set_cursor (win, x, y, bg, fg, FONT_PER_CHAR(' '), ' ', s.c.style); #endif } #ifdef GTK void gdk_draw_image_text (GdkDrawable *drawable, GdkFont *font, GdkGC *gc, gint x, gint y, const gchar *text, gint text_length) { GdkWindowPrivate *drawable_private; GdkFontPrivate *font_private; GdkGCPrivate *gc_private; g_return_if_fail (drawable != NULL); g_return_if_fail (font != NULL); g_return_if_fail (gc != NULL); g_return_if_fail (text != NULL); drawable_private = (GdkWindowPrivate*) drawable; if (drawable_private->destroyed) return; gc_private = (GdkGCPrivate*) gc; font_private = (GdkFontPrivate*) font; if (font->type == GDK_FONT_FONT) { XFontStruct *xfont = (XFontStruct *) font_private->xfont; XSetFont(drawable_private->xdisplay, gc_private->xgc, xfont->fid); if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0)) { XDrawImageString (drawable_private->xdisplay, drawable_private->xwindow, gc_private->xgc, x, y, text, text_length); } else { XDrawImageString16 (drawable_private->xdisplay, drawable_private->xwindow, gc_private->xgc, x, y, (XChar2b *) text, text_length / 2); } } else if (font->type == GDK_FONT_FONTSET) { XFontSet fontset = (XFontSet) font_private->xfont; XmbDrawImageString (drawable_private->xdisplay, drawable_private->xwindow, fontset, gc_private->xgc, x, y, text, text_length); } else g_error("undefined font type\n"); } #endif static int draw_string (Window win, int x, int y, cache_type s, XChar2b *text, wchar_t *textwc, int length) { #ifdef GTK GdkColor fg, bg; #else unsigned long fg, bg; int underlined, l; #endif #ifdef GTK set_style_color (s, &fg.pixel, &bg.pixel); gdk_gc_set_background (win->gc, &bg); gdk_gc_set_foreground (win->gc, &fg); gdk_draw_image_text (win->text_area, GTK_WIDGET (win)->style->font, win->gc, x + FONT_OFFSET_X, y + FONT_OFFSET_Y, text, length); #else underlined = set_style_color (s, &fg, &bg); CSetBackgroundColor (bg); CSetColor (fg); CImageTextWC (win, x + FONT_OFFSET_X, y + FONT_OFFSET_Y, text, textwc, length); l = CImageTextWidthWC (text, textwc, length); if (underlined) { int i, h, inc; inc = FONT_MEAN_WIDTH * 2 / 3; CSetColor (color_palette (18)); h = (x / inc) & 1; CLine (win, x, y + FONT_HEIGHT + FONT_OVERHEAD - 1 - h, x + min (l, inc - (x % inc) - 1), y + FONT_HEIGHT + FONT_OVERHEAD - 1 - h); h = h ^ 1; for (i = inc - min (l, (x % inc)); i < l; i += inc) { CLine (win, x + i, y + FONT_HEIGHT + FONT_OVERHEAD - 1 - h, x + min (l, i + inc - 1), y + FONT_HEIGHT + FONT_OVERHEAD - 1 - h); h = h ^ 1; } } #endif /* if we printed a cursor: */ #ifdef GTK if (s.c.style & MOD_CURSOR) edit_set_cursor (win, x, y, bg.pixel, fg.pixel, FONT_PER_CHAR(*textwc), *textwc, s.c.style); return x + gdk_text_width (GTK_WIDGET (win)->style->font, text, length); #else if (s.c.style & MOD_CURSOR) edit_set_cursor (win, x, y, bg, fg, FONT_PER_CHAR(*textwc), *textwc, s.c.style); return x + l; #endif } #define STYLE_DIFF (cache->_style != line->_style || cache->c.ch != line->c.ch \ || ((cache->c.style | line->c.style) & MOD_CURSOR) \ || !(cache->c.ch | cache->_style) || !(line->c.ch | line->_style)) int get_ignore_length (cache_type *cache, cache_type *line) { int i; for (i = 0; i < cache_width; i++, line++, cache++) { if (STYLE_DIFF) return i; } return cache_width; } static inline size_t lwstrnlen (const cache_type * s, size_t count) { const cache_type *sc; for (sc = s; count-- && (sc->c.ch | sc->_style) != 0; ++sc); return sc - s; } static inline size_t lwstrlen (const cache_type * s) { const cache_type *sc; for (sc = s; (sc->c.ch | sc->_style) != 0; ++sc); return sc - s; } int get_ignore_trailer (cache_type *cache, cache_type *line, int length) { int i; int cache_len, line_len; cache_len = lwstrnlen (cache, cache_width); line_len = lwstrlen (line); if (line_len > cache_len) for (i = line_len - 1; i >= cache_len && i >= length; i--) { if (line[i].c.ch == ' ' && !(line[i].c.style | line[i].c.fg | line[i].c.bg)) continue; return i + 1; } for (i = cache_len - 1, line = line + i, cache = cache + i; i > length; i--, line--, cache--) if (STYLE_DIFF) return i + 1; return length + 1; } /* erases trailing bit of old line if a new line is printed over a longer old line */ static void cover_trail (Window win, int x_start, int x_new, int x_old, int y) { if (x_new < EDIT_TEXT_HORIZONTAL_OFFSET) x_new = EDIT_TEXT_HORIZONTAL_OFFSET; if (x_new < x_old) { /* no need to print */ #ifdef GTK gdk_gc_set_foreground (win->gc, &win->color[1]); gdk_draw_rectangle (win->text_area, win->gc, 1, x_new, y + FONT_OVERHEAD, x_old - x_new, FONT_HEIGHT); #else CSetColor (edit_normal_background_color); CRectangle (win, x_new, y + FONT_OVERHEAD, x_old - x_new, FONT_HEIGHT + (FONT_OVERHEAD != 0 && !FIXED_FONT)); #endif } else { #ifdef GTK gdk_gc_set_foreground (win->gc, &win->color[1]); #else CSetColor (edit_normal_background_color); #endif } /* true type fonts print stuff out of the bounding box (aaaaaaaaarrrgh!!) */ if (!FIXED_FONT) if (FONT_OVERHEAD && x_new > EDIT_TEXT_HORIZONTAL_OFFSET) #ifdef GTK 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); #else CLine (win, max (x_start, EDIT_TEXT_HORIZONTAL_OFFSET), y + FONT_HEIGHT + FONT_OVERHEAD, x_new - 1, y + FONT_HEIGHT + FONT_OVERHEAD); #endif } #define NOT_VALID (-2000000000) struct cache_line { int x0, x1; cache_type *data; }; struct cache_line *cache_lines = 0; void edit_free_cache_lines (void) { int i; if (cache_lines) { for (i = 0; i < cache_height; i++) free (cache_lines[i].data); free (cache_lines); cache_lines = 0; } } static void edit_realloc_cache_lines (int width, int height) { if (width > cache_width || height > cache_height) { int i; edit_free_cache_lines (); cache_width = max (width + 10, cache_width); cache_height = max (height + 10, cache_height); cache_lines = malloc (sizeof (struct cache_line) * cache_height); memset (cache_lines, 0, sizeof (struct cache_line) * cache_height); for (i = 0; i < cache_height; i++) { cache_lines[i].data = malloc (sizeof (cache_type) * (cache_width + 1)); memset (cache_lines[i].data, 0, sizeof (cache_type) * (cache_width + 1)); cache_lines[i].x0 = NOT_VALID; cache_lines[i].x1 = 10000; } } } #define IS_HEBREW(c) (((c) >= 0x0590UL && (c) <= 0x05FFUL)) int option_reverse_hebrew = 1; static void reverse_text (cache_type * line) { int i, n; if (option_reverse_hebrew) { while (line->c.ch | line->_style) { while (!IS_HEBREW (line->c.ch) && (line->c.ch | line->_style)) line++; for (n = 0; (IS_HEBREW (line[n].c.ch) || (line[n].c.ch == ' ')) && (line[n].c.ch | line[n]._style); n++); while (n && !IS_HEBREW (line[n - 1].c.ch)) n--; n--; for (i = 0; i < (n + 1) / 2; i++) { cache_type t; t = line[i]; line[i] = line[n - i]; line[i].c.style |= MOD_REVERSE; line[n - i] = t; line[n - i].c.style |= MOD_REVERSE; } line += n + 1; } } } void edit_draw_proportional (void *data, void (*converttext) (void *, long, 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) { static Window last = 0; cache_type style, line[MAX_LINE_LEN], *p; XChar2b text[128]; wchar_t textwc[128]; int x0, x, ignore_text = 0, ignore_trailer = 2000000000, j, i; long q; tab_width = tabwidth; if (option_long_whitespace) tab_width = tabwidth *= 2; x_max -= 3; edit_realloc_cache_lines (x_max / 3, row + 1); /* if its not the same window, reset the screen rememberer */ if (last != win || !win) { last = win; for (i = 0; i < cache_height; i++) { cache_lines[i].x0 = NOT_VALID; cache_lines[i].x1 = x_max; } if (!win) return; } /* get point to start drawing */ x0 = (*calctextpos) (data, b, &q, -scroll_right + x_offset); /* q contains the offset in the edit buffer */ /* translate this line into printable characters with a style (=color) high byte */ for (i = 0; i < MAX_LINE_LEN; i += 8) { line[i]._style = line[i].c.ch = 0; line[i + 1]._style = line[i + 1].c.ch = 0; line[i + 2]._style = line[i + 2].c.ch = 0; line[i + 3]._style = line[i + 3].c.ch = 0; line[i + 4]._style = line[i + 4].c.ch = 0; line[i + 5]._style = line[i + 5].c.ch = 0; line[i + 6]._style = line[i + 6].c.ch = 0; line[i + 7]._style = line[i + 7].c.ch = 0; } (*converttext) (data, q, line, x0, x_max - scroll_right - EDIT_TEXT_HORIZONTAL_OFFSET, row); reverse_text (line); /* adjust for the horizontal scroll and border */ x0 += scroll_right + EDIT_TEXT_HORIZONTAL_OFFSET; x = x0; /* is some of the line identical to that already printed so that we can ignore it? */ if (!EditExposeRedraw) { if (cache_lines[row].x0 == x0 && row < cache_height) { /* i.e. also && cache_lines[row].x0 != NOT_VALID */ ignore_text = get_ignore_length (cache_lines[row].data, line); if (FIXED_FONT) ignore_trailer = get_ignore_trailer (cache_lines[row].data, line, ignore_text); } } p = line; j = 0; while (p->c.ch | p->_style) { if (p->c.style & MOD_PIXMAP) { #ifdef STILL_TO_BE_SUPPORTED x += edit_insert_pixmap (win, x, y, p->c.ch); /* the pixmap will be clipped, if it's taller than the current font, else centred top to bottom */ #endif j++; p++; continue; } if (p->c.style & MOD_TAB) { draw_space (win, x, y, *p, p->c.ch); x += p->c.ch; j++; p++; } else if (p->c.ch == '\t') { j++; if (j > ignore_text && j < ignore_trailer + 1) x = draw_tab (win, x, y, *p, scroll_right); else x += next_tab (x, scroll_right); p++; } else { style = *p; style.c.ch = 0; i = 0; do { text[i].byte2 = (unsigned char) p->c.ch; text[i].byte1 = (unsigned char) (p->c.ch >> 8); textwc[i++] = (p++)->c.ch; j++; if (j == ignore_text || j == ignore_trailer) break; } while (i < 128 && p->c.ch && style._style == p->_style && p->c.ch != '\t'); if (j > ignore_text && j < ignore_trailer + 1) { x = draw_string (win, x, y, style, text, textwc, i); } else { #ifdef GTK x += gdk_text_width (GTK_WIDGET(win)->style->font, text, i); #else x += CImageTextWidthWC (text, textwc, i); #endif } } } x = min (x, x_max); if (!EditExposeRedraw || EditClear) cover_trail (win, x0, x, cache_lines[row].x1, y); memcpy (&(cache_lines[row].data[ignore_text]), &(line[ignore_text]), (min (j, cache_width) - ignore_text) * sizeof (cache_type)); cache_lines[row].data[min (j, cache_width)].c.ch = 0; cache_lines[row].data[min (j, cache_width)]._style = 0; cache_lines[row].x0 = x0; cache_lines[row].x1 = x; if (EditExposeRedraw) last = 0; else last = win; } void edit_draw_this_line_proportional (WEdit * edit, long b, int row, int start_column, int end_column) { int fg, bg; if (row < 0 || row >= edit->num_widget_lines) return; if (row + edit->start_line > edit->total_lines) b = edit->last_byte + 1; /* force b out of range of the edit buffer for blanks lines */ if (end_column > CWidthOf (edit->widget)) end_column = CWidthOf (edit->widget); edit_get_syntax_color (edit, b - 1, &fg, &bg); edit_draw_proportional (edit, (void (*) (void *, long, cache_type *, int, int, int)) convert_text, (int (*) (void *, long, long *, int)) calc_text_pos, edit->start_col, CWindowOf (edit->widget), end_column, b, row, row * FONT_PIX_PER_LINE + EDIT_TEXT_VERTICAL_OFFSET, EditExposeRedraw ? start_column : 0, FONT_PER_CHAR(' ') * TAB_SIZE); } /*********************************************************************************/ /* The remainder is for the text box widget */ /*********************************************************************************/ static int calc_text_pos_str (unsigned char *text, long b, long *q, int l) { int x = 0, c = 0, xn = 0, d; for (;;) { d = c; c = (unsigned char) text[b]; switch (c) { case '\0': case '\n': *q = b; return x; case '\t': xn = next_tab_pos (x); break; case '\r': break; case '\b': if (d) xn = x - FONT_PER_CHAR(d); break; default: if (!FONT_PER_CHAR (c)) c = ' '; xn = x + FONT_PER_CHAR(c); break; } if (xn > l) break; x = xn; b++; } *q = b; return x; } int prop_font_strcolmove (unsigned char *str, int i, int column) { long q; CPushFont ("editor", 0); calc_text_pos_str (str, i, &q, column * FONT_MEAN_WIDTH); CPopFont (); return q; } #ifndef GTK /* b is the beginning of the line. l is the length in pixels up to a point on some character which is unknown. The character pos is returned in *q and the characters pixel x pos from b is return'ed. */ int calc_text_pos2 (CWidget * w, long b, long *q, int l) { int r; CPushFont ("editor", 0); r = calc_text_pos_str ((unsigned char *) w->text, b, q, l); CPopFont (); return r; } int highlight_this_line; /* this is for the text widget (i.e. nroff formatting) */ void convert_text2 (CWidget * w, long q, cache_type * line, int x, int x_max, int row) { int c = 0, d; cache_type s, *p; long m1, m2; m1 = min (w->mark1, w->mark2); m2 = max (w->mark1, w->mark2); p = line; p->c.ch = p->_style = 0; for (;;) { d = c; c = (unsigned char) w->text[q]; p[1].c.ch = p[1]._style = 0; p->c.fg = p->c.bg = 0xFF; /* default background colors */ if (highlight_this_line) p->c.style |= MOD_HIGHLIGHTED; if (q >= m1 && q < m2) p->c.style |= MOD_MARKED; switch (c) { case '\0': case '\n': (p++)->c.ch |= ' '; if (highlight_this_line) { q--; x += FONT_PER_CHAR (' '); } else return; break; case '\t': if (FIXED_FONT) { int i; i = next_tab_pos (x) - x; x += i; s = *p; while (i > 0) { i -= FONT_PER_CHAR (' '); (p++)->c.ch = s.c.ch | ' '; p->c.ch = p->_style = 0; } } else { (p++)->c.ch |= '\t'; x = next_tab_pos (x); } break; case '\r': break; case '\b': if (d) { --p; x -= FONT_PER_CHAR (d); if (d == '_') p->c.style |= MOD_ITALIC; else p->c.style |= MOD_BOLD; } break; default: if (!FONT_PER_CHAR (c)) { c = ' '; p->c.style |= MOD_ABNORMAL; } x += FONT_PER_CHAR (c); (p++)->c.ch = c; break; } if (x > x_max) break; q++; } p->c.ch = p->_style = 0; } #endif