Added SILC Thread Queue API
[crypto.git] / apps / silcer / src / xtext.c
1 /* X-Chat
2  * Copyright (C) 1998 Peter Zelezny.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16  * =========================================================================
17  *
18  * xtext, the text widget used by X-Chat.
19  *
20  * By Peter Zelezny <zed@linux.com>.
21  * Some functions used from Zvt and Eterm (transparency stuff).
22  *
23  */
24
25 #define USE_XLIB                                                /* turn this ON for non-xchat use. */
26 #undef XCHAT                                                    /* using xchat */
27 #define REFRESH_TIMEOUT 20
28 #define WORDWRAP_LIMIT 24
29 #define TINT_VALUE 195                          /* 195/255 of the brightness. */
30 #define MOTION_MONITOR 1                        /* URL hilights. */
31 #define MARGIN 2                                                /* dont touch. */
32
33 #include <config.h>                     /* can define USE_XLIB here */
34 #include <string.h>
35 #include <ctype.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <time.h>
39 #include <gtk/gtkmain.h>
40 #include <gtk/gtksignal.h>
41 #include <gtk/gtkselection.h>
42
43 #ifdef USE_XLIB
44 #include <gdk/gdkx.h>
45 #include <X11/Xlib.h>
46 #include <X11/Xatom.h>
47 #endif
48
49 #include "xtext.h"
50
51 #ifdef USE_GDK_PIXBUF
52 #include <gdk-pixbuf/gdk-pixbuf.h>
53 #endif
54
55 #undef GTK_WIDGET
56 #define GTK_WIDGET(n) ((GtkWidget*)n)
57 #undef GTK_OBJECT
58 #define GTK_OBJECT(n) ((GtkObject*)n)
59 #undef GTK_OBJECT_CLASS
60 #define GTK_OBJECT_CLASS(n) ((GtkObjectClass*)n)
61
62 static GtkWidgetClass *parent_class = NULL;
63
64 enum
65 {
66         WORD_CLICK,
67         LAST_SIGNAL
68 };
69 static guint xtext_signals[LAST_SIGNAL] = { 0 };
70
71 #ifdef XCHAT
72 char *nocasestrstr (char *text, char *tofind);  /* util.c */
73 #endif
74 static void gtk_xtext_render_page (GtkXText * xtext);
75 static void gtk_xtext_calc_lines (GtkXText * xtext, int);
76 #ifdef USE_XLIB
77 static void gtk_xtext_load_trans (GtkXText * xtext);
78 static void gtk_xtext_free_trans (GtkXText * xtext);
79 #endif
80 static textentry *gtk_xtext_nth (GtkXText * xtext, textentry * start_ent,
81                                                                                         int line, int width, int *subline);
82 static gint gtk_xtext_selection_kill (GtkWidget * widget,
83                                                                                                   GdkEventSelection * event);
84 static void gtk_xtext_selection_get (GtkWidget * widget,
85                                                                                                  GtkSelectionData * selection_data_ptr,
86                                                                                                  guint info, guint time);
87 static int gtk_xtext_text_width (GtkXText * xtext, unsigned char *text,
88                                                                                         int len);
89 static void gtk_xtext_adjustment_changed (GtkAdjustment * adj,
90                                                                                                                 GtkXText * xtext);
91 static void gtk_xtext_draw_sep (GtkXText * xtext, int height);
92 static void gtk_xtext_render_ents (GtkXText * xtext, textentry *, textentry *,
93                                                                                           int);
94 static void gtk_xtext_recalc_widths (GtkXText * xtext, int);
95 static void gtk_xtext_fix_indent (GtkXText * xtext);
96
97 /* some utility functions first */
98
99 #ifndef XCHAT   /* xchat has this in util.c */
100
101 static char *
102 nocasestrstr (char *s, char *wanted)
103 {
104    register const size_t len = strlen (wanted);
105
106    if (len == 0)
107      return (char *)s;
108    while (toupper(*s) != toupper(*wanted) || strncasecmp (s, wanted, len))
109      if (*s++ == '\0')
110        return (char *)NULL;
111    return (char *)s;   
112 }
113
114 #endif
115
116 static int
117 is_del (char c)
118 {
119         switch (c)
120         {
121         case ' ':
122         case 0:
123         case '\n':
124                 /*case '[':
125                    case ']': */
126         case ')':
127         case '(':
128         case '>':
129         case '<':
130                 return 1;
131         }
132         return 0;
133 }
134
135 static void
136 xtext_set_fg (GdkGC *gc, gulong pixel)
137 {
138         GdkColor col;
139
140         col.pixel = pixel;
141         gdk_gc_set_foreground (gc, &col);
142 }
143
144 static void
145 xtext_set_bg (GdkGC *gc, gulong pixel)
146 {
147         GdkColor col;
148
149         col.pixel = pixel;
150         gdk_gc_set_background (gc, &col);
151 }
152
153 static void
154 gtk_xtext_init (GtkXText * xtext)
155 {
156         xtext->old_value = -1;
157         xtext->pixmap = NULL;
158         xtext->text_first = NULL;
159         xtext->text_last = NULL;
160         xtext->io_tag = -1;
161         xtext->add_io_tag = -1;
162         xtext->scroll_tag = -1;
163 /*   xtext->frozen = 0;*/
164         xtext->num_lines = 0;
165         xtext->max_lines = 0;
166         xtext->col_back = 19;
167         xtext->col_fore = 18;
168         xtext->nc = 0;
169         xtext->scrollbar_down = TRUE;
170         xtext->bold = FALSE;
171         xtext->underline = FALSE;
172         xtext->reverse = FALSE;
173         xtext->time_stamp = FALSE;
174         xtext->font = NULL;
175         xtext->error_function = NULL;
176         xtext->urlcheck_function = NULL;
177         xtext->color_paste = FALSE;
178         xtext->skip_fills = FALSE;
179         xtext->skip_border_fills = FALSE;
180         xtext->do_underline_fills_only = FALSE;
181         xtext->tint_red = xtext->tint_green = xtext->tint_blue = TINT_VALUE;
182
183         xtext->adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 0, 1, 0, 0);
184         gtk_object_ref ((GtkObject *) xtext->adj);
185         gtk_object_sink ((GtkObject *) xtext->adj);
186
187         gtk_signal_connect (GTK_OBJECT (xtext->adj), "value_changed",
188                                                           GTK_SIGNAL_FUNC (gtk_xtext_adjustment_changed), xtext);
189         gtk_signal_connect (GTK_OBJECT (xtext), "selection_clear_event",
190                                                           GTK_SIGNAL_FUNC (gtk_xtext_selection_kill), xtext);
191         gtk_selection_add_target (GTK_WIDGET (xtext),
192                                                                           GDK_SELECTION_PRIMARY,
193                                                                           GDK_SELECTION_TYPE_STRING, 1);
194         gtk_signal_connect (GTK_OBJECT (xtext), "selection_get",
195                                                           GTK_SIGNAL_FUNC (gtk_xtext_selection_get), xtext);
196 }
197
198 static void
199 gtk_xtext_adjustment_set (GtkXText * xtext, int fire_signal)
200 {
201         GtkAdjustment *adj = xtext->adj;
202
203         adj->lower = 0;
204         adj->upper = xtext->num_lines;
205
206         adj->page_size =
207                 (GTK_WIDGET (xtext)->allocation.height -
208                  xtext->font->descent) / xtext->fontsize;
209         adj->page_increment = adj->page_size;
210
211         if (adj->value > adj->upper - adj->page_size)
212                 adj->value = adj->upper - adj->page_size;
213
214         if (fire_signal)
215                 gtk_adjustment_changed (adj);
216 }
217
218 static gint
219 gtk_xtext_adjustment_timeout (GtkXText * xtext)
220 {
221         gtk_xtext_render_page (xtext);
222         xtext->io_tag = -1;
223         return 0;
224 }
225
226 static void
227 gtk_xtext_adjustment_changed (GtkAdjustment * adj, GtkXText * xtext)
228 {
229 /*   if (xtext->frozen)
230       return;*/
231
232         if ((int) xtext->old_value != (int) xtext->adj->value)
233         {
234                 if (xtext->adj->value >= xtext->adj->upper - xtext->adj->page_size)
235                         xtext->scrollbar_down = TRUE;
236                 else
237                         xtext->scrollbar_down = FALSE;
238
239                 if (xtext->adj->value + 1 == xtext->old_value ||
240                          xtext->adj->value - 1 == xtext->old_value)     /* clicked an arrow? */
241                 {
242                         if (xtext->io_tag != -1)
243                         {
244                                 gtk_timeout_remove (xtext->io_tag);
245                                 xtext->io_tag = -1;
246                         }
247                         gtk_xtext_render_page (xtext);
248                 } else
249                 {
250                         if (xtext->io_tag == -1)
251                                 xtext->io_tag = gtk_timeout_add (REFRESH_TIMEOUT,
252                                                                                                                         (GtkFunction)
253                                                                                                                         gtk_xtext_adjustment_timeout,
254                                                                                                                         xtext);
255                 }
256         }
257         xtext->old_value = adj->value;
258 }
259
260 GtkWidget *
261 gtk_xtext_new (int indent, int separator)
262 {
263         GtkXText *xtext;
264
265         xtext = gtk_type_new (gtk_xtext_get_type ());
266         xtext->indent = indent;
267         xtext->separator = separator;
268         xtext->wordwrap = FALSE;
269         xtext->double_buffer = FALSE;
270
271         return GTK_WIDGET (xtext);
272 }
273
274 static void
275 gtk_xtext_destroy (GtkObject * object)
276 {
277         GtkXText *xtext = GTK_XTEXT (object);
278         textentry *ent, *next;
279
280         if (xtext->add_io_tag != -1)
281         {
282                 gtk_timeout_remove (xtext->add_io_tag);
283                 xtext->add_io_tag = -1;
284         }
285
286         if (xtext->scroll_tag != -1)
287         {
288                 gtk_timeout_remove (xtext->scroll_tag);
289                 xtext->scroll_tag = -1;
290         }
291
292         if (xtext->io_tag != -1)
293         {
294                 gtk_timeout_remove (xtext->io_tag);
295                 xtext->io_tag = -1;
296         }
297
298         if (xtext->pixmap)
299         {
300 #ifdef USE_XLIB
301                 if (xtext->transparent)
302                         gtk_xtext_free_trans (xtext);
303                 else
304 #endif
305                         gdk_pixmap_unref (xtext->pixmap);
306                 xtext->pixmap = NULL;
307         }
308
309         if (xtext->font)
310         {
311                 gdk_font_unref (xtext->font);
312                 xtext->font = NULL;
313         }
314
315         if (xtext->adj)
316         {
317                 gtk_signal_disconnect_by_data (GTK_OBJECT (xtext->adj), xtext);
318                 gtk_object_unref (GTK_OBJECT (xtext->adj));
319                 xtext->adj = NULL;
320         }
321
322         if (xtext->bgc)
323         {
324                 gdk_gc_destroy (xtext->bgc);
325                 xtext->bgc = NULL;
326         }
327
328         if (xtext->fgc)
329         {
330                 gdk_gc_destroy (xtext->fgc);
331                 xtext->fgc = NULL;
332         }
333
334         if (xtext->light_gc)
335         {
336                 gdk_gc_destroy (xtext->light_gc);
337                 xtext->light_gc = NULL;
338         }
339
340         if (xtext->dark_gc)
341         {
342                 gdk_gc_destroy (xtext->dark_gc);
343                 xtext->dark_gc = NULL;
344         }
345
346         if (xtext->hand_cursor)
347         {
348                 gdk_cursor_destroy (xtext->hand_cursor);
349                 xtext->hand_cursor = NULL;
350         }
351
352         ent = xtext->text_first;
353         while (ent)
354         {
355                 next = ent->next;
356                 free (ent);
357                 ent = next;
358         }
359         xtext->text_first = NULL;
360
361         if (GTK_OBJECT_CLASS (parent_class)->destroy)
362                 (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
363 }
364
365 static void
366 gtk_xtext_realize (GtkWidget * widget)
367 {
368         GtkXText *xtext;
369         GdkWindowAttr attributes;
370         GdkGCValues val;
371         GdkColor col;
372         GdkColormap *cmap;
373
374         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
375         xtext = GTK_XTEXT (widget);
376
377         attributes.x = widget->allocation.x;
378         attributes.y = widget->allocation.y;
379         attributes.width = widget->allocation.width;
380         attributes.height = widget->allocation.height;
381         attributes.wclass = GDK_INPUT_OUTPUT;
382         attributes.window_type = GDK_WINDOW_CHILD;
383         attributes.event_mask = gtk_widget_get_events (widget) |
384                 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
385 #ifdef MOTION_MONITOR
386                 | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK;
387 #else
388                 | GDK_POINTER_MOTION_MASK;
389 #endif
390
391         cmap = gtk_widget_get_colormap (widget);
392         attributes.colormap = cmap;
393         attributes.visual = gtk_widget_get_visual (widget);
394
395         widget->window = gdk_window_new (widget->parent->window, &attributes,
396                                                                                                 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL |
397                                                                                                 GDK_WA_COLORMAP);
398
399         gdk_window_set_user_data (widget->window, widget);
400
401         xtext->depth = gdk_window_get_visual (widget->window)->depth;
402
403         val.subwindow_mode = GDK_INCLUDE_INFERIORS;
404         val.graphics_exposures = 0;
405
406         xtext->bgc = gdk_gc_new_with_values (widget->window, &val,
407                                                                                                          GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
408         xtext->fgc = gdk_gc_new_with_values (widget->window, &val,
409                                                                                                          GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
410         xtext->light_gc = gdk_gc_new_with_values (widget->window, &val,
411                                                                                         GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
412         xtext->dark_gc = gdk_gc_new_with_values (widget->window, &val,
413                                                                                         GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
414
415         /* for the separator bar (light) */
416         col.red = 0xffff; col.green = 0xffff; col.blue = 0xffff;
417         /* is setting the pixel necessary (or even correct) ?? */
418         col.pixel = (gulong)((col.red & 0xff00) * 256 +
419                                                                 (col.green & 0xff00) +
420                                                                 (col.blue & 0xff00) / 256);
421         gdk_color_alloc (cmap, &col);
422         gdk_gc_set_foreground (xtext->light_gc, &col);
423
424         /* for the separator bar (dark) */
425         col.red = 0x8e38; col.green = 0x8e38; col.blue = 0x9f38;
426         col.pixel = (gulong)((col.red & 0xff00) * 256 +
427                                                                 (col.green & 0xff00) +
428                                                                 (col.blue & 0xff00) / 256);
429         gdk_color_alloc (cmap, &col);
430         gdk_gc_set_foreground (xtext->dark_gc, &col);
431
432         if (xtext->fonttype != FONT_SET && xtext->font != NULL)
433                 gdk_gc_set_font (xtext->fgc, xtext->font);
434
435         xtext_set_fg (xtext->fgc, xtext->palette[18]);
436         xtext_set_bg (xtext->fgc, xtext->palette[19]);
437         xtext_set_fg (xtext->bgc, xtext->palette[19]);
438
439 #ifdef USE_XLIB
440         if (xtext->transparent)
441         {
442                 gtk_xtext_load_trans (xtext);
443         } else if (xtext->pixmap)
444         {
445                 gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
446                 gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
447                 gdk_gc_set_fill (xtext->bgc, GDK_TILED);
448         }
449 #else
450         if (xtext->pixmap)
451         {
452                 gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
453                 gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
454                 gdk_gc_set_fill (xtext->bgc, GDK_TILED);
455         }
456 #endif
457
458         xtext->hand_cursor = gdk_cursor_new (GDK_HAND1);
459
460         gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
461
462         /* if not doublebuffer, draw directly to window */
463         if (!xtext->double_buffer)
464                 xtext->draw_buf = widget->window;
465
466         if (xtext->auto_indent)
467                 xtext->indent = 1;
468 }
469
470 static void
471 gtk_xtext_size_request (GtkWidget * widget, GtkRequisition * requisition)
472 {
473         requisition->width = GTK_XTEXT (widget)->fontwidth['Z'] * 20;
474         requisition->height = (GTK_XTEXT (widget)->fontsize * 10) + 3;
475 }
476
477 static void
478 gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
479 {
480         GtkXText *xtext = GTK_XTEXT (widget);
481
482         if (allocation->width == widget->allocation.width &&
483                  allocation->height == widget->allocation.height &&
484                  allocation->x == widget->allocation.x &&
485                  allocation->y == widget->allocation.y)
486                 return;
487
488         widget->allocation = *allocation;
489         if (GTK_WIDGET_REALIZED (widget))
490         {
491                 gdk_window_move_resize (widget->window,
492                                                                                 allocation->x, allocation->y,
493                                                                                 allocation->width, allocation->height);
494                 gtk_xtext_calc_lines (xtext, FALSE);
495         }
496 }
497
498 static void
499 gtk_xtext_draw (GtkWidget * widget, GdkRectangle * area)
500 {
501         int x, y;
502         GtkXText *xtext = GTK_XTEXT (widget);
503
504 #ifdef USE_XLIB
505         if (xtext->transparent)
506         {
507                 gdk_window_get_origin (widget->window, &x, &y);
508                 /* update transparency only if it moved */
509                 if (xtext->last_win_x != x || xtext->last_win_y != y)
510                 {
511                         xtext->last_win_x = x;
512                         xtext->last_win_y = y;
513                         gtk_xtext_free_trans (xtext);
514                         gtk_xtext_load_trans (xtext);
515                 }
516         }
517 #endif
518
519         if (xtext->scrollbar_down)
520                 gtk_adjustment_set_value (xtext->adj,
521                                                                                         xtext->adj->upper - xtext->adj->page_size);
522         gtk_xtext_render_page (xtext);
523 }
524
525 static int
526 gtk_xtext_selection_clear (GtkXText * xtext)
527 {
528         textentry *ent;
529         int ret = 0;
530
531         ent = xtext->last_ent_start;
532         while (ent)
533         {
534                 if (ent->mark_start != -1)
535                         ret = 1;
536                 ent->mark_start = -1;
537                 ent->mark_end = -1;
538                 if (ent == xtext->last_ent_end)
539                         break;
540                 ent = ent->next;
541         }
542
543         return ret;
544 }
545
546 static int
547 find_x_8bit (GtkXText *xtext, textentry *ent, char *text, int x, int indent)
548 {
549         int xx = indent;
550         int i = 0;
551         int col = FALSE;
552         int nc = 0;
553         char *orig = text;
554         int a;
555
556         while (*text)
557         {
558                 if ((col && isdigit (*text) && nc < 2) ||
559                          (col && *text == ',' && nc < 3))
560                 {
561                         nc++;
562                         if (*text == ',')
563                                 nc = 0;
564                 } else
565                 {
566                         col = FALSE;
567                         switch (*text)
568                         {
569                         case ATTR_COLOR:
570                                 col = TRUE;
571                                 nc = 0;
572                                 break;
573                         case ATTR_BEEP:
574                         case ATTR_RESET:
575                         case ATTR_REVERSE:
576                         case ATTR_BOLD:
577                         case ATTR_UNDERLINE:
578                                 break;
579                         default:
580                                 a = *((unsigned char *)text);
581                                 xx += xtext->fontwidth[a];
582                                 if (xx >= x)
583                                         return i + (orig - ent->str);
584                         }
585                 }
586                 text++;
587                 i++;
588                 if (text - orig >= ent->str_len)
589                         return ent->str_len;
590         }
591
592         return ent->str_len;
593 }
594
595 static int
596 find_x_general (GtkXText * xtext, textentry * ent, char *str, int x, int indent)
597 {
598         int str_width;
599         int len = 1;
600
601         while (1)
602         {
603                 str_width = gtk_xtext_text_width (xtext, str, len);
604                 if (str_width + indent >= x)
605                         return (str + len) - ent->str;
606                 len++;
607                 if (len + (str - ent->str) > ent->str_len)
608                         return ent->str_len;
609                 if (str_width + indent + 40 < x)
610                         len += 2;
611         }
612 }
613
614 static int
615 find_x (GtkXText * xtext, textentry * ent, char *str, int x, int indent)
616 {
617         if (xtext->fonttype == FONT_1BYTE)
618                 return find_x_8bit (xtext, ent, str, x, indent);
619
620         return find_x_general (xtext, ent, str, x, indent);
621 }
622
623 static int
624 gtk_xtext_find_x (GtkXText * xtext, int x, textentry * ent, int offset,
625                                                 int line, int win_width, int *out_of_bounds)
626 {
627         int indent;
628         char *str;
629
630         if (offset < 1)
631                 indent = ent->indent;
632         else
633                 indent = xtext->indent;
634
635         if (line > xtext->adj->page_size || line < 0)
636                 return 0;
637
638         if (xtext->grid_offset[line] > ent->str_len)
639                 return 0;
640
641         if (xtext->grid_offset[line] < 0)
642                 return 0;
643
644         str = ent->str + xtext->grid_offset[line];
645
646         if (x < indent)
647         {
648                 *out_of_bounds = 1;
649                 return (str - ent->str);
650         }
651
652         *out_of_bounds = 0;
653
654         return find_x (xtext, ent, str, x, indent);
655 }
656
657 static textentry *
658 gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off,
659                                                         int *out_of_bounds)
660 {
661         textentry *ent;
662         int line;
663         int subline;
664         int win_width;
665
666         gdk_window_get_size (GTK_WIDGET (xtext)->window, &win_width, 0);
667         win_width -= MARGIN;
668
669         line = (y - xtext->font->descent) / xtext->fontsize;
670
671         subline = xtext->pagetop_subline;
672         ent = gtk_xtext_nth (xtext, xtext->pagetop_ent, line, win_width, &subline);
673         if (!ent)
674                 return 0;
675
676         if (off)
677                 *off = gtk_xtext_find_x (xtext, x, ent, subline, line, win_width,
678                                                                 out_of_bounds);
679
680         return ent;
681 }
682
683 static gint
684 gtk_xtext_expose (GtkWidget * widget, GdkEventExpose * event)
685 {
686         GtkXText *xtext = GTK_XTEXT (widget);
687         textentry *ent_start, *ent_end;
688
689         if (xtext->double_buffer)
690         {
691                 gtk_xtext_render_page (xtext);
692                 return FALSE;
693         }
694
695         gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1,
696                                                           event->area.x, event->area.y,
697                                                           event->area.width, event->area.height);
698
699         ent_start = gtk_xtext_find_char (xtext, event->area.x, event->area.y,
700                                                                                                 NULL, NULL);
701         ent_end = gtk_xtext_find_char (xtext, event->area.x + event->area.width,
702                                                 event->area.y + event->area.height, NULL, NULL);
703
704         xtext->skip_fills = TRUE;
705         xtext->skip_border_fills = TRUE;
706
707         gtk_xtext_render_ents (xtext, ent_start, ent_end, TRUE);
708
709         xtext->skip_fills = FALSE;
710         xtext->skip_border_fills = FALSE;
711
712         return FALSE;
713 }
714
715 static void
716 gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event)
717 {
718         textentry *ent;
719         textentry *ent_end;
720         textentry *ent_start;
721         int offset_start;
722         int offset_end;
723         int low_x;
724         int low_y;
725         int high_x;
726         int high_y;
727         int tmp;
728
729         if (xtext->select_start_y > xtext->select_end_y)
730         {
731                 low_x = xtext->select_end_x;
732                 low_y = xtext->select_end_y;
733                 high_x = xtext->select_start_x;
734                 high_y = xtext->select_start_y;
735         } else
736         {
737                 low_x = xtext->select_start_x;
738                 low_y = xtext->select_start_y;
739                 high_x = xtext->select_end_x;
740                 high_y = xtext->select_end_y;
741         }
742
743         ent_start = gtk_xtext_find_char (xtext, low_x, low_y, &offset_start, &tmp);
744         ent_end = gtk_xtext_find_char (xtext, high_x, high_y, &offset_end, &tmp);
745
746         if (ent_start && !ent_end)
747         {
748                 ent_end = xtext->text_last;
749                 offset_end = ent_end->str_len;
750         }
751
752         if (!ent_start || !ent_end)
753         {
754                 if (xtext->adj->value != xtext->old_value)
755                         gtk_xtext_render_page (xtext);
756                 return;
757         }
758
759         gtk_xtext_selection_clear (xtext);
760
761         /* marking less than a complete line? */
762         if (ent_start == ent_end)
763         {
764                 ent_start->mark_start = MIN (offset_start, offset_end);
765                 ent_start->mark_end = MAX (offset_end, offset_start);
766                 if (offset_start == offset_end)
767                         ent_start->mark_end++;
768         } else
769         {
770                 ent_start->mark_start = offset_start;
771                 ent_start->mark_end = ent_start->str_len;
772
773                 if (offset_end != 0)
774                 {
775                         ent_end->mark_start = 0;
776                         ent_end->mark_end = offset_end;
777                 }
778         }
779
780         if (ent_start != ent_end)
781         {
782                 ent = ent_start->next;
783                 while (ent && ent != ent_end)
784                 {
785                         ent->mark_start = 0;
786                         ent->mark_end = ent->str_len;
787                         ent = ent->next;
788                 }
789         }
790
791         /* has the selection changed? Dont render unless necessary */
792         if (xtext->last_ent_start == ent_start &&
793                  xtext->last_ent_end == ent_end &&
794                  xtext->last_offset_start == offset_start &&
795                  xtext->last_offset_end == offset_end)
796                 return;
797
798         gtk_selection_owner_set (GTK_WIDGET (xtext), GDK_SELECTION_PRIMARY,
799                                                                          event->time);
800
801         if (xtext->double_buffer)
802         {
803                 if (xtext->io_tag == -1)
804                         xtext->io_tag = gtk_timeout_add (REFRESH_TIMEOUT,
805                                                                                                                 (GtkFunction)
806                                                                                                                 gtk_xtext_adjustment_timeout,
807                                                                                                                 xtext);
808         } else
809         {
810                 ent = xtext->last_ent_end;
811                 if (ent)
812                         if (ent->next == ent_end)
813                                 ent = ent_end;
814                 xtext->skip_border_fills = TRUE;
815                 gtk_xtext_render_ents (xtext, xtext->last_ent_start, ent, TRUE);
816                 xtext->skip_border_fills = FALSE;
817                 xtext->old_ent_start = xtext->last_ent_start;
818                 xtext->old_ent_end = xtext->last_ent_end;
819         }
820
821         xtext->last_ent_start = ent_start;
822         xtext->last_ent_end = ent_end;
823         xtext->last_offset_start = offset_start;
824         xtext->last_offset_end = offset_end;
825 }
826
827 static gint
828 gtk_xtext_scrolldown_timeout (GtkXText * xtext)
829 {
830         int p_y, win_height;
831
832         gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
833         gdk_window_get_size (GTK_WIDGET (xtext)->window, 0, &win_height);
834
835         if (p_y > win_height &&
836                  xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size))
837         {
838                 xtext->adj->value++;
839                 gtk_adjustment_changed (xtext->adj);
840                 gtk_xtext_render_page (xtext);
841                 return 1;
842         }
843
844         xtext->scroll_tag = -1;
845         return 0;
846 }
847
848 static gint
849 gtk_xtext_scrollup_timeout (GtkXText * xtext)
850 {
851         int p_y;
852
853         gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
854
855         if (p_y < 0 && xtext->adj->value > 0.0)
856         {
857                 xtext->adj->value--;
858                 gtk_adjustment_changed (xtext->adj);
859                 gtk_xtext_render_page (xtext);
860                 return 1;
861         }
862
863         xtext->scroll_tag = -1;
864         return 0;
865 }
866
867 static void
868 gtk_xtext_selection_update (GtkXText * xtext, GdkEventMotion * event, int p_y)
869 {
870         int win_height;
871         int moved;
872
873         gdk_window_get_size (GTK_WIDGET (xtext)->window, 0, &win_height);
874
875         /* selecting past top of window, scroll up! */
876         if (p_y < 0 && xtext->adj->value >= 0)
877         {
878                 if (xtext->scroll_tag == -1)
879                         xtext->scroll_tag = gtk_timeout_add (100,
880                                                                                                                          (GtkFunction)
881                                                                                                                          gtk_xtext_scrollup_timeout,
882                                                                                                                          xtext);
883                 return;
884         }
885
886         /* selecting past bottom of window, scroll down! */
887         if (p_y > win_height &&
888                  xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size))
889         {
890                 if (xtext->scroll_tag == -1)
891                         xtext->scroll_tag = gtk_timeout_add (100,
892                                                                                                                          (GtkFunction)
893                                                                                                                          gtk_xtext_scrolldown_timeout,
894                                                                                                                          xtext);
895                 return;
896         }
897
898         moved = xtext->adj->value - xtext->select_start_adj;
899         xtext->select_start_y -= (moved * xtext->fontsize);
900         xtext->select_start_adj = xtext->adj->value;
901         gtk_xtext_selection_draw (xtext, event);
902 }
903
904 static char *
905 gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
906                                                   int *ret_off, int *ret_len)
907 {
908         textentry *ent;
909         int offset;
910         char *str;
911         char *word;
912         int len;
913         int out_of_bounds;
914
915         ent = gtk_xtext_find_char (xtext, x, y, &offset, &out_of_bounds);
916         if (!ent)
917                 return 0;
918
919         if (out_of_bounds)
920                 return 0;
921
922         if (offset == ent->str_len)
923                 return 0;
924
925         if (offset < 1)
926                 return 0;
927
928         offset--;
929
930         str = ent->str + offset;
931
932         while (!is_del (*str) && str != ent->str)
933                 str--;
934         word = str + 1;
935
936         len = 0;
937         str = word;
938         while (!is_del (*str) && len != ent->str_len)
939         {
940                 str++;
941                 len++;
942         }
943
944         if (ret_ent)
945                 *ret_ent = ent;
946         if (ret_off)
947                 *ret_off = word - ent->str;
948         if (ret_len)
949                 *ret_len = str - word;
950
951         word = gtk_xtext_strip_color (word, len, NULL, NULL);
952
953         return word;
954 }
955
956 static gint
957 gtk_xtext_leave_notify (GtkWidget * widget, GdkEventCrossing * event)
958 {
959 #ifdef MOTION_MONITOR
960         GtkXText *xtext = GTK_XTEXT (widget);
961
962         if (xtext->cursor_hand)
963         {
964                 xtext->hilight_start = -1;
965                 xtext->hilight_end = -1;
966                 xtext->cursor_hand = FALSE;
967                 gdk_window_set_cursor (widget->window, 0);
968                 xtext->skip_border_fills = TRUE;
969                 xtext->do_underline_fills_only = TRUE;
970                 gtk_xtext_render_ents (xtext, xtext->hilight_ent, NULL, FALSE);
971                 xtext->skip_border_fills = FALSE;
972                 xtext->do_underline_fills_only = FALSE;
973                 xtext->hilight_ent = NULL;
974         }
975 #endif
976         return FALSE;
977 }
978
979 static gint
980 gtk_xtext_motion_notify (GtkWidget * widget, GdkEventMotion * event)
981 {
982         GtkXText *xtext = GTK_XTEXT (widget);
983         int tmp, x, y, offset, len;
984         char *word;
985         textentry *word_ent, *old_ent;
986
987         gdk_window_get_pointer (widget->window, &x, &y, 0);
988
989         if (xtext->moving_separator)
990         {
991                 if (x < (3 * widget->allocation.width) / 5 && x > 15)
992                 {
993                         tmp = xtext->indent;
994                         xtext->indent = x;
995                         gtk_xtext_fix_indent (xtext);
996                         if (tmp != xtext->indent)
997                         {
998                                 gtk_xtext_recalc_widths (xtext, FALSE);
999                                 if (xtext->scrollbar_down)
1000                                         gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
1001                                                                                                           xtext->adj->page_size);
1002                                 if (xtext->io_tag == -1)
1003                                         xtext->io_tag = gtk_timeout_add (REFRESH_TIMEOUT,
1004                                                                                                                                 (GtkFunction)
1005                                                                                                                                 gtk_xtext_adjustment_timeout,
1006                                                                                                                                 xtext);
1007                         }
1008                 }
1009                 return FALSE;
1010         }
1011
1012         if (xtext->button_down)
1013         {
1014                 gtk_grab_add (widget);
1015                 /*gdk_pointer_grab (widget->window, TRUE,
1016                                                                         GDK_BUTTON_RELEASE_MASK |
1017                                                                         GDK_BUTTON_MOTION_MASK, NULL, NULL, 0);*/
1018                 xtext->select_end_x = x;
1019                 xtext->select_end_y = y;
1020                 gtk_xtext_selection_update (xtext, event, y);
1021                 return FALSE;
1022         }
1023 #ifdef MOTION_MONITOR
1024
1025         if (xtext->urlcheck_function == NULL)
1026                 return FALSE;
1027
1028         word = gtk_xtext_get_word (xtext, x, y, &word_ent, &offset, &len);
1029         if (word)
1030         {
1031                 if (xtext->urlcheck_function (xtext, word) > 0)
1032                 {
1033                         free (word);
1034                         if (!xtext->cursor_hand ||
1035                                  xtext->hilight_ent != word_ent ||
1036                                  xtext->hilight_start != offset ||
1037                                  xtext->hilight_end != offset + len)
1038                         {
1039                                 if (!xtext->cursor_hand)
1040                                 {
1041                                         gdk_window_set_cursor (GTK_WIDGET (xtext)->window,
1042                                                                                                         xtext->hand_cursor);
1043                                         xtext->cursor_hand = TRUE;
1044                                 }
1045                                 old_ent = xtext->hilight_ent;
1046                                 xtext->hilight_ent = word_ent;
1047                                 xtext->hilight_start = offset;
1048                                 xtext->hilight_end = offset + len;
1049                                 xtext->skip_border_fills = TRUE;
1050                                 xtext->do_underline_fills_only = TRUE;
1051                                 gtk_xtext_render_ents (xtext, old_ent, word_ent, FALSE);
1052                                 xtext->skip_border_fills = FALSE;
1053                                 xtext->do_underline_fills_only = FALSE;
1054                         }
1055                         return FALSE;
1056                 }
1057                 free (word);
1058         }
1059
1060         gtk_xtext_leave_notify (widget, NULL);
1061
1062 #endif
1063
1064         return FALSE;
1065 }
1066
1067 static gint
1068 gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
1069 {
1070         GtkXText *xtext = GTK_XTEXT (widget);
1071         char *word;
1072
1073         if (xtext->moving_separator)
1074         {
1075                 xtext->moving_separator = FALSE;
1076                 if (event->x < (4 * widget->allocation.width) / 5 && event->x > 15)
1077                 {
1078                         xtext->indent = event->x;
1079                 }
1080                 gtk_xtext_fix_indent (xtext);
1081                 gtk_xtext_recalc_widths (xtext, FALSE);
1082                 gtk_xtext_adjustment_set (xtext, TRUE);
1083                 gtk_xtext_render_page (xtext);
1084                 return FALSE;
1085         }
1086
1087         if (xtext->word_or_line_select)
1088         {
1089                 xtext->word_or_line_select = FALSE;
1090                 xtext->button_down = FALSE;
1091                 return FALSE;
1092         }
1093
1094         if (event->button == 1)
1095         {
1096                 xtext->button_down = FALSE;
1097
1098                 gtk_grab_remove (widget);
1099                 /*gdk_pointer_ungrab (0);*/
1100
1101                 if (xtext->select_start_x == event->x &&
1102                          xtext->select_start_y == event->y)
1103                 {
1104                         if (gtk_xtext_selection_clear (xtext))
1105                                 gtk_xtext_render_page (xtext);
1106                 } else
1107                 {
1108                         word = gtk_xtext_get_word (xtext, event->x, event->y, 0, 0, 0);
1109                         if (word)
1110                         {
1111                                 gtk_signal_emit (GTK_OBJECT (xtext), xtext_signals[WORD_CLICK],
1112                                                                           word, event);
1113                                 free (word);
1114                                 return FALSE;
1115                         }
1116                 }
1117         }
1118
1119         return FALSE;
1120 }
1121
1122 static gint
1123 gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
1124 {
1125         GtkXText *xtext = GTK_XTEXT (widget);
1126         textentry *ent;
1127         char *word;
1128         int line_x, x, y, offset, len;
1129         gfloat new_value;
1130
1131         gdk_window_get_pointer (widget->window, &x, &y, 0);
1132
1133         if (event->button == 3)           /* right click */
1134         {
1135                 word = gtk_xtext_get_word (xtext, x, y, 0, 0, 0);
1136                 if (word)
1137                 {
1138                         gtk_signal_emit (GTK_OBJECT (xtext), xtext_signals[WORD_CLICK],
1139                                                                   word, event);
1140                         free (word);
1141                 } else
1142                         gtk_signal_emit (GTK_OBJECT (xtext), xtext_signals[WORD_CLICK],
1143                                                                   "", event);
1144                 return FALSE;
1145         }
1146
1147         if (event->button == 4)           /* mouse wheel pageUp */
1148         {
1149                 new_value = xtext->adj->value - xtext->adj->page_increment;
1150                 if (new_value < xtext->adj->lower)
1151                         new_value = xtext->adj->lower;
1152                 gtk_adjustment_set_value (xtext->adj, new_value);
1153                 return FALSE;
1154         }
1155
1156         if (event->button == 5)           /* mouse wheel pageDn */
1157         {
1158                 new_value = xtext->adj->value + xtext->adj->page_increment;
1159                 if (new_value > (xtext->adj->upper - xtext->adj->page_size))
1160                         new_value = xtext->adj->upper - xtext->adj->page_size;
1161                 gtk_adjustment_set_value (xtext->adj, new_value);
1162                 return FALSE;
1163         }
1164
1165         if (event->button == 2)
1166         {
1167                 gtk_signal_emit (GTK_OBJECT (xtext), xtext_signals[WORD_CLICK], "", event);
1168                 return FALSE;
1169         }
1170
1171         if (event->button != 1)           /* we only want left button */
1172                 return FALSE;
1173
1174         if (event->type == GDK_2BUTTON_PRESS)   /* WORD select */
1175         {
1176                 word = gtk_xtext_get_word (xtext, x, y, &ent, &offset, &len);
1177                 if (word)
1178                 {
1179                         free (word);
1180                         if (len == 0)
1181                                 return FALSE;
1182                         gtk_xtext_selection_clear (xtext);
1183                         ent->mark_start = offset;
1184                         ent->mark_end = offset + len;
1185                         xtext->last_ent_start = ent;
1186                         xtext->last_ent_end = ent;
1187                         gtk_xtext_render_page (xtext);
1188                         xtext->word_or_line_select = TRUE;
1189                         gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, event->time);
1190                 }
1191
1192                 return FALSE;
1193         }
1194
1195         if (event->type == GDK_3BUTTON_PRESS)   /* LINE select */
1196         {
1197                 word = gtk_xtext_get_word (xtext, x, y, &ent, 0, 0);
1198                 if (word)
1199                 {
1200                         free (word);
1201                         gtk_xtext_selection_clear (xtext);
1202                         ent->mark_start = 0;
1203                         ent->mark_end = ent->str_len;
1204                         xtext->last_ent_start = ent;
1205                         xtext->last_ent_end = ent;
1206                         gtk_xtext_render_page (xtext);
1207                         xtext->word_or_line_select = TRUE;
1208                         gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, event->time);
1209                 }
1210
1211                 return FALSE;
1212         }
1213
1214         /* check if it was a separator-bar click */
1215         if (xtext->separator && xtext->indent)
1216         {
1217                 line_x = xtext->indent - ((xtext->space_width + 1) / 2);
1218                 if (line_x == x || line_x == x + 1 || line_x == x - 1)
1219                 {
1220                         xtext->moving_separator = TRUE;
1221                         gtk_xtext_render_page (xtext);
1222                         return FALSE;
1223                 }
1224         }
1225
1226         xtext->button_down = TRUE;
1227
1228         xtext->select_start_x = x;
1229         xtext->select_start_y = y;
1230
1231         xtext->select_start_adj = xtext->adj->value;
1232
1233         return FALSE;
1234 }
1235
1236 /* another program has claimed the selection */
1237
1238 static gint
1239 gtk_xtext_selection_kill (GtkWidget * widget, GdkEventSelection * event)
1240 {
1241         if (gtk_xtext_selection_clear (GTK_XTEXT (widget)))
1242                 gtk_xtext_render_page (GTK_XTEXT (widget));
1243         return TRUE;
1244 }
1245
1246 /* another program is asking for our selection */
1247
1248 static void
1249 gtk_xtext_selection_get (GtkWidget * widget,
1250                                                                  GtkSelectionData * selection_data_ptr,
1251                                                                  guint info, guint time)
1252 {
1253         GtkXText *xtext = GTK_XTEXT (widget);
1254         textentry *ent;
1255         char *txt;
1256         char *pos;
1257         char *stripped;
1258         int len;
1259         int first = TRUE;
1260
1261         /* first find out how much we need to malloc ... */
1262         len = 0;
1263         ent = xtext->text_first;
1264         while (ent)
1265         {
1266                 if (ent->mark_start != -1)
1267                 {
1268                         if (ent->mark_end - ent->mark_start > 0)
1269                                 len += (ent->mark_end - ent->mark_start) + 1;
1270                         else
1271                                 len++;
1272                 }
1273                 ent = ent->next;
1274         }
1275
1276         /* now allocate mem and copy buffer */
1277         pos = txt = malloc (len);
1278         ent = xtext->text_first;
1279         while (ent)
1280         {
1281                 if (ent->mark_start != -1)
1282                 {
1283                         if (!first)
1284                         {
1285                                 *pos = '\n';
1286                                 pos++;
1287                         }
1288                         first = FALSE;
1289                         if (ent->mark_end - ent->mark_start > 0)
1290                         {
1291                                 memcpy (pos, ent->str + ent->mark_start,
1292                                                   ent->mark_end - ent->mark_start);
1293                                 pos += ent->mark_end - ent->mark_start;
1294                         }
1295                 }
1296                 ent = ent->next;
1297         }
1298         *pos = 0;
1299
1300         if (xtext->color_paste)
1301         {
1302                 gtk_selection_data_set (selection_data_ptr, GDK_SELECTION_TYPE_STRING,
1303                                                                                 8, txt, strlen (txt));
1304         } else
1305         {
1306                 stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, NULL);
1307                 gtk_selection_data_set (selection_data_ptr, GDK_SELECTION_TYPE_STRING,
1308                                                                                 8, stripped, strlen (stripped));
1309                 free (stripped);
1310         }
1311
1312         free (txt);
1313 }
1314
1315 static void
1316 gtk_xtext_class_init (GtkXTextClass * class)
1317 {
1318         GtkObjectClass *object_class;
1319         GtkWidgetClass *widget_class;
1320         GtkXTextClass *xtext_class;
1321
1322         object_class = (GtkObjectClass *) class;
1323         widget_class = (GtkWidgetClass *) class;
1324         xtext_class = (GtkXTextClass *) class;
1325
1326         parent_class = gtk_type_class (gtk_widget_get_type ());
1327
1328         xtext_signals[WORD_CLICK] =
1329                 gtk_signal_new (/*name*/"word_click",
1330                                                          /*GtkSignalRunType*/GTK_RUN_FIRST,
1331                                                          /*GtkType*/object_class->type,
1332                                                          /*funcoffset*/GTK_SIGNAL_OFFSET (GtkXTextClass, word_click),
1333                                                          /*GtkSignalMarshaller*/gtk_marshal_NONE__POINTER_POINTER,
1334                                                          /*returnval*/GTK_TYPE_NONE,
1335                                                          /*num args*/2, /*args*/GTK_TYPE_POINTER, GTK_TYPE_POINTER);
1336         gtk_object_class_add_signals (object_class, xtext_signals, LAST_SIGNAL);
1337
1338         object_class->destroy = gtk_xtext_destroy;
1339
1340         widget_class->realize = gtk_xtext_realize;
1341         widget_class->size_request = gtk_xtext_size_request;
1342         widget_class->size_allocate = gtk_xtext_size_allocate;
1343         widget_class->button_press_event = gtk_xtext_button_press;
1344         widget_class->button_release_event = gtk_xtext_button_release;
1345         widget_class->motion_notify_event = gtk_xtext_motion_notify;
1346         widget_class->leave_notify_event = gtk_xtext_leave_notify;
1347         widget_class->draw = gtk_xtext_draw;
1348         widget_class->expose_event = gtk_xtext_expose;
1349
1350         xtext_class->word_click = NULL;
1351 }
1352
1353 guint gtk_xtext_get_type ()
1354 {
1355         static guint xtext_type = 0;
1356
1357         if (!xtext_type)
1358         {
1359                 GtkTypeInfo xtext_info = {
1360                         "GtkXText",
1361                         sizeof (GtkXText),
1362                         sizeof (GtkXTextClass),
1363                         (GtkClassInitFunc) gtk_xtext_class_init,
1364                         (GtkObjectInitFunc) gtk_xtext_init,
1365                         (GtkArgSetFunc) NULL,
1366                         (GtkArgGetFunc) NULL,
1367                 };
1368
1369                 xtext_type = gtk_type_unique (gtk_widget_get_type (), &xtext_info);
1370         }
1371
1372         return xtext_type;
1373 }
1374
1375 /*void
1376 gtk_xtext_thaw (GtkXText *xtext)
1377 {
1378    if (xtext->frozen > 0)
1379       xtext->frozen--;
1380
1381    if (xtext->frozen == 0)
1382       gtk_xtext_render_page (xtext);
1383 }
1384
1385 void
1386 gtk_xtext_freeze (GtkXText *xtext)
1387 {
1388    xtext->frozen++;
1389 }*/
1390
1391 /* strip MIRC colors and other attribs. */
1392
1393 char *
1394 gtk_xtext_strip_color (unsigned char *text, int len, char *outbuf, int *newlen)
1395 {
1396         int nc = 0;
1397         int i = 0;
1398         int col = FALSE;
1399         char *new_str;
1400
1401         if (outbuf == NULL)
1402                 new_str = malloc (len + 2);
1403         else
1404                 new_str = outbuf;
1405
1406         while (len > 0)
1407         {
1408                 if ((col && isdigit (*text) && nc < 2) ||
1409                          (col && *text == ',' && nc < 3))
1410                 {
1411                         nc++;
1412                         if (*text == ',')
1413                                 nc = 0;
1414                 } else
1415                 {
1416                         if (col)
1417                                 col = FALSE;
1418                         switch (*text)
1419                         {
1420                         case ATTR_COLOR:
1421                                 col = TRUE;
1422                                 nc = 0;
1423                                 break;
1424                         case ATTR_BEEP:
1425                         case ATTR_RESET:
1426                         case ATTR_REVERSE:
1427                         case ATTR_BOLD:
1428                         case ATTR_UNDERLINE:
1429                                 break;
1430                         default:
1431                                 new_str[i] = *text;
1432                                 i++;
1433                         }
1434                 }
1435                 text++;
1436                 len--;
1437         }
1438
1439         new_str[i] = 0;
1440
1441         if (newlen != NULL)
1442                 *newlen = i;
1443
1444         return new_str;
1445 }
1446
1447 /* gives width of a 8bit string - with no mIRC codes in it */
1448
1449 static int
1450 gtk_xtext_text_width_simple (GtkXText * xtext, unsigned char *str, int len)
1451 {
1452         int width = 0;
1453
1454         if (xtext->fixed_width_font)
1455                 return (xtext->space_width * len);
1456
1457         while (len)
1458         {
1459                 width += xtext->fontwidth[*str];
1460                 len--;
1461                 str++;
1462         }
1463
1464         return width;
1465 }
1466
1467 /* gives width of a string, excluding the mIRC codes */
1468
1469 static int
1470 gtk_xtext_text_width (GtkXText * xtext, unsigned char *text, int len)
1471 {
1472         unsigned char *tmp, *new_buf;
1473         int width, new_len;
1474
1475         new_buf = gtk_xtext_strip_color (text, len, xtext->scratch_buffer, &new_len);
1476
1477         if (xtext->fonttype == FONT_1BYTE)
1478         {
1479                 if (xtext->fixed_width_font)
1480                 {
1481                         width = xtext->space_width * new_len;
1482                 } else
1483                 {
1484                         width = 0;
1485                         tmp = new_buf;
1486                         while (*tmp)
1487                         {
1488                                 width += xtext->fontwidth[*tmp];
1489                                 tmp++;
1490                         }
1491                 }
1492         } else
1493         {
1494                 width = gdk_text_width (xtext->font, new_buf, new_len);
1495         }
1496
1497         return width;
1498 }
1499
1500 /* actually draw text to screen */
1501
1502 static int
1503 gtk_xtext_render_flush (GtkXText * xtext, int x, int y, char *str, int len,
1504                                                                 GdkGC *gc)
1505 {
1506         int str_width;
1507         int dofill;
1508 #ifdef USE_XLIB
1509         XFontStruct *xfont;
1510         GC xgc;
1511         Drawable xdraw_buf;
1512         Display *xdisplay;
1513 #endif
1514
1515         if (xtext->dont_render)
1516                 return 0;
1517
1518         if (xtext->fonttype == FONT_1BYTE)
1519                 str_width = gtk_xtext_text_width_simple (xtext, str, len);
1520         else
1521                 str_width = gdk_text_width (xtext->font, str, len);
1522
1523         if (str_width < 1)
1524                 return 0;
1525
1526         if (!xtext->backcolor && xtext->pixmap)
1527         {
1528                 dofill = FALSE;
1529                 /* draw the background pixmap behind the text - CAUSES FLICKER HERE !! */
1530                 if (!xtext->double_buffer && !xtext->skip_fills)
1531                 {
1532                         if (xtext->do_underline_fills_only)
1533                         {
1534                                 gdk_draw_rectangle (GTK_WIDGET (xtext)->window, xtext->bgc, 1,
1535                                                                                   x, y + 1, str_width, 1);
1536                                 if (xtext->underline)           /* optimization */
1537                                         goto dounder;
1538                         } else
1539                         {
1540                                 gdk_draw_rectangle (GTK_WIDGET (xtext)->window, xtext->bgc, 1,
1541                                                                                   x, y - xtext->font->ascent, str_width,
1542                                                                                   xtext->fontsize);
1543                         }
1544                 }
1545         } else
1546         {
1547                 dofill = TRUE;
1548                 if (xtext->skip_fills && !xtext->backcolor)
1549                         dofill = FALSE;
1550         }
1551
1552 #ifdef USE_XLIB
1553
1554         xgc = GDK_GC_XGC (gc);
1555         xdraw_buf = GDK_WINDOW_XWINDOW (xtext->draw_buf);
1556         xdisplay = GDK_WINDOW_XDISPLAY (GTK_WIDGET (xtext)->window);
1557         xfont = GDK_FONT_XFONT (xtext->font);
1558
1559         switch (xtext->fonttype)
1560         {
1561         case FONT_1BYTE:
1562                 if (dofill)
1563                         XDrawImageString (xdisplay, xdraw_buf, xgc, x, y, str, len);
1564                 else
1565                         XDrawString (xdisplay, xdraw_buf, xgc, x, y, str, len);
1566                 if (xtext->bold)
1567                         XDrawString (xdisplay, xdraw_buf, xgc, x + 1, y, str, len);
1568                 break;
1569
1570         case FONT_2BYTE:
1571                 len /= 2;
1572                 if (dofill)
1573                         XDrawImageString16 (xdisplay, xdraw_buf,
1574                                                                           xgc, x, y, (XChar2b *) str, len);
1575                 else
1576                         XDrawString16 (xdisplay, xdraw_buf,
1577                                                                 xgc, x, y, (XChar2b *) str, len);
1578                 if (xtext->bold)
1579                         XDrawString16 (xdisplay, xdraw_buf,
1580                                                                 xgc, x + 1, y, (XChar2b *) str, len);
1581                 break;
1582
1583         case FONT_SET:
1584                 if (dofill)
1585                         XmbDrawImageString (xdisplay, xdraw_buf,
1586                                                                           (XFontSet) xfont, xgc, x, y, str, len);
1587                 else
1588                         XmbDrawString (xdisplay, xdraw_buf,
1589                                                                 (XFontSet) xfont, xgc, x, y, str, len);
1590                 if (xtext->bold)
1591                         XmbDrawString (xdisplay, xdraw_buf,
1592                                                                 (XFontSet) xfont, xgc, x + 1, y, str, len);
1593         }
1594
1595 #else
1596
1597         /* don't have Xlib, gdk version --- */
1598         if (dofill)
1599         {
1600                 GdkGCValues val;
1601                 gdk_gc_get_values (gc, &val);
1602                 xtext_set_fg (gc, val.background.pixel);
1603                 gdk_draw_rectangle (xtext->draw_buf, gc, 1,
1604                                                                 x, y - xtext->font->ascent, str_width,
1605                                                                 xtext->fontsize);
1606                 xtext_set_fg (gc, val.foreground.pixel);
1607         }
1608         gdk_draw_text (xtext->draw_buf, xtext->font, gc, x, y, str, len);
1609         if (xtext->bold)
1610                 gdk_draw_text (xtext->draw_buf, xtext->font, gc, x + 1, y, str, len);
1611
1612 #endif
1613
1614         if (xtext->underline)
1615 dounder:
1616                 gdk_draw_line (xtext->draw_buf, gc, x, y+1, x+str_width-1, y+1);
1617
1618         return str_width;
1619 }
1620
1621 static void
1622 gtk_xtext_reset (GtkXText * xtext, int mark, int attribs)
1623 {
1624         if (attribs)
1625         {
1626                 xtext->underline = FALSE;
1627                 xtext->bold = FALSE;
1628         }
1629         if (!mark)
1630         {
1631                 xtext->backcolor = FALSE;
1632                 if (xtext->col_fore != 18)
1633                         xtext_set_fg (xtext->fgc, xtext->palette[18]);
1634                 if (xtext->col_back != 19)
1635                         xtext_set_bg (xtext->fgc, xtext->palette[19]);
1636         }
1637         xtext->col_fore = 18;
1638         xtext->col_back = 19;
1639 }
1640
1641 /* render a single line, which WONT wrap, and parse mIRC colors */
1642
1643 static void
1644 gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent, char *str,
1645                                                          int len, int win_width, int indent, int line)
1646 {
1647         GdkGC *gc;
1648         int i = 0, x = indent, j = 0;
1649         char *pstr = str;
1650         int col_num, tmp;
1651         int offset;
1652         int mark = FALSE;
1653         int hilight = FALSE;
1654
1655         offset = str - ent->str;
1656
1657         if (line < 255 && line >= 0)
1658                 xtext->grid_offset[line] = offset;
1659
1660         gc = xtext->fgc;                                  /* our foreground GC */
1661
1662         if (ent->mark_start != -1 &&
1663                  ent->mark_start <= i + offset && ent->mark_end > i + offset)
1664         {
1665                 xtext_set_bg (gc, xtext->palette[16]);
1666                 xtext_set_fg (gc, xtext->palette[17]);
1667                 xtext->backcolor = TRUE;
1668                 mark = TRUE;
1669         }
1670 #ifdef MOTION_MONITOR
1671         if (xtext->hilight_ent == ent &&
1672                  xtext->hilight_start <= i + offset && xtext->hilight_end > i + offset)
1673         {
1674                 xtext->underline = TRUE;
1675 /*      xtext->bold = TRUE;*/
1676                 hilight = TRUE;
1677         }
1678 #endif
1679
1680         if (!xtext->double_buffer)
1681         {
1682                 /* draw background to the left of the text */
1683                 if (str == ent->str && indent && xtext->time_stamp)
1684                 {
1685                         /* don't overwrite the timestamp */
1686                         if (indent > xtext->stamp_width)
1687                         {
1688                                 if (!xtext->skip_border_fills)
1689                                         gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1, 
1690                                                                          xtext->stamp_width, y - xtext->font->ascent,
1691                                                                          indent - xtext->stamp_width, xtext->fontsize);
1692                         }
1693                 } else
1694                 {
1695                         /* fill the indent area with background gc */
1696                         if (!xtext->skip_border_fills)
1697                                 gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1, 
1698                                                                  0, y - xtext->font->ascent, indent, xtext->fontsize);
1699                 }
1700         }
1701
1702         while (i < len)
1703         {
1704
1705 #ifdef MOTION_MONITOR
1706                 if (xtext->hilight_ent == ent && xtext->hilight_start == (i + offset))
1707                 {
1708                         x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1709                         pstr += j;
1710                         j = 0;
1711                         xtext->underline = TRUE;
1712 /*         xtext->bold = TRUE;*/
1713                         hilight = TRUE;
1714                 }
1715 #endif
1716
1717                 if (!mark && ent->mark_start == (i + offset))
1718                 {
1719                         x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1720                         pstr += j;
1721                         j = 0;
1722                         xtext_set_bg (gc, xtext->palette[16]);
1723                         xtext_set_fg (gc, xtext->palette[17]);
1724                         xtext->backcolor = TRUE;
1725                         mark = TRUE;
1726                 }
1727
1728                 if ((xtext->parsing_color && isdigit (str[i]) && xtext->nc < 2) ||
1729                          (xtext->parsing_color && str[i] == ',' && xtext->nc < 3))
1730                 {
1731                         pstr++;
1732                         if (str[i] == ',')
1733                         {
1734                                 xtext->parsing_backcolor = TRUE;
1735                                 if (xtext->nc)
1736                                 {
1737                                         xtext->num[xtext->nc] = 0;
1738                                         xtext->nc = 0;
1739                                         col_num = atoi (xtext->num) % 16;
1740                                         xtext->col_fore = col_num;
1741                                         if (!mark)
1742                                                 xtext_set_fg (gc, xtext->palette[col_num]);
1743                                 }
1744                         } else
1745                         {
1746                                 xtext->num[xtext->nc] = str[i];
1747                                 if (xtext->nc < 7)
1748                                         xtext->nc++;
1749                         }
1750                 } else
1751                 {
1752                         if (xtext->parsing_color)
1753                         {
1754                                 xtext->parsing_color = FALSE;
1755                                 if (xtext->nc)
1756                                 {
1757                                         xtext->num[xtext->nc] = 0;
1758                                         xtext->nc = 0;
1759                                         col_num = atoi (xtext->num);
1760                                         if (col_num == 99)      /* mIRC lameness */
1761                                                 col_num = 19;
1762                                         else
1763                                                 col_num = col_num % 16;
1764                                         if (xtext->parsing_backcolor)
1765                                         {
1766                                                 if (col_num == 1)
1767                                                         xtext->backcolor = FALSE;
1768                                                 else
1769                                                         xtext->backcolor = TRUE;
1770                                                 if (!mark)
1771                                                         xtext_set_bg (gc, xtext->palette[col_num]);
1772                                                 xtext->col_back = col_num;
1773                                         } else
1774                                         {
1775                                                 if (!mark)
1776                                                         xtext_set_fg (gc, xtext->palette[col_num]);
1777                                                 xtext->col_fore = col_num;
1778                                         }
1779                                         xtext->parsing_backcolor = FALSE;
1780                                 } else
1781                                 {
1782                                         /* got a \003<non-digit>... i.e. reset colors */
1783                                         x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1784                                         pstr += j;
1785                                         j = 0;
1786                                         gtk_xtext_reset (xtext, mark, FALSE);
1787                                 }
1788                         }
1789
1790                         switch (str[i])
1791                         {
1792                         case '\t':
1793                                 str[i] = ' ';
1794                                 j++;
1795                                 break;
1796                         case '\n':
1797                         case ATTR_BEEP:
1798                                 break;
1799                         case ATTR_REVERSE:
1800                                 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1801                                 pstr += j + 1;
1802                                 j = 0;
1803                                 tmp = xtext->col_fore;
1804                                 xtext->col_fore = xtext->col_back;
1805                                 xtext->col_back = tmp;
1806                                 if (!mark)
1807                                 {
1808                                         xtext_set_fg (gc, xtext->palette[xtext->col_fore]);
1809                                         xtext_set_bg (gc, xtext->palette[xtext->col_back]);
1810                                 }
1811                                 if (xtext->col_back != 19)
1812                                         xtext->backcolor = TRUE;
1813                                 else
1814                                         xtext->backcolor = FALSE;
1815                                 break;
1816                         case ATTR_BOLD:
1817                                 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1818                                 xtext->bold = !xtext->bold;
1819                                 pstr += j + 1;
1820                                 j = 0;
1821                                 break;
1822                         case ATTR_UNDERLINE:
1823                                 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1824                                 xtext->underline = !xtext->underline;
1825                                 pstr += j + 1;
1826                                 j = 0;
1827                                 break;
1828                         case ATTR_RESET:
1829                                 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1830                                 pstr += j + 1;
1831                                 j = 0;
1832                                 gtk_xtext_reset (xtext, mark, !hilight);
1833                                 break;
1834                         case ATTR_COLOR:
1835                                 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1836                                 xtext->parsing_color = TRUE;
1837                                 pstr += j + 1;
1838                                 j = 0;
1839                                 break;
1840                         default:
1841                                 j++;
1842                         }
1843                 }
1844                 i++;
1845
1846 #ifdef MOTION_MONITOR
1847                 if (xtext->hilight_ent == ent && xtext->hilight_end == (i + offset))
1848                 {
1849                         x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1850                         pstr += j;
1851                         j = 0;
1852                         xtext->underline = FALSE;
1853 /*         xtext->bold = FALSE;*/
1854                         hilight = FALSE;
1855                 }
1856 #endif
1857
1858                 if (mark && ent->mark_end == (i + offset))
1859                 {
1860                         x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1861                         pstr += j;
1862                         j = 0;
1863                         xtext_set_bg (gc, xtext->palette[xtext->col_back]);
1864                         xtext_set_fg (gc, xtext->palette[xtext->col_fore]);
1865                         if (xtext->col_back != 19)
1866                                 xtext->backcolor = TRUE;
1867                         else
1868                                 xtext->backcolor = FALSE;
1869                         mark = FALSE;
1870                 }
1871         }
1872
1873         if (j)
1874                 x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
1875
1876         if (!xtext->double_buffer)
1877         {
1878                 /* draw separator now so it doesn't appear to flicker */
1879                 gtk_xtext_draw_sep (xtext, y + 1);
1880                 /* draw background to the right of the text */
1881                 if (!xtext->skip_border_fills)
1882                         gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1, 
1883                                                          x, y - xtext->font->ascent, (win_width + MARGIN) - x,
1884                                                          xtext->fontsize);
1885         }
1886 }
1887
1888 #ifdef USE_XLIB
1889
1890 /* get the desktop/root window - thanks Eterm */
1891
1892 static Window desktop_window = None;
1893
1894 Window
1895 get_desktop_window (Window the_window)
1896 {
1897         Atom prop, type, prop2;
1898         int format;
1899         unsigned long length, after;
1900         unsigned char *data;
1901         unsigned int nchildren;
1902         Window w, root, *children, parent;
1903
1904         prop = XInternAtom (GDK_DISPLAY (), "_XROOTPMAP_ID", True);
1905         prop2 = XInternAtom (GDK_DISPLAY (), "_XROOTCOLOR_PIXEL", True);
1906
1907         if (prop == None && prop2 == None)
1908                 return None;
1909
1910         for (w = the_window; w; w = parent)
1911         {
1912                 if ((XQueryTree (GDK_DISPLAY (), w, &root, &parent, &children,
1913                                 &nchildren)) == False)
1914                         return None;
1915
1916                 if (nchildren)
1917                         XFree (children);
1918
1919                 if (prop != None)
1920                 {
1921                         XGetWindowProperty (GDK_DISPLAY (), w, prop, 0L, 1L, False,
1922                                                                           AnyPropertyType, &type, &format, &length, &after,
1923                                                                           &data);
1924                 } else
1925                 {
1926                         XGetWindowProperty (GDK_DISPLAY (), w, prop2, 0L, 1L, False,
1927                                                                           AnyPropertyType, &type, &format, &length, &after,
1928                                                                           &data);
1929                 }
1930
1931                 if (data)
1932                         XFree (data);
1933
1934                 if (type != None)
1935                 {
1936                         return (desktop_window = w);
1937                 }
1938         }
1939
1940         return (desktop_window = None);
1941 }
1942
1943 /* stolen from zvt, which was stolen from Eterm */
1944
1945 static Pixmap
1946 get_pixmap_prop (Window the_window)
1947 {
1948         Atom prop, type;
1949         int format;
1950         unsigned long length, after;
1951         unsigned char *data;
1952         Pixmap pix = None;
1953
1954         if (desktop_window == None)
1955                 desktop_window = get_desktop_window (the_window);
1956         if (desktop_window == None)
1957                 desktop_window = GDK_ROOT_WINDOW ();
1958
1959         prop = XInternAtom (GDK_DISPLAY (), "_XROOTPMAP_ID", True);
1960         if (prop == None)
1961                 return None;
1962
1963         XGetWindowProperty (GDK_DISPLAY (), desktop_window, prop, 0L, 1L, False,
1964                                                           AnyPropertyType, &type, &format, &length, &after,
1965                                                           &data);
1966         if (data)
1967         {
1968                 if (type == XA_PIXMAP)
1969                         pix = *((Pixmap *) data);
1970
1971                 XFree (data);
1972         }
1973
1974         return pix;
1975 }
1976
1977 #ifdef USE_GDK_PIXBUF
1978
1979 static GdkPixmap *
1980 create_shaded_pixmap (GtkXText * xtext, Pixmap p, int x, int y, int w, int h)
1981 {
1982         GdkPixmap *pp, *tmp, *shaded_pixmap;
1983         GdkPixbuf *pixbuf;
1984         GdkColormap *cmap;
1985         GdkGC *tgc;
1986         unsigned char *buf;
1987         unsigned char *pbuf;
1988         int width, height, depth;
1989         int rowstride;
1990         int pbwidth;
1991         int pbheight;
1992         int i, j;
1993         int offset;
1994         int r, g, b, a;
1995
1996         pp = gdk_pixmap_foreign_new (p);
1997         cmap = gtk_widget_get_colormap (GTK_WIDGET (xtext));
1998         gdk_window_get_geometry (pp, NULL, NULL, &width, &height, &depth);
1999
2000         if (width < x + w || height < y + h || x < 0 || y < 0)
2001         {
2002                 tgc = gdk_gc_new (pp);
2003                 tmp = gdk_pixmap_new (pp, w, h, depth);
2004                 gdk_gc_set_tile (tgc, pp);
2005                 gdk_gc_set_fill (tgc, GDK_TILED);
2006                 gdk_gc_set_ts_origin (tgc, -x, -y);
2007                 gdk_draw_rectangle (tmp, tgc, TRUE, 0, 0, w, h);
2008                 gdk_gc_destroy (tgc);
2009
2010                 pixbuf = gdk_pixbuf_get_from_drawable (NULL, tmp, cmap,
2011                                                                                                                         0, 0, 0, 0, w, h);
2012                 gdk_pixmap_unref (tmp);
2013         } else
2014         {
2015                 pixbuf = gdk_pixbuf_get_from_drawable (NULL, pp, cmap,
2016                                                                                                                         x, y, 0, 0, w, h);
2017         }
2018         gdk_xid_table_remove (GDK_WINDOW_XWINDOW (pp));
2019         g_dataset_destroy (pp);
2020         g_free (pp);
2021
2022         if (!pixbuf)
2023                 return NULL;
2024
2025         buf = gdk_pixbuf_get_pixels (pixbuf);
2026         rowstride = gdk_pixbuf_get_rowstride (pixbuf);
2027         pbwidth = gdk_pixbuf_get_width (pixbuf);
2028         pbheight = gdk_pixbuf_get_height (pixbuf);
2029
2030         a = 128;        /* alpha */
2031         r = xtext->tint_red;
2032         g = xtext->tint_green;
2033         b = xtext->tint_blue;
2034
2035         if (gdk_pixbuf_get_has_alpha (pixbuf))
2036                 offset = 4;
2037         else
2038                 offset = 3;
2039
2040         for (i=0;i<pbheight;i++)
2041         {
2042                 pbuf = buf;
2043                 for (j=0;j<pbwidth;j++)
2044                 {
2045                         pbuf[0] = ((pbuf[0] * r) >> 8);
2046                         pbuf[1] = ((pbuf[1] * g) >> 8);
2047                         pbuf[2] = ((pbuf[2] * b) >> 8);
2048                         pbuf+=offset;
2049                 }
2050                 buf+=rowstride;
2051         }
2052
2053         gdk_pixbuf_render_pixmap_and_mask (pixbuf, &shaded_pixmap, NULL, 0);
2054         gdk_pixbuf_unref (pixbuf);
2055
2056         return shaded_pixmap;
2057 }
2058
2059 #endif
2060
2061 /* free transparency xtext->pixmap */
2062
2063 static void
2064 gtk_xtext_free_trans (GtkXText * xtext)
2065 {
2066         if (xtext->pixmap)
2067         {
2068                 if (xtext->shaded)
2069                 {
2070                         gdk_pixmap_unref (xtext->pixmap);
2071                 } else
2072                 {
2073                         gdk_xid_table_remove (GDK_WINDOW_XWINDOW (xtext->pixmap));
2074                         g_dataset_destroy (xtext->pixmap);
2075                         g_free (xtext->pixmap);
2076                 }
2077                 xtext->pixmap = NULL;
2078         }
2079 }
2080
2081 /* grab pixmap from root window and set xtext->pixmap */
2082
2083 static void
2084 gtk_xtext_load_trans (GtkXText * xtext)
2085 {
2086         Pixmap rootpix;
2087         Window childret;
2088         GtkWidget *widget = GTK_WIDGET (xtext);
2089         int x, y;
2090
2091         rootpix = get_pixmap_prop (GDK_WINDOW_XWINDOW (widget->window));
2092         if (rootpix == None)
2093         {
2094                 if (xtext->error_function)
2095                         xtext->error_function ("Unable to get root window pixmap!\n\n"
2096                                                                                   "You may need to use Esetroot or Gnome\n"
2097                                                                                   "control-center to set your background.\n");
2098                 xtext->transparent = FALSE;
2099                 return;
2100         }
2101
2102         XTranslateCoordinates (GDK_WINDOW_XDISPLAY (widget->window),
2103                                                                   GDK_WINDOW_XWINDOW (widget->window),
2104                                                                   GDK_ROOT_WINDOW (), 0, 0, &x, &y, &childret);
2105
2106 #ifdef USE_GDK_PIXBUF
2107         if (xtext->shaded)
2108         {
2109                 int width, height;
2110                 gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, &height);
2111                 xtext->pixmap =
2112                         create_shaded_pixmap (xtext, rootpix, x, y, width, height);
2113                 gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
2114                 gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
2115         } else
2116         {
2117 #endif
2118                 xtext->pixmap = gdk_pixmap_foreign_new (rootpix);
2119                 gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
2120                 gdk_gc_set_ts_origin (xtext->bgc, -x, -y);
2121 #ifdef USE_GDK_PIXBUF
2122         }
2123 #endif
2124         gdk_gc_set_fill (xtext->bgc, GDK_TILED);
2125 }
2126
2127 #endif
2128
2129 /* render a single line, which may wrap to more lines */
2130
2131 static int
2132 gtk_xtext_render_line (GtkXText * xtext, textentry * ent, char *str, int len,
2133                                                           int line, int lines_max, int subline, int indent,
2134                                                           int str_width)
2135 {
2136         char *time_str;
2137         int y;
2138         int width;
2139         int orig_len;
2140         int ret = 1;
2141         int tmp;
2142
2143         if (!str)
2144                 return 0;
2145
2146         if (len == -1)
2147                 len = strlen (str);
2148         orig_len = len;
2149
2150         gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, 0);
2151         width -= MARGIN;
2152
2153         if (xtext->time_stamp)
2154         {
2155                 time_str = ctime (&ent->stamp) + 10;
2156                 time_str[0] = '[';
2157                 time_str[9] = ']';
2158                 time_str[10] = 0;
2159                 y = (xtext->fontsize * line) + xtext->font->ascent;
2160                 gtk_xtext_render_str (xtext, y, ent, time_str, 10, width, 2, line);
2161         }
2162
2163  startrl:
2164
2165         y = (xtext->fontsize * line) + xtext->font->ascent;
2166
2167         if (str_width == -1)
2168                 str_width = gtk_xtext_text_width (xtext, str, len);
2169
2170         str_width += indent;
2171
2172         tmp = 0;
2173         while (str_width > width || (!is_del (str[len]) && xtext->wordwrap))
2174         {
2175                 if (str_width <= width && !tmp)
2176                         tmp = len;
2177                 len--;
2178                 if (xtext->wordwrap && tmp - len > WORDWRAP_LIMIT)
2179                 {
2180                         len = tmp;
2181                         str_width = gtk_xtext_text_width (xtext, str, len) + indent;
2182                         break;
2183                 }
2184                 if (len == 0)
2185                         return 1;
2186
2187                 /* this is quite a HACK but it speeds things up! */
2188                 if (str_width > width + 256)
2189                         len -= 10;
2190                 /* -- */
2191
2192                 str_width = gtk_xtext_text_width (xtext, str, len) + indent;
2193         }
2194
2195         if (!subline)
2196         {
2197                 gtk_xtext_render_str (xtext, y, ent, str, len, width, indent, line);
2198         } else
2199         {
2200                 xtext->dont_render = TRUE;
2201                 gtk_xtext_render_str (xtext, y, ent, str, len, width, indent, line);
2202                 xtext->dont_render = FALSE;
2203                 subline--;
2204                 line--;
2205                 ret--;
2206         }
2207
2208         if (xtext->wordwrap && str[len] == ' ')
2209                 len++;
2210
2211         if (len != orig_len && lines_max > line + 1)
2212         {                                                                         /* FIXME: recursion sux! */
2213 /*      ret += gtk_xtext_render_line (xtext, ent, str + len, -1, line+1, lines_max, subline, xtext->indent, -1);*/
2214                 ret++;
2215                 str += len;
2216                 len = orig_len = strlen (str);
2217                 line++;
2218                 indent = xtext->indent;
2219                 str_width = -1;
2220                 /* FIXME: gotos suck! */
2221                 goto startrl;
2222         }
2223
2224         return ret;
2225 }
2226
2227 void
2228 gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[])
2229 {
2230         int i;
2231
2232         for (i = 0; i < 20; i++)
2233                 xtext->palette[i] = palette[i].pixel;
2234
2235         if (GTK_WIDGET_REALIZED (xtext))
2236         {
2237                 xtext_set_fg (xtext->fgc, xtext->palette[18]);
2238                 xtext_set_bg (xtext->fgc, xtext->palette[19]);
2239                 xtext_set_fg (xtext->bgc, xtext->palette[19]);
2240         }
2241         xtext->col_fore = 18;
2242         xtext->col_back = 19;
2243 }
2244
2245 static void
2246 gtk_xtext_fix_indent (GtkXText * xtext)
2247 {
2248         int j;
2249
2250         if (xtext->indent)                        /* make indent a multiple of the space width */
2251         {
2252                 j = 0;
2253                 while (j < xtext->indent)
2254                 {
2255                         j += xtext->space_width;
2256                 }
2257                 xtext->indent = j;
2258         }
2259 }
2260
2261 static void
2262 gtk_xtext_recalc_widths (GtkXText * xtext, int do_str_width)
2263 {
2264         textentry *ent;
2265
2266         /* since we have a new font, we have to recalc the text widths */
2267         ent = xtext->text_first;
2268         while (ent)
2269         {
2270                 if (do_str_width)
2271                 {
2272                         ent->str_width =
2273                                 gtk_xtext_text_width (xtext, ent->str, ent->str_len);
2274                 }
2275                 if (ent->left_len != -1)
2276                 {
2277                         ent->indent =
2278                                 (xtext->indent -
2279                                  gtk_xtext_text_width (xtext, ent->str,
2280                                                                                           ent->left_len)) - xtext->space_width;
2281                         if (ent->indent < MARGIN)
2282                                 ent->indent = MARGIN;
2283                 }
2284                 ent = ent->next;
2285         }
2286
2287         gtk_xtext_calc_lines (xtext, FALSE);
2288 }
2289
2290 void
2291 gtk_xtext_set_font (GtkXText * xtext, GdkFont * font, char *name)
2292 {
2293 #ifdef USE_XLIB
2294         unsigned char i;
2295         XFontStruct *xfont;
2296 #endif
2297
2298         if (xtext->font)
2299                 gdk_font_unref (xtext->font);
2300
2301         if (font)
2302         {
2303                 xtext->font = font;
2304                 gdk_font_ref (font);
2305         } else
2306                 font = xtext->font = gdk_font_load (name);
2307
2308         if (!font)
2309                 font = xtext->font = gdk_font_load ("fixed");
2310
2311         switch (font->type)
2312         {
2313         case GDK_FONT_FONT:
2314                         xtext->fontsize = font->ascent + font->descent;
2315 #ifdef USE_XLIB
2316                         xfont = GDK_FONT_XFONT (font);
2317                         if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0))
2318                         {
2319                                 xtext->fonttype = FONT_1BYTE;
2320                                 for (i = 0; i < 255; i++)
2321                                 {
2322                                         xtext->fontwidth[i] = gdk_char_width (font, i);
2323                                 }
2324                                 xtext->space_width = xtext->fontwidth[' '];
2325                         } else
2326                         {
2327 #endif
2328                                 /* without X11 pretend they are all 2BYTE- This is ok, just
2329                                         a bit slower. */
2330                                 xtext->fonttype = FONT_2BYTE;
2331                                 xtext->space_width = gdk_char_width (font, ' ');
2332 #ifdef USE_XLIB
2333                         }
2334 #endif
2335                         break;
2336
2337         case GDK_FONT_FONTSET:
2338                         xtext->fontsize = gdk_text_height (font, " ", 1);
2339                         xtext->fonttype = FONT_SET;
2340                         xtext->space_width = gdk_char_width (font, ' ');
2341                         break;
2342         }
2343
2344 #ifdef USE_XLIB
2345         xfont = GDK_FONT_XFONT (font);
2346         /* check if it's a fixed width font */
2347         if (xfont->min_bounds.width == xfont->max_bounds.width)
2348                 xtext->fixed_width_font = TRUE;
2349         else
2350                 xtext->fixed_width_font = FALSE;
2351 #else
2352         /* kudgy fixed-width font checking */
2353         if (xtext->space_width == gdk_char_width (xtext->font, 'Z'))
2354                 xtext->fixed_width_font = TRUE;
2355         else
2356                 xtext->fixed_width_font = FALSE;
2357 #endif
2358
2359         xtext->stamp_width =
2360                 gtk_xtext_text_width (xtext, "[88:88:88]", 10) + MARGIN;
2361
2362         gtk_xtext_fix_indent (xtext);
2363
2364         if (GTK_WIDGET_REALIZED (xtext))
2365         {
2366                 if (xtext->fonttype != FONT_SET)
2367                         gdk_gc_set_font (xtext->fgc, xtext->font);
2368
2369                 gtk_xtext_recalc_widths (xtext, TRUE);
2370         }
2371 }
2372
2373 void
2374 gtk_xtext_set_background (GtkXText * xtext, GdkPixmap * pixmap, int trans,
2375                                                                   int shaded)
2376 {
2377         GdkGCValues val;
2378
2379 #ifndef USE_GDK_PIXBUF
2380         shaded = FALSE;
2381 #endif
2382
2383 #ifndef USE_XLIB
2384         shaded = FALSE;
2385         trans = FALSE;
2386 #endif
2387
2388         if (xtext->pixmap)
2389         {
2390 #ifdef USE_XLIB
2391                 if (xtext->transparent)
2392                         gtk_xtext_free_trans (xtext);
2393                 else
2394 #endif
2395                         gdk_pixmap_unref (xtext->pixmap);
2396                 xtext->pixmap = NULL;
2397         }
2398
2399         xtext->transparent = trans;
2400
2401 #ifdef USE_XLIB
2402         if (trans)
2403         {
2404                 xtext->shaded = shaded;
2405                 if (GTK_WIDGET_REALIZED (xtext))
2406                         gtk_xtext_load_trans (xtext);
2407                 return;
2408         }
2409 #endif
2410
2411         xtext->pixmap = pixmap;
2412
2413         if (pixmap != 0)
2414         {
2415                 gdk_pixmap_ref (pixmap);
2416                 if (GTK_WIDGET_REALIZED (xtext))
2417                 {
2418                         gdk_gc_set_tile (xtext->bgc, pixmap);
2419                         gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
2420                         gdk_gc_set_fill (xtext->bgc, GDK_TILED);
2421                 }
2422         } else
2423         {
2424                 if (GTK_WIDGET_REALIZED (xtext))
2425                 {
2426                         gdk_gc_destroy (xtext->bgc);
2427                         val.subwindow_mode = GDK_INCLUDE_INFERIORS;
2428                         val.graphics_exposures = 0;
2429                         xtext->bgc = gdk_gc_new_with_values (GTK_WIDGET (xtext)->window,
2430                                                                         &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
2431                         xtext_set_fg (xtext->bgc, xtext->palette[19]);
2432                 }
2433         }
2434 }
2435
2436 gchar *
2437 gtk_xtext_get_chars (GtkXText * xtext)
2438 {
2439         int lenght = 0;
2440         gchar *chars;
2441         textentry *tentry = xtext->text_first;
2442         while (tentry != NULL)
2443         {
2444                 lenght += tentry->str_len + 1;
2445                 tentry = tentry->next;
2446         }
2447         if (lenght == 0)
2448                 return NULL;
2449         chars = g_malloc (lenght + 1);
2450         *chars = 0;
2451
2452         tentry = xtext->text_first;
2453         while (tentry != NULL)
2454         {
2455                 strcat (chars, tentry->str);
2456                 strcat (chars, "\n");
2457                 tentry = tentry->next;
2458         }
2459
2460         return chars;
2461 }
2462
2463 static int
2464 gtk_xtext_lines_taken (GtkXText * xtext, textentry * ent)
2465 {
2466         int tmp, orig_len, indent, len, win_width, str_width, lines = 0;
2467         char *str;
2468
2469         str = ent->str;
2470         len = orig_len = ent->str_len;
2471         indent = ent->indent;
2472
2473         if (len < 2)
2474                 return 1;
2475
2476         win_width = GTK_WIDGET (xtext)->allocation.width - MARGIN;
2477         str_width = ent->str_width + indent;
2478
2479         while (1)
2480         {
2481                 lines++;
2482                 if (str_width <= win_width)
2483                         break;
2484                 tmp = 0;
2485                 while (str_width > win_width || (!is_del (str[len]) && xtext->wordwrap))
2486                 {
2487                         if (str_width <= win_width && !tmp)
2488                                 tmp = len;
2489                         len--;
2490                         if (xtext->wordwrap && tmp - len > WORDWRAP_LIMIT)
2491                         {
2492                                 len = tmp;
2493                                 str_width = gtk_xtext_text_width (xtext, str, len) + indent;
2494                                 break;
2495                         }
2496                         if (len == 0)
2497                                 return 1;
2498                         if (str_width > win_width + 256)        /* this might not work 100% but it */
2499                                 len -= 10;                        /* sure speeds things up ALOT!     */
2500                         str_width = gtk_xtext_text_width (xtext, str, len) + indent;
2501                 }
2502
2503                 if (len == orig_len)
2504                         break;
2505
2506                 if (xtext->wordwrap && str[len] == ' ')
2507                         len++;
2508
2509                 str += len;
2510                 indent = xtext->indent;
2511                 len = strlen (str);
2512                 str_width = gtk_xtext_text_width (xtext, str, len) + indent;
2513         }
2514         return lines;
2515 }
2516
2517 /* Calculate number of actual lines (with wraps), to set adj->lower. *
2518  * This should only be called when the window resizes.               */
2519
2520 static void
2521 gtk_xtext_calc_lines (GtkXText * xtext, int fire_signal)
2522 {
2523         textentry *ent;
2524         int width;
2525         int height;
2526         int lines;
2527
2528         width = GTK_WIDGET (xtext)->allocation.width - MARGIN;
2529         height = GTK_WIDGET (xtext)->allocation.height;
2530
2531         if (width < 30 || height < xtext->fontsize || width < xtext->indent + 30)
2532                 return;
2533
2534         lines = 0;
2535         ent = xtext->text_first;
2536         while (ent)
2537         {
2538                 ent->lines_taken = gtk_xtext_lines_taken (xtext, ent);
2539                 lines += ent->lines_taken;
2540                 ent = ent->next;
2541         }
2542
2543         xtext->pagetop_ent = NULL;
2544         xtext->num_lines = lines;
2545         gtk_xtext_adjustment_set (xtext, fire_signal);
2546 }
2547
2548 /* find the n-th line in the linked list, this includes wrap calculations */
2549
2550 static textentry *
2551 gtk_xtext_nth (GtkXText * xtext, textentry * ent, int line, int width,
2552                                         int *subline)
2553 {
2554         int lines = 0;
2555
2556         if (ent == NULL)
2557         {
2558                 ent = xtext->text_first;
2559                 line += xtext->adj->value;
2560         } else
2561         {
2562                 lines -= *subline;
2563         }
2564
2565         while (ent)
2566         {
2567                 lines += ent->lines_taken;
2568                 if (lines > line)
2569                 {
2570                         *subline = ent->lines_taken - (lines - line);
2571                         return ent;
2572                 }
2573                 ent = ent->next;
2574         }
2575         return 0;
2576 }
2577
2578 static void
2579 gtk_xtext_draw_sep (GtkXText * xtext, int y)
2580 {
2581         int x, height;
2582         GdkGC *light, *dark;
2583
2584         if (y == -1)
2585         {
2586                 y = 2;
2587                 height = GTK_WIDGET (xtext)->allocation.height - 2;
2588         } else
2589         {
2590                 height = xtext->fontsize;
2591         }
2592
2593         /* draw the separator line */
2594         if (xtext->separator && xtext->indent)
2595         {
2596                 light = xtext->light_gc;
2597                 dark = xtext->dark_gc;
2598
2599                 x = xtext->indent - ((xtext->space_width + 1) / 2);
2600                 if (x < 1)
2601                         return;
2602
2603                 if (xtext->thinline)
2604                 {
2605                         if (xtext->moving_separator)
2606                                 gdk_draw_line (xtext->draw_buf, light, x, y, x, height);
2607                         else
2608                                 gdk_draw_line (xtext->draw_buf, dark, x, y, x, height);
2609                 } else
2610                 {
2611                         if (xtext->moving_separator)
2612                         {
2613                                 gdk_draw_line (xtext->draw_buf, light, x - 1, y, x - 1, height);
2614                                 gdk_draw_line (xtext->draw_buf, dark, x, y, x, height);
2615                         } else
2616                         {
2617                                 gdk_draw_line (xtext->draw_buf, dark, x - 1, y, x - 1, height);
2618                                 gdk_draw_line (xtext->draw_buf, light, x, y, x, height);
2619                         }
2620                 }
2621         }
2622 }
2623
2624 /* render 2 ents (or an inclusive range) */
2625
2626 static void
2627 gtk_xtext_render_ents (GtkXText * xtext, textentry * enta, textentry * entb,
2628                                                           int inclusive)
2629 {
2630         textentry *ent, *orig_ent, *tmp_ent;
2631         int line;
2632         int lines_taken;
2633         int lines_max;
2634         int width;
2635         int height;
2636         int subline;
2637         int drawing = FALSE;
2638
2639         if (xtext->double_buffer)
2640         {
2641                 gtk_xtext_render_page (xtext);
2642                 return;
2643         }
2644
2645         if (xtext->indent < MARGIN)
2646                 xtext->indent = MARGIN;   /* 2 pixels is our left margin */
2647
2648         gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, &height);
2649         width -= MARGIN;
2650
2651         if (width < 32 || height < xtext->fontsize || width < xtext->indent + 30)
2652                 return;
2653
2654         lines_max = (height - xtext->font->descent) / xtext->fontsize;
2655         line = 0;
2656         orig_ent = xtext->pagetop_ent;
2657         subline = xtext->pagetop_subline;
2658
2659         /* check if enta is before the start of this page */
2660         if (inclusive)
2661         {
2662                 tmp_ent = orig_ent;
2663                 while (tmp_ent)
2664                 {
2665                         if (tmp_ent == enta)
2666                                 break;
2667                         if (tmp_ent == entb)
2668                         {
2669                                 drawing = TRUE;
2670                                 break;
2671                         }
2672                         tmp_ent = tmp_ent->next;
2673                 }
2674         }
2675
2676         line = 0;
2677
2678         ent = orig_ent;
2679         while (ent)
2680         {
2681                 if (inclusive && ent == enta)
2682                         drawing = TRUE;
2683
2684                 if (drawing || ent == entb || ent == enta)
2685                 {
2686                         gtk_xtext_reset (xtext, FALSE, TRUE);
2687                         lines_taken = gtk_xtext_render_line (xtext, ent, ent->str,
2688                                                                                                                          ent->str_len, line, lines_max,
2689                                                                                                                          subline, ent->indent,
2690                                                                                                                          ent->str_width);
2691                         line += ent->lines_taken;
2692                         line -= subline;
2693                         if (ent == orig_ent)
2694                                 subline = 0;
2695                 } else
2696                 {
2697                         if (ent == orig_ent)
2698                         {
2699                                 line -= subline;
2700                                 subline = 0;
2701                         }
2702                         line += ent->lines_taken;
2703                 }
2704
2705                 if (inclusive && ent == entb)
2706                         break;
2707
2708                 if (line >= lines_max)
2709                         break;
2710
2711                 ent = ent->next;
2712         }
2713
2714         /* draw the separator line */
2715         gtk_xtext_draw_sep (xtext, -1);
2716 }
2717
2718 /* render a whole page/window, starting from 'startline' */
2719
2720 static void
2721 gtk_xtext_render_page (GtkXText * xtext)
2722 {
2723         textentry *ent;
2724         int line;
2725         int lines_max;
2726         int width;
2727         int height;
2728         int subline;
2729         int startline = xtext->adj->value;
2730
2731         if (xtext->indent < MARGIN)
2732                 xtext->indent = MARGIN;   /* 2 pixels is our left margin */
2733
2734         gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, &height);
2735         width -= MARGIN;
2736
2737         if (width < 32 || height < xtext->fontsize || width < xtext->indent + 30)
2738                 return;
2739
2740         lines_max = (height - xtext->font->descent) / xtext->fontsize;
2741
2742         subline = line = 0;
2743         ent = xtext->text_first;
2744
2745         if (startline > 0)
2746                 ent = gtk_xtext_nth (xtext, ent, startline, width, &subline);
2747
2748         xtext->pagetop_ent = ent;
2749         xtext->pagetop_subline = subline;
2750
2751         if (xtext->double_buffer)
2752         {
2753                 xtext->tmp_pix = gdk_pixmap_new (((GtkWidget*)xtext)->window,
2754                                                                                                 width + MARGIN, height, xtext->depth);
2755                 xtext->draw_buf = xtext->tmp_pix;
2756                 /* render the backdrop */
2757                 gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1, 0, 0,
2758                                                                         width + MARGIN, height);
2759         }
2760
2761         while (ent)
2762         {
2763                 gtk_xtext_reset (xtext, FALSE, TRUE);
2764                 line +=
2765                         gtk_xtext_render_line (xtext, ent, ent->str, ent->str_len, line,
2766                                                                                   lines_max, subline, ent->indent,
2767                                                                                   ent->str_width);
2768                 subline = 0;
2769
2770                 if (line >= lines_max)
2771                         break;
2772
2773                 ent = ent->next;
2774         }
2775
2776         if (!xtext->double_buffer)
2777         {
2778                 line = (xtext->fontsize * line);
2779                 /* fill any space below the last line with our background GC */
2780                 gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1,
2781                                                          0, line, width + MARGIN, height - line);
2782         }
2783
2784         /* draw the separator line */
2785         gtk_xtext_draw_sep (xtext, -1);
2786
2787         if (xtext->double_buffer)
2788         {
2789                 /* send our double buffer to the actual window */
2790                 gdk_draw_pixmap (((GtkWidget*)xtext)->window, xtext->fgc, xtext->tmp_pix,
2791                                                                 0, 0, 0, 0, width + MARGIN, height);
2792                 gdk_pixmap_unref (xtext->tmp_pix);
2793         }
2794 }
2795
2796 void
2797 gtk_xtext_refresh (GtkXText * xtext, int do_trans)
2798 {
2799         if (GTK_WIDGET_REALIZED (GTK_WIDGET (xtext)))
2800         {
2801 #ifdef USE_XLIB
2802                 if (xtext->transparent && do_trans)
2803                 {
2804                         gtk_xtext_free_trans (xtext);
2805                         gtk_xtext_load_trans (xtext);
2806                 }
2807 #endif
2808                 gtk_xtext_render_page (xtext);
2809         }
2810 }
2811
2812 /* remove the topline from the list */
2813
2814 static void
2815 gtk_xtext_remove_top (GtkXText * xtext)
2816 {
2817         textentry *ent;
2818
2819         ent = xtext->text_first;
2820         xtext->num_lines -= ent->lines_taken;
2821         xtext->pagetop_ent = NULL;
2822         xtext->text_first = ent->next;
2823         free (ent);
2824 }
2825
2826 void
2827 gtk_xtext_remove_lines (GtkXText * xtext, int lines, int refresh)
2828 {
2829         textentry *next;
2830
2831         while (xtext->text_first && lines)
2832         {
2833                 next = xtext->text_first->next;
2834                 free (xtext->text_first);
2835                 xtext->text_first = next;
2836                 lines--;
2837         }
2838         if (!xtext->text_first)
2839                 xtext->text_last = NULL;
2840
2841         if (refresh)
2842         {
2843                 gtk_xtext_calc_lines (xtext, TRUE);
2844                 gtk_xtext_refresh (xtext, 0);
2845         }
2846 }
2847
2848 void *
2849 gtk_xtext_search (GtkXText * xtext, char *text, void *start)
2850 {
2851         textentry *ent, *fent;
2852         char *str;
2853         int line;
2854
2855         gtk_xtext_selection_clear (xtext);
2856
2857         if (start)
2858                 ent = ((textentry *) start)->next;
2859         else
2860                 ent = xtext->text_first;
2861         while (ent)
2862         {
2863                 if ((str = nocasestrstr (ent->str, text)))
2864                 {
2865                         ent->mark_start = str - ent->str;
2866                         ent->mark_end = ent->mark_start + strlen (text);
2867                         break;
2868                 }
2869                 ent = ent->next;
2870         }
2871
2872         fent = ent;
2873         ent = xtext->text_first;
2874         line = 0;
2875         while (ent)
2876         {
2877                 line += ent->lines_taken;
2878                 ent = ent->next;
2879                 if (ent == fent)
2880                         break;
2881         }
2882         while (line > xtext->adj->upper - xtext->adj->page_size)
2883                 line--;
2884
2885         if (fent != 0)
2886         {
2887                 xtext->adj->value = line;
2888                 xtext->scrollbar_down = FALSE;
2889                 gtk_adjustment_changed (xtext->adj);
2890         }
2891         gtk_xtext_render_page (xtext);
2892
2893         return fent;
2894 }
2895
2896 static int
2897 gtk_xtext_render_page_timeout (GtkXText * xtext)
2898 {
2899         GtkAdjustment *adj = xtext->adj;
2900         gfloat val;
2901
2902         if (xtext->scrollbar_down)
2903         {
2904                 gtk_xtext_adjustment_set (xtext, FALSE);
2905                 gtk_adjustment_set_value (adj, adj->upper - adj->page_size);
2906         } else
2907         {
2908                 val = adj->value;
2909                 gtk_xtext_adjustment_set (xtext, TRUE);
2910                 gtk_adjustment_set_value (adj, val);
2911         }
2912
2913         if (adj->value >= adj->upper - adj->page_size || adj->value < 1)
2914                 gtk_xtext_render_page (xtext);
2915
2916         xtext->add_io_tag = -1;
2917
2918         return 0;
2919 }
2920
2921 /* append a textentry to our linked list */
2922
2923 static void
2924 gtk_xtext_append_entry (GtkXText * xtext, textentry * ent)
2925 {
2926         ent->stamp = time (0);
2927         ent->str_width = gtk_xtext_text_width (xtext, ent->str, ent->str_len);
2928         ent->mark_start = -1;
2929         ent->mark_end = -1;
2930         ent->next = NULL;
2931
2932         if (ent->indent < MARGIN)
2933                 ent->indent = MARGIN;     /* 2 pixels is the left margin */
2934
2935         /* append to our linked list */
2936         if (xtext->text_last)
2937                 xtext->text_last->next = ent;
2938         else
2939                 xtext->text_first = ent;
2940         xtext->text_last = ent;
2941
2942         ent->lines_taken = gtk_xtext_lines_taken (xtext, ent);
2943         xtext->num_lines += ent->lines_taken;
2944
2945         if (xtext->max_lines > 2 && xtext->max_lines < xtext->num_lines)
2946         {
2947                 gtk_xtext_remove_top (xtext);
2948         }
2949
2950 /*   if (xtext->frozen == 0 && xtext->add_io_tag == -1)*/
2951         if (xtext->add_io_tag == -1)
2952         {
2953                 xtext->add_io_tag = gtk_timeout_add (REFRESH_TIMEOUT * 2,
2954                                                                                                                  (GtkFunction)
2955                                                                                                                  gtk_xtext_render_page_timeout,
2956                                                                                                                  xtext);
2957         }
2958 }
2959
2960 /* the main two public functions */
2961
2962 void
2963 gtk_xtext_append_indent (GtkXText * xtext,
2964                                                                  char *left_text, int left_len,
2965                                                                  char *right_text, int right_len)
2966 {
2967         textentry *ent;
2968         char *str;
2969         int space;
2970
2971         if (left_len == -1)
2972                 left_len = strlen (left_text);
2973
2974         if (right_len == -1)
2975                 right_len = strlen (right_text);
2976
2977         ent = malloc (left_len + right_len + 2 + sizeof (textentry));
2978         str = (char *) ent + sizeof (textentry);
2979
2980         space = xtext->indent - gtk_xtext_text_width (xtext, left_text, left_len);
2981
2982         memcpy (str, left_text, left_len);
2983         str[left_len] = ' ';
2984         memcpy (str + left_len + 1, right_text, right_len);
2985         str[left_len + 1 + right_len] = 0;
2986
2987         ent->left_len = left_len;
2988         ent->str = str;
2989         ent->str_len = left_len + 1 + right_len;
2990         ent->indent = space - xtext->space_width;
2991
2992         if (xtext->time_stamp)
2993                 space = xtext->stamp_width;
2994         else
2995                 space = 0;
2996
2997         /* do we need to auto adjust the separator position? */
2998         if (xtext->auto_indent && ent->indent < MARGIN + space)
2999         {
3000                 xtext->indent -= ent->indent;
3001                 xtext->indent += MARGIN;
3002                 xtext->indent += space;
3003                 if (xtext->indent > xtext->max_auto_indent)
3004                         xtext->indent = xtext->max_auto_indent;
3005                 gtk_xtext_fix_indent (xtext);
3006                 gtk_xtext_recalc_widths (xtext, FALSE);
3007                 space =
3008                         xtext->indent - gtk_xtext_text_width (xtext, left_text, left_len);
3009                 ent->indent = space - xtext->space_width;
3010         }
3011
3012         gtk_xtext_append_entry (xtext, ent);
3013 }
3014
3015 void
3016 gtk_xtext_append (GtkXText * xtext, char *text, int len)
3017 {
3018         textentry *ent;
3019
3020         if (len == -1)
3021                 len = strlen (text);
3022
3023         ent = malloc (len + 1 + sizeof (textentry));
3024         ent->str = (char *) ent + sizeof (textentry);
3025         ent->str_len = len;
3026         if (len)
3027                 memcpy (ent->str, text, len);
3028         ent->str[len] = 0;
3029         ent->indent = 0;
3030         ent->left_len = -1;
3031
3032         gtk_xtext_append_entry (xtext, ent);
3033 }