remove all remaining WIN32 code
[rofl0r-ixchat.git] / src / fe-gtk / xtext.c
blob1c24df6040c3db3bde7b792b8550aea7df44113a
1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
17 * =========================================================================
19 * xtext, the text widget used by X-Chat.
20 * By Peter Zelezny <zed@xchat.org>.
24 #define XCHAT /* using xchat */
25 #define TINT_VALUE 195 /* 195/255 of the brightness. */
26 #define MOTION_MONITOR /* URL hilights. */
27 #define SMOOTH_SCROLL /* line-by-line or pixel scroll? */
28 #define SCROLL_HACK /* use XCopyArea scroll, or full redraw? */
29 #undef COLOR_HILIGHT /* Color instead of underline? */
30 /* Italic is buggy because it assumes drawing an italic string will have
31 identical extents to the normal font. This is only true some of the
32 time, so we can't use this hack yet. */
33 #undef ITALIC /* support Italic? */
34 #define GDK_MULTIHEAD_SAFE
35 #define USE_DB /* double buffer */
37 #define MARGIN 2 /* dont touch. */
38 #define REFRESH_TIMEOUT 20
39 #define WORDWRAP_LIMIT 24
41 #include <string.h>
42 #include <ctype.h>
43 #include <stdlib.h>
44 #include <time.h>
45 #include <unistd.h>
46 #include <gtk/gtkmain.h>
47 #include <gtk/gtksignal.h>
48 #include <gtk/gtkselection.h>
49 #include <gtk/gtkclipboard.h>
50 #include <gtk/gtkversion.h>
51 #include <gtk/gtkwindow.h>
53 #ifdef XCHAT
54 #else
55 #define USE_XLIB
56 #endif
58 #ifdef USE_XLIB
59 #include <gdk/gdkx.h>
60 #include <X11/Xlib.h>
61 #include <X11/Xatom.h>
62 #endif
64 #include "xtext.h"
66 #define charlen(str) g_utf8_skip[*(guchar *)(str)]
69 /* is delimiter */
70 #define is_del(c) \
71 (c == ' ' || c == '\n' || c == ')' || c == '(' || \
72 c == '>' || c == '<' || c == ATTR_RESET || c == ATTR_BOLD || c == 0)
74 #ifdef SCROLL_HACK
75 /* force scrolling off */
76 #define dontscroll(buf) (buf)->last_pixel_pos = 0x7fffffff
77 #else
78 #define dontscroll(buf)
79 #endif
81 static GtkWidgetClass *parent_class = NULL;
83 struct textentry
85 struct textentry *next;
86 struct textentry *prev;
87 unsigned char *str;
88 time_t stamp;
89 gint16 str_width;
90 gint16 str_len;
91 gint16 mark_start;
92 gint16 mark_end;
93 gint16 indent;
94 gint16 left_len;
95 gint16 lines_taken;
96 #define RECORD_WRAPS 4
97 guint16 wrap_offset[RECORD_WRAPS];
98 guchar mb; /* boolean: is multibyte? */
99 guchar tag;
100 guchar pad1;
101 guchar pad2; /* 32-bit align : 44 bytes total */
104 enum
106 WORD_CLICK,
107 LAST_SIGNAL
110 /* values for selection info */
111 enum
113 TARGET_UTF8_STRING,
114 TARGET_STRING,
115 TARGET_TEXT,
116 TARGET_COMPOUND_TEXT
119 static guint xtext_signals[LAST_SIGNAL];
121 #ifdef XCHAT
122 char *nocasestrstr (const char *text, const char *tofind); /* util.c */
123 int xtext_get_stamp_str (time_t, char **);
124 #endif
125 static void gtk_xtext_render_page (GtkXText * xtext);
126 static void gtk_xtext_calc_lines (xtext_buffer *buf, int);
127 #if defined(USE_XLIB)
128 static void gtk_xtext_load_trans (GtkXText * xtext);
129 static void gtk_xtext_free_trans (GtkXText * xtext);
130 #endif
131 static char *gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret);
132 static textentry *gtk_xtext_nth (GtkXText *xtext, int line, int *subline);
133 static void gtk_xtext_adjustment_changed (GtkAdjustment * adj,
134 GtkXText * xtext);
135 static int gtk_xtext_render_ents (GtkXText * xtext, textentry *, textentry *);
136 static void gtk_xtext_recalc_widths (xtext_buffer *buf, int);
137 static void gtk_xtext_fix_indent (xtext_buffer *buf);
138 static int gtk_xtext_find_subline (GtkXText *xtext, textentry *ent, int line);
139 static char *gtk_xtext_conv_color (unsigned char *text, int len, int *newlen);
140 static unsigned char *
141 gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
142 int *newlen, int *mb_ret, int strip_hidden);
143 static gboolean gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add);
144 static int gtk_xtext_render_page_timeout (GtkXText * xtext);
146 /* some utility functions first */
148 #ifndef XCHAT /* xchat has this in util.c */
150 static char *
151 nocasestrstr (const char *s, const char *tofind)
153 register const size_t len = strlen (tofind);
155 if (len == 0)
156 return (char *)s;
157 while (toupper(*s) != toupper(*tofind) || strncasecmp (s, tofind, len))
158 if (*s++ == '\0')
159 return (char *)NULL;
160 return (char *)s;
163 #endif
165 /* gives width of a 8bit string - with no mIRC codes in it */
167 static int
168 gtk_xtext_text_width_8bit (GtkXText *xtext, unsigned char *str, int len)
170 int width = 0;
172 while (len)
174 width += xtext->fontwidth[*str];
175 str++;
176 len--;
179 return width;
182 #define xtext_draw_bg(xt,x,y,w,h) gdk_draw_rectangle(xt->draw_buf, xt->bgc, 1,x,y,w,h);
184 /* ========================================= */
185 /* ========== XFT 1 and 2 BACKEND ========== */
186 /* ========================================= */
188 #ifdef USE_XFT
190 static void
191 backend_font_close (GtkXText *xtext)
193 XftFontClose (GDK_WINDOW_XDISPLAY (xtext->draw_buf), xtext->font);
194 #ifdef ITALIC
195 XftFontClose (GDK_WINDOW_XDISPLAY (xtext->draw_buf), xtext->ifont);
196 #endif
199 static void
200 backend_init (GtkXText *xtext)
202 if (xtext->xftdraw == NULL)
204 xtext->xftdraw = XftDrawCreate (
205 GDK_WINDOW_XDISPLAY (xtext->draw_buf),
206 GDK_WINDOW_XWINDOW (xtext->draw_buf),
207 GDK_VISUAL_XVISUAL (gdk_drawable_get_visual (xtext->draw_buf)),
208 GDK_COLORMAP_XCOLORMAP (gdk_drawable_get_colormap (xtext->draw_buf)));
209 XftDrawSetSubwindowMode (xtext->xftdraw, IncludeInferiors);
213 static void
214 backend_deinit (GtkXText *xtext)
216 if (xtext->xftdraw)
218 XftDrawDestroy (xtext->xftdraw);
219 xtext->xftdraw = NULL;
223 static XftFont *
224 backend_font_open_real (Display *xdisplay, char *name, gboolean italics)
226 XftFont *font = NULL;
227 PangoFontDescription *fontd;
228 int weight, slant, screen = DefaultScreen (xdisplay);
230 fontd = pango_font_description_from_string (name);
232 if (pango_font_description_get_size (fontd) != 0)
234 weight = pango_font_description_get_weight (fontd);
235 /* from pangoft2-fontmap.c */
236 if (weight < (PANGO_WEIGHT_NORMAL + PANGO_WEIGHT_LIGHT) / 2)
237 weight = XFT_WEIGHT_LIGHT;
238 else if (weight < (PANGO_WEIGHT_NORMAL + 600) / 2)
239 weight = XFT_WEIGHT_MEDIUM;
240 else if (weight < (600 + PANGO_WEIGHT_BOLD) / 2)
241 weight = XFT_WEIGHT_DEMIBOLD;
242 else if (weight < (PANGO_WEIGHT_BOLD + PANGO_WEIGHT_ULTRABOLD) / 2)
243 weight = XFT_WEIGHT_BOLD;
244 else
245 weight = XFT_WEIGHT_BLACK;
247 slant = pango_font_description_get_style (fontd);
248 if (slant == PANGO_STYLE_ITALIC)
249 slant = XFT_SLANT_ITALIC;
250 else if (slant == PANGO_STYLE_OBLIQUE)
251 slant = XFT_SLANT_OBLIQUE;
252 else
253 slant = XFT_SLANT_ROMAN;
255 font = XftFontOpen (xdisplay, screen,
256 XFT_FAMILY, XftTypeString, pango_font_description_get_family (fontd),
257 XFT_CORE, XftTypeBool, False,
258 XFT_SIZE, XftTypeDouble, (double)pango_font_description_get_size (fontd)/PANGO_SCALE,
259 XFT_WEIGHT, XftTypeInteger, weight,
260 XFT_SLANT, XftTypeInteger, italics ? XFT_SLANT_ITALIC : slant,
261 NULL);
263 pango_font_description_free (fontd);
265 if (font == NULL)
267 font = XftFontOpenName (xdisplay, screen, name);
268 if (font == NULL)
269 font = XftFontOpenName (xdisplay, screen, "sans-11");
272 return font;
275 static void
276 backend_font_open (GtkXText *xtext, char *name)
278 Display *dis = GDK_WINDOW_XDISPLAY (xtext->draw_buf);
280 xtext->font = backend_font_open_real (dis, name, FALSE);
281 #ifdef ITALIC
282 xtext->ifont = backend_font_open_real (dis, name, TRUE);
283 #endif
286 inline static int
287 backend_get_char_width (GtkXText *xtext, unsigned char *str, int *mbl_ret)
289 XGlyphInfo ext;
291 if (*str < 128)
293 *mbl_ret = 1;
294 return xtext->fontwidth[*str];
297 *mbl_ret = charlen (str);
298 XftTextExtentsUtf8 (GDK_WINDOW_XDISPLAY (xtext->draw_buf), xtext->font, str, *mbl_ret, &ext);
300 return ext.xOff;
303 static int
304 backend_get_text_width (GtkXText *xtext, guchar *str, int len, int is_mb)
306 XGlyphInfo ext;
308 if (!is_mb)
309 return gtk_xtext_text_width_8bit (xtext, str, len);
311 XftTextExtentsUtf8 (GDK_WINDOW_XDISPLAY (xtext->draw_buf), xtext->font, str, len, &ext);
312 return ext.xOff;
315 static void
316 backend_draw_text (GtkXText *xtext, int dofill, GdkGC *gc, int x, int y,
317 char *str, int len, int str_width, int is_mb)
319 /*Display *xdisplay = GDK_WINDOW_XDISPLAY (xtext->draw_buf);*/
320 void (*draw_func) (XftDraw *, XftColor *, XftFont *, int, int, XftChar8 *, int) = (void *)XftDrawString8;
321 XftFont *font;
323 /* if all ascii, use String8 to avoid the conversion penalty */
324 if (is_mb)
325 draw_func = (void *)XftDrawStringUtf8;
327 if (dofill)
329 /* register GC xgc = GDK_GC_XGC (gc);
330 XSetForeground (xdisplay, xgc, xtext->xft_bg->pixel);
331 XFillRectangle (xdisplay, GDK_WINDOW_XWINDOW (xtext->draw_buf), xgc, x,
332 y - xtext->font->ascent, str_width, xtext->fontsize);*/
333 XftDrawRect (xtext->xftdraw, xtext->xft_bg, x,
334 y - xtext->font->ascent, str_width, xtext->fontsize);
337 font = xtext->font;
338 #ifdef ITALIC
339 if (xtext->italics)
340 font = xtext->ifont;
341 #endif
343 draw_func (xtext->xftdraw, xtext->xft_fg, font, x, y, str, len);
345 if (xtext->overdraw)
346 draw_func (xtext->xftdraw, xtext->xft_fg, font, x, y, str, len);
348 if (xtext->bold)
349 draw_func (xtext->xftdraw, xtext->xft_fg, font, x + 1, y, str, len);
352 /*static void
353 backend_set_clip (GtkXText *xtext, GdkRectangle *area)
355 gdk_gc_set_clip_rectangle (xtext->fgc, area);
356 gdk_gc_set_clip_rectangle (xtext->bgc, area);
359 static void
360 backend_clear_clip (GtkXText *xtext)
362 gdk_gc_set_clip_rectangle (xtext->fgc, NULL);
363 gdk_gc_set_clip_rectangle (xtext->bgc, NULL);
366 /*static void
367 backend_set_clip (GtkXText *xtext, GdkRectangle *area)
369 Region reg;
370 XRectangle rect;
372 rect.x = area->x;
373 rect.y = area->y;
374 rect.width = area->width;
375 rect.height = area->height;
377 reg = XCreateRegion ();
378 XUnionRectWithRegion (&rect, reg, reg);
379 XftDrawSetClip (xtext->xftdraw, reg);
380 XDestroyRegion (reg);
382 gdk_gc_set_clip_rectangle (xtext->fgc, area);
385 static void
386 backend_clear_clip (GtkXText *xtext)
388 XftDrawSetClip (xtext->xftdraw, NULL);
389 gdk_gc_set_clip_rectangle (xtext->fgc, NULL);
392 #else /* !USE_XFT */
394 /* ======================================= */
395 /* ============ PANGO BACKEND ============ */
396 /* ======================================= */
398 static void
399 backend_font_close (GtkXText *xtext)
401 pango_font_description_free (xtext->font->font);
402 #ifdef ITALIC
403 pango_font_description_free (xtext->font->ifont);
404 #endif
407 static void
408 backend_init (GtkXText *xtext)
410 if (xtext->layout == NULL)
412 xtext->layout = gtk_widget_create_pango_layout (GTK_WIDGET (xtext), 0);
413 if (xtext->font)
414 pango_layout_set_font_description (xtext->layout, xtext->font->font);
418 static void
419 backend_deinit (GtkXText *xtext)
421 if (xtext->layout)
423 g_object_unref (xtext->layout);
424 xtext->layout = NULL;
428 static PangoFontDescription *
429 backend_font_open_real (char *name)
431 PangoFontDescription *font;
433 font = pango_font_description_from_string (name);
434 if (font && pango_font_description_get_size (font) == 0)
436 pango_font_description_free (font);
437 font = pango_font_description_from_string ("sans 11");
439 if (!font)
440 font = pango_font_description_from_string ("sans 11");
442 return font;
445 static void
446 backend_font_open (GtkXText *xtext, char *name)
448 PangoLanguage *lang;
449 PangoContext *context;
450 PangoFontMetrics *metrics;
452 xtext->font = &xtext->pango_font;
453 xtext->font->font = backend_font_open_real (name);
454 if (!xtext->font->font)
456 xtext->font = NULL;
457 return;
459 #ifdef ITALIC
460 xtext->font->ifont = backend_font_open_real (name);
461 pango_font_description_set_style (xtext->font->ifont, PANGO_STYLE_ITALIC);
462 #endif
464 backend_init (xtext);
465 pango_layout_set_font_description (xtext->layout, xtext->font->font);
467 /* vte does it this way */
468 context = gtk_widget_get_pango_context (GTK_WIDGET (xtext));
469 lang = pango_context_get_language (context);
470 metrics = pango_context_get_metrics (context, xtext->font->font, lang);
471 xtext->font->ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
472 xtext->font->descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
473 pango_font_metrics_unref (metrics);
476 static int
477 backend_get_text_width (GtkXText *xtext, guchar *str, int len, int is_mb)
479 int width;
481 if (!is_mb)
482 return gtk_xtext_text_width_8bit (xtext, str, len);
484 if (*str == 0)
485 return 0;
487 pango_layout_set_text (xtext->layout, str, len);
488 pango_layout_get_pixel_size (xtext->layout, &width, NULL);
490 return width;
493 inline static int
494 backend_get_char_width (GtkXText *xtext, unsigned char *str, int *mbl_ret)
496 int width;
498 if (*str < 128)
500 *mbl_ret = 1;
501 return xtext->fontwidth[*str];
504 *mbl_ret = charlen (str);
505 pango_layout_set_text (xtext->layout, str, *mbl_ret);
506 pango_layout_get_pixel_size (xtext->layout, &width, NULL);
508 return width;
511 /* simplified version of gdk_draw_layout_line_with_colors() */
513 static void
514 xtext_draw_layout_line (GdkDrawable *drawable,
515 GdkGC *gc,
516 gint x,
517 gint y,
518 PangoLayoutLine *line)
520 GSList *tmp_list = line->runs;
521 PangoRectangle logical_rect;
522 gint x_off = 0;
524 while (tmp_list)
526 PangoLayoutRun *run = tmp_list->data;
528 pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
529 NULL, &logical_rect);
531 gdk_draw_glyphs (drawable, gc, run->item->analysis.font,
532 x + x_off / PANGO_SCALE, y, run->glyphs);
534 x_off += logical_rect.width;
535 tmp_list = tmp_list->next;
539 static void
540 backend_draw_text (GtkXText *xtext, int dofill, GdkGC *gc, int x, int y,
541 char *str, int len, int str_width, int is_mb)
543 GdkGCValues val;
544 GdkColor col;
545 PangoLayoutLine *line;
547 #ifdef ITALIC
548 if (xtext->italics)
549 pango_layout_set_font_description (xtext->layout, xtext->font->ifont);
550 #endif
552 pango_layout_set_text (xtext->layout, str, len);
554 if (dofill)
556 gdk_gc_get_values (gc, &val);
557 col.pixel = val.background.pixel;
558 gdk_gc_set_foreground (gc, &col);
559 gdk_draw_rectangle (xtext->draw_buf, gc, 1, x, y -
560 xtext->font->ascent, str_width, xtext->fontsize);
561 col.pixel = val.foreground.pixel;
562 gdk_gc_set_foreground (gc, &col);
565 line = pango_layout_get_lines (xtext->layout)->data;
567 xtext_draw_layout_line (xtext->draw_buf, gc, x, y, line);
569 if (xtext->overdraw)
570 xtext_draw_layout_line (xtext->draw_buf, gc, x, y, line);
572 if (xtext->bold)
573 xtext_draw_layout_line (xtext->draw_buf, gc, x + 1, y, line);
575 #ifdef ITALIC
576 if (xtext->italics)
577 pango_layout_set_font_description (xtext->layout, xtext->font->font);
578 #endif
581 /*static void
582 backend_set_clip (GtkXText *xtext, GdkRectangle *area)
584 gdk_gc_set_clip_rectangle (xtext->fgc, area);
585 gdk_gc_set_clip_rectangle (xtext->bgc, area);
588 static void
589 backend_clear_clip (GtkXText *xtext)
591 gdk_gc_set_clip_rectangle (xtext->fgc, NULL);
592 gdk_gc_set_clip_rectangle (xtext->bgc, NULL);
595 #endif /* !USE_PANGO */
597 static void
598 xtext_set_fg (GtkXText *xtext, GdkGC *gc, int index)
600 GdkColor col;
602 col.pixel = xtext->palette[index];
603 gdk_gc_set_foreground (gc, &col);
605 #ifdef USE_XFT
606 if (gc == xtext->fgc)
607 xtext->xft_fg = &xtext->color[index];
608 else
609 xtext->xft_bg = &xtext->color[index];
610 #endif
613 #ifdef USE_XFT
615 #define xtext_set_bg(xt,gc,index) xt->xft_bg = &xt->color[index]
617 #else
619 static void
620 xtext_set_bg (GtkXText *xtext, GdkGC *gc, int index)
622 GdkColor col;
624 col.pixel = xtext->palette[index];
625 gdk_gc_set_background (gc, &col);
628 #endif
630 static void
631 gtk_xtext_init (GtkXText * xtext)
633 xtext->pixmap = NULL;
634 xtext->io_tag = 0;
635 xtext->add_io_tag = 0;
636 xtext->scroll_tag = 0;
637 xtext->max_lines = 0;
638 xtext->col_back = XTEXT_BG;
639 xtext->col_fore = XTEXT_FG;
640 xtext->nc = 0;
641 xtext->pixel_offset = 0;
642 xtext->bold = FALSE;
643 xtext->underline = FALSE;
644 xtext->italics = FALSE;
645 xtext->hidden = FALSE;
646 xtext->font = NULL;
647 #ifdef USE_XFT
648 xtext->xftdraw = NULL;
649 #else
650 xtext->layout = NULL;
651 #endif
652 xtext->jump_out_offset = 0;
653 xtext->jump_in_offset = 0;
654 xtext->ts_x = 0;
655 xtext->ts_y = 0;
656 xtext->clip_x = 0;
657 xtext->clip_x2 = 1000000;
658 xtext->clip_y = 0;
659 xtext->clip_y2 = 1000000;
660 xtext->error_function = NULL;
661 xtext->urlcheck_function = NULL;
662 xtext->color_paste = FALSE;
663 xtext->skip_border_fills = FALSE;
664 xtext->skip_stamp = FALSE;
665 xtext->render_hilights_only = FALSE;
666 xtext->un_hilight = FALSE;
667 xtext->recycle = FALSE;
668 xtext->dont_render = FALSE;
669 xtext->dont_render2 = FALSE;
670 xtext->overdraw = FALSE;
671 xtext->tint_red = xtext->tint_green = xtext->tint_blue = TINT_VALUE;
673 xtext->adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 1, 1, 1, 1);
674 g_object_ref (G_OBJECT (xtext->adj));
675 g_object_ref_sink (G_OBJECT (xtext->adj));
676 g_object_unref (G_OBJECT (xtext->adj));
678 xtext->vc_signal_tag = g_signal_connect (G_OBJECT (xtext->adj),
679 "value_changed", G_CALLBACK (gtk_xtext_adjustment_changed), xtext);
681 static const GtkTargetEntry targets[] = {
682 { "UTF8_STRING", 0, TARGET_UTF8_STRING },
683 { "STRING", 0, TARGET_STRING },
684 { "TEXT", 0, TARGET_TEXT },
685 { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }
687 static const gint n_targets = sizeof (targets) / sizeof (targets[0]);
689 gtk_selection_add_targets (GTK_WIDGET (xtext), GDK_SELECTION_PRIMARY,
690 targets, n_targets);
693 if (getenv ("XCHAT_OVERDRAW"))
694 xtext->overdraw = TRUE;
697 static void
698 gtk_xtext_adjustment_set (xtext_buffer *buf, int fire_signal)
700 GtkAdjustment *adj = buf->xtext->adj;
702 if (buf->xtext->buffer == buf)
704 adj->lower = 0;
705 adj->upper = buf->num_lines;
707 if (adj->upper == 0)
708 adj->upper = 1;
710 adj->page_size =
711 (GTK_WIDGET (buf->xtext)->allocation.height -
712 buf->xtext->font->descent) / buf->xtext->fontsize;
713 adj->page_increment = adj->page_size;
715 if (adj->value > adj->upper - adj->page_size)
716 adj->value = adj->upper - adj->page_size;
718 if (adj->value < 0)
719 adj->value = 0;
721 if (fire_signal)
722 gtk_adjustment_changed (adj);
726 static gint
727 gtk_xtext_adjustment_timeout (GtkXText * xtext)
729 gtk_xtext_render_page (xtext);
730 xtext->io_tag = 0;
731 return 0;
734 static void
735 gtk_xtext_adjustment_changed (GtkAdjustment * adj, GtkXText * xtext)
737 #ifdef SMOOTH_SCROLL
738 if (xtext->buffer->old_value != xtext->adj->value)
739 #else
740 if ((int) xtext->buffer->old_value != (int) xtext->adj->value)
741 #endif
743 if (xtext->adj->value >= xtext->adj->upper - xtext->adj->page_size)
744 xtext->buffer->scrollbar_down = TRUE;
745 else
746 xtext->buffer->scrollbar_down = FALSE;
748 if (xtext->adj->value + 1 == xtext->buffer->old_value ||
749 xtext->adj->value - 1 == xtext->buffer->old_value) /* clicked an arrow? */
751 if (xtext->io_tag)
753 g_source_remove (xtext->io_tag);
754 xtext->io_tag = 0;
756 gtk_xtext_render_page (xtext);
757 } else
759 if (!xtext->io_tag)
760 xtext->io_tag = g_timeout_add (REFRESH_TIMEOUT,
761 (GSourceFunc)
762 gtk_xtext_adjustment_timeout,
763 xtext);
766 xtext->buffer->old_value = adj->value;
769 GtkWidget *
770 gtk_xtext_new (GdkColor palette[], int separator)
772 GtkXText *xtext;
774 xtext = g_object_new (gtk_xtext_get_type (), NULL);
775 xtext->separator = separator;
776 xtext->wordwrap = TRUE;
777 xtext->buffer = gtk_xtext_buffer_new (xtext);
778 xtext->orig_buffer = xtext->buffer;
780 gtk_widget_set_double_buffered (GTK_WIDGET (xtext), FALSE);
781 gtk_xtext_set_palette (xtext, palette);
783 return GTK_WIDGET (xtext);
786 static void
787 gtk_xtext_destroy (GtkObject * object)
789 GtkXText *xtext = GTK_XTEXT (object);
791 if (xtext->add_io_tag)
793 g_source_remove (xtext->add_io_tag);
794 xtext->add_io_tag = 0;
797 if (xtext->scroll_tag)
799 g_source_remove (xtext->scroll_tag);
800 xtext->scroll_tag = 0;
803 if (xtext->io_tag)
805 g_source_remove (xtext->io_tag);
806 xtext->io_tag = 0;
809 if (xtext->pixmap)
811 #if defined(USE_XLIB)
812 if (xtext->transparent)
813 gtk_xtext_free_trans (xtext);
814 else
815 #endif
816 g_object_unref (xtext->pixmap);
817 xtext->pixmap = NULL;
820 if (xtext->font)
822 backend_font_close (xtext);
823 xtext->font = NULL;
826 if (xtext->adj)
828 g_signal_handlers_disconnect_matched (G_OBJECT (xtext->adj),
829 G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xtext);
830 /* gtk_signal_disconnect_by_data (GTK_OBJECT (xtext->adj), xtext);*/
831 g_object_unref (G_OBJECT (xtext->adj));
832 xtext->adj = NULL;
835 if (xtext->bgc)
837 g_object_unref (xtext->bgc);
838 xtext->bgc = NULL;
841 if (xtext->fgc)
843 g_object_unref (xtext->fgc);
844 xtext->fgc = NULL;
847 if (xtext->light_gc)
849 g_object_unref (xtext->light_gc);
850 xtext->light_gc = NULL;
853 if (xtext->dark_gc)
855 g_object_unref (xtext->dark_gc);
856 xtext->dark_gc = NULL;
859 if (xtext->thin_gc)
861 g_object_unref (xtext->thin_gc);
862 xtext->thin_gc = NULL;
865 if (xtext->marker_gc)
867 g_object_unref (xtext->marker_gc);
868 xtext->marker_gc = NULL;
871 if (xtext->hand_cursor)
873 gdk_cursor_unref (xtext->hand_cursor);
874 xtext->hand_cursor = NULL;
877 if (xtext->resize_cursor)
879 gdk_cursor_unref (xtext->resize_cursor);
880 xtext->resize_cursor = NULL;
883 if (xtext->orig_buffer)
885 gtk_xtext_buffer_free (xtext->orig_buffer);
886 xtext->orig_buffer = NULL;
889 if (GTK_OBJECT_CLASS (parent_class)->destroy)
890 (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
893 static void
894 gtk_xtext_unrealize (GtkWidget * widget)
896 backend_deinit (GTK_XTEXT (widget));
898 /* if there are still events in the queue, this'll avoid segfault */
899 gdk_window_set_user_data (widget->window, NULL);
901 if (parent_class->unrealize)
902 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
905 static void
906 gtk_xtext_realize (GtkWidget * widget)
908 GtkXText *xtext;
909 GdkWindowAttr attributes;
910 GdkGCValues val;
911 GdkColor col;
912 GdkColormap *cmap;
914 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
915 xtext = GTK_XTEXT (widget);
917 attributes.x = widget->allocation.x;
918 attributes.y = widget->allocation.y;
919 attributes.width = widget->allocation.width;
920 attributes.height = widget->allocation.height;
921 attributes.wclass = GDK_INPUT_OUTPUT;
922 attributes.window_type = GDK_WINDOW_CHILD;
923 attributes.event_mask = gtk_widget_get_events (widget) |
924 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
925 #ifdef MOTION_MONITOR
926 | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK;
927 #else
928 | GDK_POINTER_MOTION_MASK;
929 #endif
931 cmap = gtk_widget_get_colormap (widget);
932 attributes.colormap = cmap;
933 attributes.visual = gtk_widget_get_visual (widget);
935 widget->window = gdk_window_new (widget->parent->window, &attributes,
936 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL |
937 GDK_WA_COLORMAP);
939 gdk_window_set_user_data (widget->window, widget);
941 xtext->depth = gdk_drawable_get_visual (widget->window)->depth;
943 val.subwindow_mode = GDK_INCLUDE_INFERIORS;
944 val.graphics_exposures = 0;
946 xtext->bgc = gdk_gc_new_with_values (widget->window, &val,
947 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
948 xtext->fgc = gdk_gc_new_with_values (widget->window, &val,
949 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
950 xtext->light_gc = gdk_gc_new_with_values (widget->window, &val,
951 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
952 xtext->dark_gc = gdk_gc_new_with_values (widget->window, &val,
953 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
954 xtext->thin_gc = gdk_gc_new_with_values (widget->window, &val,
955 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
956 xtext->marker_gc = gdk_gc_new_with_values (widget->window, &val,
957 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
959 /* for the separator bar (light) */
960 col.red = 0xffff; col.green = 0xffff; col.blue = 0xffff;
961 gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE);
962 gdk_gc_set_foreground (xtext->light_gc, &col);
964 /* for the separator bar (dark) */
965 col.red = 0x1111; col.green = 0x1111; col.blue = 0x1111;
966 gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE);
967 gdk_gc_set_foreground (xtext->dark_gc, &col);
969 /* for the separator bar (thinline) */
970 col.red = 0x8e38; col.green = 0x8e38; col.blue = 0x9f38;
971 gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE);
972 gdk_gc_set_foreground (xtext->thin_gc, &col);
974 /* for the marker bar (marker) */
975 col.pixel = xtext->palette[XTEXT_MARKER];
976 gdk_gc_set_foreground (xtext->marker_gc, &col);
978 xtext_set_fg (xtext, xtext->fgc, XTEXT_FG);
979 xtext_set_bg (xtext, xtext->fgc, XTEXT_BG);
980 xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
982 /* draw directly to window */
983 xtext->draw_buf = widget->window;
985 #if defined(USE_XLIB)
986 if (xtext->transparent)
988 gtk_xtext_load_trans (xtext);
989 } else
990 #endif
991 if (xtext->pixmap)
993 gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
994 gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
995 xtext->ts_x = xtext->ts_y = 0;
996 gdk_gc_set_fill (xtext->bgc, GDK_TILED);
999 xtext->hand_cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (widget->window), GDK_HAND1);
1000 xtext->resize_cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (widget->window), GDK_LEFT_SIDE);
1002 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
1003 widget->style = gtk_style_attach (widget->style, widget->window);
1005 backend_init (xtext);
1008 static void
1009 gtk_xtext_size_request (GtkWidget * widget, GtkRequisition * requisition)
1011 requisition->width = 200;
1012 requisition->height = 90;
1015 static void
1016 gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
1018 GtkXText *xtext = GTK_XTEXT (widget);
1019 int height_only = FALSE;
1020 int do_trans = TRUE;
1022 if (allocation->width == xtext->buffer->window_width)
1023 height_only = TRUE;
1025 if (allocation->x == widget->allocation.x &&
1026 allocation->y == widget->allocation.y && xtext->avoid_trans)
1027 do_trans = FALSE;
1029 xtext->avoid_trans = FALSE;
1031 widget->allocation = *allocation;
1032 if (GTK_WIDGET_REALIZED (widget))
1034 xtext->buffer->window_width = allocation->width;
1035 xtext->buffer->window_height = allocation->height;
1037 gdk_window_move_resize (widget->window, allocation->x, allocation->y,
1038 allocation->width, allocation->height);
1039 dontscroll (xtext->buffer); /* force scrolling off */
1040 if (!height_only)
1041 gtk_xtext_calc_lines (xtext->buffer, FALSE);
1042 else
1044 xtext->buffer->pagetop_ent = NULL;
1045 gtk_xtext_adjustment_set (xtext->buffer, FALSE);
1047 #if defined(USE_XLIB)
1048 if (do_trans && xtext->transparent && xtext->shaded)
1050 gtk_xtext_free_trans (xtext);
1051 gtk_xtext_load_trans (xtext);
1053 #endif
1054 if (xtext->buffer->scrollbar_down)
1055 gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
1056 xtext->adj->page_size);
1060 static void
1061 gtk_xtext_selection_clear_full (xtext_buffer *buf)
1063 textentry *ent = buf->text_first;
1064 while (ent)
1066 ent->mark_start = -1;
1067 ent->mark_end = -1;
1068 ent = ent->next;
1072 static int
1073 gtk_xtext_selection_clear (xtext_buffer *buf)
1075 textentry *ent;
1076 int ret = 0;
1078 ent = buf->last_ent_start;
1079 while (ent)
1081 if (ent->mark_start != -1)
1082 ret = 1;
1083 ent->mark_start = -1;
1084 ent->mark_end = -1;
1085 if (ent == buf->last_ent_end)
1086 break;
1087 ent = ent->next;
1090 return ret;
1093 static int
1094 find_x (GtkXText *xtext, textentry *ent, unsigned char *text, int x, int indent)
1096 int xx = indent;
1097 int i = 0;
1098 int rcol = 0, bgcol = 0;
1099 int hidden = FALSE;
1100 unsigned char *orig = text;
1101 int mbl;
1102 int char_width;
1104 while (*text)
1106 mbl = 1;
1107 if (rcol > 0 && (isdigit (*text) || (*text == ',' && isdigit (text[1]) && !bgcol)))
1109 if (text[1] != ',') rcol--;
1110 if (*text == ',')
1112 rcol = 2;
1113 bgcol = 1;
1115 text++;
1116 } else
1118 rcol = bgcol = 0;
1119 switch (*text)
1121 case ATTR_COLOR:
1122 rcol = 2;
1123 case ATTR_BEEP:
1124 case ATTR_RESET:
1125 case ATTR_REVERSE:
1126 case ATTR_BOLD:
1127 case ATTR_UNDERLINE:
1128 case ATTR_ITALICS:
1129 text++;
1130 break;
1131 case ATTR_HIDDEN:
1132 if (xtext->ignore_hidden)
1133 goto def;
1134 hidden = !hidden;
1135 text++;
1136 break;
1137 default:
1138 def:
1139 char_width = backend_get_char_width (xtext, text, &mbl);
1140 if (!hidden) xx += char_width;
1141 text += mbl;
1142 if (xx >= x)
1143 return i + (orig - ent->str);
1147 i += mbl;
1148 if (text - orig >= ent->str_len)
1149 return ent->str_len;
1152 return ent->str_len;
1155 static int
1156 gtk_xtext_find_x (GtkXText * xtext, int x, textentry * ent, int subline,
1157 int line, int *out_of_bounds)
1159 int indent;
1160 unsigned char *str;
1162 if (subline < 1)
1163 indent = ent->indent;
1164 else
1165 indent = xtext->buffer->indent;
1167 if (line > xtext->adj->page_size || line < 0)
1168 return 0;
1170 if (xtext->buffer->grid_dirty || line > 255)
1172 str = ent->str + gtk_xtext_find_subline (xtext, ent, subline);
1173 if (str >= ent->str + ent->str_len)
1174 return 0;
1175 } else
1177 if (xtext->buffer->grid_offset[line] > ent->str_len)
1178 return 0;
1179 str = ent->str + xtext->buffer->grid_offset[line];
1182 if (x < indent)
1184 *out_of_bounds = 1;
1185 return (str - ent->str);
1188 *out_of_bounds = 0;
1190 return find_x (xtext, ent, str, x, indent);
1193 static textentry *
1194 gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off,
1195 int *out_of_bounds)
1197 textentry *ent;
1198 int line;
1199 int subline;
1201 line = (y + xtext->pixel_offset) / xtext->fontsize;
1202 ent = gtk_xtext_nth (xtext, line + (int)xtext->adj->value, &subline);
1203 if (!ent)
1204 return 0;
1206 if (off)
1207 *off = gtk_xtext_find_x (xtext, x, ent, subline, line, out_of_bounds);
1209 return ent;
1212 static void
1213 gtk_xtext_draw_sep (GtkXText * xtext, int y)
1215 int x, height;
1216 GdkGC *light, *dark;
1218 if (y == -1)
1220 y = 0;
1221 height = GTK_WIDGET (xtext)->allocation.height;
1222 } else
1224 height = xtext->fontsize;
1227 /* draw the separator line */
1228 if (xtext->separator && xtext->buffer->indent)
1230 light = xtext->light_gc;
1231 dark = xtext->dark_gc;
1233 x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
1234 if (x < 1)
1235 return;
1237 if (xtext->thinline)
1239 if (xtext->moving_separator)
1240 gdk_draw_line (xtext->draw_buf, light, x, y, x, y + height);
1241 else
1242 gdk_draw_line (xtext->draw_buf, xtext->thin_gc, x, y, x, y + height);
1243 } else
1245 if (xtext->moving_separator)
1247 gdk_draw_line (xtext->draw_buf, light, x - 1, y, x - 1, y + height);
1248 gdk_draw_line (xtext->draw_buf, dark, x, y, x, y + height);
1249 } else
1251 gdk_draw_line (xtext->draw_buf, dark, x - 1, y, x - 1, y + height);
1252 gdk_draw_line (xtext->draw_buf, light, x, y, x, y + height);
1258 static void
1259 gtk_xtext_draw_marker (GtkXText * xtext, textentry * ent, int y)
1261 int x, width, render_y;
1263 if (!xtext->marker) return;
1265 if (xtext->buffer->marker_pos == ent)
1267 render_y = y + xtext->font->descent;
1269 else if (xtext->buffer->marker_pos == ent->next && ent->next != NULL)
1271 render_y = y + xtext->font->descent + xtext->fontsize * ent->lines_taken;
1273 else return;
1275 x = 0;
1276 width = GTK_WIDGET (xtext)->allocation.width;
1278 gdk_draw_line (xtext->draw_buf, xtext->marker_gc, x, render_y, x + width, render_y);
1280 if (gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (xtext)))))
1282 xtext->buffer->marker_seen = TRUE;
1286 #ifdef USE_SHM
1287 static int
1288 have_shm_pixmaps(Display *dpy)
1290 int major, minor;
1291 static int checked = 0;
1292 static int have = FALSE;
1294 if (!checked)
1296 XShmQueryVersion (dpy, &major, &minor, &have);
1297 checked = 1;
1300 return have;
1302 #endif
1304 static void
1305 gtk_xtext_paint (GtkWidget *widget, GdkRectangle *area)
1307 GtkXText *xtext = GTK_XTEXT (widget);
1308 textentry *ent_start, *ent_end;
1309 int x, y;
1311 #if defined(USE_XLIB)
1312 if (xtext->transparent)
1314 gdk_window_get_origin (widget->window, &x, &y);
1315 /* update transparency only if it moved */
1316 if (xtext->last_win_x != x || xtext->last_win_y != y)
1318 xtext->last_win_x = x;
1319 xtext->last_win_y = y;
1320 #ifdef USE_SHM
1321 if (xtext->shaded && !have_shm_pixmaps(GDK_WINDOW_XDISPLAY (xtext->draw_buf)))
1322 #else
1323 if (xtext->shaded)
1324 #endif
1326 xtext->recycle = TRUE;
1327 gtk_xtext_load_trans (xtext);
1328 xtext->recycle = FALSE;
1329 } else
1331 gtk_xtext_free_trans (xtext);
1332 gtk_xtext_load_trans (xtext);
1336 #endif
1338 if (area->x == 0 && area->y == 0 &&
1339 area->height == widget->allocation.height &&
1340 area->width == widget->allocation.width)
1342 dontscroll (xtext->buffer); /* force scrolling off */
1343 gtk_xtext_render_page (xtext);
1344 return;
1347 ent_start = gtk_xtext_find_char (xtext, area->x, area->y, NULL, NULL);
1348 if (!ent_start)
1350 xtext_draw_bg (xtext, area->x, area->y, area->width, area->height);
1351 goto xit;
1353 ent_end = gtk_xtext_find_char (xtext, area->x + area->width,
1354 area->y + area->height, NULL, NULL);
1355 if (!ent_end)
1356 ent_end = xtext->buffer->text_last;
1358 /* can't set a clip here, because fgc/bgc are used to draw the DB too */
1359 /* backend_set_clip (xtext, area);*/
1360 xtext->clip_x = area->x;
1361 xtext->clip_x2 = area->x + area->width;
1362 xtext->clip_y = area->y;
1363 xtext->clip_y2 = area->y + area->height;
1365 /* y is the last pixel y location it rendered text at */
1366 y = gtk_xtext_render_ents (xtext, ent_start, ent_end);
1368 if (y && y < widget->allocation.height && !ent_end->next)
1370 GdkRectangle rect;
1372 rect.x = 0;
1373 rect.y = y;
1374 rect.width = widget->allocation.width;
1375 rect.height = widget->allocation.height - y;
1377 /* fill any space below the last line that also intersects with
1378 the exposure rectangle */
1379 if (gdk_rectangle_intersect (area, &rect, &rect))
1381 xtext_draw_bg (xtext, rect.x, rect.y, rect.width, rect.height);
1385 /*backend_clear_clip (xtext);*/
1386 xtext->clip_x = 0;
1387 xtext->clip_x2 = 1000000;
1388 xtext->clip_y = 0;
1389 xtext->clip_y2 = 1000000;
1391 xit:
1392 x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
1393 if (area->x <= x)
1394 gtk_xtext_draw_sep (xtext, -1);
1397 static gboolean
1398 gtk_xtext_expose (GtkWidget * widget, GdkEventExpose * event)
1400 gtk_xtext_paint (widget, &event->area);
1401 return FALSE;
1404 /* render a selection that has extended or contracted upward */
1406 static void
1407 gtk_xtext_selection_up (GtkXText *xtext, textentry *start, textentry *end,
1408 int start_offset)
1410 /* render all the complete lines */
1411 if (start->next == end)
1412 gtk_xtext_render_ents (xtext, end, NULL);
1413 else
1414 gtk_xtext_render_ents (xtext, start->next, end);
1416 /* now the incomplete upper line */
1417 if (start == xtext->buffer->last_ent_start)
1418 xtext->jump_in_offset = xtext->buffer->last_offset_start;
1419 else
1420 xtext->jump_in_offset = start_offset;
1421 gtk_xtext_render_ents (xtext, start, NULL);
1422 xtext->jump_in_offset = 0;
1425 /* render a selection that has extended or contracted downward */
1427 static void
1428 gtk_xtext_selection_down (GtkXText *xtext, textentry *start, textentry *end,
1429 int end_offset)
1431 /* render all the complete lines */
1432 if (end->prev == start)
1433 gtk_xtext_render_ents (xtext, start, NULL);
1434 else
1435 gtk_xtext_render_ents (xtext, start, end->prev);
1437 /* now the incomplete bottom line */
1438 if (end == xtext->buffer->last_ent_end)
1439 xtext->jump_out_offset = xtext->buffer->last_offset_end;
1440 else
1441 xtext->jump_out_offset = end_offset;
1442 gtk_xtext_render_ents (xtext, end, NULL);
1443 xtext->jump_out_offset = 0;
1446 static void
1447 gtk_xtext_selection_render (GtkXText *xtext,
1448 textentry *start_ent, int start_offset,
1449 textentry *end_ent, int end_offset)
1451 textentry *ent;
1452 int start, end;
1454 xtext->skip_border_fills = TRUE;
1455 xtext->skip_stamp = TRUE;
1457 /* force an optimized render if there was no previous selection */
1458 if (xtext->buffer->last_ent_start == NULL && start_ent == end_ent)
1460 xtext->buffer->last_offset_start = start_offset;
1461 xtext->buffer->last_offset_end = end_offset;
1462 goto lamejump;
1465 /* mark changed within 1 ent only? */
1466 if (xtext->buffer->last_ent_start == start_ent &&
1467 xtext->buffer->last_ent_end == end_ent)
1469 /* when only 1 end of the selection is changed, we can really
1470 save on rendering */
1471 if (xtext->buffer->last_offset_start == start_offset ||
1472 xtext->buffer->last_offset_end == end_offset)
1474 lamejump:
1475 ent = end_ent;
1476 /* figure out where to start and end the rendering */
1477 if (end_offset > xtext->buffer->last_offset_end)
1479 end = end_offset;
1480 start = xtext->buffer->last_offset_end;
1481 } else if (end_offset < xtext->buffer->last_offset_end)
1483 end = xtext->buffer->last_offset_end;
1484 start = end_offset;
1485 } else if (start_offset < xtext->buffer->last_offset_start)
1487 end = xtext->buffer->last_offset_start;
1488 start = start_offset;
1489 ent = start_ent;
1490 } else if (start_offset > xtext->buffer->last_offset_start)
1492 end = start_offset;
1493 start = xtext->buffer->last_offset_start;
1494 ent = start_ent;
1495 } else
1496 { /* WORD selects end up here */
1497 end = end_offset;
1498 start = start_offset;
1500 } else
1502 /* LINE selects end up here */
1503 /* so which ent actually changed? */
1504 ent = start_ent;
1505 if (xtext->buffer->last_offset_start == start_offset)
1506 ent = end_ent;
1508 end = MAX (xtext->buffer->last_offset_end, end_offset);
1509 start = MIN (xtext->buffer->last_offset_start, start_offset);
1512 xtext->jump_out_offset = end;
1513 xtext->jump_in_offset = start;
1514 gtk_xtext_render_ents (xtext, ent, NULL);
1515 xtext->jump_out_offset = 0;
1516 xtext->jump_in_offset = 0;
1518 /* marking downward? */
1519 else if (xtext->buffer->last_ent_start == start_ent &&
1520 xtext->buffer->last_offset_start == start_offset)
1522 /* find the range that covers both old and new selection */
1523 ent = start_ent;
1524 while (ent)
1526 if (ent == xtext->buffer->last_ent_end)
1528 gtk_xtext_selection_down (xtext, ent, end_ent, end_offset);
1529 /*gtk_xtext_render_ents (xtext, ent, end_ent);*/
1530 break;
1532 if (ent == end_ent)
1534 gtk_xtext_selection_down (xtext, ent, xtext->buffer->last_ent_end, end_offset);
1535 /*gtk_xtext_render_ents (xtext, ent, xtext->buffer->last_ent_end);*/
1536 break;
1538 ent = ent->next;
1541 /* marking upward? */
1542 else if (xtext->buffer->last_ent_end == end_ent &&
1543 xtext->buffer->last_offset_end == end_offset)
1545 ent = end_ent;
1546 while (ent)
1548 if (ent == start_ent)
1550 gtk_xtext_selection_up (xtext, xtext->buffer->last_ent_start, ent, start_offset);
1551 /*gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, ent);*/
1552 break;
1554 if (ent == xtext->buffer->last_ent_start)
1556 gtk_xtext_selection_up (xtext, start_ent, ent, start_offset);
1557 /*gtk_xtext_render_ents (xtext, start_ent, ent);*/
1558 break;
1560 ent = ent->prev;
1563 else /* cross-over mark (stretched or shrunk at both ends) */
1565 /* unrender the old mark */
1566 gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, xtext->buffer->last_ent_end);
1567 /* now render the new mark, but skip overlaps */
1568 if (start_ent == xtext->buffer->last_ent_start)
1570 /* if the new mark is a sub-set of the old, do nothing */
1571 if (start_ent != end_ent)
1572 gtk_xtext_render_ents (xtext, start_ent->next, end_ent);
1573 } else if (end_ent == xtext->buffer->last_ent_end)
1575 /* if the new mark is a sub-set of the old, do nothing */
1576 if (start_ent != end_ent)
1577 gtk_xtext_render_ents (xtext, start_ent, end_ent->prev);
1578 } else
1579 gtk_xtext_render_ents (xtext, start_ent, end_ent);
1582 xtext->buffer->last_ent_start = start_ent;
1583 xtext->buffer->last_ent_end = end_ent;
1584 xtext->buffer->last_offset_start = start_offset;
1585 xtext->buffer->last_offset_end = end_offset;
1587 xtext->skip_border_fills = FALSE;
1588 xtext->skip_stamp = FALSE;
1591 static void
1592 gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean render)
1594 textentry *ent;
1595 textentry *ent_end;
1596 textentry *ent_start;
1597 int offset_start;
1598 int offset_end;
1599 int low_x;
1600 int low_y;
1601 int high_x;
1602 int high_y;
1603 int tmp;
1605 if (xtext->select_start_y > xtext->select_end_y)
1607 low_x = xtext->select_end_x;
1608 low_y = xtext->select_end_y;
1609 high_x = xtext->select_start_x;
1610 high_y = xtext->select_start_y;
1611 } else
1613 low_x = xtext->select_start_x;
1614 low_y = xtext->select_start_y;
1615 high_x = xtext->select_end_x;
1616 high_y = xtext->select_end_y;
1619 ent_start = gtk_xtext_find_char (xtext, low_x, low_y, &offset_start, &tmp);
1620 if (!ent_start)
1622 if (xtext->adj->value != xtext->buffer->old_value)
1623 gtk_xtext_render_page (xtext);
1624 return;
1627 ent_end = gtk_xtext_find_char (xtext, high_x, high_y, &offset_end, &tmp);
1628 if (!ent_end)
1630 ent_end = xtext->buffer->text_last;
1631 if (!ent_end)
1633 if (xtext->adj->value != xtext->buffer->old_value)
1634 gtk_xtext_render_page (xtext);
1635 return;
1637 offset_end = ent_end->str_len;
1640 /* marking less than a complete line? */
1641 /* make sure "start" is smaller than "end" (swap them if need be) */
1642 if (ent_start == ent_end && offset_start > offset_end)
1644 tmp = offset_start;
1645 offset_start = offset_end;
1646 offset_end = tmp;
1649 /* has the selection changed? Dont render unless necessary */
1650 if (xtext->buffer->last_ent_start == ent_start &&
1651 xtext->buffer->last_ent_end == ent_end &&
1652 xtext->buffer->last_offset_start == offset_start &&
1653 xtext->buffer->last_offset_end == offset_end)
1654 return;
1656 /* set all the old mark_ fields to -1 */
1657 gtk_xtext_selection_clear (xtext->buffer);
1659 ent_start->mark_start = offset_start;
1660 ent_start->mark_end = offset_end;
1662 if (ent_start != ent_end)
1664 ent_start->mark_end = ent_start->str_len;
1665 if (offset_end != 0)
1667 ent_end->mark_start = 0;
1668 ent_end->mark_end = offset_end;
1671 /* set all the mark_ fields of the ents within the selection */
1672 ent = ent_start->next;
1673 while (ent && ent != ent_end)
1675 ent->mark_start = 0;
1676 ent->mark_end = ent->str_len;
1677 ent = ent->next;
1681 if (render)
1682 gtk_xtext_selection_render (xtext, ent_start, offset_start, ent_end, offset_end);
1685 static gint
1686 gtk_xtext_scrolldown_timeout (GtkXText * xtext)
1688 int p_y, win_height;
1690 gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
1691 gdk_drawable_get_size (GTK_WIDGET (xtext)->window, 0, &win_height);
1693 if (p_y > win_height &&
1694 xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size))
1696 xtext->adj->value++;
1697 gtk_adjustment_changed (xtext->adj);
1698 gtk_xtext_render_page (xtext);
1699 return 1;
1702 xtext->scroll_tag = 0;
1703 return 0;
1706 static gint
1707 gtk_xtext_scrollup_timeout (GtkXText * xtext)
1709 int p_y;
1711 gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
1713 if (p_y < 0 && xtext->adj->value > 0.0)
1715 xtext->adj->value--;
1716 gtk_adjustment_changed (xtext->adj);
1717 gtk_xtext_render_page (xtext);
1718 return 1;
1721 xtext->scroll_tag = 0;
1722 return 0;
1725 static void
1726 gtk_xtext_selection_update (GtkXText * xtext, GdkEventMotion * event, int p_y, gboolean render)
1728 int win_height;
1729 int moved;
1731 gdk_drawable_get_size (GTK_WIDGET (xtext)->window, 0, &win_height);
1733 /* selecting past top of window, scroll up! */
1734 if (p_y < 0 && xtext->adj->value >= 0)
1736 if (!xtext->scroll_tag)
1737 xtext->scroll_tag = g_timeout_add (100,
1738 (GSourceFunc)
1739 gtk_xtext_scrollup_timeout,
1740 xtext);
1741 return;
1744 /* selecting past bottom of window, scroll down! */
1745 if (p_y > win_height &&
1746 xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size))
1748 if (!xtext->scroll_tag)
1749 xtext->scroll_tag = g_timeout_add (100,
1750 (GSourceFunc)
1751 gtk_xtext_scrolldown_timeout,
1752 xtext);
1753 return;
1756 moved = (int)xtext->adj->value - xtext->select_start_adj;
1757 xtext->select_start_y -= (moved * xtext->fontsize);
1758 xtext->select_start_adj = xtext->adj->value;
1759 gtk_xtext_selection_draw (xtext, event, render);
1762 static char *
1763 gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
1764 int *ret_off, int *ret_len)
1766 textentry *ent;
1767 int offset;
1768 unsigned char *str;
1769 unsigned char *word;
1770 int len;
1771 int out_of_bounds = 0;
1773 ent = gtk_xtext_find_char (xtext, x, y, &offset, &out_of_bounds);
1774 if (!ent)
1775 return 0;
1777 if (out_of_bounds)
1778 return 0;
1780 if (offset == ent->str_len)
1781 return 0;
1783 if (offset < 1)
1784 return 0;
1786 /*offset--;*/ /* FIXME: not all chars are 1 byte */
1788 str = ent->str + offset;
1790 while (!is_del (*str) && str != ent->str)
1791 str--;
1792 word = str + 1;
1794 len = 0;
1795 str = word;
1796 while (!is_del (*str) && len != ent->str_len)
1798 str++;
1799 len++;
1802 if (len > 0 && word[len-1]=='.')
1804 len--;
1805 str--;
1808 if (ret_ent)
1809 *ret_ent = ent;
1810 if (ret_off)
1811 *ret_off = word - ent->str;
1812 if (ret_len)
1813 *ret_len = str - word;
1815 return gtk_xtext_strip_color (word, len, xtext->scratch_buffer, NULL, NULL, FALSE);
1818 #ifdef MOTION_MONITOR
1820 static void
1821 gtk_xtext_unrender_hilight (GtkXText *xtext)
1823 xtext->render_hilights_only = TRUE;
1824 xtext->skip_border_fills = TRUE;
1825 xtext->skip_stamp = TRUE;
1826 xtext->un_hilight = TRUE;
1828 gtk_xtext_render_ents (xtext, xtext->hilight_ent, NULL);
1830 xtext->render_hilights_only = FALSE;
1831 xtext->skip_border_fills = FALSE;
1832 xtext->skip_stamp = FALSE;
1833 xtext->un_hilight = FALSE;
1836 static gboolean
1837 gtk_xtext_leave_notify (GtkWidget * widget, GdkEventCrossing * event)
1839 GtkXText *xtext = GTK_XTEXT (widget);
1841 if (xtext->cursor_hand)
1843 gtk_xtext_unrender_hilight (xtext);
1844 xtext->hilight_start = -1;
1845 xtext->hilight_end = -1;
1846 xtext->cursor_hand = FALSE;
1847 gdk_window_set_cursor (widget->window, 0);
1848 xtext->hilight_ent = NULL;
1851 if (xtext->cursor_resize)
1853 gtk_xtext_unrender_hilight (xtext);
1854 xtext->hilight_start = -1;
1855 xtext->hilight_end = -1;
1856 xtext->cursor_resize = FALSE;
1857 gdk_window_set_cursor (widget->window, 0);
1858 xtext->hilight_ent = NULL;
1861 return FALSE;
1864 #endif
1866 /* check if we should mark time stamps, and if a redraw is needed */
1868 static gboolean
1869 gtk_xtext_check_mark_stamp (GtkXText *xtext, GdkModifierType mask)
1871 gboolean redraw = FALSE;
1873 if ((mask & GDK_SHIFT_MASK))
1875 if (!xtext->mark_stamp)
1877 redraw = TRUE; /* must redraw all */
1878 xtext->mark_stamp = TRUE;
1880 } else
1882 if (xtext->mark_stamp)
1884 redraw = TRUE; /* must redraw all */
1885 xtext->mark_stamp = FALSE;
1888 return redraw;
1891 static gboolean
1892 gtk_xtext_motion_notify (GtkWidget * widget, GdkEventMotion * event)
1894 GtkXText *xtext = GTK_XTEXT (widget);
1895 GdkModifierType mask;
1896 int redraw, tmp, x, y, offset, len, line_x;
1897 unsigned char *word;
1898 textentry *word_ent;
1900 gdk_window_get_pointer (widget->window, &x, &y, &mask);
1902 if (xtext->moving_separator)
1904 if (x < (3 * widget->allocation.width) / 5 && x > 15)
1906 tmp = xtext->buffer->indent;
1907 xtext->buffer->indent = x;
1908 gtk_xtext_fix_indent (xtext->buffer);
1909 if (tmp != xtext->buffer->indent)
1911 gtk_xtext_recalc_widths (xtext->buffer, FALSE);
1912 if (xtext->buffer->scrollbar_down)
1913 gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
1914 xtext->adj->page_size);
1915 if (!xtext->io_tag)
1916 xtext->io_tag = g_timeout_add (REFRESH_TIMEOUT,
1917 (GSourceFunc)
1918 gtk_xtext_adjustment_timeout,
1919 xtext);
1922 return FALSE;
1925 if (xtext->button_down)
1927 redraw = gtk_xtext_check_mark_stamp (xtext, mask);
1928 gtk_grab_add (widget);
1929 /*gdk_pointer_grab (widget->window, TRUE,
1930 GDK_BUTTON_RELEASE_MASK |
1931 GDK_BUTTON_MOTION_MASK, NULL, NULL, 0);*/
1932 xtext->select_end_x = x;
1933 xtext->select_end_y = y;
1934 gtk_xtext_selection_update (xtext, event, y, !redraw);
1935 xtext->hilighting = TRUE;
1937 /* user has pressed or released SHIFT, must redraw entire selection */
1938 if (redraw)
1940 xtext->force_stamp = TRUE;
1941 gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start,
1942 xtext->buffer->last_ent_end);
1943 xtext->force_stamp = FALSE;
1945 return FALSE;
1947 #ifdef MOTION_MONITOR
1949 if (xtext->separator && xtext->buffer->indent)
1951 line_x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
1952 if (line_x == x || line_x == x + 1 || line_x == x - 1)
1954 if (!xtext->cursor_resize)
1956 gdk_window_set_cursor (GTK_WIDGET (xtext)->window,
1957 xtext->resize_cursor);
1958 xtext->cursor_resize = TRUE;
1960 return FALSE;
1964 if (xtext->urlcheck_function == NULL)
1965 return FALSE;
1967 word = gtk_xtext_get_word (xtext, x, y, &word_ent, &offset, &len);
1968 if (word)
1970 if (xtext->urlcheck_function (GTK_WIDGET (xtext), word, len) > 0)
1972 if (!xtext->cursor_hand ||
1973 xtext->hilight_ent != word_ent ||
1974 xtext->hilight_start != offset ||
1975 xtext->hilight_end != offset + len)
1977 if (!xtext->cursor_hand)
1979 gdk_window_set_cursor (GTK_WIDGET (xtext)->window,
1980 xtext->hand_cursor);
1981 xtext->cursor_hand = TRUE;
1984 /* un-render the old hilight */
1985 if (xtext->hilight_ent)
1986 gtk_xtext_unrender_hilight (xtext);
1988 xtext->hilight_ent = word_ent;
1989 xtext->hilight_start = offset;
1990 xtext->hilight_end = offset + len;
1992 xtext->skip_border_fills = TRUE;
1993 xtext->render_hilights_only = TRUE;
1994 xtext->skip_stamp = TRUE;
1996 gtk_xtext_render_ents (xtext, word_ent, NULL);
1998 xtext->skip_border_fills = FALSE;
1999 xtext->render_hilights_only = FALSE;
2000 xtext->skip_stamp = FALSE;
2002 return FALSE;
2006 gtk_xtext_leave_notify (widget, NULL);
2008 #endif
2010 return FALSE;
2013 static void
2014 gtk_xtext_set_clip_owner (GtkWidget * xtext, GdkEventButton * event)
2016 char *str;
2017 int len;
2019 if (GTK_XTEXT (xtext)->selection_buffer &&
2020 GTK_XTEXT (xtext)->selection_buffer != GTK_XTEXT (xtext)->buffer)
2021 gtk_xtext_selection_clear (GTK_XTEXT (xtext)->selection_buffer);
2023 GTK_XTEXT (xtext)->selection_buffer = GTK_XTEXT (xtext)->buffer;
2025 str = gtk_xtext_selection_get_text (GTK_XTEXT (xtext), &len);
2026 if (str)
2028 gtk_clipboard_set_text (gtk_widget_get_clipboard (xtext, GDK_SELECTION_CLIPBOARD),
2029 str, len);
2030 free (str);
2033 gtk_selection_owner_set (xtext, GDK_SELECTION_PRIMARY, event->time);
2036 static void
2037 gtk_xtext_unselect (GtkXText *xtext)
2039 xtext_buffer *buf = xtext->buffer;
2041 xtext->skip_border_fills = TRUE;
2042 xtext->skip_stamp = TRUE;
2044 xtext->jump_in_offset = buf->last_ent_start->mark_start;
2045 /* just a single ent was marked? */
2046 if (buf->last_ent_start == buf->last_ent_end)
2048 xtext->jump_out_offset = buf->last_ent_start->mark_end;
2049 buf->last_ent_end = NULL;
2052 gtk_xtext_selection_clear (xtext->buffer);
2054 /* FIXME: use jump_out on multi-line selects too! */
2055 gtk_xtext_render_ents (xtext, buf->last_ent_start, buf->last_ent_end);
2057 xtext->jump_in_offset = 0;
2058 xtext->jump_out_offset = 0;
2060 xtext->skip_border_fills = FALSE;
2061 xtext->skip_stamp = FALSE;
2063 xtext->buffer->last_ent_start = NULL;
2064 xtext->buffer->last_ent_end = NULL;
2067 static gboolean
2068 gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
2070 GtkXText *xtext = GTK_XTEXT (widget);
2071 unsigned char *word;
2072 int old;
2074 if (xtext->moving_separator)
2076 xtext->moving_separator = FALSE;
2077 old = xtext->buffer->indent;
2078 if (event->x < (4 * widget->allocation.width) / 5 && event->x > 15)
2079 xtext->buffer->indent = event->x;
2080 gtk_xtext_fix_indent (xtext->buffer);
2081 if (xtext->buffer->indent != old)
2083 gtk_xtext_recalc_widths (xtext->buffer, FALSE);
2084 gtk_xtext_adjustment_set (xtext->buffer, TRUE);
2085 gtk_xtext_render_page (xtext);
2086 } else
2087 gtk_xtext_draw_sep (xtext, -1);
2088 return FALSE;
2091 if (xtext->word_or_line_select)
2093 xtext->word_or_line_select = FALSE;
2094 xtext->button_down = FALSE;
2095 return FALSE;
2098 if (event->button == 1)
2100 xtext->button_down = FALSE;
2102 gtk_grab_remove (widget);
2103 /*gdk_pointer_ungrab (0);*/
2105 /* got a new selection? */
2106 if (xtext->buffer->last_ent_start)
2108 xtext->color_paste = FALSE;
2109 if (event->state & GDK_CONTROL_MASK)
2110 xtext->color_paste = TRUE;
2111 gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
2114 if (xtext->select_start_x == event->x &&
2115 xtext->select_start_y == event->y &&
2116 xtext->buffer->last_ent_start)
2118 gtk_xtext_unselect (xtext);
2119 xtext->mark_stamp = FALSE;
2120 return FALSE;
2123 if (!xtext->hilighting)
2125 word = gtk_xtext_get_word (xtext, event->x, event->y, 0, 0, 0);
2126 g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0, word ? word : NULL, event);
2127 } else
2129 xtext->hilighting = FALSE;
2134 return FALSE;
2137 static gboolean
2138 gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
2140 GtkXText *xtext = GTK_XTEXT (widget);
2141 GdkModifierType mask;
2142 textentry *ent;
2143 unsigned char *word;
2144 int line_x, x, y, offset, len;
2146 gdk_window_get_pointer (widget->window, &x, &y, &mask);
2148 if (event->button == 3 || event->button == 2) /* right/middle click */
2150 word = gtk_xtext_get_word (xtext, x, y, 0, 0, 0);
2151 if (word)
2153 g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0,
2154 word, event);
2155 } else
2156 g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0,
2157 "", event);
2158 return FALSE;
2161 if (event->button != 1) /* we only want left button */
2162 return FALSE;
2164 if (event->type == GDK_2BUTTON_PRESS) /* WORD select */
2166 gtk_xtext_check_mark_stamp (xtext, mask);
2167 if (gtk_xtext_get_word (xtext, x, y, &ent, &offset, &len))
2169 if (len == 0)
2170 return FALSE;
2171 gtk_xtext_selection_clear (xtext->buffer);
2172 ent->mark_start = offset;
2173 ent->mark_end = offset + len;
2174 gtk_xtext_selection_render (xtext, ent, offset, ent, offset + len);
2175 xtext->word_or_line_select = TRUE;
2176 gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
2179 return FALSE;
2182 if (event->type == GDK_3BUTTON_PRESS) /* LINE select */
2184 gtk_xtext_check_mark_stamp (xtext, mask);
2185 if (gtk_xtext_get_word (xtext, x, y, &ent, 0, 0))
2187 gtk_xtext_selection_clear (xtext->buffer);
2188 ent->mark_start = 0;
2189 ent->mark_end = ent->str_len;
2190 gtk_xtext_selection_render (xtext, ent, 0, ent, ent->str_len);
2191 xtext->word_or_line_select = TRUE;
2192 gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
2195 return FALSE;
2198 /* check if it was a separator-bar click */
2199 if (xtext->separator && xtext->buffer->indent)
2201 line_x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
2202 if (line_x == x || line_x == x + 1 || line_x == x - 1)
2204 xtext->moving_separator = TRUE;
2205 /* draw the separator line */
2206 gtk_xtext_draw_sep (xtext, -1);
2207 return FALSE;
2211 xtext->button_down = TRUE;
2212 xtext->select_start_x = x;
2213 xtext->select_start_y = y;
2214 xtext->select_start_adj = xtext->adj->value;
2216 return FALSE;
2219 /* another program has claimed the selection */
2221 static gboolean
2222 gtk_xtext_selection_kill (GtkXText *xtext, GdkEventSelection *event)
2224 if (xtext->buffer->last_ent_start)
2225 gtk_xtext_unselect (xtext);
2226 return TRUE;
2229 static char *
2230 gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret)
2232 textentry *ent;
2233 char *txt;
2234 char *pos;
2235 char *stripped;
2236 int len;
2237 int first = TRUE;
2238 xtext_buffer *buf;
2240 buf = xtext->selection_buffer;
2241 if (!buf)
2242 return NULL;
2244 /* first find out how much we need to malloc ... */
2245 len = 0;
2246 ent = buf->last_ent_start;
2247 while (ent)
2249 if (ent->mark_start != -1)
2251 /* include timestamp? */
2252 if (ent->mark_start == 0 && xtext->mark_stamp)
2254 char *time_str;
2255 int stamp_size = xtext_get_stamp_str (ent->stamp, &time_str);
2256 g_free (time_str);
2257 len += stamp_size;
2260 if (ent->mark_end - ent->mark_start > 0)
2261 len += (ent->mark_end - ent->mark_start) + 1;
2262 else
2263 len++;
2265 if (ent == buf->last_ent_end)
2266 break;
2267 ent = ent->next;
2270 if (len < 1)
2271 return NULL;
2273 /* now allocate mem and copy buffer */
2274 pos = txt = malloc (len);
2275 ent = buf->last_ent_start;
2276 while (ent)
2278 if (ent->mark_start != -1)
2280 if (!first)
2282 *pos = '\n';
2283 pos++;
2285 first = FALSE;
2286 if (ent->mark_end - ent->mark_start > 0)
2288 /* include timestamp? */
2289 if (ent->mark_start == 0 && xtext->mark_stamp)
2291 char *time_str;
2292 int stamp_size = xtext_get_stamp_str (ent->stamp, &time_str);
2293 memcpy (pos, time_str, stamp_size);
2294 g_free (time_str);
2295 pos += stamp_size;
2298 memcpy (pos, ent->str + ent->mark_start,
2299 ent->mark_end - ent->mark_start);
2300 pos += ent->mark_end - ent->mark_start;
2303 if (ent == buf->last_ent_end)
2304 break;
2305 ent = ent->next;
2307 *pos = 0;
2309 if (xtext->color_paste)
2311 /*stripped = gtk_xtext_conv_color (txt, strlen (txt), &len);*/
2312 stripped = txt;
2313 len = strlen (txt);
2314 } else
2316 stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, &len, 0, FALSE);
2317 free (txt);
2320 *len_ret = len;
2321 return stripped;
2324 /* another program is asking for our selection */
2326 static void
2327 gtk_xtext_selection_get (GtkWidget * widget,
2328 GtkSelectionData * selection_data_ptr,
2329 guint info, guint time)
2331 GtkXText *xtext = GTK_XTEXT (widget);
2332 char *stripped;
2333 guchar *new_text;
2334 int len;
2335 gsize glen;
2337 stripped = gtk_xtext_selection_get_text (xtext, &len);
2338 if (!stripped)
2339 return;
2341 switch (info)
2343 case TARGET_UTF8_STRING:
2344 /* it's already in utf8 */
2345 gtk_selection_data_set_text (selection_data_ptr, stripped, len);
2346 break;
2347 case TARGET_TEXT:
2348 case TARGET_COMPOUND_TEXT:
2350 GdkAtom encoding;
2351 gint format;
2352 gint new_length;
2354 gdk_string_to_compound_text_for_display (
2355 gdk_drawable_get_display (widget->window),
2356 stripped, &encoding, &format, &new_text,
2357 &new_length);
2358 gtk_selection_data_set (selection_data_ptr, encoding, format,
2359 new_text, new_length);
2360 gdk_free_compound_text (new_text);
2362 break;
2363 default:
2364 new_text = g_locale_from_utf8 (stripped, len, NULL, &glen, NULL);
2365 gtk_selection_data_set (selection_data_ptr, GDK_SELECTION_TYPE_STRING,
2366 8, new_text, glen);
2367 g_free (new_text);
2370 free (stripped);
2373 static gboolean
2374 gtk_xtext_scroll (GtkWidget *widget, GdkEventScroll *event)
2376 GtkXText *xtext = GTK_XTEXT (widget);
2377 gfloat new_value;
2379 if (event->direction == GDK_SCROLL_UP) /* mouse wheel pageUp */
2381 new_value = xtext->adj->value - (xtext->adj->page_increment / 10);
2382 if (new_value < xtext->adj->lower)
2383 new_value = xtext->adj->lower;
2384 gtk_adjustment_set_value (xtext->adj, new_value);
2386 else if (event->direction == GDK_SCROLL_DOWN) /* mouse wheel pageDn */
2388 new_value = xtext->adj->value + (xtext->adj->page_increment / 10);
2389 if (new_value > (xtext->adj->upper - xtext->adj->page_size))
2390 new_value = xtext->adj->upper - xtext->adj->page_size;
2391 gtk_adjustment_set_value (xtext->adj, new_value);
2394 return FALSE;
2397 static void
2398 gtk_xtext_class_init (GtkXTextClass * class)
2400 GtkObjectClass *object_class;
2401 GtkWidgetClass *widget_class;
2402 GtkXTextClass *xtext_class;
2404 object_class = (GtkObjectClass *) class;
2405 widget_class = (GtkWidgetClass *) class;
2406 xtext_class = (GtkXTextClass *) class;
2408 parent_class = gtk_type_class (gtk_widget_get_type ());
2410 xtext_signals[WORD_CLICK] =
2411 g_signal_new ("word_click",
2412 G_TYPE_FROM_CLASS (object_class),
2413 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
2414 G_STRUCT_OFFSET (GtkXTextClass, word_click),
2415 NULL, NULL,
2416 gtk_marshal_VOID__POINTER_POINTER,
2417 G_TYPE_NONE,
2418 2, G_TYPE_POINTER, G_TYPE_POINTER);
2419 object_class->destroy = gtk_xtext_destroy;
2421 widget_class->realize = gtk_xtext_realize;
2422 widget_class->unrealize = gtk_xtext_unrealize;
2423 widget_class->size_request = gtk_xtext_size_request;
2424 widget_class->size_allocate = gtk_xtext_size_allocate;
2425 widget_class->button_press_event = gtk_xtext_button_press;
2426 widget_class->button_release_event = gtk_xtext_button_release;
2427 widget_class->motion_notify_event = gtk_xtext_motion_notify;
2428 widget_class->selection_clear_event = (void *)gtk_xtext_selection_kill;
2429 widget_class->selection_get = gtk_xtext_selection_get;
2430 widget_class->expose_event = gtk_xtext_expose;
2431 widget_class->scroll_event = gtk_xtext_scroll;
2432 #ifdef MOTION_MONITOR
2433 widget_class->leave_notify_event = gtk_xtext_leave_notify;
2434 #endif
2436 xtext_class->word_click = NULL;
2439 GType
2440 gtk_xtext_get_type (void)
2442 static GType xtext_type = 0;
2444 if (!xtext_type)
2446 static const GTypeInfo xtext_info =
2448 sizeof (GtkXTextClass),
2449 NULL, /* base_init */
2450 NULL, /* base_finalize */
2451 (GClassInitFunc) gtk_xtext_class_init,
2452 NULL, /* class_finalize */
2453 NULL, /* class_data */
2454 sizeof (GtkXText),
2455 0, /* n_preallocs */
2456 (GInstanceInitFunc) gtk_xtext_init,
2459 xtext_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkXText",
2460 &xtext_info, 0);
2463 return xtext_type;
2466 /* strip MIRC colors and other attribs. */
2468 /* CL: needs to strip hidden when called by gtk_xtext_text_width, but not when copying text */
2470 static unsigned char *
2471 gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
2472 int *newlen, int *mb_ret, int strip_hidden)
2474 int i = 0;
2475 int rcol = 0, bgcol = 0;
2476 int hidden = FALSE;
2477 unsigned char *new_str;
2478 int mb = FALSE;
2480 if (outbuf == NULL)
2481 new_str = malloc (len + 2);
2482 else
2483 new_str = outbuf;
2485 while (len > 0)
2487 if (*text >= 128)
2488 mb = TRUE;
2490 if (rcol > 0 && (isdigit (*text) || (*text == ',' && isdigit (text[1]) && !bgcol)))
2492 if (text[1] != ',') rcol--;
2493 if (*text == ',')
2495 rcol = 2;
2496 bgcol = 1;
2498 } else
2500 rcol = bgcol = 0;
2501 switch (*text)
2503 case ATTR_COLOR:
2504 rcol = 2;
2505 break;
2506 case ATTR_BEEP:
2507 case ATTR_RESET:
2508 case ATTR_REVERSE:
2509 case ATTR_BOLD:
2510 case ATTR_UNDERLINE:
2511 case ATTR_ITALICS:
2512 break;
2513 case ATTR_HIDDEN:
2514 hidden = !hidden;
2515 break;
2516 default:
2517 if (!(hidden && strip_hidden))
2518 new_str[i++] = *text;
2521 text++;
2522 len--;
2525 new_str[i] = 0;
2527 if (newlen != NULL)
2528 *newlen = i;
2530 if (mb_ret != NULL)
2531 *mb_ret = mb;
2533 return new_str;
2536 /* GeEkMaN: converts mIRC control codes to literal control codes */
2538 static char *
2539 gtk_xtext_conv_color (unsigned char *text, int len, int *newlen)
2541 int i, j = 2;
2542 char cchar = 0;
2543 char *new_str;
2544 int mbl;
2546 for (i = 0; i < len;)
2548 switch (text[i])
2550 case ATTR_COLOR:
2551 case ATTR_RESET:
2552 case ATTR_REVERSE:
2553 case ATTR_BOLD:
2554 case ATTR_UNDERLINE:
2555 case ATTR_ITALICS:
2556 case ATTR_HIDDEN:
2557 j += 3;
2558 i++;
2559 break;
2560 default:
2561 mbl = charlen (text + i);
2562 j += mbl;
2563 i += mbl;
2567 new_str = malloc (j);
2568 j = 0;
2570 for (i = 0; i < len;)
2572 switch (text[i])
2574 case ATTR_COLOR:
2575 cchar = 'C';
2576 break;
2577 case ATTR_RESET:
2578 cchar = 'O';
2579 break;
2580 case ATTR_REVERSE:
2581 cchar = 'R';
2582 break;
2583 case ATTR_BOLD:
2584 cchar = 'B';
2585 break;
2586 case ATTR_UNDERLINE:
2587 cchar = 'U';
2588 break;
2589 case ATTR_ITALICS:
2590 cchar = 'I';
2591 break;
2592 case ATTR_HIDDEN:
2593 cchar = 'H';
2594 break;
2595 case ATTR_BEEP:
2596 break;
2597 default:
2598 mbl = charlen (text + i);
2599 if (mbl == 1)
2601 new_str[j] = text[i];
2602 j++;
2603 i++;
2604 } else
2606 /* invalid utf8 safe guard */
2607 if (i + mbl > len)
2608 mbl = len - i;
2609 memcpy (new_str + j, text + i, mbl);
2610 j += mbl;
2611 i += mbl;
2614 if (cchar != 0)
2616 new_str[j++] = '%';
2617 new_str[j++] = cchar;
2618 cchar = 0;
2619 i++;
2623 new_str[j] = 0;
2624 *newlen = j;
2626 return new_str;
2629 /* gives width of a string, excluding the mIRC codes */
2631 static int
2632 gtk_xtext_text_width (GtkXText *xtext, unsigned char *text, int len,
2633 int *mb_ret)
2635 unsigned char *new_buf;
2636 int new_len, mb;
2638 new_buf = gtk_xtext_strip_color (text, len, xtext->scratch_buffer,
2639 &new_len, &mb, !xtext->ignore_hidden);
2641 if (mb_ret)
2642 *mb_ret = mb;
2644 return backend_get_text_width (xtext, new_buf, new_len, mb);
2647 /* actually draw text to screen (one run with the same color/attribs) */
2649 static int
2650 gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
2651 int len, GdkGC *gc, int is_mb)
2653 int str_width, dofill;
2654 GdkDrawable *pix = NULL;
2655 int dest_x, dest_y;
2657 if (xtext->dont_render || len < 1 || xtext->hidden)
2658 return 0;
2660 str_width = backend_get_text_width (xtext, str, len, is_mb);
2662 if (xtext->dont_render2)
2663 return str_width;
2665 /* roll-your-own clipping (avoiding XftDrawString is always good!) */
2666 if (x > xtext->clip_x2 || x + str_width < xtext->clip_x)
2667 return str_width;
2668 if (y - xtext->font->ascent > xtext->clip_y2 || (y - xtext->font->ascent) + xtext->fontsize < xtext->clip_y)
2669 return str_width;
2671 if (xtext->render_hilights_only)
2673 if (!xtext->in_hilight) /* is it a hilight prefix? */
2674 return str_width;
2675 #ifndef COLOR_HILIGHT
2676 if (!xtext->un_hilight) /* doing a hilight? no need to draw the text */
2677 goto dounder;
2678 #endif
2681 #ifdef USE_DB
2683 pix = gdk_pixmap_new (xtext->draw_buf, str_width, xtext->fontsize, xtext->depth);
2684 if (pix)
2686 #ifdef USE_XFT
2687 XftDrawChange (xtext->xftdraw, GDK_WINDOW_XWINDOW (pix));
2688 #endif
2689 dest_x = x;
2690 dest_y = y - xtext->font->ascent;
2692 gdk_gc_set_ts_origin (xtext->bgc, xtext->ts_x - x, xtext->ts_y - dest_y);
2694 x = 0;
2695 y = xtext->font->ascent;
2696 xtext->draw_buf = pix;
2699 #endif
2701 dofill = TRUE;
2703 /* backcolor is always handled by XDrawImageString */
2704 if (!xtext->backcolor && xtext->pixmap)
2706 /* draw the background pixmap behind the text - CAUSES FLICKER HERE!! */
2707 xtext_draw_bg (xtext, x, y - xtext->font->ascent, str_width,
2708 xtext->fontsize);
2709 dofill = FALSE; /* already drawn the background */
2712 backend_draw_text (xtext, dofill, gc, x, y, str, len, str_width, is_mb);
2714 #ifdef USE_DB
2715 if (pix)
2717 GdkRectangle clip;
2718 GdkRectangle dest;
2720 gdk_gc_set_ts_origin (xtext->bgc, xtext->ts_x, xtext->ts_y);
2721 xtext->draw_buf = GTK_WIDGET (xtext)->window;
2722 #ifdef USE_XFT
2723 XftDrawChange (xtext->xftdraw, GDK_WINDOW_XWINDOW (xtext->draw_buf));
2724 #endif
2725 #if 0
2726 gdk_draw_drawable (xtext->draw_buf, xtext->bgc, pix, 0, 0, dest_x,
2727 dest_y, str_width, xtext->fontsize);
2728 #else
2729 clip.x = xtext->clip_x;
2730 clip.y = xtext->clip_y;
2731 clip.width = xtext->clip_x2 - xtext->clip_x;
2732 clip.height = xtext->clip_y2 - xtext->clip_y;
2734 dest.x = dest_x;
2735 dest.y = dest_y;
2736 dest.width = str_width;
2737 dest.height = xtext->fontsize;
2739 if (gdk_rectangle_intersect (&clip, &dest, &dest))
2740 /* dump the DB to window, but only within the clip_x/x2/y/y2 */
2741 gdk_draw_drawable (xtext->draw_buf, xtext->bgc, pix,
2742 dest.x - dest_x, dest.y - dest_y,
2743 dest.x, dest.y, dest.width, dest.height);
2744 #endif
2745 g_object_unref (pix);
2747 #endif
2749 if (xtext->underline)
2751 #ifdef USE_XFT
2752 GdkColor col;
2753 #endif
2755 #ifndef COLOR_HILIGHT
2756 dounder:
2757 #endif
2759 #ifdef USE_XFT
2760 col.pixel = xtext->xft_fg->pixel;
2761 gdk_gc_set_foreground (gc, &col);
2762 #endif
2763 if (pix)
2764 y = dest_y + xtext->font->ascent + 1;
2765 else
2767 y++;
2768 dest_x = x;
2770 /* draw directly to window, it's out of the range of our DB */
2771 gdk_draw_line (xtext->draw_buf, gc, dest_x, y, dest_x + str_width - 1, y);
2774 return str_width;
2777 static void
2778 gtk_xtext_reset (GtkXText * xtext, int mark, int attribs)
2780 if (attribs)
2782 xtext->underline = FALSE;
2783 xtext->bold = FALSE;
2784 xtext->italics = FALSE;
2785 xtext->hidden = FALSE;
2787 if (!mark)
2789 xtext->backcolor = FALSE;
2790 if (xtext->col_fore != XTEXT_FG)
2791 xtext_set_fg (xtext, xtext->fgc, XTEXT_FG);
2792 if (xtext->col_back != XTEXT_BG)
2793 xtext_set_bg (xtext, xtext->fgc, XTEXT_BG);
2795 xtext->col_fore = XTEXT_FG;
2796 xtext->col_back = XTEXT_BG;
2797 xtext->parsing_color = FALSE;
2798 xtext->parsing_backcolor = FALSE;
2799 xtext->nc = 0;
2802 /* render a single line, which WONT wrap, and parse mIRC colors */
2804 static int
2805 gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
2806 unsigned char *str, int len, int win_width, int indent,
2807 int line, int left_only, int *x_size_ret)
2809 GdkGC *gc;
2810 int i = 0, x = indent, j = 0;
2811 unsigned char *pstr = str;
2812 int col_num, tmp;
2813 int offset;
2814 int mark = FALSE;
2815 int ret = 1;
2817 xtext->in_hilight = FALSE;
2819 offset = str - ent->str;
2821 if (line < 255 && line >= 0)
2822 xtext->buffer->grid_offset[line] = offset;
2824 gc = xtext->fgc; /* our foreground GC */
2826 if (ent->mark_start != -1 &&
2827 ent->mark_start <= i + offset && ent->mark_end > i + offset)
2829 xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
2830 xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
2831 xtext->backcolor = TRUE;
2832 mark = TRUE;
2834 #ifdef MOTION_MONITOR
2835 if (xtext->hilight_ent == ent &&
2836 xtext->hilight_start <= i + offset && xtext->hilight_end > i + offset)
2838 if (!xtext->un_hilight)
2840 #ifdef COLOR_HILIGHT
2841 xtext_set_bg (xtext, gc, 2);
2842 #else
2843 xtext->underline = TRUE;
2844 #endif
2846 xtext->in_hilight = TRUE;
2848 #endif
2850 if (!xtext->skip_border_fills && !xtext->dont_render)
2852 /* draw background to the left of the text */
2853 if (str == ent->str && indent > MARGIN && xtext->buffer->time_stamp)
2855 /* don't overwrite the timestamp */
2856 if (indent > xtext->stamp_width)
2858 xtext_draw_bg (xtext, xtext->stamp_width, y - xtext->font->ascent,
2859 indent - xtext->stamp_width, xtext->fontsize);
2861 } else
2863 /* fill the indent area with background gc */
2864 if (indent >= xtext->clip_x)
2866 xtext_draw_bg (xtext, 0, y - xtext->font->ascent,
2867 MIN (indent, xtext->clip_x2), xtext->fontsize);
2872 if (xtext->jump_in_offset > 0 && offset < xtext->jump_in_offset)
2873 xtext->dont_render2 = TRUE;
2875 while (i < len)
2878 #ifdef MOTION_MONITOR
2879 if (xtext->hilight_ent == ent && xtext->hilight_start == (i + offset))
2881 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
2882 pstr += j;
2883 j = 0;
2884 if (!xtext->un_hilight)
2886 #ifdef COLOR_HILIGHT
2887 xtext_set_bg (xtext, gc, 2);
2888 #else
2889 xtext->underline = TRUE;
2890 #endif
2893 xtext->in_hilight = TRUE;
2895 #endif
2897 if ((xtext->parsing_color && isdigit (str[i]) && xtext->nc < 2) ||
2898 (xtext->parsing_color && str[i] == ',' && isdigit (str[i+1]) && xtext->nc < 3 && !xtext->parsing_backcolor))
2900 pstr++;
2901 if (str[i] == ',')
2903 xtext->parsing_backcolor = TRUE;
2904 if (xtext->nc)
2906 xtext->num[xtext->nc] = 0;
2907 xtext->nc = 0;
2908 col_num = atoi (xtext->num);
2909 if (col_num == 99) /* mIRC lameness */
2910 col_num = XTEXT_FG;
2911 else
2912 col_num = col_num % XTEXT_MIRC_COLS;
2913 xtext->col_fore = col_num;
2914 if (!mark)
2915 xtext_set_fg (xtext, gc, col_num);
2917 } else
2919 xtext->num[xtext->nc] = str[i];
2920 if (xtext->nc < 7)
2921 xtext->nc++;
2923 } else
2925 if (xtext->parsing_color)
2927 xtext->parsing_color = FALSE;
2928 if (xtext->nc)
2930 xtext->num[xtext->nc] = 0;
2931 xtext->nc = 0;
2932 col_num = atoi (xtext->num);
2933 if (xtext->parsing_backcolor)
2935 if (col_num == 99) /* mIRC lameness */
2936 col_num = XTEXT_BG;
2937 else
2938 col_num = col_num % XTEXT_MIRC_COLS;
2939 if (col_num == XTEXT_BG)
2940 xtext->backcolor = FALSE;
2941 else
2942 xtext->backcolor = TRUE;
2943 if (!mark)
2944 xtext_set_bg (xtext, gc, col_num);
2945 xtext->col_back = col_num;
2946 } else
2948 if (col_num == 99) /* mIRC lameness */
2949 col_num = XTEXT_FG;
2950 else
2951 col_num = col_num % XTEXT_MIRC_COLS;
2952 if (!mark)
2953 xtext_set_fg (xtext, gc, col_num);
2954 xtext->col_fore = col_num;
2956 xtext->parsing_backcolor = FALSE;
2957 } else
2959 /* got a \003<non-digit>... i.e. reset colors */
2960 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
2961 pstr += j;
2962 j = 0;
2963 gtk_xtext_reset (xtext, mark, FALSE);
2967 switch (str[i])
2969 case '\n':
2970 /*case ATTR_BEEP:*/
2971 break;
2972 case ATTR_REVERSE:
2973 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
2974 pstr += j + 1;
2975 j = 0;
2976 tmp = xtext->col_fore;
2977 xtext->col_fore = xtext->col_back;
2978 xtext->col_back = tmp;
2979 if (!mark)
2981 xtext_set_fg (xtext, gc, xtext->col_fore);
2982 xtext_set_bg (xtext, gc, xtext->col_back);
2984 if (xtext->col_back != XTEXT_BG)
2985 xtext->backcolor = TRUE;
2986 else
2987 xtext->backcolor = FALSE;
2988 break;
2989 case ATTR_BOLD:
2990 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
2991 xtext->bold = !xtext->bold;
2992 pstr += j + 1;
2993 j = 0;
2994 break;
2995 case ATTR_UNDERLINE:
2996 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
2997 xtext->underline = !xtext->underline;
2998 pstr += j + 1;
2999 j = 0;
3000 break;
3001 case ATTR_ITALICS:
3002 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3003 xtext->italics = !xtext->italics;
3004 pstr += j + 1;
3005 j = 0;
3006 break;
3007 case ATTR_HIDDEN:
3008 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3009 xtext->hidden = (!xtext->hidden) & (!xtext->ignore_hidden);
3010 pstr += j + 1;
3011 j = 0;
3012 break;
3013 case ATTR_RESET:
3014 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3015 pstr += j + 1;
3016 j = 0;
3017 gtk_xtext_reset (xtext, mark, !xtext->in_hilight);
3018 break;
3019 case ATTR_COLOR:
3020 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3021 xtext->parsing_color = TRUE;
3022 pstr += j + 1;
3023 j = 0;
3024 break;
3025 default:
3026 tmp = charlen (str + i);
3027 /* invalid utf8 safe guard */
3028 if (tmp + i > len)
3029 tmp = len - i;
3030 j += tmp; /* move to the next utf8 char */
3033 i += charlen (str + i); /* move to the next utf8 char */
3034 /* invalid utf8 safe guard */
3035 if (i > len)
3036 i = len;
3038 /* Separate the left part, the space and the right part
3039 into separate runs, and reset bidi state inbetween.
3040 Perform this only on the first line of the message.
3042 if (offset == 0)
3044 /* we've reached the end of the left part? */
3045 if ((pstr-str)+j == ent->left_len)
3047 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3048 pstr += j;
3049 j = 0;
3051 else if ((pstr-str)+j == ent->left_len+1)
3053 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3054 pstr += j;
3055 j = 0;
3059 /* have we been told to stop rendering at this point? */
3060 if (xtext->jump_out_offset > 0 && xtext->jump_out_offset <= (i + offset))
3062 gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3063 ret = 0; /* skip the rest of the lines, we're done. */
3064 j = 0;
3065 break;
3068 if (xtext->jump_in_offset > 0 && xtext->jump_in_offset == (i + offset))
3070 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3071 pstr += j;
3072 j = 0;
3073 xtext->dont_render2 = FALSE;
3076 #ifdef MOTION_MONITOR
3077 if (xtext->hilight_ent == ent && xtext->hilight_end == (i + offset))
3079 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3080 pstr += j;
3081 j = 0;
3082 #ifdef COLOR_HILIGHT
3083 if (mark)
3085 xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
3086 xtext->backcolor = TRUE;
3087 } else
3089 xtext_set_bg (xtext, gc, xtext->col_back);
3090 if (xtext->col_back != XTEXT_BG)
3091 xtext->backcolor = TRUE;
3092 else
3093 xtext->backcolor = FALSE;
3095 #else
3096 xtext->underline = FALSE;
3097 #endif
3098 xtext->in_hilight = FALSE;
3099 if (xtext->render_hilights_only)
3101 /* stop drawing this ent */
3102 ret = 0;
3103 break;
3106 #endif
3108 if (!mark && ent->mark_start == (i + offset))
3110 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3111 pstr += j;
3112 j = 0;
3113 xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
3114 xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
3115 xtext->backcolor = TRUE;
3116 mark = TRUE;
3119 if (mark && ent->mark_end == (i + offset))
3121 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3122 pstr += j;
3123 j = 0;
3124 xtext_set_bg (xtext, gc, xtext->col_back);
3125 xtext_set_fg (xtext, gc, xtext->col_fore);
3126 if (xtext->col_back != XTEXT_BG)
3127 xtext->backcolor = TRUE;
3128 else
3129 xtext->backcolor = FALSE;
3130 mark = FALSE;
3135 if (j)
3136 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
3138 if (mark)
3140 xtext_set_bg (xtext, gc, xtext->col_back);
3141 xtext_set_fg (xtext, gc, xtext->col_fore);
3142 if (xtext->col_back != XTEXT_BG)
3143 xtext->backcolor = TRUE;
3144 else
3145 xtext->backcolor = FALSE;
3148 /* draw background to the right of the text */
3149 if (!left_only && !xtext->dont_render)
3151 /* draw separator now so it doesn't appear to flicker */
3152 gtk_xtext_draw_sep (xtext, y - xtext->font->ascent);
3153 if (!xtext->skip_border_fills && xtext->clip_x2 >= x)
3155 int xx = MAX (x, xtext->clip_x);
3157 xtext_draw_bg (xtext,
3158 xx, /* x */
3159 y - xtext->font->ascent, /* y */
3160 MIN (xtext->clip_x2 - xx, (win_width + MARGIN) - xx), /* width */
3161 xtext->fontsize); /* height */
3165 xtext->dont_render2 = FALSE;
3167 /* return how much we drew in the x direction */
3168 if (x_size_ret)
3169 *x_size_ret = x - indent;
3171 return ret;
3174 #ifdef USE_XLIB
3176 /* get the desktop/root window */
3178 static Window desktop_window = None;
3180 static Window
3181 get_desktop_window (Display *xdisplay, Window the_window)
3183 Atom prop, type;
3184 int format;
3185 unsigned long length, after;
3186 unsigned char *data;
3187 unsigned int nchildren;
3188 Window w, root, *children, parent;
3190 prop = XInternAtom (xdisplay, "_XROOTPMAP_ID", True);
3191 if (prop == None)
3193 prop = XInternAtom (xdisplay, "_XROOTCOLOR_PIXEL", True);
3194 if (prop == None)
3195 return None;
3198 for (w = the_window; w; w = parent)
3200 if ((XQueryTree (xdisplay, w, &root, &parent, &children,
3201 &nchildren)) == False)
3202 return None;
3204 if (nchildren)
3205 XFree (children);
3207 XGetWindowProperty (xdisplay, w, prop, 0L, 1L, False,
3208 AnyPropertyType, &type, &format, &length, &after,
3209 &data);
3210 if (data)
3211 XFree (data);
3213 if (type != None)
3214 return (desktop_window = w);
3217 return (desktop_window = None);
3220 /* find the root window (backdrop) Pixmap */
3222 static Pixmap
3223 get_pixmap_prop (Display *xdisplay, Window the_window)
3225 Atom type;
3226 int format;
3227 unsigned long length, after;
3228 unsigned char *data;
3229 Pixmap pix = None;
3230 static Atom prop = None;
3232 if (desktop_window == None)
3233 desktop_window = get_desktop_window (xdisplay, the_window);
3234 if (desktop_window == None)
3235 desktop_window = DefaultRootWindow (xdisplay);
3237 if (prop == None)
3238 prop = XInternAtom (xdisplay, "_XROOTPMAP_ID", True);
3239 if (prop == None)
3240 return None;
3242 XGetWindowProperty (xdisplay, desktop_window, prop, 0L, 1L, False,
3243 AnyPropertyType, &type, &format, &length, &after,
3244 &data);
3245 if (data)
3247 if (type == XA_PIXMAP)
3248 pix = *((Pixmap *) data);
3250 XFree (data);
3253 return pix;
3256 /* slow generic routine, for the depths/bpp we don't know about */
3258 static void
3259 shade_ximage_generic (GdkVisual *visual, XImage *ximg, int bpl, int w, int h, int rm, int gm, int bm, int bg)
3261 int x, y;
3262 int bgr = (256 - rm) * (bg & visual->red_mask);
3263 int bgg = (256 - gm) * (bg & visual->green_mask);
3264 int bgb = (256 - bm) * (bg & visual->blue_mask);
3266 for (x = 0; x < w; x++)
3268 for (y = 0; y < h; y++)
3270 unsigned long pixel = XGetPixel (ximg, x, y);
3271 int r, g, b;
3273 r = rm * (pixel & visual->red_mask) + bgr;
3274 g = gm * (pixel & visual->green_mask) + bgg;
3275 b = bm * (pixel & visual->blue_mask) + bgb;
3277 XPutPixel (ximg, x, y,
3278 ((r >> 8) & visual->red_mask) |
3279 ((g >> 8) & visual->green_mask) |
3280 ((b >> 8) & visual->blue_mask));
3285 #endif
3287 /* Fast shading routine. Based on code by Willem Monsuwe <willem@stack.nl> */
3289 #define SHADE_IMAGE(bytes, type, rmask, gmask, bmask) \
3290 unsigned char *ptr; \
3291 int x, y; \
3292 int bgr = (256 - rm) * (bg & rmask); \
3293 int bgg = (256 - gm) * (bg & gmask); \
3294 int bgb = (256 - bm) * (bg & bmask); \
3295 ptr = (unsigned char *) data + (w * bytes); \
3296 for (y = h; --y >= 0;) \
3298 for (x = -w; x < 0; x++) \
3300 int r, g, b; \
3301 b = ((type *) ptr)[x]; \
3302 r = rm * (b & rmask) + bgr; \
3303 g = gm * (b & gmask) + bgg; \
3304 b = bm * (b & bmask) + bgb; \
3305 ((type *) ptr)[x] = ((r >> 8) & rmask) \
3306 | ((g >> 8) & gmask) \
3307 | ((b >> 8) & bmask); \
3309 ptr += bpl; \
3312 /* RGB 15 */
3313 static void
3314 shade_ximage_15 (void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg)
3316 SHADE_IMAGE (2, guint16, 0x7c00, 0x3e0, 0x1f);
3319 /* RGB 16 */
3320 static void
3321 shade_ximage_16 (void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg)
3323 SHADE_IMAGE (2, guint16, 0xf800, 0x7e0, 0x1f);
3326 /* RGB 24 */
3327 static void
3328 shade_ximage_24 (void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg)
3330 /* 24 has to be a special case, there's no guint24, or 24bit MOV :) */
3331 unsigned char *ptr;
3332 int x, y;
3333 int bgr = (256 - rm) * ((bg & 0xff0000) >> 16);
3334 int bgg = (256 - gm) * ((bg & 0xff00) >> 8);
3335 int bgb = (256 - bm) * (bg & 0xff);
3337 ptr = (unsigned char *) data + (w * 3);
3338 for (y = h; --y >= 0;)
3340 for (x = -(w * 3); x < 0; x += 3)
3342 int r, g, b;
3344 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
3345 r = (ptr[x + 0] * rm + bgr) >> 8;
3346 g = (ptr[x + 1] * gm + bgg) >> 8;
3347 b = (ptr[x + 2] * bm + bgb) >> 8;
3348 ptr[x + 0] = r;
3349 ptr[x + 1] = g;
3350 ptr[x + 2] = b;
3351 #else
3352 r = (ptr[x + 2] * rm + bgr) >> 8;
3353 g = (ptr[x + 1] * gm + bgg) >> 8;
3354 b = (ptr[x + 0] * bm + bgb) >> 8;
3355 ptr[x + 2] = r;
3356 ptr[x + 1] = g;
3357 ptr[x + 0] = b;
3358 #endif
3360 ptr += bpl;
3364 /* RGB 32 */
3365 static void
3366 shade_ximage_32 (void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg)
3368 SHADE_IMAGE (4, guint32, 0xff0000, 0xff00, 0xff);
3371 static void
3372 shade_image (GdkVisual *visual, void *data, int bpl, int bpp, int w, int h,
3373 int rm, int gm, int bm, int bg, int depth)
3375 int bg_r, bg_g, bg_b;
3377 bg_r = bg & visual->red_mask;
3378 bg_g = bg & visual->green_mask;
3379 bg_b = bg & visual->blue_mask;
3381 switch (depth)
3383 case 15:
3384 shade_ximage_15 (data, bpl, w, h, rm, gm, bm, bg);
3385 break;
3386 case 16:
3387 shade_ximage_16 (data, bpl, w, h, rm, gm, bm, bg);
3388 break;
3389 case 24:
3390 if (bpp != 32)
3392 shade_ximage_24 (data, bpl, w, h, rm, gm, bm, bg);
3393 break;
3395 case 32:
3396 shade_ximage_32 (data, bpl, w, h, rm, gm, bm, bg);
3400 #ifdef USE_XLIB
3402 #ifdef USE_SHM
3404 static XImage *
3405 get_shm_image (Display *xdisplay, XShmSegmentInfo *shminfo, int x, int y,
3406 int w, int h, int depth, Pixmap pix)
3408 XImage *ximg;
3410 shminfo->shmid = -1;
3411 shminfo->shmaddr = (char*) -1;
3412 ximg = XShmCreateImage (xdisplay, 0, depth, ZPixmap, 0, shminfo, w, h);
3413 if (!ximg)
3414 return NULL;
3416 shminfo->shmid = shmget (IPC_PRIVATE, ximg->bytes_per_line * ximg->height,
3417 IPC_CREAT|0600);
3418 if (shminfo->shmid == -1)
3420 XDestroyImage (ximg);
3421 return NULL;
3424 shminfo->readOnly = False;
3425 ximg->data = shminfo->shmaddr = (char *)shmat (shminfo->shmid, 0, 0);
3426 if (shminfo->shmaddr == ((char *)-1))
3428 shmctl (shminfo->shmid, IPC_RMID, 0);
3429 XDestroyImage (ximg);
3430 return NULL;
3433 XShmAttach (xdisplay, shminfo);
3434 XSync (xdisplay, False);
3435 shmctl (shminfo->shmid, IPC_RMID, 0);
3436 XShmGetImage (xdisplay, pix, ximg, x, y, AllPlanes);
3438 return ximg;
3441 static XImage *
3442 get_image (GtkXText *xtext, Display *xdisplay, XShmSegmentInfo *shminfo,
3443 int x, int y, int w, int h, int depth, Pixmap pix)
3445 XImage *ximg;
3447 xtext->shm = 1;
3448 ximg = get_shm_image (xdisplay, shminfo, x, y, w, h, depth, pix);
3449 if (!ximg)
3451 xtext->shm = 0;
3452 ximg = XGetImage (xdisplay, pix, x, y, w, h, -1, ZPixmap);
3455 return ximg;
3458 #endif
3460 static GdkPixmap *
3461 shade_pixmap (GtkXText * xtext, Pixmap p, int x, int y, int w, int h)
3463 unsigned int dummy, width, height, depth;
3464 GdkPixmap *shaded_pix;
3465 Window root;
3466 Pixmap tmp;
3467 XImage *ximg;
3468 XGCValues gcv;
3469 GC tgc;
3470 Display *xdisplay = GDK_WINDOW_XDISPLAY (xtext->draw_buf);
3472 #ifdef USE_SHM
3473 int shm_pixmaps;
3474 shm_pixmaps = have_shm_pixmaps(xdisplay);
3475 #endif
3477 XGetGeometry (xdisplay, p, &root, &dummy, &dummy, &width, &height,
3478 &dummy, &depth);
3480 if (width < x + w || height < y + h || x < 0 || y < 0)
3482 gcv.subwindow_mode = IncludeInferiors;
3483 gcv.graphics_exposures = False;
3484 tgc = XCreateGC (xdisplay, p, GCGraphicsExposures|GCSubwindowMode,
3485 &gcv);
3486 tmp = XCreatePixmap (xdisplay, p, w, h, depth);
3487 XSetTile (xdisplay, tgc, p);
3488 XSetFillStyle (xdisplay, tgc, FillTiled);
3489 XSetTSOrigin (xdisplay, tgc, -x, -y);
3490 XFillRectangle (xdisplay, tmp, tgc, 0, 0, w, h);
3491 XFreeGC (xdisplay, tgc);
3493 #ifdef USE_SHM
3494 if (shm_pixmaps)
3495 ximg = get_image (xtext, xdisplay, &xtext->shminfo, 0, 0, w, h, depth, tmp);
3496 else
3497 #endif
3498 ximg = XGetImage (xdisplay, tmp, 0, 0, w, h, -1, ZPixmap);
3499 XFreePixmap (xdisplay, tmp);
3500 } else
3502 #ifdef USE_SHM
3503 if (shm_pixmaps)
3504 ximg = get_image (xtext, xdisplay, &xtext->shminfo, x, y, w, h, depth, p);
3505 else
3506 #endif
3507 ximg = XGetImage (xdisplay, p, x, y, w, h, -1, ZPixmap);
3510 if (!ximg)
3511 return NULL;
3513 if (depth <= 14)
3515 shade_ximage_generic (gdk_drawable_get_visual (GTK_WIDGET (xtext)->window),
3516 ximg, ximg->bytes_per_line, w, h, xtext->tint_red,
3517 xtext->tint_green, xtext->tint_blue,
3518 xtext->palette[XTEXT_BG]);
3519 } else
3521 shade_image (gdk_drawable_get_visual (GTK_WIDGET (xtext)->window),
3522 ximg->data, ximg->bytes_per_line, ximg->bits_per_pixel,
3523 w, h, xtext->tint_red, xtext->tint_green, xtext->tint_blue,
3524 xtext->palette[XTEXT_BG], depth);
3527 if (xtext->recycle)
3528 shaded_pix = xtext->pixmap;
3529 else
3531 #ifdef USE_SHM
3532 if (xtext->shm && shm_pixmaps)
3534 shaded_pix = gdk_pixmap_foreign_new_for_display (
3535 gdk_drawable_get_display (xtext->draw_buf),
3536 XShmCreatePixmap (xdisplay, p, ximg->data, &xtext->shminfo, w, h, depth));
3537 } else
3538 #endif
3540 shaded_pix = gdk_pixmap_new (GTK_WIDGET (xtext)->window, w, h, depth);
3544 #ifdef USE_SHM
3545 if (!xtext->shm || !shm_pixmaps)
3546 #endif
3547 XPutImage (xdisplay, GDK_WINDOW_XWINDOW (shaded_pix),
3548 GDK_GC_XGC (xtext->fgc), ximg, 0, 0, 0, 0, w, h);
3549 XDestroyImage (ximg);
3551 return shaded_pix;
3554 #endif /* !USE_XLIB */
3556 /* free transparency xtext->pixmap */
3557 #if defined(USE_XLIB)
3559 static void
3560 gtk_xtext_free_trans (GtkXText * xtext)
3562 if (xtext->pixmap)
3564 #ifdef USE_SHM
3565 if (xtext->shm && have_shm_pixmaps(GDK_WINDOW_XDISPLAY (xtext->draw_buf)))
3567 XFreePixmap (GDK_WINDOW_XDISPLAY (xtext->pixmap),
3568 GDK_WINDOW_XWINDOW (xtext->pixmap));
3569 XShmDetach (GDK_WINDOW_XDISPLAY (xtext->draw_buf), &xtext->shminfo);
3570 shmdt (xtext->shminfo.shmaddr);
3572 #endif
3573 g_object_unref (xtext->pixmap);
3574 xtext->pixmap = NULL;
3575 xtext->shm = 0;
3579 #endif
3581 /* grab pixmap from root window and set xtext->pixmap */
3582 #if defined(USE_XLIB)
3584 static void
3585 gtk_xtext_load_trans (GtkXText * xtext)
3587 Pixmap rootpix;
3588 GtkWidget *widget = GTK_WIDGET (xtext);
3589 int x, y;
3591 rootpix = get_pixmap_prop (GDK_WINDOW_XDISPLAY (widget->window), GDK_WINDOW_XWINDOW (widget->window));
3592 if (rootpix == None)
3594 if (xtext->error_function)
3595 xtext->error_function (0);
3596 xtext->transparent = FALSE;
3597 return;
3600 gdk_window_get_origin (widget->window, &x, &y);
3602 if (xtext->shaded)
3604 int width, height;
3605 gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
3606 xtext->pixmap = shade_pixmap (xtext, rootpix, x, y, width+105, height);
3607 if (xtext->pixmap == NULL)
3609 xtext->shaded = 0;
3610 goto noshade;
3612 gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
3613 gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
3614 xtext->ts_x = xtext->ts_y = 0;
3615 } else
3617 noshade:
3618 xtext->pixmap = gdk_pixmap_foreign_new_for_display (gdk_drawable_get_display (GTK_WIDGET (xtext)->window), rootpix);
3619 gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
3620 gdk_gc_set_ts_origin (xtext->bgc, -x, -y);
3621 xtext->ts_x = -x;
3622 xtext->ts_y = -y;
3624 gdk_gc_set_fill (xtext->bgc, GDK_TILED);
3627 #endif /* XLIB */
3629 /* walk through str until this line doesn't fit anymore */
3631 static int
3632 find_next_wrap (GtkXText * xtext, textentry * ent, unsigned char *str,
3633 int win_width, int indent)
3635 unsigned char *last_space = str;
3636 unsigned char *orig_str = str;
3637 int str_width = indent;
3638 int rcol = 0, bgcol = 0;
3639 int hidden = FALSE;
3640 int mbl;
3641 int char_width;
3642 int ret;
3643 int limit_offset = 0;
3645 /* single liners */
3646 if (win_width >= ent->str_width + ent->indent)
3647 return ent->str_len;
3649 /* it does happen! */
3650 if (win_width < 1)
3652 ret = ent->str_len - (str - ent->str);
3653 goto done;
3656 while (1)
3658 if (rcol > 0 && (isdigit (*str) || (*str == ',' && isdigit (str[1]) && !bgcol)))
3660 if (str[1] != ',') rcol--;
3661 if (*str == ',')
3663 rcol = 2;
3664 bgcol = 1;
3666 limit_offset++;
3667 str++;
3668 } else
3670 rcol = bgcol = 0;
3671 switch (*str)
3673 case ATTR_COLOR:
3674 rcol = 2;
3675 case ATTR_BEEP:
3676 case ATTR_RESET:
3677 case ATTR_REVERSE:
3678 case ATTR_BOLD:
3679 case ATTR_UNDERLINE:
3680 case ATTR_ITALICS:
3681 limit_offset++;
3682 str++;
3683 break;
3684 case ATTR_HIDDEN:
3685 if (xtext->ignore_hidden)
3686 goto def;
3687 hidden = !hidden;
3688 limit_offset++;
3689 str++;
3690 break;
3691 default:
3692 def:
3693 char_width = backend_get_char_width (xtext, str, &mbl);
3694 if (!hidden) str_width += char_width;
3695 if (str_width > win_width)
3697 if (xtext->wordwrap)
3699 if (str - last_space > WORDWRAP_LIMIT + limit_offset)
3700 ret = str - orig_str; /* fall back to character wrap */
3701 else
3703 if (*last_space == ' ')
3704 last_space++;
3705 ret = last_space - orig_str;
3706 if (ret == 0) /* fall back to character wrap */
3707 ret = str - orig_str;
3709 goto done;
3711 ret = str - orig_str;
3712 goto done;
3715 /* keep a record of the last space, for wordwrapping */
3716 if (is_del (*str))
3718 last_space = str;
3719 limit_offset = 0;
3722 /* progress to the next char */
3723 str += mbl;
3728 if (str >= ent->str + ent->str_len)
3730 ret = str - orig_str;
3731 goto done;
3735 done:
3737 /* must make progress */
3738 if (ret < 1)
3739 ret = 1;
3741 return ret;
3744 /* find the offset, in bytes, that wrap number 'line' starts at */
3746 static int
3747 gtk_xtext_find_subline (GtkXText *xtext, textentry *ent, int line)
3749 int win_width;
3750 unsigned char *str;
3751 int indent, str_pos, line_pos, len;
3753 if (ent->lines_taken < 2 || line < 1)
3754 return 0;
3756 /* we record the first 4 lines' wraps, so take a shortcut */
3757 if (line <= RECORD_WRAPS)
3758 return ent->wrap_offset[line - 1];
3760 gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &win_width, 0);
3761 win_width -= MARGIN;
3763 /* indent = ent->indent;
3764 str = ent->str;
3765 line_pos = str_pos = 0;*/
3767 /* start from the last recorded wrap, and move forward */
3768 indent = xtext->buffer->indent;
3769 str_pos = ent->wrap_offset[RECORD_WRAPS-1];
3770 str = str_pos + ent->str;
3771 line_pos = RECORD_WRAPS;
3775 len = find_next_wrap (xtext, ent, str, win_width, indent);
3776 indent = xtext->buffer->indent;
3777 str += len;
3778 str_pos += len;
3779 line_pos++;
3780 if (line_pos >= line)
3781 return str_pos;
3783 while (str < ent->str + ent->str_len);
3785 return 0;
3788 /* horrible hack for drawing time stamps */
3790 static void
3791 gtk_xtext_render_stamp (GtkXText * xtext, textentry * ent,
3792 char *text, int len, int line, int win_width)
3794 textentry tmp_ent;
3795 int jo, ji, hs;
3796 int xsize, y;
3798 /* trashing ent here, so make a backup first */
3799 memcpy (&tmp_ent, ent, sizeof (tmp_ent));
3800 ent->mb = TRUE; /* make non-english days of the week work */
3801 jo = xtext->jump_out_offset; /* back these up */
3802 ji = xtext->jump_in_offset;
3803 hs = xtext->hilight_start;
3804 xtext->jump_out_offset = 0;
3805 xtext->jump_in_offset = 0;
3806 xtext->hilight_start = 0xffff; /* temp disable */
3808 if (xtext->mark_stamp)
3810 /* if this line is marked, mark this stamp too */
3811 if (ent->mark_start == 0)
3813 ent->mark_start = 0;
3814 ent->mark_end = len;
3816 else
3818 ent->mark_start = -1;
3819 ent->mark_end = -1;
3821 ent->str = text;
3824 y = (xtext->fontsize * line) + xtext->font->ascent - xtext->pixel_offset;
3825 gtk_xtext_render_str (xtext, y, ent, text, len,
3826 win_width, 2, line, TRUE, &xsize);
3828 /* restore everything back to how it was */
3829 memcpy (ent, &tmp_ent, sizeof (tmp_ent));
3830 xtext->jump_out_offset = jo;
3831 xtext->jump_in_offset = ji;
3832 xtext->hilight_start = hs;
3834 /* with a non-fixed-width font, sometimes we don't draw enough
3835 background i.e. when this stamp is shorter than xtext->stamp_width */
3836 xsize += MARGIN;
3837 if (xsize < xtext->stamp_width)
3839 y -= xtext->font->ascent;
3840 xtext_draw_bg (xtext,
3841 xsize, /* x */
3842 y, /* y */
3843 xtext->stamp_width - xsize, /* width */
3844 xtext->fontsize /* height */);
3848 /* render a single line, which may wrap to more lines */
3850 static int
3851 gtk_xtext_render_line (GtkXText * xtext, textentry * ent, int line,
3852 int lines_max, int subline, int win_width)
3854 unsigned char *str;
3855 int indent, taken, entline, len, y, start_subline;
3857 entline = taken = 0;
3858 str = ent->str;
3859 indent = ent->indent;
3860 start_subline = subline;
3862 #ifdef XCHAT
3863 /* draw the timestamp */
3864 if (xtext->auto_indent && xtext->buffer->time_stamp &&
3865 (!xtext->skip_stamp || xtext->mark_stamp || xtext->force_stamp))
3867 char *time_str;
3868 int len;
3870 len = xtext_get_stamp_str (ent->stamp, &time_str);
3871 gtk_xtext_render_stamp (xtext, ent, time_str, len, line, win_width);
3872 g_free (time_str);
3874 #endif
3876 /* draw each line one by one */
3879 /* if it's one of the first 4 wraps, we don't need to calculate it, it's
3880 recorded in ->wrap_offset. This saves us a loop. */
3881 if (entline < RECORD_WRAPS)
3883 if (ent->lines_taken < 2)
3884 len = ent->str_len;
3885 else
3887 if (entline > 0)
3888 len = ent->wrap_offset[entline] - ent->wrap_offset[entline-1];
3889 else
3890 len = ent->wrap_offset[0];
3892 } else
3893 len = find_next_wrap (xtext, ent, str, win_width, indent);
3895 entline++;
3897 y = (xtext->fontsize * line) + xtext->font->ascent - xtext->pixel_offset;
3898 if (!subline)
3900 if (!gtk_xtext_render_str (xtext, y, ent, str, len, win_width,
3901 indent, line, FALSE, NULL))
3903 /* small optimization */
3904 gtk_xtext_draw_marker (xtext, ent, y - xtext->fontsize * (taken + start_subline + 1));
3905 return ent->lines_taken - subline;
3907 } else
3909 xtext->dont_render = TRUE;
3910 gtk_xtext_render_str (xtext, y, ent, str, len, win_width,
3911 indent, line, FALSE, NULL);
3912 xtext->dont_render = FALSE;
3913 subline--;
3914 line--;
3915 taken--;
3918 indent = xtext->buffer->indent;
3919 line++;
3920 taken++;
3921 str += len;
3923 if (line >= lines_max)
3924 break;
3927 while (str < ent->str + ent->str_len);
3929 gtk_xtext_draw_marker (xtext, ent, y - xtext->fontsize * (taken + start_subline));
3931 return taken;
3934 void
3935 gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[])
3937 int i;
3938 GdkColor col;
3940 for (i = (XTEXT_COLS-1); i >= 0; i--)
3942 #ifdef USE_XFT
3943 xtext->color[i].color.red = palette[i].red;
3944 xtext->color[i].color.green = palette[i].green;
3945 xtext->color[i].color.blue = palette[i].blue;
3946 xtext->color[i].color.alpha = 0xffff;
3947 xtext->color[i].pixel = palette[i].pixel;
3948 #endif
3949 xtext->palette[i] = palette[i].pixel;
3952 if (GTK_WIDGET_REALIZED (xtext))
3954 xtext_set_fg (xtext, xtext->fgc, XTEXT_FG);
3955 xtext_set_bg (xtext, xtext->fgc, XTEXT_BG);
3956 xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
3958 col.pixel = xtext->palette[XTEXT_MARKER];
3959 gdk_gc_set_foreground (xtext->marker_gc, &col);
3961 xtext->col_fore = XTEXT_FG;
3962 xtext->col_back = XTEXT_BG;
3965 static void
3966 gtk_xtext_fix_indent (xtext_buffer *buf)
3968 int j;
3970 /* make indent a multiple of the space width */
3971 if (buf->indent && buf->xtext->space_width)
3973 j = 0;
3974 while (j < buf->indent)
3976 j += buf->xtext->space_width;
3978 buf->indent = j;
3981 dontscroll (buf); /* force scrolling off */
3984 static void
3985 gtk_xtext_recalc_widths (xtext_buffer *buf, int do_str_width)
3987 textentry *ent;
3989 /* since we have a new font, we have to recalc the text widths */
3990 ent = buf->text_first;
3991 while (ent)
3993 if (do_str_width)
3995 ent->str_width = gtk_xtext_text_width (buf->xtext, ent->str,
3996 ent->str_len, NULL);
3998 if (ent->left_len != -1)
4000 ent->indent =
4001 (buf->indent -
4002 gtk_xtext_text_width (buf->xtext, ent->str,
4003 ent->left_len, NULL)) - buf->xtext->space_width;
4004 if (ent->indent < MARGIN)
4005 ent->indent = MARGIN;
4007 ent = ent->next;
4010 gtk_xtext_calc_lines (buf, FALSE);
4014 gtk_xtext_set_font (GtkXText *xtext, char *name)
4016 int i;
4017 unsigned char c;
4019 if (xtext->font)
4020 backend_font_close (xtext);
4022 /* realize now, so that font_open has a XDisplay */
4023 gtk_widget_realize (GTK_WIDGET (xtext));
4025 backend_font_open (xtext, name);
4026 if (xtext->font == NULL)
4027 return FALSE;
4029 /* measure the width of every char; only the ASCII ones for XFT */
4030 for (i = 0; i < sizeof(xtext->fontwidth)/sizeof(xtext->fontwidth[0]); i++)
4032 c = i;
4033 xtext->fontwidth[i] = backend_get_text_width (xtext, &c, 1, TRUE);
4035 xtext->space_width = xtext->fontwidth[' '];
4036 xtext->fontsize = xtext->font->ascent + xtext->font->descent;
4038 #ifdef XCHAT
4040 char *time_str;
4041 int stamp_size = xtext_get_stamp_str (time(0), &time_str);
4042 xtext->stamp_width =
4043 gtk_xtext_text_width (xtext, time_str, stamp_size, NULL) + MARGIN;
4044 g_free (time_str);
4046 #endif
4048 gtk_xtext_fix_indent (xtext->buffer);
4050 if (GTK_WIDGET_REALIZED (xtext))
4051 gtk_xtext_recalc_widths (xtext->buffer, TRUE);
4053 return TRUE;
4056 void
4057 gtk_xtext_set_background (GtkXText * xtext, GdkPixmap * pixmap, gboolean trans)
4059 GdkGCValues val;
4060 gboolean shaded = FALSE;
4062 if (trans && (xtext->tint_red != 255 || xtext->tint_green != 255 || xtext->tint_blue != 255))
4063 shaded = TRUE;
4065 #if !defined(USE_XLIB)
4066 shaded = FALSE;
4067 trans = FALSE;
4068 #endif
4070 if (xtext->pixmap)
4072 #if defined(USE_XLIB)
4073 if (xtext->transparent)
4074 gtk_xtext_free_trans (xtext);
4075 else
4076 #endif
4077 g_object_unref (xtext->pixmap);
4078 xtext->pixmap = NULL;
4081 xtext->transparent = trans;
4083 #if defined(USE_XLIB)
4084 if (trans)
4086 xtext->shaded = shaded;
4087 if (GTK_WIDGET_REALIZED (xtext))
4088 gtk_xtext_load_trans (xtext);
4089 return;
4091 #endif
4093 dontscroll (xtext->buffer);
4094 xtext->pixmap = pixmap;
4096 if (pixmap != 0)
4098 g_object_ref (pixmap);
4099 if (GTK_WIDGET_REALIZED (xtext))
4101 gdk_gc_set_tile (xtext->bgc, pixmap);
4102 gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
4103 xtext->ts_x = xtext->ts_y = 0;
4104 gdk_gc_set_fill (xtext->bgc, GDK_TILED);
4106 } else if (GTK_WIDGET_REALIZED (xtext))
4108 g_object_unref (xtext->bgc);
4109 val.subwindow_mode = GDK_INCLUDE_INFERIORS;
4110 val.graphics_exposures = 0;
4111 xtext->bgc = gdk_gc_new_with_values (GTK_WIDGET (xtext)->window,
4112 &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
4113 xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
4117 void
4118 gtk_xtext_save (GtkXText * xtext, int fh)
4120 textentry *ent;
4121 int newlen;
4122 char *buf;
4124 ent = xtext->buffer->text_first;
4125 while (ent)
4127 buf = gtk_xtext_strip_color (ent->str, ent->str_len, NULL,
4128 &newlen, NULL, FALSE);
4129 write (fh, buf, newlen);
4130 write (fh, "\n", 1);
4131 free (buf);
4132 ent = ent->next;
4136 /* count how many lines 'ent' will take (with wraps) */
4138 static int
4139 gtk_xtext_lines_taken (xtext_buffer *buf, textentry * ent)
4141 unsigned char *str;
4142 int indent, taken, len;
4143 int win_width;
4145 win_width = buf->window_width - MARGIN;
4147 if (ent->str_width + ent->indent < win_width)
4148 return 1;
4150 indent = ent->indent;
4151 str = ent->str;
4152 taken = 0;
4156 len = find_next_wrap (buf->xtext, ent, str, win_width, indent);
4157 if (taken < RECORD_WRAPS)
4158 ent->wrap_offset[taken] = (str + len) - ent->str;
4159 indent = buf->indent;
4160 taken++;
4161 str += len;
4163 while (str < ent->str + ent->str_len);
4165 return taken;
4168 /* Calculate number of actual lines (with wraps), to set adj->lower. *
4169 * This should only be called when the window resizes. */
4171 static void
4172 gtk_xtext_calc_lines (xtext_buffer *buf, int fire_signal)
4174 textentry *ent;
4175 int width;
4176 int height;
4177 int lines;
4179 gdk_drawable_get_size (GTK_WIDGET (buf->xtext)->window, &width, &height);
4180 width -= MARGIN;
4182 if (width < 30 || height < buf->xtext->fontsize || width < buf->indent + 30)
4183 return;
4185 lines = 0;
4186 ent = buf->text_first;
4187 while (ent)
4189 ent->lines_taken = gtk_xtext_lines_taken (buf, ent);
4190 lines += ent->lines_taken;
4191 ent = ent->next;
4194 buf->pagetop_ent = NULL;
4195 buf->num_lines = lines;
4196 gtk_xtext_adjustment_set (buf, fire_signal);
4199 /* find the n-th line in the linked list, this includes wrap calculations */
4201 static textentry *
4202 gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
4204 int lines = 0;
4205 textentry *ent;
4207 ent = xtext->buffer->text_first;
4209 /* -- optimization -- try to make a short-cut using the pagetop ent */
4210 if (xtext->buffer->pagetop_ent)
4212 if (line == xtext->buffer->pagetop_line)
4214 *subline = xtext->buffer->pagetop_subline;
4215 return xtext->buffer->pagetop_ent;
4217 if (line > xtext->buffer->pagetop_line)
4219 /* lets start from the pagetop instead of the absolute beginning */
4220 ent = xtext->buffer->pagetop_ent;
4221 lines = xtext->buffer->pagetop_line - xtext->buffer->pagetop_subline;
4223 else if (line > xtext->buffer->pagetop_line - line)
4225 /* move backwards from pagetop */
4226 ent = xtext->buffer->pagetop_ent;
4227 lines = xtext->buffer->pagetop_line - xtext->buffer->pagetop_subline;
4228 while (1)
4230 if (lines <= line)
4232 *subline = line - lines;
4233 return ent;
4235 ent = ent->prev;
4236 if (!ent)
4237 break;
4238 lines -= ent->lines_taken;
4240 return 0;
4243 /* -- end of optimization -- */
4245 while (ent)
4247 lines += ent->lines_taken;
4248 if (lines > line)
4250 *subline = ent->lines_taken - (lines - line);
4251 return ent;
4253 ent = ent->next;
4255 return 0;
4258 /* render enta (or an inclusive range enta->entb) */
4260 static int
4261 gtk_xtext_render_ents (GtkXText * xtext, textentry * enta, textentry * entb)
4263 textentry *ent, *orig_ent, *tmp_ent;
4264 int line;
4265 int lines_max;
4266 int width;
4267 int height;
4268 int subline;
4269 int drawing = FALSE;
4271 if (xtext->buffer->indent < MARGIN)
4272 xtext->buffer->indent = MARGIN; /* 2 pixels is our left margin */
4274 gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
4275 width -= MARGIN;
4277 if (width < 32 || height < xtext->fontsize || width < xtext->buffer->indent + 30)
4278 return 0;
4280 lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1;
4281 line = 0;
4282 orig_ent = xtext->buffer->pagetop_ent;
4283 subline = xtext->buffer->pagetop_subline;
4285 /* used before a complete page is in buffer */
4286 if (orig_ent == NULL)
4287 orig_ent = xtext->buffer->text_first;
4289 /* check if enta is before the start of this page */
4290 if (entb)
4292 tmp_ent = orig_ent;
4293 while (tmp_ent)
4295 if (tmp_ent == enta)
4296 break;
4297 if (tmp_ent == entb)
4299 drawing = TRUE;
4300 break;
4302 tmp_ent = tmp_ent->next;
4306 ent = orig_ent;
4307 while (ent)
4309 if (entb && ent == enta)
4310 drawing = TRUE;
4312 if (drawing || ent == entb || ent == enta)
4314 gtk_xtext_reset (xtext, FALSE, TRUE);
4315 line += gtk_xtext_render_line (xtext, ent, line, lines_max,
4316 subline, width);
4317 subline = 0;
4318 xtext->jump_in_offset = 0; /* jump_in_offset only for the 1st */
4319 } else
4321 if (ent == orig_ent)
4323 line -= subline;
4324 subline = 0;
4326 line += ent->lines_taken;
4329 if (ent == entb)
4330 break;
4332 if (line >= lines_max)
4333 break;
4335 ent = ent->next;
4338 /* space below last line */
4339 return (xtext->fontsize * line) - xtext->pixel_offset;
4342 /* render a whole page/window, starting from 'startline' */
4344 static void
4345 gtk_xtext_render_page (GtkXText * xtext)
4347 textentry *ent;
4348 int line;
4349 int lines_max;
4350 int width;
4351 int height;
4352 int subline;
4353 int startline = xtext->adj->value;
4355 if(!GTK_WIDGET_REALIZED(xtext))
4356 return;
4358 if (xtext->buffer->indent < MARGIN)
4359 xtext->buffer->indent = MARGIN; /* 2 pixels is our left margin */
4361 gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
4363 if (width < 34 || height < xtext->fontsize || width < xtext->buffer->indent + 32)
4364 return;
4366 #ifdef SMOOTH_SCROLL
4367 xtext->pixel_offset = (xtext->adj->value - startline) * xtext->fontsize;
4368 #else
4369 xtext->pixel_offset = 0;
4370 #endif
4372 subline = line = 0;
4373 ent = xtext->buffer->text_first;
4375 if (startline > 0)
4376 ent = gtk_xtext_nth (xtext, startline, &subline);
4378 xtext->buffer->pagetop_ent = ent;
4379 xtext->buffer->pagetop_subline = subline;
4380 xtext->buffer->pagetop_line = startline;
4382 #ifdef SCROLL_HACK
4384 int pos, overlap;
4385 GdkRectangle area;
4387 if (xtext->buffer->num_lines <= xtext->adj->page_size)
4388 dontscroll (xtext->buffer);
4390 #ifdef SMOOTH_SCROLL
4391 pos = xtext->adj->value * xtext->fontsize;
4392 #else
4393 pos = startline * xtext->fontsize;
4394 #endif
4395 overlap = xtext->buffer->last_pixel_pos - pos;
4396 xtext->buffer->last_pixel_pos = pos;
4398 #ifdef USE_DB
4399 if (!xtext->pixmap && abs (overlap) < height)
4400 #else
4401 /* dont scroll PageUp/Down without a DB, it looks ugly */
4402 if (!xtext->pixmap && abs (overlap) < height - (3*xtext->fontsize))
4403 #endif
4405 /* so the obscured regions are exposed */
4406 gdk_gc_set_exposures (xtext->fgc, TRUE);
4407 if (overlap < 1) /* DOWN */
4409 int remainder;
4411 gdk_draw_drawable (xtext->draw_buf, xtext->fgc, xtext->draw_buf,
4412 0, -overlap, 0, 0, width, height + overlap);
4413 remainder = ((height - xtext->font->descent) % xtext->fontsize) +
4414 xtext->font->descent;
4415 area.y = (height + overlap) - remainder;
4416 area.height = remainder - overlap;
4417 } else
4419 gdk_draw_drawable (xtext->draw_buf, xtext->fgc, xtext->draw_buf,
4420 0, 0, 0, overlap, width, height - overlap);
4421 area.y = 0;
4422 area.height = overlap;
4424 gdk_gc_set_exposures (xtext->fgc, FALSE);
4426 if (area.height > 0)
4428 area.x = 0;
4429 area.width = width;
4430 gtk_xtext_paint (GTK_WIDGET (xtext), &area);
4432 xtext->buffer->grid_dirty = TRUE;
4434 return;
4437 #endif
4439 xtext->buffer->grid_dirty = FALSE;
4440 width -= MARGIN;
4441 lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1;
4443 while (ent)
4445 gtk_xtext_reset (xtext, FALSE, TRUE);
4446 line += gtk_xtext_render_line (xtext, ent, line, lines_max,
4447 subline, width);
4448 subline = 0;
4450 if (line >= lines_max)
4451 break;
4453 ent = ent->next;
4456 line = (xtext->fontsize * line) - xtext->pixel_offset;
4457 /* fill any space below the last line with our background GC */
4458 xtext_draw_bg (xtext, 0, line, width + MARGIN, height - line);
4460 /* draw the separator line */
4461 gtk_xtext_draw_sep (xtext, -1);
4464 void
4465 gtk_xtext_refresh (GtkXText * xtext, int do_trans)
4467 if (GTK_WIDGET_REALIZED (GTK_WIDGET (xtext)))
4469 #if defined(USE_XLIB)
4470 if (xtext->transparent && do_trans)
4472 gtk_xtext_free_trans (xtext);
4473 gtk_xtext_load_trans (xtext);
4475 #endif
4476 gtk_xtext_render_page (xtext);
4480 static int
4481 gtk_xtext_kill_ent (xtext_buffer *buffer, textentry *ent)
4483 int visible;
4485 /* Set visible to TRUE if this is the current buffer */
4486 /* and this ent shows up on the screen now */
4487 visible = buffer->xtext->buffer == buffer &&
4488 gtk_xtext_check_ent_visibility (buffer->xtext, ent, 0);
4490 if (ent == buffer->pagetop_ent)
4491 buffer->pagetop_ent = NULL;
4493 if (ent == buffer->last_ent_start)
4495 buffer->last_ent_start = ent->next;
4496 buffer->last_offset_start = 0;
4499 if (ent == buffer->last_ent_end)
4501 buffer->last_ent_start = NULL;
4502 buffer->last_ent_end = NULL;
4505 if (buffer->marker_pos == ent) buffer->marker_pos = NULL;
4507 free (ent);
4508 return visible;
4511 /* remove the topline from the list */
4513 static void
4514 gtk_xtext_remove_top (xtext_buffer *buffer)
4516 textentry *ent;
4518 ent = buffer->text_first;
4519 if (!ent)
4520 return;
4521 buffer->num_lines -= ent->lines_taken;
4522 buffer->pagetop_line -= ent->lines_taken;
4523 buffer->last_pixel_pos -= (ent->lines_taken * buffer->xtext->fontsize);
4524 buffer->text_first = ent->next;
4525 if (buffer->text_first)
4526 buffer->text_first->prev = NULL;
4527 else
4528 buffer->text_last = NULL;
4530 buffer->old_value -= ent->lines_taken;
4531 if (buffer->xtext->buffer == buffer) /* is it the current buffer? */
4533 buffer->xtext->adj->value -= ent->lines_taken;
4534 buffer->xtext->select_start_adj -= ent->lines_taken;
4537 if (gtk_xtext_kill_ent (buffer, ent))
4539 if (!buffer->xtext->add_io_tag)
4541 /* remove scrolling events */
4542 if (buffer->xtext->io_tag)
4544 g_source_remove (buffer->xtext->io_tag);
4545 buffer->xtext->io_tag = 0;
4547 buffer->xtext->force_render = TRUE;
4548 buffer->xtext->add_io_tag = g_timeout_add (REFRESH_TIMEOUT * 2,
4549 (GSourceFunc)
4550 gtk_xtext_render_page_timeout,
4551 buffer->xtext);
4556 static void
4557 gtk_xtext_remove_bottom (xtext_buffer *buffer)
4559 textentry *ent;
4561 ent = buffer->text_last;
4562 if (!ent)
4563 return;
4564 buffer->num_lines -= ent->lines_taken;
4565 buffer->text_last = ent->prev;
4566 if (buffer->text_last)
4567 buffer->text_last->next = NULL;
4568 else
4569 buffer->text_first = NULL;
4571 gtk_xtext_kill_ent (buffer, ent);
4574 /* If lines=0 => clear all */
4576 void
4577 gtk_xtext_clear (xtext_buffer *buf, int lines)
4579 textentry *next;
4581 if (lines != 0)
4583 if (lines < 0)
4585 /* delete lines from bottom */
4586 lines *= -1;
4587 while (lines)
4589 gtk_xtext_remove_bottom (buf);
4590 lines--;
4593 else
4595 /* delete lines from top */
4596 while (lines)
4598 gtk_xtext_remove_top (buf);
4599 lines--;
4603 else
4605 /* delete all */
4606 if (buf->xtext->auto_indent)
4607 buf->indent = MARGIN;
4608 buf->scrollbar_down = TRUE;
4609 buf->last_ent_start = NULL;
4610 buf->last_ent_end = NULL;
4611 buf->marker_pos = NULL;
4612 dontscroll (buf);
4614 while (buf->text_first)
4616 next = buf->text_first->next;
4617 free (buf->text_first);
4618 buf->text_first = next;
4620 buf->text_last = NULL;
4623 if (buf->xtext->buffer == buf)
4625 gtk_xtext_calc_lines (buf, TRUE);
4626 gtk_xtext_refresh (buf->xtext, 0);
4627 } else
4629 gtk_xtext_calc_lines (buf, FALSE);
4633 static gboolean
4634 gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add)
4636 textentry *ent;
4637 int lines_max;
4638 int line = 0;
4639 int width;
4640 int height;
4642 gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
4644 lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + add;
4645 ent = xtext->buffer->pagetop_ent;
4647 while (ent && line < lines_max)
4649 if (find_ent == ent)
4650 return TRUE;
4651 line += ent->lines_taken;
4652 ent = ent->next;
4655 return FALSE;
4658 void
4659 gtk_xtext_check_marker_visibility (GtkXText * xtext)
4661 if (gtk_xtext_check_ent_visibility (xtext, xtext->buffer->marker_pos, 1))
4662 xtext->buffer->marker_seen = TRUE;
4665 textentry *
4666 gtk_xtext_search (GtkXText * xtext, const gchar *text, textentry *start, gboolean case_match, gboolean backward)
4668 textentry *ent, *fent;
4669 int line;
4670 gchar *str, *nee, *hay; /* needle in haystack */
4672 gtk_xtext_selection_clear_full (xtext->buffer);
4673 xtext->buffer->last_ent_start = NULL;
4674 xtext->buffer->last_ent_end = NULL;
4676 /* set up text comparand for Case Match or Ignore */
4677 if (case_match)
4678 nee = g_strdup (text);
4679 else
4680 nee = g_utf8_casefold (text, strlen (text));
4682 /* Validate that start gives a currently valid ent pointer */
4683 ent = xtext->buffer->text_first;
4684 while (ent)
4686 if (ent == start)
4687 break;
4688 ent = ent->next;
4690 if (!ent)
4691 start = NULL;
4693 /* Choose first ent to look at */
4694 if (start)
4695 ent = backward? start->prev: start->next;
4696 else
4697 ent = backward? xtext->buffer->text_last: xtext->buffer->text_first;
4699 /* Search from there to one end or the other until found */
4700 while (ent)
4702 /* If Case Ignore, fold before & free after calling strstr */
4703 if (case_match)
4704 hay = g_strdup (ent->str);
4705 else
4706 hay = g_utf8_casefold (ent->str, strlen (ent->str));
4707 /* Try to find the needle in this haystack */
4708 str = g_strstr_len (hay, strlen (hay), nee);
4709 g_free (hay);
4710 if (str)
4711 break;
4712 ent = backward? ent->prev: ent->next;
4714 fent = ent;
4716 /* Save distance to start, end of found string */
4717 if (ent)
4719 ent->mark_start = str - hay;
4720 ent->mark_end = ent->mark_start + strlen (nee);
4722 /* is the match visible? Might need to scroll */
4723 if (!gtk_xtext_check_ent_visibility (xtext, ent, 0))
4725 ent = xtext->buffer->text_first;
4726 line = 0;
4727 while (ent)
4729 line += ent->lines_taken;
4730 ent = ent->next;
4731 if (ent == fent)
4732 break;
4734 while (line > xtext->adj->upper - xtext->adj->page_size)
4735 line--;
4736 if (backward)
4737 line -= xtext->adj->page_size - ent->lines_taken;
4738 xtext->adj->value = line;
4739 xtext->buffer->scrollbar_down = FALSE;
4740 gtk_adjustment_changed (xtext->adj);
4744 g_free (nee);
4745 gtk_widget_queue_draw (GTK_WIDGET (xtext));
4747 return fent;
4750 static int
4751 gtk_xtext_render_page_timeout (GtkXText * xtext)
4753 GtkAdjustment *adj = xtext->adj;
4755 xtext->add_io_tag = 0;
4757 /* less than a complete page? */
4758 if (xtext->buffer->num_lines <= adj->page_size)
4760 xtext->buffer->old_value = 0;
4761 adj->value = 0;
4762 gtk_xtext_render_page (xtext);
4763 } else if (xtext->buffer->scrollbar_down)
4765 g_signal_handler_block (xtext->adj, xtext->vc_signal_tag);
4766 gtk_xtext_adjustment_set (xtext->buffer, FALSE);
4767 gtk_adjustment_set_value (adj, adj->upper - adj->page_size);
4768 g_signal_handler_unblock (xtext->adj, xtext->vc_signal_tag);
4769 xtext->buffer->old_value = adj->value;
4770 gtk_xtext_render_page (xtext);
4771 } else
4773 gtk_xtext_adjustment_set (xtext->buffer, TRUE);
4774 if (xtext->force_render)
4776 xtext->force_render = FALSE;
4777 gtk_xtext_render_page (xtext);
4781 return 0;
4784 /* append a textentry to our linked list */
4786 static void
4787 gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp)
4789 unsigned int mb;
4790 int i;
4792 /* we don't like tabs */
4793 i = 0;
4794 while (i < ent->str_len)
4796 if (ent->str[i] == '\t')
4797 ent->str[i] = ' ';
4798 i++;
4801 ent->stamp = stamp;
4802 if (stamp == 0)
4803 ent->stamp = time (0);
4804 ent->str_width = gtk_xtext_text_width (buf->xtext, ent->str, ent->str_len, &mb);
4805 ent->mb = FALSE;
4806 if (mb)
4807 ent->mb = TRUE;
4808 ent->mark_start = -1;
4809 ent->mark_end = -1;
4810 ent->next = NULL;
4812 if (ent->indent < MARGIN)
4813 ent->indent = MARGIN; /* 2 pixels is the left margin */
4815 /* append to our linked list */
4816 if (buf->text_last)
4817 buf->text_last->next = ent;
4818 else
4819 buf->text_first = ent;
4820 ent->prev = buf->text_last;
4821 buf->text_last = ent;
4823 ent->lines_taken = gtk_xtext_lines_taken (buf, ent);
4824 buf->num_lines += ent->lines_taken;
4826 if (buf->reset_marker_pos ||
4827 ((buf->marker_pos == NULL || buf->marker_seen) && (buf->xtext->buffer != buf ||
4828 !gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (buf->xtext)))))))
4830 buf->marker_pos = ent;
4831 dontscroll (buf); /* force scrolling off */
4832 buf->marker_seen = FALSE;
4833 buf->reset_marker_pos = FALSE;
4836 if (buf->xtext->max_lines > 2 && buf->xtext->max_lines < buf->num_lines)
4838 gtk_xtext_remove_top (buf);
4841 if (buf->xtext->buffer == buf)
4843 #ifdef SCROLL_HACK
4844 /* this could be improved */
4845 if ((buf->num_lines - 1) <= buf->xtext->adj->page_size)
4846 dontscroll (buf);
4847 #endif
4849 if (!buf->xtext->add_io_tag)
4851 /* remove scrolling events */
4852 if (buf->xtext->io_tag)
4854 g_source_remove (buf->xtext->io_tag);
4855 buf->xtext->io_tag = 0;
4857 buf->xtext->add_io_tag = g_timeout_add (REFRESH_TIMEOUT * 2,
4858 (GSourceFunc)
4859 gtk_xtext_render_page_timeout,
4860 buf->xtext);
4862 } else if (buf->scrollbar_down)
4864 buf->old_value = buf->num_lines - buf->xtext->adj->page_size;
4865 if (buf->old_value < 0)
4866 buf->old_value = 0;
4870 /* the main two public functions */
4872 void
4873 gtk_xtext_append_indent (xtext_buffer *buf,
4874 unsigned char *left_text, int left_len,
4875 unsigned char *right_text, int right_len,
4876 time_t stamp)
4878 textentry *ent;
4879 unsigned char *str;
4880 int space;
4881 int tempindent;
4882 int left_width;
4884 if (left_len == -1)
4885 left_len = strlen (left_text);
4887 if (right_len == -1)
4888 right_len = strlen (right_text);
4890 if (right_len >= sizeof (buf->xtext->scratch_buffer))
4891 right_len = sizeof (buf->xtext->scratch_buffer) - 1;
4893 if (right_text[right_len-1] == '\n')
4894 right_len--;
4896 ent = malloc (left_len + right_len + 2 + sizeof (textentry));
4897 str = (unsigned char *) ent + sizeof (textentry);
4899 memcpy (str, left_text, left_len);
4900 str[left_len] = ' ';
4901 memcpy (str + left_len + 1, right_text, right_len);
4902 str[left_len + 1 + right_len] = 0;
4904 left_width = gtk_xtext_text_width (buf->xtext, left_text, left_len, NULL);
4906 ent->left_len = left_len;
4907 ent->str = str;
4908 ent->str_len = left_len + 1 + right_len;
4909 ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
4911 if (buf->time_stamp)
4912 space = buf->xtext->stamp_width;
4913 else
4914 space = 0;
4916 /* do we need to auto adjust the separator position? */
4917 if (buf->xtext->auto_indent && ent->indent < MARGIN + space)
4919 tempindent = MARGIN + space + buf->xtext->space_width + left_width;
4921 if (tempindent > buf->indent)
4922 buf->indent = tempindent;
4924 if (buf->indent > buf->xtext->max_auto_indent)
4925 buf->indent = buf->xtext->max_auto_indent;
4927 gtk_xtext_fix_indent (buf);
4928 gtk_xtext_recalc_widths (buf, FALSE);
4930 ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
4931 buf->xtext->force_render = TRUE;
4934 gtk_xtext_append_entry (buf, ent, stamp);
4937 void
4938 gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len)
4940 textentry *ent;
4942 if (len == -1)
4943 len = strlen (text);
4945 if (text[len-1] == '\n')
4946 len--;
4948 if (len >= sizeof (buf->xtext->scratch_buffer))
4949 len = sizeof (buf->xtext->scratch_buffer) - 1;
4951 ent = malloc (len + 1 + sizeof (textentry));
4952 ent->str = (unsigned char *) ent + sizeof (textentry);
4953 ent->str_len = len;
4954 if (len)
4955 memcpy (ent->str, text, len);
4956 ent->str[len] = 0;
4957 ent->indent = 0;
4958 ent->left_len = -1;
4960 gtk_xtext_append_entry (buf, ent, 0);
4963 gboolean
4964 gtk_xtext_is_empty (xtext_buffer *buf)
4966 return buf->text_first == NULL;
4970 gtk_xtext_lastlog (xtext_buffer *out, xtext_buffer *search_area,
4971 int (*cmp_func) (char *, void *), void *userdata)
4973 textentry *ent = search_area->text_first;
4974 int matches = 0;
4976 while (ent)
4978 if (cmp_func (ent->str, userdata))
4980 matches++;
4981 /* copy the text over */
4982 if (search_area->xtext->auto_indent)
4983 gtk_xtext_append_indent (out, ent->str, ent->left_len,
4984 ent->str + ent->left_len + 1,
4985 ent->str_len - ent->left_len - 1, 0);
4986 else
4987 gtk_xtext_append (out, ent->str, ent->str_len);
4988 /* copy the timestamp over */
4989 out->text_last->stamp = ent->stamp;
4991 ent = ent->next;
4994 return matches;
4997 void
4998 gtk_xtext_foreach (xtext_buffer *buf, GtkXTextForeach func, void *data)
5000 textentry *ent = buf->text_first;
5002 while (ent)
5004 (*func) (buf->xtext, ent->str, data);
5005 ent = ent->next;
5009 void
5010 gtk_xtext_set_error_function (GtkXText *xtext, void (*error_function) (int))
5012 xtext->error_function = error_function;
5015 void
5016 gtk_xtext_set_indent (GtkXText *xtext, gboolean indent)
5018 xtext->auto_indent = indent;
5021 void
5022 gtk_xtext_set_max_indent (GtkXText *xtext, int max_auto_indent)
5024 xtext->max_auto_indent = max_auto_indent;
5027 void
5028 gtk_xtext_set_max_lines (GtkXText *xtext, int max_lines)
5030 xtext->max_lines = max_lines;
5033 void
5034 gtk_xtext_set_show_marker (GtkXText *xtext, gboolean show_marker)
5036 xtext->marker = show_marker;
5039 void
5040 gtk_xtext_set_show_separator (GtkXText *xtext, gboolean show_separator)
5042 xtext->separator = show_separator;
5045 void
5046 gtk_xtext_set_thin_separator (GtkXText *xtext, gboolean thin_separator)
5048 xtext->thinline = thin_separator;
5051 void
5052 gtk_xtext_set_time_stamp (xtext_buffer *buf, gboolean time_stamp)
5054 buf->time_stamp = time_stamp;
5057 void
5058 gtk_xtext_set_tint (GtkXText *xtext, int tint_red, int tint_green, int tint_blue)
5060 xtext->tint_red = tint_red;
5061 xtext->tint_green = tint_green;
5062 xtext->tint_blue = tint_blue;
5064 /*if (xtext->tint_red != 255 || xtext->tint_green != 255 || xtext->tint_blue != 255)
5065 shaded = TRUE;*/
5068 void
5069 gtk_xtext_set_urlcheck_function (GtkXText *xtext, int (*urlcheck_function) (GtkWidget *, char *, int))
5071 xtext->urlcheck_function = urlcheck_function;
5074 void
5075 gtk_xtext_set_wordwrap (GtkXText *xtext, gboolean wordwrap)
5077 xtext->wordwrap = wordwrap;
5080 void
5081 gtk_xtext_reset_marker_pos (GtkXText *xtext)
5083 xtext->buffer->marker_pos = NULL;
5084 dontscroll (xtext->buffer); /* force scrolling off */
5085 gtk_xtext_render_page (xtext);
5086 xtext->buffer->reset_marker_pos = TRUE;
5089 void
5090 gtk_xtext_buffer_show (GtkXText *xtext, xtext_buffer *buf, int render)
5092 int w, h;
5094 buf->xtext = xtext;
5096 if (xtext->buffer == buf)
5097 return;
5099 /*printf("text_buffer_show: xtext=%p buffer=%p\n", xtext, buf);*/
5101 if (xtext->add_io_tag)
5103 g_source_remove (xtext->add_io_tag);
5104 xtext->add_io_tag = 0;
5107 if (xtext->io_tag)
5109 g_source_remove (xtext->io_tag);
5110 xtext->io_tag = 0;
5113 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (xtext)))
5114 gtk_widget_realize (GTK_WIDGET (xtext));
5116 gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &w, &h);
5118 /* after a font change */
5119 if (buf->needs_recalc)
5121 buf->needs_recalc = FALSE;
5122 gtk_xtext_recalc_widths (buf, TRUE);
5125 /* now change to the new buffer */
5126 xtext->buffer = buf;
5127 dontscroll (buf); /* force scrolling off */
5128 xtext->adj->value = buf->old_value;
5129 xtext->adj->upper = buf->num_lines;
5130 if (xtext->adj->upper == 0)
5131 xtext->adj->upper = 1;
5132 /* sanity check */
5133 else if (xtext->adj->value > xtext->adj->upper - xtext->adj->page_size)
5135 /*buf->pagetop_ent = NULL;*/
5136 xtext->adj->value = xtext->adj->upper - xtext->adj->page_size;
5137 if (xtext->adj->value < 0)
5138 xtext->adj->value = 0;
5141 if (render)
5143 /* did the window change size since this buffer was last shown? */
5144 if (buf->window_width != w)
5146 buf->window_width = w;
5147 gtk_xtext_calc_lines (buf, FALSE);
5148 if (buf->scrollbar_down)
5149 gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
5150 xtext->adj->page_size);
5151 } else if (buf->window_height != h)
5153 buf->window_height = h;
5154 buf->pagetop_ent = NULL;
5155 gtk_xtext_adjustment_set (buf, FALSE);
5158 gtk_xtext_render_page (xtext);
5159 gtk_adjustment_changed (xtext->adj);
5160 } else
5162 /* avoid redoing the transparency */
5163 xtext->avoid_trans = TRUE;
5167 xtext_buffer *
5168 gtk_xtext_buffer_new (GtkXText *xtext)
5170 xtext_buffer *buf;
5172 buf = malloc (sizeof (xtext_buffer));
5173 memset (buf, 0, sizeof (xtext_buffer));
5174 buf->old_value = -1;
5175 buf->xtext = xtext;
5176 buf->scrollbar_down = TRUE;
5177 buf->indent = xtext->space_width * 2;
5178 dontscroll (buf);
5180 return buf;
5183 void
5184 gtk_xtext_buffer_free (xtext_buffer *buf)
5186 textentry *ent, *next;
5188 if (buf->xtext->buffer == buf)
5189 buf->xtext->buffer = buf->xtext->orig_buffer;
5191 if (buf->xtext->selection_buffer == buf)
5192 buf->xtext->selection_buffer = NULL;
5194 ent = buf->text_first;
5195 while (ent)
5197 next = ent->next;
5198 free (ent);
5199 ent = next;
5202 free (buf);