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 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 * =========================================================================
18 *
19 * xtext, the text widget used by X-Chat.
20 * By Peter Zelezny <zed@xchat.org>.
21 *
22 */
23
24 #define GDK_MULTIHEAD_SAFE
25 #define MARGIN 2 /* dont touch. */
26 #define REFRESH_TIMEOUT 20
27 #define WORDWRAP_LIMIT 24
28
29 #include <string.h>
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <time.h>
33
34 #include "config.h"
35 #include "../common/hexchat.h"
36 #include "../common/fe.h"
37 #include "../common/util.h"
38 #include "../common/hexchatc.h"
39 #include "../common/url.h"
40
41 #ifdef WIN32
42 #include "marshal.h"
43 #else
44 #include "../common/marshal.h"
45 #endif
46
47 #include "fe-gtk.h"
48 #include "xtext.h"
49 #include "fkeys.h"
50
51 #define charlen(str) g_utf8_skip[*(guchar *)(str)]
52
53 #ifdef WIN32
54 #include <windows.h>
55 #include <io.h>
56 #include <gdk/gdk.h>
57 #include <gdk/gdkwin32.h>
58 #else
59 #include <unistd.h>
60 #ifdef GDK_WINDOWING_X11
61 #include <gdk/gdkx.h>
62 #endif
63 #endif
64
65 /* is delimiter */
66 #define is_del(c) \
67 (c == ' ' || c == '\n' || c == '>' || c == '<' || c == 0)
68
69 /* force scrolling off */
70 #define dontscroll(buf) (buf)->last_pixel_pos = 0x7fffffff
71
72 static GtkWidgetClass *parent_class = NULL;
73
74 struct textentry
75 {
76 struct textentry *next;
77 struct textentry *prev;
78 unsigned char *str;
79 time_t stamp;
80 gint16 str_width;
81 gint16 str_len;
82 gint16 mark_start;
83 gint16 mark_end;
84 gint16 indent;
85 gint16 left_len;
86 GSList *slp;
87 GSList *sublines;
88 guchar tag;
89 guchar pad1;
90 guchar pad2; /* 32-bit align : 44 bytes total */
91 GList *marks; /* List of found strings */
92 };
93
94 enum
95 {
96 WORD_CLICK,
97 SET_SCROLL_ADJUSTMENTS,
98 LAST_SIGNAL
99 };
100
101 /* values for selection info */
102 enum
103 {
104 TARGET_UTF8_STRING,
105 TARGET_STRING,
106 TARGET_TEXT,
107 TARGET_COMPOUND_TEXT
108 };
109
110 static guint xtext_signals[LAST_SIGNAL];
111
112 char *nocasestrstr (const char *text, const char *tofind); /* util.c */
113 int xtext_get_stamp_str (time_t, char **);
114 static void gtk_xtext_render_page (GtkXText * xtext);
115 static void gtk_xtext_calc_lines (xtext_buffer *buf, int);
116 static gboolean gtk_xtext_is_selecting (GtkXText *xtext);
117 static char *gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret);
118 static textentry *gtk_xtext_nth (GtkXText *xtext, int line, int *subline);
119 static void gtk_xtext_adjustment_changed (GtkAdjustment * adj,
120 GtkXText * xtext);
121 static void gtk_xtext_scroll_adjustments (GtkXText *xtext, GtkAdjustment *hadj,
122 GtkAdjustment *vadj);
123 static int gtk_xtext_render_ents (GtkXText * xtext, textentry *, textentry *);
124 static void gtk_xtext_recalc_widths (xtext_buffer *buf, int);
125 static void gtk_xtext_fix_indent (xtext_buffer *buf);
126 static int gtk_xtext_find_subline (GtkXText *xtext, textentry *ent, int line);
127 /* static char *gtk_xtext_conv_color (unsigned char *text, int len, int *newlen); */
128 /* For use by gtk_xtext_strip_color() and its callers -- */
129 struct offlen_s {
130 guint16 off;
131 guint16 len;
132 guint16 emph;
133 guint16 width;
134 };
135 typedef struct offlen_s offlen_t;
136 static unsigned char *
137 gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
138 int *newlen, GSList **slp, int strip_hidden);
139 static gboolean gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add);
140 static int gtk_xtext_render_page_timeout (GtkXText * xtext);
141 static int gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off);
142 static GList * gtk_xtext_search_textentry (xtext_buffer *, textentry *);
143 static void gtk_xtext_search_textentry_add (xtext_buffer *, textentry *, GList *, gboolean);
144 static void gtk_xtext_search_textentry_del (xtext_buffer *, textentry *);
145 static void gtk_xtext_search_textentry_fini (gpointer, gpointer);
146 static void gtk_xtext_search_fini (xtext_buffer *);
147 static gboolean gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gtk_xtext_search_flags flags, GError **perr);
148 static char * gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent, int *ret_off, int *ret_len, GSList **slp);
149
150 #define xtext_draw_bg(xt,x,y,w,h) gdk_draw_rectangle(xt->draw_buf, xt->bgc, 1, x, y, w, h);
151
152 /* ======================================= */
153 /* ============ PANGO BACKEND ============ */
154 /* ======================================= */
155
156 #define EMPH_ITAL 1
157 #define EMPH_BOLD 2
158 #define EMPH_HIDDEN 4
159 static PangoAttrList *attr_lists[4];
160 static int fontwidths[4][128];
161
162 static PangoAttribute *
xtext_pango_attr(PangoAttribute * attr)163 xtext_pango_attr (PangoAttribute *attr)
164 {
165 attr->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
166 attr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
167 return attr;
168 }
169
170 static void
xtext_pango_init(GtkXText * xtext)171 xtext_pango_init (GtkXText *xtext)
172 {
173 int i, j;
174 char buf[2] = "\000";
175
176 if (attr_lists[0])
177 {
178 for (i = 0; i < (EMPH_ITAL | EMPH_BOLD); i++)
179 pango_attr_list_unref (attr_lists[i]);
180 }
181
182 for (i = 0; i < sizeof attr_lists / sizeof attr_lists[0]; i++)
183 {
184 attr_lists[i] = pango_attr_list_new ();
185 switch (i)
186 {
187 case 0: /* Roman */
188 break;
189 case EMPH_ITAL: /* Italic */
190 pango_attr_list_insert (attr_lists[i],
191 xtext_pango_attr (pango_attr_style_new (PANGO_STYLE_ITALIC)));
192 break;
193 case EMPH_BOLD: /* Bold */
194 pango_attr_list_insert (attr_lists[i],
195 xtext_pango_attr (pango_attr_weight_new (PANGO_WEIGHT_BOLD)));
196 break;
197 case EMPH_ITAL | EMPH_BOLD: /* Italic Bold */
198 pango_attr_list_insert (attr_lists[i],
199 xtext_pango_attr (pango_attr_style_new (PANGO_STYLE_ITALIC)));
200 pango_attr_list_insert (attr_lists[i],
201 xtext_pango_attr (pango_attr_weight_new (PANGO_WEIGHT_BOLD)));
202 break;
203 }
204
205 /* Now initialize fontwidths[i] */
206 pango_layout_set_attributes (xtext->layout, attr_lists[i]);
207 for (j = 0; j < 128; j++)
208 {
209 buf[0] = j;
210 pango_layout_set_text (xtext->layout, buf, 1);
211 pango_layout_get_pixel_size (xtext->layout, &fontwidths[i][j], NULL);
212 }
213 }
214 xtext->space_width = fontwidths[0][' '];
215 }
216
217 static void
backend_font_close(GtkXText * xtext)218 backend_font_close (GtkXText *xtext)
219 {
220 pango_font_description_free (xtext->font->font);
221 }
222
223 static void
backend_init(GtkXText * xtext)224 backend_init (GtkXText *xtext)
225 {
226 if (xtext->layout == NULL)
227 {
228 xtext->layout = gtk_widget_create_pango_layout (GTK_WIDGET (xtext), 0);
229 if (xtext->font)
230 pango_layout_set_font_description (xtext->layout, xtext->font->font);
231 }
232 }
233
234 static void
backend_deinit(GtkXText * xtext)235 backend_deinit (GtkXText *xtext)
236 {
237 if (xtext->layout)
238 {
239 g_object_unref (xtext->layout);
240 xtext->layout = NULL;
241 }
242 }
243
244 static PangoFontDescription *
backend_font_open_real(char * name)245 backend_font_open_real (char *name)
246 {
247 PangoFontDescription *font;
248
249 font = pango_font_description_from_string (name);
250 if (font && pango_font_description_get_size (font) == 0)
251 {
252 pango_font_description_free (font);
253 font = pango_font_description_from_string ("sans 11");
254 }
255 if (!font)
256 font = pango_font_description_from_string ("sans 11");
257
258 return font;
259 }
260
261 static void
backend_font_open(GtkXText * xtext,char * name)262 backend_font_open (GtkXText *xtext, char *name)
263 {
264 PangoLanguage *lang;
265 PangoContext *context;
266 PangoFontMetrics *metrics;
267
268 xtext->font = &xtext->pango_font;
269 xtext->font->font = backend_font_open_real (name);
270 if (!xtext->font->font)
271 {
272 xtext->font = NULL;
273 return;
274 }
275
276 backend_init (xtext);
277 pango_layout_set_font_description (xtext->layout, xtext->font->font);
278 xtext_pango_init (xtext);
279
280 /* vte does it this way */
281 context = gtk_widget_get_pango_context (GTK_WIDGET (xtext));
282 lang = pango_context_get_language (context);
283 metrics = pango_context_get_metrics (context, xtext->font->font, lang);
284 xtext->font->ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
285 xtext->font->descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
286
287 /*
288 * In later versions of pango, a font's height should be calculated like
289 * this to account for line gap; a typical symptom of not doing so is
290 * cutting off the underscore on some fonts.
291 */
292 #if PANGO_VERSION_CHECK(1, 44, 0)
293 xtext->fontsize = pango_font_metrics_get_height (metrics) / PANGO_SCALE + 1;
294
295 if (xtext->fontsize == 0)
296 xtext->fontsize = xtext->font->ascent + xtext->font->descent;
297 #else
298 xtext->fontsize = xtext->font->ascent + xtext->font->descent;
299 #endif
300
301 pango_font_metrics_unref (metrics);
302 }
303
304 static int
backend_get_text_width_emph(GtkXText * xtext,guchar * str,int len,int emphasis)305 backend_get_text_width_emph (GtkXText *xtext, guchar *str, int len, int emphasis)
306 {
307 int width;
308 int deltaw;
309 int mbl;
310
311 if (*str == 0)
312 return 0;
313
314 if ((emphasis & EMPH_HIDDEN))
315 return 0;
316 emphasis &= (EMPH_ITAL | EMPH_BOLD);
317
318 width = 0;
319 pango_layout_set_attributes (xtext->layout, attr_lists[emphasis]);
320 while (len > 0)
321 {
322 mbl = charlen(str);
323 if (*str < 128)
324 deltaw = fontwidths[emphasis][*str];
325 else
326 {
327 pango_layout_set_text (xtext->layout, str, mbl);
328 pango_layout_get_pixel_size (xtext->layout, &deltaw, NULL);
329 }
330 width += deltaw;
331 str += mbl;
332 len -= mbl;
333 }
334
335 return width;
336 }
337
338 static int
backend_get_text_width_slp(GtkXText * xtext,guchar * str,GSList * slp)339 backend_get_text_width_slp (GtkXText *xtext, guchar *str, GSList *slp)
340 {
341 int width = 0;
342
343 while (slp)
344 {
345 offlen_t *meta;
346
347 meta = slp->data;
348 width += backend_get_text_width_emph (xtext, str, meta->len, meta->emph);
349 str += meta->len;
350 slp = g_slist_next (slp);
351 }
352
353 return width;
354 }
355
356 /* simplified version of gdk_draw_layout_line_with_colors() */
357
358 static void
xtext_draw_layout_line(GdkDrawable * drawable,GdkGC * gc,gint x,gint y,PangoLayoutLine * line)359 xtext_draw_layout_line (GdkDrawable *drawable,
360 GdkGC *gc,
361 gint x,
362 gint y,
363 PangoLayoutLine *line)
364 {
365 GSList *tmp_list = line->runs;
366 PangoRectangle logical_rect;
367 gint x_off = 0;
368
369 while (tmp_list)
370 {
371 PangoLayoutRun *run = tmp_list->data;
372
373 pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
374 NULL, &logical_rect);
375
376 gdk_draw_glyphs (drawable, gc, run->item->analysis.font,
377 x + x_off / PANGO_SCALE, y, run->glyphs);
378
379 x_off += logical_rect.width;
380 tmp_list = tmp_list->next;
381 }
382 }
383
384 static void
backend_draw_text_emph(GtkXText * xtext,int dofill,GdkGC * gc,int x,int y,char * str,int len,int str_width,int emphasis)385 backend_draw_text_emph (GtkXText *xtext, int dofill, GdkGC *gc, int x, int y,
386 char *str, int len, int str_width, int emphasis)
387 {
388 GdkGCValues val;
389 GdkColor col;
390 PangoLayoutLine *line;
391
392 pango_layout_set_attributes (xtext->layout, attr_lists[emphasis]);
393 pango_layout_set_text (xtext->layout, str, len);
394
395 if (dofill)
396 {
397 gdk_gc_get_values (gc, &val);
398 col.pixel = val.background.pixel;
399 gdk_gc_set_foreground (gc, &col);
400 gdk_draw_rectangle (xtext->draw_buf, gc, 1, x, y -
401 xtext->font->ascent, str_width, xtext->fontsize);
402 col.pixel = val.foreground.pixel;
403 gdk_gc_set_foreground (gc, &col);
404 }
405
406 line = pango_layout_get_lines (xtext->layout)->data;
407
408 xtext_draw_layout_line (xtext->draw_buf, gc, x, y, line);
409 }
410
411 static void
xtext_set_fg(GtkXText * xtext,GdkGC * gc,int index)412 xtext_set_fg (GtkXText *xtext, GdkGC *gc, int index)
413 {
414 gdk_gc_set_foreground (gc, &xtext->palette[index]);
415 }
416
417 static void
xtext_set_bg(GtkXText * xtext,GdkGC * gc,int index)418 xtext_set_bg (GtkXText *xtext, GdkGC *gc, int index)
419 {
420 gdk_gc_set_background (gc, &xtext->palette[index]);
421 }
422
423 static void
gtk_xtext_init(GtkXText * xtext)424 gtk_xtext_init (GtkXText * xtext)
425 {
426 xtext->pixmap = NULL;
427 xtext->io_tag = 0;
428 xtext->add_io_tag = 0;
429 xtext->scroll_tag = 0;
430 xtext->max_lines = 0;
431 xtext->col_back = XTEXT_BG;
432 xtext->col_fore = XTEXT_FG;
433 xtext->nc = 0;
434 xtext->pixel_offset = 0;
435 xtext->underline = FALSE;
436 xtext->strikethrough = FALSE;
437 xtext->hidden = FALSE;
438 xtext->font = NULL;
439 xtext->layout = NULL;
440 xtext->jump_out_offset = 0;
441 xtext->jump_in_offset = 0;
442 xtext->ts_x = 0;
443 xtext->ts_y = 0;
444 xtext->clip_x = 0;
445 xtext->clip_x2 = 1000000;
446 xtext->clip_y = 0;
447 xtext->clip_y2 = 1000000;
448 xtext->urlcheck_function = NULL;
449 xtext->color_paste = FALSE;
450 xtext->skip_border_fills = FALSE;
451 xtext->skip_stamp = FALSE;
452 xtext->render_hilights_only = FALSE;
453 xtext->un_hilight = FALSE;
454 xtext->recycle = FALSE;
455 xtext->dont_render = FALSE;
456 xtext->dont_render2 = FALSE;
457 gtk_xtext_scroll_adjustments (xtext, NULL, NULL);
458
459 {
460 static const GtkTargetEntry targets[] = {
461 { "UTF8_STRING", 0, TARGET_UTF8_STRING },
462 { "STRING", 0, TARGET_STRING },
463 { "TEXT", 0, TARGET_TEXT },
464 { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }
465 };
466 static const gint n_targets = sizeof (targets) / sizeof (targets[0]);
467
468 gtk_selection_add_targets (GTK_WIDGET (xtext), GDK_SELECTION_PRIMARY,
469 targets, n_targets);
470 }
471 }
472
473 static void
gtk_xtext_adjustment_set(xtext_buffer * buf,int fire_signal)474 gtk_xtext_adjustment_set (xtext_buffer *buf, int fire_signal)
475 {
476 GtkAdjustment *adj = buf->xtext->adj;
477
478 if (buf->xtext->buffer == buf)
479 {
480 adj->lower = 0;
481 adj->upper = buf->num_lines;
482
483 if (adj->upper == 0)
484 adj->upper = 1;
485
486 adj->page_size = GTK_WIDGET (buf->xtext)->allocation.height /
487 buf->xtext->fontsize;
488 adj->page_increment = adj->page_size;
489
490 if (adj->value > adj->upper - adj->page_size)
491 {
492 buf->scrollbar_down = TRUE;
493 adj->value = adj->upper - adj->page_size;
494 }
495
496 if (adj->value < 0)
497 adj->value = 0;
498
499 if (fire_signal)
500 gtk_adjustment_changed (adj);
501 }
502 }
503
504 static gint
gtk_xtext_adjustment_timeout(GtkXText * xtext)505 gtk_xtext_adjustment_timeout (GtkXText * xtext)
506 {
507 gtk_xtext_render_page (xtext);
508 xtext->io_tag = 0;
509 return 0;
510 }
511
512 static void
gtk_xtext_adjustment_changed(GtkAdjustment * adj,GtkXText * xtext)513 gtk_xtext_adjustment_changed (GtkAdjustment * adj, GtkXText * xtext)
514 {
515 if (!gtk_widget_get_realized (GTK_WIDGET (xtext)))
516 return;
517
518 if (xtext->buffer->old_value != xtext->adj->value)
519 {
520 if (xtext->adj->value >= xtext->adj->upper - xtext->adj->page_size)
521 xtext->buffer->scrollbar_down = TRUE;
522 else
523 xtext->buffer->scrollbar_down = FALSE;
524
525 if (xtext->adj->value + 1 == xtext->buffer->old_value ||
526 xtext->adj->value - 1 == xtext->buffer->old_value) /* clicked an arrow? */
527 {
528 if (xtext->io_tag)
529 {
530 g_source_remove (xtext->io_tag);
531 xtext->io_tag = 0;
532 }
533 gtk_xtext_render_page (xtext);
534 } else
535 {
536 if (!xtext->io_tag)
537 xtext->io_tag = g_timeout_add (REFRESH_TIMEOUT,
538 (GSourceFunc)
539 gtk_xtext_adjustment_timeout,
540 xtext);
541 }
542 }
543 xtext->buffer->old_value = adj->value;
544 }
545
546 GtkWidget *
gtk_xtext_new(GdkColor palette[],int separator)547 gtk_xtext_new (GdkColor palette[], int separator)
548 {
549 GtkXText *xtext;
550
551 xtext = g_object_new (gtk_xtext_get_type (), NULL);
552 xtext->separator = separator;
553 xtext->wordwrap = TRUE;
554 xtext->buffer = gtk_xtext_buffer_new (xtext);
555 xtext->orig_buffer = xtext->buffer;
556
557 gtk_widget_set_double_buffered (GTK_WIDGET (xtext), FALSE);
558 gtk_xtext_set_palette (xtext, palette);
559
560 return GTK_WIDGET (xtext);
561 }
562
563 static void
gtk_xtext_destroy(GtkObject * object)564 gtk_xtext_destroy (GtkObject * object)
565 {
566 GtkXText *xtext = GTK_XTEXT (object);
567
568 if (xtext->add_io_tag)
569 {
570 g_source_remove (xtext->add_io_tag);
571 xtext->add_io_tag = 0;
572 }
573
574 if (xtext->scroll_tag)
575 {
576 g_source_remove (xtext->scroll_tag);
577 xtext->scroll_tag = 0;
578 }
579
580 if (xtext->io_tag)
581 {
582 g_source_remove (xtext->io_tag);
583 xtext->io_tag = 0;
584 }
585
586 if (xtext->pixmap)
587 {
588 g_object_unref (xtext->pixmap);
589 xtext->pixmap = NULL;
590 }
591
592 if (xtext->font)
593 {
594 backend_font_close (xtext);
595 xtext->font = NULL;
596 }
597
598 if (xtext->adj)
599 {
600 g_signal_handlers_disconnect_matched (G_OBJECT (xtext->adj),
601 G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xtext);
602 /* gtk_signal_disconnect_by_data (G_OBJECT (xtext->adj), xtext);*/
603 g_object_unref (G_OBJECT (xtext->adj));
604 xtext->adj = NULL;
605 }
606
607 if (xtext->bgc)
608 {
609 g_object_unref (xtext->bgc);
610 xtext->bgc = NULL;
611 }
612
613 if (xtext->fgc)
614 {
615 g_object_unref (xtext->fgc);
616 xtext->fgc = NULL;
617 }
618
619 if (xtext->light_gc)
620 {
621 g_object_unref (xtext->light_gc);
622 xtext->light_gc = NULL;
623 }
624
625 if (xtext->dark_gc)
626 {
627 g_object_unref (xtext->dark_gc);
628 xtext->dark_gc = NULL;
629 }
630
631 if (xtext->thin_gc)
632 {
633 g_object_unref (xtext->thin_gc);
634 xtext->thin_gc = NULL;
635 }
636
637 if (xtext->marker_gc)
638 {
639 g_object_unref (xtext->marker_gc);
640 xtext->marker_gc = NULL;
641 }
642
643 if (xtext->hand_cursor)
644 {
645 gdk_cursor_unref (xtext->hand_cursor);
646 xtext->hand_cursor = NULL;
647 }
648
649 if (xtext->resize_cursor)
650 {
651 gdk_cursor_unref (xtext->resize_cursor);
652 xtext->resize_cursor = NULL;
653 }
654
655 if (xtext->orig_buffer)
656 {
657 gtk_xtext_buffer_free (xtext->orig_buffer);
658 xtext->orig_buffer = NULL;
659 }
660
661 if (GTK_OBJECT_CLASS (parent_class)->destroy)
662 (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
663 }
664
665 static void
gtk_xtext_unrealize(GtkWidget * widget)666 gtk_xtext_unrealize (GtkWidget * widget)
667 {
668 backend_deinit (GTK_XTEXT (widget));
669
670 /* if there are still events in the queue, this'll avoid segfault */
671 gdk_window_set_user_data (widget->window, NULL);
672
673 if (parent_class->unrealize)
674 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
675 }
676
677 static void
gtk_xtext_realize(GtkWidget * widget)678 gtk_xtext_realize (GtkWidget * widget)
679 {
680 GtkXText *xtext;
681 GdkWindowAttr attributes;
682 GdkGCValues val;
683 GdkColor col;
684 GdkColormap *cmap;
685
686 gtk_widget_set_realized (widget, TRUE);
687 xtext = GTK_XTEXT (widget);
688
689 attributes.x = widget->allocation.x;
690 attributes.y = widget->allocation.y;
691 attributes.width = widget->allocation.width;
692 attributes.height = widget->allocation.height;
693 attributes.wclass = GDK_INPUT_OUTPUT;
694 attributes.window_type = GDK_WINDOW_CHILD;
695 attributes.event_mask = gtk_widget_get_events (widget) |
696 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
697 | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK;
698
699 cmap = gtk_widget_get_colormap (widget);
700 attributes.colormap = cmap;
701 attributes.visual = gtk_widget_get_visual (widget);
702
703 widget->window = gdk_window_new (widget->parent->window, &attributes,
704 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL |
705 GDK_WA_COLORMAP);
706
707 gdk_window_set_user_data (widget->window, widget);
708
709 xtext->depth = gdk_window_get_visual (widget->window)->depth;
710
711 val.subwindow_mode = GDK_INCLUDE_INFERIORS;
712 val.graphics_exposures = 0;
713
714 xtext->bgc = gdk_gc_new_with_values (widget->window, &val,
715 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
716 xtext->fgc = gdk_gc_new_with_values (widget->window, &val,
717 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
718 xtext->light_gc = gdk_gc_new_with_values (widget->window, &val,
719 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
720 xtext->dark_gc = gdk_gc_new_with_values (widget->window, &val,
721 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
722 xtext->thin_gc = gdk_gc_new_with_values (widget->window, &val,
723 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
724 xtext->marker_gc = gdk_gc_new_with_values (widget->window, &val,
725 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
726
727 /* for the separator bar (light) */
728 col.red = 0xffff; col.green = 0xffff; col.blue = 0xffff;
729 gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE);
730 gdk_gc_set_foreground (xtext->light_gc, &col);
731
732 /* for the separator bar (dark) */
733 col.red = 0x1111; col.green = 0x1111; col.blue = 0x1111;
734 gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE);
735 gdk_gc_set_foreground (xtext->dark_gc, &col);
736
737 /* for the separator bar (thinline) */
738 col.red = 0x8e38; col.green = 0x8e38; col.blue = 0x9f38;
739 gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE);
740 gdk_gc_set_foreground (xtext->thin_gc, &col);
741
742 /* for the marker bar (marker) */
743 gdk_gc_set_foreground (xtext->marker_gc, &xtext->palette[XTEXT_MARKER]);
744
745 xtext_set_fg (xtext, xtext->fgc, XTEXT_FG);
746 xtext_set_bg (xtext, xtext->fgc, XTEXT_BG);
747 xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
748
749 /* draw directly to window */
750 xtext->draw_buf = widget->window;
751
752 if (xtext->pixmap)
753 {
754 gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
755 gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
756 xtext->ts_x = xtext->ts_y = 0;
757 gdk_gc_set_fill (xtext->bgc, GDK_TILED);
758 }
759
760 xtext->hand_cursor = gdk_cursor_new_for_display (gdk_window_get_display (widget->window), GDK_HAND1);
761 xtext->resize_cursor = gdk_cursor_new_for_display (gdk_window_get_display (widget->window), GDK_LEFT_SIDE);
762
763 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
764 widget->style = gtk_style_attach (widget->style, widget->window);
765
766 backend_init (xtext);
767 }
768
769 static void
gtk_xtext_size_request(GtkWidget * widget,GtkRequisition * requisition)770 gtk_xtext_size_request (GtkWidget * widget, GtkRequisition * requisition)
771 {
772 requisition->width = 200;
773 requisition->height = 90;
774 }
775
776 static void
gtk_xtext_size_allocate(GtkWidget * widget,GtkAllocation * allocation)777 gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
778 {
779 GtkXText *xtext = GTK_XTEXT (widget);
780 int height_only = FALSE;
781
782 if (allocation->width == xtext->buffer->window_width)
783 height_only = TRUE;
784
785 widget->allocation = *allocation;
786 if (gtk_widget_get_realized (GTK_WIDGET(widget)))
787 {
788 xtext->buffer->window_width = allocation->width;
789 xtext->buffer->window_height = allocation->height;
790
791 gdk_window_move_resize (widget->window, allocation->x, allocation->y,
792 allocation->width, allocation->height);
793 dontscroll (xtext->buffer); /* force scrolling off */
794 if (!height_only)
795 gtk_xtext_calc_lines (xtext->buffer, FALSE);
796 else
797 {
798 xtext->buffer->pagetop_ent = NULL;
799 gtk_xtext_adjustment_set (xtext->buffer, FALSE);
800 }
801 if (xtext->buffer->scrollbar_down)
802 gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
803 xtext->adj->page_size);
804 }
805 }
806
807 static int
gtk_xtext_selection_clear(xtext_buffer * buf)808 gtk_xtext_selection_clear (xtext_buffer *buf)
809 {
810 textentry *ent;
811 int ret = 0;
812
813 ent = buf->last_ent_start;
814 while (ent)
815 {
816 if (ent->mark_start != -1)
817 ret = 1;
818 ent->mark_start = -1;
819 ent->mark_end = -1;
820 if (ent == buf->last_ent_end)
821 break;
822 ent = ent->next;
823 }
824
825 return ret;
826 }
827
828 static int
find_x(GtkXText * xtext,textentry * ent,int x,int subline,int indent)829 find_x (GtkXText *xtext, textentry *ent, int x, int subline, int indent)
830 {
831 int xx = indent;
832 int suboff;
833 GSList *list;
834 GSList *hid = NULL;
835 offlen_t *meta;
836 int off, len, wid, mbl, mbw;
837
838 /* Skip to the first chunk of stuff for the subline */
839 if (subline > 0)
840 {
841 suboff = GPOINTER_TO_INT (g_slist_nth_data (ent->sublines, subline - 1));
842 for (list = ent->slp; list; list = g_slist_next (list))
843 {
844 meta = list->data;
845 if (meta->off + meta->len > suboff)
846 break;
847 }
848 }
849 else
850 {
851 suboff = 0;
852 list = ent->slp;
853 }
854 /* Step to the first character of the subline */
855 if (list == NULL)
856 return 0;
857 meta = list->data;
858 off = meta->off;
859 len = meta->len;
860 if (meta->emph & EMPH_HIDDEN)
861 hid = list;
862 while (len > 0)
863 {
864 if (off >= suboff)
865 break;
866 mbl = charlen (ent->str + off);
867 len -= mbl;
868 off += mbl;
869 }
870 if (len < 0)
871 return ent->str_len; /* Bad char -- return max offset. */
872
873 /* Step through characters to find the one at the x position */
874 wid = x - indent;
875 len = meta->len - (off - meta->off);
876 while (wid > 0)
877 {
878 mbl = charlen (ent->str + off);
879 mbw = backend_get_text_width_emph (xtext, ent->str + off, mbl, meta->emph);
880 wid -= mbw;
881 xx += mbw;
882 if (xx >= x)
883 return off;
884 len -= mbl;
885 off += mbl;
886 if (len <= 0)
887 {
888 if (meta->emph & EMPH_HIDDEN)
889 hid = list;
890 list = g_slist_next (list);
891 if (list == NULL)
892 return ent->str_len;
893 meta = list->data;
894 off = meta->off;
895 len = meta->len;
896 }
897 }
898
899 /* If previous chunk exists and is marked hidden, regard it as unhidden */
900 if (hid && list && hid->next == list)
901 {
902 meta = hid->data;
903 off = meta->off;
904 }
905
906 /* Return offset of character at x within subline */
907 return off;
908 }
909
910 static int
gtk_xtext_find_x(GtkXText * xtext,int x,textentry * ent,int subline,int line,int * out_of_bounds)911 gtk_xtext_find_x (GtkXText * xtext, int x, textentry * ent, int subline,
912 int line, int *out_of_bounds)
913 {
914 int indent;
915 unsigned char *str;
916
917 if (subline < 1)
918 indent = ent->indent;
919 else
920 indent = xtext->buffer->indent;
921
922 if (line > xtext->adj->page_size || line < 0)
923 {
924 *out_of_bounds = TRUE;
925 return 0;
926 }
927
928 str = ent->str + gtk_xtext_find_subline (xtext, ent, subline);
929 if (str >= ent->str + ent->str_len)
930 return 0;
931
932 /* Let user select left a few pixels to grab hidden text e.g. '<' */
933 if (x < indent - xtext->space_width)
934 {
935 *out_of_bounds = 1;
936 return (str - ent->str);
937 }
938
939 *out_of_bounds = 0;
940
941 return find_x (xtext, ent, x, subline, indent);
942 }
943
944 static textentry *
gtk_xtext_find_char(GtkXText * xtext,int x,int y,int * off,int * out_of_bounds)945 gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off, int *out_of_bounds)
946 {
947 textentry *ent;
948 int line;
949 int subline;
950 int outofbounds;
951
952 /* Adjust y value for negative rounding, double to int */
953 if (y < 0)
954 y -= xtext->fontsize;
955
956 line = (y + xtext->pixel_offset) / xtext->fontsize;
957 ent = gtk_xtext_nth (xtext, line + (int)xtext->adj->value, &subline);
958 if (!ent)
959 return NULL;
960
961 if (off)
962 *off = gtk_xtext_find_x (xtext, x, ent, subline, line, &outofbounds);
963 if (out_of_bounds)
964 *out_of_bounds = outofbounds;
965
966 return ent;
967 }
968
969 static void
gtk_xtext_draw_sep(GtkXText * xtext,int y)970 gtk_xtext_draw_sep (GtkXText * xtext, int y)
971 {
972 int x, height;
973 GdkGC *light, *dark;
974
975 if (y == -1)
976 {
977 y = 0;
978 height = GTK_WIDGET (xtext)->allocation.height;
979 } else
980 {
981 height = xtext->fontsize;
982 }
983
984 /* draw the separator line */
985 if (xtext->separator && xtext->buffer->indent)
986 {
987 light = xtext->light_gc;
988 dark = xtext->dark_gc;
989
990 x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
991 if (x < 1)
992 return;
993
994 if (xtext->thinline)
995 {
996 if (xtext->moving_separator)
997 gdk_draw_line (xtext->draw_buf, light, x, y, x, y + height);
998 else
999 gdk_draw_line (xtext->draw_buf, xtext->thin_gc, x, y, x, y + height);
1000 } else
1001 {
1002 if (xtext->moving_separator)
1003 {
1004 gdk_draw_line (xtext->draw_buf, light, x - 1, y, x - 1, y + height);
1005 gdk_draw_line (xtext->draw_buf, dark, x, y, x, y + height);
1006 } else
1007 {
1008 gdk_draw_line (xtext->draw_buf, dark, x - 1, y, x - 1, y + height);
1009 gdk_draw_line (xtext->draw_buf, light, x, y, x, y + height);
1010 }
1011 }
1012 }
1013 }
1014
1015 static void
gtk_xtext_draw_marker(GtkXText * xtext,textentry * ent,int y)1016 gtk_xtext_draw_marker (GtkXText * xtext, textentry * ent, int y)
1017 {
1018 int x, width, render_y;
1019
1020 if (!xtext->marker) return;
1021
1022 if (xtext->buffer->marker_pos == ent)
1023 {
1024 render_y = y + xtext->font->descent;
1025 }
1026 else if (xtext->buffer->marker_pos == ent->next && ent->next != NULL)
1027 {
1028 render_y = y + xtext->font->descent + xtext->fontsize * g_slist_length (ent->sublines);
1029 }
1030 else return;
1031
1032 x = 0;
1033 width = GTK_WIDGET (xtext)->allocation.width;
1034
1035 gdk_draw_line (xtext->draw_buf, xtext->marker_gc, x, render_y, x + width, render_y);
1036
1037 if (gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (xtext)))))
1038 {
1039 xtext->buffer->marker_seen = TRUE;
1040 }
1041 }
1042
1043 static void
gtk_xtext_paint(GtkWidget * widget,GdkRectangle * area)1044 gtk_xtext_paint (GtkWidget *widget, GdkRectangle *area)
1045 {
1046 GtkXText *xtext = GTK_XTEXT (widget);
1047 textentry *ent_start, *ent_end;
1048 int x, y;
1049
1050 if (area->x == 0 && area->y == 0 &&
1051 area->height == widget->allocation.height &&
1052 area->width == widget->allocation.width)
1053 {
1054 dontscroll (xtext->buffer); /* force scrolling off */
1055 gtk_xtext_render_page (xtext);
1056 return;
1057 }
1058
1059 ent_start = gtk_xtext_find_char (xtext, area->x, area->y, NULL, NULL);
1060 if (!ent_start)
1061 {
1062 xtext_draw_bg (xtext, area->x, area->y, area->width, area->height);
1063 goto xit;
1064 }
1065 ent_end = gtk_xtext_find_char (xtext, area->x + area->width,
1066 area->y + area->height, NULL, NULL);
1067 if (!ent_end)
1068 ent_end = xtext->buffer->text_last;
1069
1070 /* can't set a clip here, because fgc/bgc are used to draw the DB too */
1071 /* backend_set_clip (xtext, area);*/
1072 xtext->clip_x = area->x;
1073 xtext->clip_x2 = area->x + area->width;
1074 xtext->clip_y = area->y;
1075 xtext->clip_y2 = area->y + area->height;
1076
1077 /* y is the last pixel y location it rendered text at */
1078 y = gtk_xtext_render_ents (xtext, ent_start, ent_end);
1079
1080 if (y && y < widget->allocation.height && !ent_end->next)
1081 {
1082 GdkRectangle rect;
1083
1084 rect.x = 0;
1085 rect.y = y;
1086 rect.width = widget->allocation.width;
1087 rect.height = widget->allocation.height - y;
1088
1089 /* fill any space below the last line that also intersects with
1090 the exposure rectangle */
1091 if (gdk_rectangle_intersect (area, &rect, &rect))
1092 {
1093 xtext_draw_bg (xtext, rect.x, rect.y, rect.width, rect.height);
1094 }
1095 }
1096
1097 /*backend_clear_clip (xtext);*/
1098 xtext->clip_x = 0;
1099 xtext->clip_x2 = 1000000;
1100 xtext->clip_y = 0;
1101 xtext->clip_y2 = 1000000;
1102
1103 xit:
1104 x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
1105 if (area->x <= x)
1106 gtk_xtext_draw_sep (xtext, -1);
1107 }
1108
1109 static gboolean
gtk_xtext_expose(GtkWidget * widget,GdkEventExpose * event)1110 gtk_xtext_expose (GtkWidget * widget, GdkEventExpose * event)
1111 {
1112 gtk_xtext_paint (widget, &event->area);
1113 return FALSE;
1114 }
1115
1116 /* render a selection that has extended or contracted upward */
1117
1118 static void
gtk_xtext_selection_up(GtkXText * xtext,textentry * start,textentry * end,int start_offset)1119 gtk_xtext_selection_up (GtkXText *xtext, textentry *start, textentry *end,
1120 int start_offset)
1121 {
1122 /* render all the complete lines */
1123 if (start->next == end)
1124 gtk_xtext_render_ents (xtext, end, NULL);
1125 else
1126 gtk_xtext_render_ents (xtext, start->next, end);
1127
1128 /* now the incomplete upper line */
1129 if (start == xtext->buffer->last_ent_start)
1130 xtext->jump_in_offset = xtext->buffer->last_offset_start;
1131 else
1132 xtext->jump_in_offset = start_offset;
1133 gtk_xtext_render_ents (xtext, start, NULL);
1134 xtext->jump_in_offset = 0;
1135 }
1136
1137 /* render a selection that has extended or contracted downward */
1138
1139 static void
gtk_xtext_selection_down(GtkXText * xtext,textentry * start,textentry * end,int end_offset)1140 gtk_xtext_selection_down (GtkXText *xtext, textentry *start, textentry *end,
1141 int end_offset)
1142 {
1143 /* render all the complete lines */
1144 if (end->prev == start)
1145 gtk_xtext_render_ents (xtext, start, NULL);
1146 else
1147 gtk_xtext_render_ents (xtext, start, end->prev);
1148
1149 /* now the incomplete bottom line */
1150 if (end == xtext->buffer->last_ent_end)
1151 xtext->jump_out_offset = xtext->buffer->last_offset_end;
1152 else
1153 xtext->jump_out_offset = end_offset;
1154 gtk_xtext_render_ents (xtext, end, NULL);
1155 xtext->jump_out_offset = 0;
1156 }
1157
1158 static void
gtk_xtext_selection_render(GtkXText * xtext,textentry * start_ent,textentry * end_ent)1159 gtk_xtext_selection_render (GtkXText *xtext, textentry *start_ent, textentry *end_ent)
1160 {
1161 textentry *ent;
1162 int start_offset = start_ent->mark_start;
1163 int end_offset = end_ent->mark_end;
1164 int start, end;
1165
1166 xtext->skip_border_fills = TRUE;
1167 xtext->skip_stamp = TRUE;
1168
1169 /* force an optimized render if there was no previous selection */
1170 if (xtext->buffer->last_ent_start == NULL && start_ent == end_ent)
1171 {
1172 xtext->buffer->last_offset_start = start_offset;
1173 xtext->buffer->last_offset_end = end_offset;
1174 goto lamejump;
1175 }
1176
1177 /* mark changed within 1 ent only? */
1178 if (xtext->buffer->last_ent_start == start_ent &&
1179 xtext->buffer->last_ent_end == end_ent)
1180 {
1181 /* when only 1 end of the selection is changed, we can really
1182 save on rendering */
1183 if (xtext->buffer->last_offset_start == start_offset ||
1184 xtext->buffer->last_offset_end == end_offset)
1185 {
1186 lamejump:
1187 ent = end_ent;
1188 /* figure out where to start and end the rendering */
1189 if (end_offset > xtext->buffer->last_offset_end)
1190 {
1191 end = end_offset;
1192 start = xtext->buffer->last_offset_end;
1193 } else if (end_offset < xtext->buffer->last_offset_end)
1194 {
1195 end = xtext->buffer->last_offset_end;
1196 start = end_offset;
1197 } else if (start_offset < xtext->buffer->last_offset_start)
1198 {
1199 end = xtext->buffer->last_offset_start;
1200 start = start_offset;
1201 ent = start_ent;
1202 } else if (start_offset > xtext->buffer->last_offset_start)
1203 {
1204 end = start_offset;
1205 start = xtext->buffer->last_offset_start;
1206 ent = start_ent;
1207 } else
1208 { /* WORD selects end up here */
1209 end = end_offset;
1210 start = start_offset;
1211 }
1212 } else
1213 {
1214 /* LINE selects end up here */
1215 /* so which ent actually changed? */
1216 ent = start_ent;
1217 if (xtext->buffer->last_offset_start == start_offset)
1218 ent = end_ent;
1219
1220 end = MAX (xtext->buffer->last_offset_end, end_offset);
1221 start = MIN (xtext->buffer->last_offset_start, start_offset);
1222 }
1223
1224 xtext->jump_out_offset = end;
1225 xtext->jump_in_offset = start;
1226 gtk_xtext_render_ents (xtext, ent, NULL);
1227 xtext->jump_out_offset = 0;
1228 xtext->jump_in_offset = 0;
1229 }
1230 /* marking downward? */
1231 else if (xtext->buffer->last_ent_start == start_ent &&
1232 xtext->buffer->last_offset_start == start_offset)
1233 {
1234 /* find the range that covers both old and new selection */
1235 ent = start_ent;
1236 while (ent)
1237 {
1238 if (ent == xtext->buffer->last_ent_end)
1239 {
1240 gtk_xtext_selection_down (xtext, ent, end_ent, end_offset);
1241 /*gtk_xtext_render_ents (xtext, ent, end_ent);*/
1242 break;
1243 }
1244 if (ent == end_ent)
1245 {
1246 gtk_xtext_selection_down (xtext, ent, xtext->buffer->last_ent_end, end_offset);
1247 /*gtk_xtext_render_ents (xtext, ent, xtext->buffer->last_ent_end);*/
1248 break;
1249 }
1250 ent = ent->next;
1251 }
1252 }
1253 /* marking upward? */
1254 else if (xtext->buffer->last_ent_start != NULL &&
1255 xtext->buffer->last_ent_end == end_ent &&
1256 xtext->buffer->last_offset_end == end_offset)
1257 {
1258 ent = end_ent;
1259 while (ent)
1260 {
1261 if (ent == start_ent && xtext->buffer->last_ent_start)
1262 {
1263 gtk_xtext_selection_up (xtext, xtext->buffer->last_ent_start, ent, start_offset);
1264 /*gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, ent);*/
1265 break;
1266 }
1267 if (ent == xtext->buffer->last_ent_start)
1268 {
1269 gtk_xtext_selection_up (xtext, start_ent, ent, start_offset);
1270 /*gtk_xtext_render_ents (xtext, start_ent, ent);*/
1271 break;
1272 }
1273 ent = ent->prev;
1274 }
1275 }
1276 else /* cross-over mark (stretched or shrunk at both ends) */
1277 {
1278 /* unrender the old mark */
1279 gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, xtext->buffer->last_ent_end);
1280 /* now render the new mark, but skip overlaps */
1281 if (start_ent == xtext->buffer->last_ent_start)
1282 {
1283 /* if the new mark is a sub-set of the old, do nothing */
1284 if (start_ent != end_ent)
1285 gtk_xtext_render_ents (xtext, start_ent->next, end_ent);
1286 } else if (end_ent == xtext->buffer->last_ent_end)
1287 {
1288 /* if the new mark is a sub-set of the old, do nothing */
1289 if (start_ent != end_ent)
1290 gtk_xtext_render_ents (xtext, start_ent, end_ent->prev);
1291 } else
1292 gtk_xtext_render_ents (xtext, start_ent, end_ent);
1293 }
1294
1295 xtext->buffer->last_ent_start = start_ent;
1296 xtext->buffer->last_ent_end = end_ent;
1297 xtext->buffer->last_offset_start = start_offset;
1298 xtext->buffer->last_offset_end = end_offset;
1299
1300 xtext->skip_border_fills = FALSE;
1301 xtext->skip_stamp = FALSE;
1302 }
1303
1304 static void
gtk_xtext_selection_draw(GtkXText * xtext,GdkEventMotion * event,gboolean render)1305 gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean render)
1306 {
1307 textentry *ent;
1308 textentry *ent_end;
1309 textentry *ent_start;
1310 int offset_start = 0;
1311 int offset_end = 0;
1312 textentry *low_ent, *high_ent;
1313 int low_x, low_y, low_offs;
1314 int high_x, high_y, high_offs, high_len;
1315
1316 if (xtext->buffer->text_first == NULL)
1317 return;
1318
1319 ent_start = gtk_xtext_find_char (xtext, xtext->select_start_x, xtext->select_start_y, &offset_start, NULL);
1320 ent_end = gtk_xtext_find_char (xtext, xtext->select_end_x, xtext->select_end_y, &offset_end, NULL);
1321 if (ent_start == NULL && ent_end == NULL)
1322 return;
1323
1324 if ((ent_start != ent_end && xtext->select_start_y > xtext->select_end_y) || /* different entries */
1325 (ent_start == ent_end && offset_start > offset_end)) /* same entry, different character offsets */
1326 {
1327 /* marking up */
1328 low_ent = ent_end;
1329 low_x = xtext->select_end_x;
1330 low_y = xtext->select_end_y;
1331 low_offs = offset_end;
1332 high_ent = ent_start;
1333 high_x = xtext->select_start_x;
1334 high_y = xtext->select_start_y;
1335 high_offs = offset_start;
1336 }
1337 else
1338 {
1339 /* marking down */
1340 low_ent = ent_start;
1341 low_x = xtext->select_start_x;
1342 low_y = xtext->select_start_y;
1343 low_offs = offset_start;
1344 high_ent = ent_end;
1345 high_x = xtext->select_end_x;
1346 high_y = xtext->select_end_y;
1347 high_offs = offset_end;
1348 }
1349 if (low_ent == NULL)
1350 {
1351 low_ent = xtext->buffer->text_first;
1352 low_offs = 0;
1353 }
1354 if (high_ent == NULL)
1355 {
1356 high_ent = xtext->buffer->text_last;
1357 high_offs = high_ent->str_len;
1358 }
1359
1360 /* word selection */
1361 if (xtext->word_select)
1362 {
1363 /* a word selection cannot be started if the cursor is out of bounds in gtk_xtext_button_press */
1364 gtk_xtext_get_word (xtext, low_x, low_y, NULL, &low_offs, NULL, NULL);
1365
1366 /* in case the cursor is out of bounds we keep offset_end from gtk_xtext_find_char and fix the length */
1367 if (gtk_xtext_get_word (xtext, high_x, high_y, NULL, &high_offs, &high_len, NULL) == NULL)
1368 high_len = high_offs == high_ent->str_len? 0: -1; /* -1 for the space, 0 if at the end */
1369 high_offs += high_len;
1370 if (low_y < 0)
1371 low_offs = xtext->buffer->last_offset_start;
1372 if (high_y > xtext->buffer->window_height)
1373 high_offs = xtext->buffer->last_offset_end;
1374 }
1375 /* line/ent selection */
1376 else if (xtext->line_select)
1377 {
1378 low_offs = 0;
1379 high_offs = high_ent->str_len;
1380 }
1381 /* character selection */
1382 else
1383 {
1384 if (low_y < 0)
1385 low_offs = xtext->buffer->last_offset_start;
1386 if (high_y > xtext->buffer->window_height)
1387 high_offs = xtext->buffer->last_offset_end;
1388 }
1389
1390 /* set all the old mark_ fields to -1 */
1391 gtk_xtext_selection_clear (xtext->buffer);
1392
1393 low_ent->mark_start = low_offs;
1394 low_ent->mark_end = high_offs;
1395
1396 if (low_ent != high_ent)
1397 {
1398 low_ent->mark_end = low_ent->str_len;
1399 if (high_offs != 0)
1400 {
1401 high_ent->mark_start = 0;
1402 high_ent->mark_end = high_offs;
1403 }
1404
1405 /* set all the mark_ fields of the ents within the selection */
1406 ent = low_ent->next;
1407 while (ent && ent != high_ent)
1408 {
1409 ent->mark_start = 0;
1410 ent->mark_end = ent->str_len;
1411 ent = ent->next;
1412 }
1413 }
1414
1415 if (render)
1416 gtk_xtext_selection_render (xtext, low_ent, high_ent);
1417 }
1418
1419 static int
gtk_xtext_timeout_ms(GtkXText * xtext,int pixes)1420 gtk_xtext_timeout_ms (GtkXText *xtext, int pixes)
1421 {
1422 int apixes = abs(pixes);
1423
1424 if (apixes < 6) return 100;
1425 if (apixes < 12) return 50;
1426 if (apixes < 20) return 20;
1427 return 10;
1428 }
1429 static gint
gtk_xtext_scrolldown_timeout(GtkXText * xtext)1430 gtk_xtext_scrolldown_timeout (GtkXText * xtext)
1431 {
1432 int p_y, win_height;
1433 xtext_buffer *buf = xtext->buffer;
1434 GtkAdjustment *adj = xtext->adj;
1435
1436 gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
1437 win_height = gdk_window_get_height (gtk_widget_get_window (GTK_WIDGET (xtext)));
1438
1439 if (buf->last_ent_end == NULL || /* If context has changed OR */
1440 buf->pagetop_ent == NULL || /* pagetop_ent is reset OR */
1441 p_y <= win_height || /* pointer not below bottom margin OR */
1442 adj->value >= adj->upper - adj->page_size) /* we're scrolled to bottom */
1443 {
1444 xtext->scroll_tag = 0;
1445 return 0;
1446 }
1447
1448 xtext->select_start_y -= xtext->fontsize;
1449 xtext->select_start_adj++;
1450 adj->value++;
1451 gtk_adjustment_value_changed (adj);
1452 gtk_xtext_selection_draw (xtext, NULL, TRUE);
1453 gtk_xtext_render_ents (xtext, buf->pagetop_ent->next, buf->last_ent_end);
1454 xtext->scroll_tag = g_timeout_add (gtk_xtext_timeout_ms (xtext, p_y - win_height),
1455 (GSourceFunc)
1456 gtk_xtext_scrolldown_timeout,
1457 xtext);
1458
1459 return 0;
1460 }
1461
1462 static gint
gtk_xtext_scrollup_timeout(GtkXText * xtext)1463 gtk_xtext_scrollup_timeout (GtkXText * xtext)
1464 {
1465 int p_y;
1466 xtext_buffer *buf = xtext->buffer;
1467 GtkAdjustment *adj = xtext->adj;
1468 int delta_y;
1469
1470 gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
1471
1472 if (buf->last_ent_start == NULL || /* If context has changed OR */
1473 buf->pagetop_ent == NULL || /* pagetop_ent is reset OR */
1474 p_y >= 0 || /* not above top margin OR */
1475 adj->value == 0) /* we're scrolled to the top */
1476 {
1477 xtext->scroll_tag = 0;
1478 return 0;
1479 }
1480
1481 if (adj->value < 0)
1482 {
1483 delta_y = adj->value * xtext->fontsize;
1484 adj->value = 0;
1485 } else {
1486 delta_y = xtext->fontsize;
1487 adj->value--;
1488 }
1489 xtext->select_start_y += delta_y;
1490 xtext->select_start_adj = adj->value;
1491 gtk_adjustment_value_changed (adj);
1492 gtk_xtext_selection_draw (xtext, NULL, TRUE);
1493 gtk_xtext_render_ents (xtext, buf->pagetop_ent->prev, buf->last_ent_end);
1494 xtext->scroll_tag = g_timeout_add (gtk_xtext_timeout_ms (xtext, p_y),
1495 (GSourceFunc)
1496 gtk_xtext_scrollup_timeout,
1497 xtext);
1498
1499 return 0;
1500 }
1501
1502 static void
gtk_xtext_selection_update(GtkXText * xtext,GdkEventMotion * event,int p_y,gboolean render)1503 gtk_xtext_selection_update (GtkXText * xtext, GdkEventMotion * event, int p_y, gboolean render)
1504 {
1505 int win_height;
1506 int moved;
1507
1508 if (xtext->scroll_tag)
1509 {
1510 return;
1511 }
1512
1513 win_height = gdk_window_get_height (gtk_widget_get_window (GTK_WIDGET (xtext)));
1514
1515 /* selecting past top of window, scroll up! */
1516 if (p_y < 0 && xtext->adj->value >= 0)
1517 {
1518 gtk_xtext_scrollup_timeout (xtext);
1519 }
1520
1521 /* selecting past bottom of window, scroll down! */
1522 else if (p_y > win_height &&
1523 xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size))
1524 {
1525 gtk_xtext_scrolldown_timeout (xtext);
1526 }
1527 else
1528 {
1529 moved = (int)xtext->adj->value - xtext->select_start_adj;
1530 xtext->select_start_y -= (moved * xtext->fontsize);
1531 xtext->select_start_adj = xtext->adj->value;
1532 gtk_xtext_selection_draw (xtext, event, render);
1533 }
1534 }
1535
1536 static char *
gtk_xtext_get_word(GtkXText * xtext,int x,int y,textentry ** ret_ent,int * ret_off,int * ret_len,GSList ** slp)1537 gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
1538 int *ret_off, int *ret_len, GSList **slp)
1539 {
1540 textentry *ent;
1541 int offset;
1542 unsigned char *word;
1543 unsigned char *last, *end;
1544 int len;
1545 int out_of_bounds = 0;
1546 int len_to_offset = 0;
1547
1548 ent = gtk_xtext_find_char (xtext, x, y, &offset, &out_of_bounds);
1549 if (ent == NULL || out_of_bounds || offset < 0 || offset >= ent->str_len)
1550 return NULL;
1551
1552 word = ent->str + offset;
1553 while ((word = g_utf8_find_prev_char (ent->str, word)))
1554 {
1555 if (is_del (*word))
1556 {
1557 word++;
1558 len_to_offset--;
1559 break;
1560 }
1561 len_to_offset += charlen (word);
1562 }
1563 if (!word)
1564 word = ent->str;
1565
1566 /* remove color characters from the length */
1567 gtk_xtext_strip_color (word, len_to_offset, xtext->scratch_buffer, &len_to_offset, NULL, FALSE);
1568
1569 last = word;
1570 end = ent->str + ent->str_len;
1571 len = 0;
1572 do
1573 {
1574 if (is_del (*last))
1575 break;
1576 len += charlen (last);
1577 last = g_utf8_find_next_char (last, end);
1578 }
1579 while (last);
1580
1581 if (len > 0 && word[len-1]=='.')
1582 len--;
1583
1584 if (ret_ent)
1585 *ret_ent = ent;
1586 if (ret_off)
1587 *ret_off = word - ent->str;
1588 if (ret_len)
1589 *ret_len = len; /* Length before stripping */
1590
1591 word = gtk_xtext_strip_color (word, len, xtext->scratch_buffer, NULL, slp, FALSE);
1592
1593 /* avoid turning the cursor into a hand for non-url part of the word */
1594 if (xtext->urlcheck_function && xtext->urlcheck_function (GTK_WIDGET (xtext), word))
1595 {
1596 int start, end;
1597 url_last (&start, &end);
1598
1599 /* make sure we're not before the start of the match */
1600 if (len_to_offset < start)
1601 return NULL;
1602
1603 /* and not after it */
1604 if (len_to_offset - start >= end - start)
1605 return NULL;
1606 }
1607
1608 return word;
1609 }
1610
1611 static void
gtk_xtext_unrender_hilight(GtkXText * xtext)1612 gtk_xtext_unrender_hilight (GtkXText *xtext)
1613 {
1614 xtext->render_hilights_only = TRUE;
1615 xtext->skip_border_fills = TRUE;
1616 xtext->skip_stamp = TRUE;
1617 xtext->un_hilight = TRUE;
1618
1619 gtk_xtext_render_ents (xtext, xtext->hilight_ent, NULL);
1620
1621 xtext->render_hilights_only = FALSE;
1622 xtext->skip_border_fills = FALSE;
1623 xtext->skip_stamp = FALSE;
1624 xtext->un_hilight = FALSE;
1625 }
1626
1627 static gboolean
gtk_xtext_leave_notify(GtkWidget * widget,GdkEventCrossing * event)1628 gtk_xtext_leave_notify (GtkWidget * widget, GdkEventCrossing * event)
1629 {
1630 GtkXText *xtext = GTK_XTEXT (widget);
1631
1632 if (xtext->cursor_hand)
1633 {
1634 gtk_xtext_unrender_hilight (xtext);
1635 xtext->hilight_start = -1;
1636 xtext->hilight_end = -1;
1637 xtext->cursor_hand = FALSE;
1638 gdk_window_set_cursor (widget->window, 0);
1639 xtext->hilight_ent = NULL;
1640 }
1641
1642 if (xtext->cursor_resize)
1643 {
1644 gtk_xtext_unrender_hilight (xtext);
1645 xtext->hilight_start = -1;
1646 xtext->hilight_end = -1;
1647 xtext->cursor_resize = FALSE;
1648 gdk_window_set_cursor (widget->window, 0);
1649 xtext->hilight_ent = NULL;
1650 }
1651
1652 return FALSE;
1653 }
1654
1655 /* check if we should mark time stamps, and if a redraw is needed */
1656
1657 static gboolean
gtk_xtext_check_mark_stamp(GtkXText * xtext,GdkModifierType mask)1658 gtk_xtext_check_mark_stamp (GtkXText *xtext, GdkModifierType mask)
1659 {
1660 gboolean redraw = FALSE;
1661
1662 if ((mask & STATE_SHIFT || prefs.hex_text_autocopy_stamp)
1663 && (!prefs.hex_stamp_text || prefs.hex_text_indent))
1664 {
1665 if (!xtext->mark_stamp)
1666 {
1667 redraw = TRUE; /* must redraw all */
1668 xtext->mark_stamp = TRUE;
1669 }
1670 } else
1671 {
1672 if (xtext->mark_stamp)
1673 {
1674 redraw = TRUE; /* must redraw all */
1675 xtext->mark_stamp = FALSE;
1676 }
1677 }
1678 return redraw;
1679 }
1680
1681 static int
gtk_xtext_get_word_adjust(GtkXText * xtext,int x,int y,textentry ** word_ent,int * offset,int * len)1682 gtk_xtext_get_word_adjust (GtkXText *xtext, int x, int y, textentry **word_ent, int *offset, int *len)
1683 {
1684 GSList *slp = NULL;
1685 unsigned char *word;
1686 int word_type = 0;
1687
1688 word = gtk_xtext_get_word (xtext, x, y, word_ent, offset, len, &slp);
1689 if (word)
1690 {
1691 int laststart, lastend;
1692
1693 word_type = xtext->urlcheck_function (GTK_WIDGET (xtext), word);
1694 if (word_type > 0)
1695 {
1696 if (url_last (&laststart, &lastend))
1697 {
1698 int cumlen, startadj = 0, endadj = 0;
1699 offlen_t *meta;
1700 GSList *sl;
1701
1702 for (sl = slp, cumlen = 0; sl; sl = g_slist_next (sl))
1703 {
1704 meta = sl->data;
1705 startadj = meta->off - cumlen;
1706 cumlen += meta->len;
1707 if (laststart < cumlen)
1708 break;
1709 }
1710 for (sl = slp, cumlen = 0; sl; sl = g_slist_next (sl))
1711 {
1712 meta = sl->data;
1713 endadj = meta->off - cumlen;
1714 cumlen += meta->len;
1715 if (lastend < cumlen)
1716 break;
1717 }
1718 laststart += startadj;
1719 *offset += laststart;
1720 *len = lastend + endadj - laststart;
1721 }
1722 }
1723 }
1724 g_slist_free_full (slp, g_free);
1725
1726 return word_type;
1727 }
1728
1729 static gboolean
gtk_xtext_motion_notify(GtkWidget * widget,GdkEventMotion * event)1730 gtk_xtext_motion_notify (GtkWidget * widget, GdkEventMotion * event)
1731 {
1732 GtkXText *xtext = GTK_XTEXT (widget);
1733 GdkModifierType mask;
1734 int redraw, tmp, x, y, offset, len, line_x;
1735 textentry *word_ent;
1736 int word_type;
1737
1738 gdk_window_get_pointer (widget->window, &x, &y, &mask);
1739
1740 if (xtext->moving_separator)
1741 {
1742 if (x < (3 * widget->allocation.width) / 5 && x > 15)
1743 {
1744 tmp = xtext->buffer->indent;
1745 xtext->buffer->indent = x;
1746 gtk_xtext_fix_indent (xtext->buffer);
1747 if (tmp != xtext->buffer->indent)
1748 {
1749 gtk_xtext_recalc_widths (xtext->buffer, FALSE);
1750 if (xtext->buffer->scrollbar_down)
1751 gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
1752 xtext->adj->page_size);
1753 if (!xtext->io_tag)
1754 xtext->io_tag = g_timeout_add (REFRESH_TIMEOUT,
1755 (GSourceFunc)
1756 gtk_xtext_adjustment_timeout,
1757 xtext);
1758 }
1759 }
1760 return FALSE;
1761 }
1762
1763 if (xtext->button_down)
1764 {
1765 redraw = gtk_xtext_check_mark_stamp (xtext, mask);
1766 gtk_grab_add (widget);
1767 /*gdk_pointer_grab (widget->window, TRUE,
1768 GDK_BUTTON_RELEASE_MASK |
1769 GDK_BUTTON_MOTION_MASK, NULL, NULL, 0);*/
1770 xtext->select_end_x = x;
1771 xtext->select_end_y = y;
1772 gtk_xtext_selection_update (xtext, event, y, !redraw);
1773
1774 /* user has pressed or released SHIFT, must redraw entire selection */
1775 if (redraw)
1776 {
1777 xtext->force_stamp = TRUE;
1778 gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start,
1779 xtext->buffer->last_ent_end);
1780 xtext->force_stamp = FALSE;
1781 }
1782 return FALSE;
1783 }
1784
1785 if (xtext->separator && xtext->buffer->indent)
1786 {
1787 line_x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
1788 if (line_x == x || line_x == x + 1 || line_x == x - 1)
1789 {
1790 if (!xtext->cursor_resize)
1791 {
1792 gdk_window_set_cursor (GTK_WIDGET (xtext)->window,
1793 xtext->resize_cursor);
1794 xtext->cursor_hand = FALSE;
1795 xtext->cursor_resize = TRUE;
1796 }
1797 return FALSE;
1798 }
1799 }
1800
1801 if (xtext->urlcheck_function == NULL)
1802 return FALSE;
1803
1804 word_type = gtk_xtext_get_word_adjust (xtext, x, y, &word_ent, &offset, &len);
1805 if (word_type > 0)
1806 {
1807 if (!xtext->cursor_hand ||
1808 xtext->hilight_ent != word_ent ||
1809 xtext->hilight_start != offset ||
1810 xtext->hilight_end != offset + len)
1811 {
1812 if (!xtext->cursor_hand)
1813 {
1814 gdk_window_set_cursor (GTK_WIDGET (xtext)->window,
1815 xtext->hand_cursor);
1816 xtext->cursor_hand = TRUE;
1817 xtext->cursor_resize = FALSE;
1818 }
1819
1820 /* un-render the old hilight */
1821 if (xtext->hilight_ent)
1822 gtk_xtext_unrender_hilight (xtext);
1823
1824 xtext->hilight_ent = word_ent;
1825 xtext->hilight_start = offset;
1826 xtext->hilight_end = offset + len;
1827
1828 xtext->skip_border_fills = TRUE;
1829 xtext->render_hilights_only = TRUE;
1830 xtext->skip_stamp = TRUE;
1831
1832 gtk_xtext_render_ents (xtext, word_ent, NULL);
1833
1834 xtext->skip_border_fills = FALSE;
1835 xtext->render_hilights_only = FALSE;
1836 xtext->skip_stamp = FALSE;
1837 }
1838 return FALSE;
1839 }
1840
1841 gtk_xtext_leave_notify (widget, NULL);
1842
1843 return FALSE;
1844 }
1845
1846 static void
gtk_xtext_set_clip_owner(GtkWidget * xtext,GdkEventButton * event)1847 gtk_xtext_set_clip_owner (GtkWidget * xtext, GdkEventButton * event)
1848 {
1849 char *str;
1850 int len;
1851
1852 if (GTK_XTEXT (xtext)->selection_buffer &&
1853 GTK_XTEXT (xtext)->selection_buffer != GTK_XTEXT (xtext)->buffer)
1854 gtk_xtext_selection_clear (GTK_XTEXT (xtext)->selection_buffer);
1855
1856 GTK_XTEXT (xtext)->selection_buffer = GTK_XTEXT (xtext)->buffer;
1857
1858 str = gtk_xtext_selection_get_text (GTK_XTEXT (xtext), &len);
1859 if (str)
1860 {
1861 if (str[0])
1862 {
1863 gtk_clipboard_set_text (gtk_widget_get_clipboard (xtext, GDK_SELECTION_CLIPBOARD), str, len);
1864
1865 gtk_selection_owner_set (xtext, GDK_SELECTION_PRIMARY, event ? event->time : GDK_CURRENT_TIME);
1866 gtk_selection_owner_set (xtext, GDK_SELECTION_SECONDARY, event ? event->time : GDK_CURRENT_TIME);
1867 }
1868
1869 g_free (str);
1870 }
1871 }
1872
1873 void
gtk_xtext_copy_selection(GtkXText * xtext)1874 gtk_xtext_copy_selection (GtkXText *xtext)
1875 {
1876 gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), NULL);
1877 }
1878
1879 static void
gtk_xtext_unselect(GtkXText * xtext)1880 gtk_xtext_unselect (GtkXText *xtext)
1881 {
1882 xtext_buffer *buf = xtext->buffer;
1883
1884 xtext->skip_border_fills = TRUE;
1885 xtext->skip_stamp = TRUE;
1886
1887 xtext->jump_in_offset = buf->last_ent_start->mark_start;
1888 /* just a single ent was marked? */
1889 if (buf->last_ent_start == buf->last_ent_end)
1890 {
1891 xtext->jump_out_offset = buf->last_ent_start->mark_end;
1892 buf->last_ent_end = NULL;
1893 }
1894
1895 gtk_xtext_selection_clear (xtext->buffer);
1896
1897 /* FIXME: use jump_out on multi-line selects too! */
1898 xtext->jump_in_offset = 0;
1899 xtext->jump_out_offset = 0;
1900 gtk_xtext_render_ents (xtext, buf->last_ent_start, buf->last_ent_end);
1901
1902 xtext->skip_border_fills = FALSE;
1903 xtext->skip_stamp = FALSE;
1904
1905 xtext->buffer->last_ent_start = NULL;
1906 xtext->buffer->last_ent_end = NULL;
1907 }
1908
1909 static gboolean
gtk_xtext_button_release(GtkWidget * widget,GdkEventButton * event)1910 gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
1911 {
1912 GtkXText *xtext = GTK_XTEXT (widget);
1913 unsigned char *word;
1914 int old;
1915
1916 if (xtext->moving_separator)
1917 {
1918 xtext->moving_separator = FALSE;
1919 old = xtext->buffer->indent;
1920 if (event->x < (4 * widget->allocation.width) / 5 && event->x > 15)
1921 xtext->buffer->indent = event->x;
1922 gtk_xtext_fix_indent (xtext->buffer);
1923 if (xtext->buffer->indent != old)
1924 {
1925 gtk_xtext_recalc_widths (xtext->buffer, FALSE);
1926 gtk_xtext_adjustment_set (xtext->buffer, TRUE);
1927 gtk_xtext_render_page (xtext);
1928 } else
1929 gtk_xtext_draw_sep (xtext, -1);
1930 return FALSE;
1931 }
1932
1933 if (event->button == 1)
1934 {
1935 xtext->button_down = FALSE;
1936 if (xtext->scroll_tag)
1937 {
1938 g_source_remove (xtext->scroll_tag);
1939 xtext->scroll_tag = 0;
1940 }
1941
1942 gtk_grab_remove (widget);
1943 /*gdk_pointer_ungrab (0);*/
1944
1945 /* got a new selection? */
1946 if (xtext->buffer->last_ent_start)
1947 {
1948 xtext->color_paste = FALSE;
1949 if (event->state & STATE_CTRL || prefs.hex_text_autocopy_color)
1950 xtext->color_paste = TRUE;
1951 if (prefs.hex_text_autocopy_text)
1952 {
1953 gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
1954 }
1955 }
1956
1957 if (xtext->word_select || xtext->line_select)
1958 {
1959 xtext->word_select = FALSE;
1960 xtext->line_select = FALSE;
1961 return FALSE;
1962 }
1963
1964 if (xtext->select_start_x == event->x &&
1965 xtext->select_start_y == event->y &&
1966 xtext->buffer->last_ent_start)
1967 {
1968 gtk_xtext_unselect (xtext);
1969 xtext->mark_stamp = FALSE;
1970 return FALSE;
1971 }
1972
1973 if (!gtk_xtext_is_selecting (xtext))
1974 {
1975 word = gtk_xtext_get_word (xtext, event->x, event->y, 0, 0, 0, 0);
1976 g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0, word ? word : NULL, event);
1977 }
1978 }
1979
1980 return FALSE;
1981 }
1982
1983 static gboolean
gtk_xtext_button_press(GtkWidget * widget,GdkEventButton * event)1984 gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
1985 {
1986 GtkXText *xtext = GTK_XTEXT (widget);
1987 GdkModifierType mask;
1988 textentry *ent;
1989 unsigned char *word;
1990 int line_x, x, y, offset, len;
1991
1992 gdk_window_get_pointer (widget->window, &x, &y, &mask);
1993
1994 if (event->button == 3 || event->button == 2) /* right/middle click */
1995 {
1996 word = gtk_xtext_get_word (xtext, x, y, 0, 0, 0, 0);
1997 if (word)
1998 {
1999 g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0,
2000 word, event);
2001 } else
2002 g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0,
2003 "", event);
2004 return FALSE;
2005 }
2006
2007 if (event->button != 1) /* we only want left button */
2008 return FALSE;
2009
2010 if (event->type == GDK_2BUTTON_PRESS) /* WORD select */
2011 {
2012 gtk_xtext_check_mark_stamp (xtext, mask);
2013 if (gtk_xtext_get_word (xtext, x, y, &ent, &offset, &len, 0))
2014 {
2015 if (len == 0)
2016 return FALSE;
2017 gtk_xtext_selection_clear (xtext->buffer);
2018 ent->mark_start = offset;
2019 ent->mark_end = offset + len;
2020 gtk_xtext_selection_render (xtext, ent, ent);
2021 xtext->word_select = TRUE;
2022 }
2023
2024 return FALSE;
2025 }
2026
2027 if (event->type == GDK_3BUTTON_PRESS) /* LINE select */
2028 {
2029 gtk_xtext_check_mark_stamp (xtext, mask);
2030 if (gtk_xtext_get_word (xtext, x, y, &ent, 0, 0, 0))
2031 {
2032 gtk_xtext_selection_clear (xtext->buffer);
2033 ent->mark_start = 0;
2034 ent->mark_end = ent->str_len;
2035 gtk_xtext_selection_render (xtext, ent, ent);
2036 xtext->line_select = TRUE;
2037 }
2038
2039 return FALSE;
2040 }
2041
2042 /* check if it was a separator-bar click */
2043 if (xtext->separator && xtext->buffer->indent)
2044 {
2045 line_x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
2046 if (line_x == x || line_x == x + 1 || line_x == x - 1)
2047 {
2048 xtext->moving_separator = TRUE;
2049 /* draw the separator line */
2050 gtk_xtext_draw_sep (xtext, -1);
2051 return FALSE;
2052 }
2053 }
2054
2055 xtext->button_down = TRUE;
2056 xtext->select_start_x = x;
2057 xtext->select_start_y = y;
2058 xtext->select_start_adj = xtext->adj->value;
2059
2060 return FALSE;
2061 }
2062
2063 /* another program has claimed the selection */
2064
2065 static gboolean
gtk_xtext_selection_kill(GtkXText * xtext,GdkEventSelection * event)2066 gtk_xtext_selection_kill (GtkXText *xtext, GdkEventSelection *event)
2067 {
2068 #ifndef WIN32
2069 if (xtext->buffer->last_ent_start)
2070 gtk_xtext_unselect (xtext);
2071 #endif
2072 return TRUE;
2073 }
2074
2075 static gboolean
gtk_xtext_is_selecting(GtkXText * xtext)2076 gtk_xtext_is_selecting (GtkXText *xtext)
2077 {
2078 textentry *ent;
2079 xtext_buffer *buf;
2080
2081 buf = xtext->selection_buffer;
2082 if (!buf)
2083 return FALSE;
2084
2085 for (ent = buf->last_ent_start; ent; ent = ent->next)
2086 {
2087 if (ent->mark_start != -1 && ent->mark_end - ent->mark_start > 0)
2088 return TRUE;
2089
2090 if (ent == buf->last_ent_end)
2091 break;
2092 }
2093
2094 return FALSE;
2095 }
2096
2097 static char *
gtk_xtext_selection_get_text(GtkXText * xtext,int * len_ret)2098 gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret)
2099 {
2100 textentry *ent;
2101 char *txt;
2102 char *pos;
2103 char *stripped;
2104 int len;
2105 int first = TRUE;
2106 xtext_buffer *buf;
2107
2108 buf = xtext->selection_buffer;
2109 if (!buf)
2110 return NULL;
2111
2112 /* first find out how much we need to malloc ... */
2113 len = 0;
2114 ent = buf->last_ent_start;
2115 while (ent)
2116 {
2117 if (ent->mark_start != -1)
2118 {
2119 /* include timestamp? */
2120 if (ent->mark_start == 0 && xtext->mark_stamp)
2121 {
2122 char *time_str;
2123 int stamp_size = xtext_get_stamp_str (ent->stamp, &time_str);
2124 g_free (time_str);
2125 len += stamp_size;
2126 }
2127
2128 if (ent->mark_end - ent->mark_start > 0)
2129 len += (ent->mark_end - ent->mark_start) + 1;
2130 else
2131 len++;
2132 }
2133 if (ent == buf->last_ent_end)
2134 break;
2135 ent = ent->next;
2136 }
2137
2138 if (len < 1)
2139 return NULL;
2140
2141 /* now allocate mem and copy buffer */
2142 pos = txt = g_malloc (len);
2143 ent = buf->last_ent_start;
2144 while (ent)
2145 {
2146 if (ent->mark_start != -1)
2147 {
2148 if (!first)
2149 {
2150 *pos = '\n';
2151 pos++;
2152 }
2153 first = FALSE;
2154 if (ent->mark_end - ent->mark_start > 0)
2155 {
2156 /* include timestamp? */
2157 if (ent->mark_start == 0 && xtext->mark_stamp)
2158 {
2159 char *time_str;
2160 int stamp_size = xtext_get_stamp_str (ent->stamp, &time_str);
2161 memcpy (pos, time_str, stamp_size);
2162 g_free (time_str);
2163 pos += stamp_size;
2164 }
2165
2166 memcpy (pos, ent->str + ent->mark_start,
2167 ent->mark_end - ent->mark_start);
2168 pos += ent->mark_end - ent->mark_start;
2169 }
2170 }
2171 if (ent == buf->last_ent_end)
2172 break;
2173 ent = ent->next;
2174 }
2175 *pos = 0;
2176
2177 if (xtext->color_paste)
2178 {
2179 /*stripped = gtk_xtext_conv_color (txt, strlen (txt), &len);*/
2180 stripped = txt;
2181 len = strlen (txt);
2182 }
2183 else
2184 {
2185 stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, &len, NULL, FALSE);
2186 g_free (txt);
2187 }
2188
2189 *len_ret = len;
2190 return stripped;
2191 }
2192
2193 /* another program is asking for our selection */
2194
2195 static void
gtk_xtext_selection_get(GtkWidget * widget,GtkSelectionData * selection_data_ptr,guint info,guint time)2196 gtk_xtext_selection_get (GtkWidget * widget,
2197 GtkSelectionData * selection_data_ptr,
2198 guint info, guint time)
2199 {
2200 GtkXText *xtext = GTK_XTEXT (widget);
2201 char *stripped;
2202 guchar *new_text;
2203 int len;
2204 gsize glen;
2205
2206 stripped = gtk_xtext_selection_get_text (xtext, &len);
2207 if (!stripped)
2208 return;
2209
2210 switch (info)
2211 {
2212 case TARGET_UTF8_STRING:
2213 /* it's already in utf8 */
2214 gtk_selection_data_set_text (selection_data_ptr, stripped, len);
2215 break;
2216 case TARGET_TEXT:
2217 case TARGET_COMPOUND_TEXT:
2218 #ifdef GDK_WINDOWING_X11
2219 {
2220 GdkDisplay *display = gdk_window_get_display (widget->window);
2221 GdkAtom encoding;
2222 gint format;
2223 gint new_length;
2224
2225 gdk_x11_display_string_to_compound_text (display, stripped, &encoding,
2226 &format, &new_text, &new_length);
2227 gtk_selection_data_set (selection_data_ptr, encoding, format,
2228 new_text, new_length);
2229 gdk_x11_free_compound_text (new_text);
2230
2231 }
2232 break;
2233 #endif
2234 default:
2235 new_text = g_locale_from_utf8 (stripped, len, NULL, &glen, NULL);
2236 gtk_selection_data_set (selection_data_ptr, GDK_SELECTION_TYPE_STRING,
2237 8, new_text, glen);
2238 g_free (new_text);
2239 }
2240
2241 g_free (stripped);
2242 }
2243
2244 static gboolean
gtk_xtext_scroll(GtkWidget * widget,GdkEventScroll * event)2245 gtk_xtext_scroll (GtkWidget *widget, GdkEventScroll *event)
2246 {
2247 GtkXText *xtext = GTK_XTEXT (widget);
2248 gfloat new_value;
2249
2250 if (event->direction == GDK_SCROLL_UP) /* mouse wheel pageUp */
2251 {
2252 new_value = xtext->adj->value - (xtext->adj->page_increment / 10);
2253 if (new_value < xtext->adj->lower)
2254 new_value = xtext->adj->lower;
2255 gtk_adjustment_set_value (xtext->adj, new_value);
2256 }
2257 else if (event->direction == GDK_SCROLL_DOWN) /* mouse wheel pageDn */
2258 {
2259 new_value = xtext->adj->value + (xtext->adj->page_increment / 10);
2260 if (new_value > (xtext->adj->upper - xtext->adj->page_size))
2261 new_value = xtext->adj->upper - xtext->adj->page_size;
2262 gtk_adjustment_set_value (xtext->adj, new_value);
2263 }
2264
2265 return FALSE;
2266 }
2267
2268 static void
gtk_xtext_scroll_adjustments(GtkXText * xtext,GtkAdjustment * hadj,GtkAdjustment * vadj)2269 gtk_xtext_scroll_adjustments (GtkXText *xtext, GtkAdjustment *hadj, GtkAdjustment *vadj)
2270 {
2271 /* hadj is ignored entirely */
2272
2273 if (vadj)
2274 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
2275 else
2276 vadj = GTK_ADJUSTMENT(gtk_adjustment_new (0, 0, 1, 1, 1, 1));
2277
2278 if (xtext->adj && (xtext->adj != vadj))
2279 {
2280 g_signal_handlers_disconnect_by_func (xtext->adj,
2281 gtk_xtext_adjustment_changed,
2282 xtext);
2283 g_object_unref (xtext->adj);
2284 }
2285
2286 if (xtext->adj != vadj)
2287 {
2288 xtext->adj = vadj;
2289 g_object_ref_sink (xtext->adj);
2290
2291 xtext->vc_signal_tag = g_signal_connect (xtext->adj, "value-changed",
2292 G_CALLBACK (gtk_xtext_adjustment_changed),
2293 xtext);
2294
2295 gtk_xtext_adjustment_changed (xtext->adj, xtext);
2296 }
2297 }
2298
2299 static void
gtk_xtext_class_init(GtkXTextClass * class)2300 gtk_xtext_class_init (GtkXTextClass * class)
2301 {
2302 GtkObjectClass *object_class;
2303 GtkWidgetClass *widget_class;
2304 GtkXTextClass *xtext_class;
2305
2306 object_class = (GtkObjectClass *) class;
2307 widget_class = (GtkWidgetClass *) class;
2308 xtext_class = (GtkXTextClass *) class;
2309
2310 parent_class = g_type_class_peek (gtk_widget_get_type ());
2311
2312 xtext_signals[WORD_CLICK] =
2313 g_signal_new ("word_click",
2314 G_TYPE_FROM_CLASS (object_class),
2315 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
2316 G_STRUCT_OFFSET (GtkXTextClass, word_click),
2317 NULL, NULL,
2318 _hexchat_marshal_VOID__POINTER_POINTER,
2319 G_TYPE_NONE,
2320 2, G_TYPE_POINTER, G_TYPE_POINTER);
2321 xtext_signals[SET_SCROLL_ADJUSTMENTS] =
2322 g_signal_new ("set_scroll_adjustments",
2323 G_OBJECT_CLASS_TYPE (object_class),
2324 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2325 G_STRUCT_OFFSET (GtkXTextClass, set_scroll_adjustments),
2326 NULL, NULL,
2327 _hexchat_marshal_VOID__OBJECT_OBJECT,
2328 G_TYPE_NONE,
2329 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
2330
2331 object_class->destroy = gtk_xtext_destroy;
2332
2333 widget_class->realize = gtk_xtext_realize;
2334 widget_class->unrealize = gtk_xtext_unrealize;
2335 widget_class->size_request = gtk_xtext_size_request;
2336 widget_class->size_allocate = gtk_xtext_size_allocate;
2337 widget_class->button_press_event = gtk_xtext_button_press;
2338 widget_class->button_release_event = gtk_xtext_button_release;
2339 widget_class->motion_notify_event = gtk_xtext_motion_notify;
2340 widget_class->selection_clear_event = (void *)gtk_xtext_selection_kill;
2341 widget_class->selection_get = gtk_xtext_selection_get;
2342 widget_class->expose_event = gtk_xtext_expose;
2343 widget_class->scroll_event = gtk_xtext_scroll;
2344 widget_class->leave_notify_event = gtk_xtext_leave_notify;
2345 widget_class->set_scroll_adjustments_signal = xtext_signals[SET_SCROLL_ADJUSTMENTS];
2346
2347 xtext_class->word_click = NULL;
2348 xtext_class->set_scroll_adjustments = gtk_xtext_scroll_adjustments;
2349 }
2350
2351 GType
gtk_xtext_get_type(void)2352 gtk_xtext_get_type (void)
2353 {
2354 static GType xtext_type = 0;
2355
2356 if (!xtext_type)
2357 {
2358 static const GTypeInfo xtext_info =
2359 {
2360 sizeof (GtkXTextClass),
2361 NULL, /* base_init */
2362 NULL, /* base_finalize */
2363 (GClassInitFunc) gtk_xtext_class_init,
2364 NULL, /* class_finalize */
2365 NULL, /* class_data */
2366 sizeof (GtkXText),
2367 0, /* n_preallocs */
2368 (GInstanceInitFunc) gtk_xtext_init,
2369 };
2370
2371 xtext_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkXText",
2372 &xtext_info, 0);
2373 }
2374
2375 return xtext_type;
2376 }
2377
2378 /* strip MIRC colors and other attribs. */
2379
2380 /* CL: needs to strip hidden when called by gtk_xtext_text_width, but not when copying text */
2381
2382 typedef struct chunk_s {
2383 GSList *slp;
2384 int off1, len1, emph;
2385 offlen_t meta;
2386 } chunk_t;
2387
2388 static void
xtext_do_chunk(chunk_t * c)2389 xtext_do_chunk(chunk_t *c)
2390 {
2391 offlen_t *meta;
2392
2393 if (c->len1 == 0)
2394 return;
2395
2396 meta = g_new (offlen_t, 1);
2397 meta->off = c->off1;
2398 meta->len = c->len1;
2399 meta->emph = c->emph;
2400 meta->width = 0;
2401 c->slp = g_slist_append (c->slp, meta);
2402
2403 c->len1 = 0;
2404 }
2405
2406 static unsigned char *
gtk_xtext_strip_color(unsigned char * text,int len,unsigned char * outbuf,int * newlen,GSList ** slpp,int strip_hidden)2407 gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
2408 int *newlen, GSList **slpp, int strip_hidden)
2409 {
2410 chunk_t c;
2411 int i = 0;
2412 int rcol = 0, bgcol = 0;
2413 int hidden = FALSE;
2414 unsigned char *new_str;
2415 unsigned char *text0 = text;
2416 int mbl; /* multi-byte length */
2417
2418 if (outbuf == NULL)
2419 new_str = g_malloc (len + 2);
2420 else
2421 new_str = outbuf;
2422
2423 c.slp = NULL;
2424 c.off1 = 0;
2425 c.len1 = 0;
2426 c.emph = 0;
2427 while (len > 0)
2428 {
2429 mbl = charlen (text);
2430 if (mbl > len)
2431 goto bad_utf8;
2432
2433 if (rcol > 0 && (isdigit (*text) || (*text == ',' && isdigit (text[1]) && !bgcol)))
2434 {
2435 if (text[1] != ',') rcol--;
2436 if (*text == ',')
2437 {
2438 rcol = 2;
2439 bgcol = 1;
2440 }
2441 } else
2442 {
2443 rcol = bgcol = 0;
2444 switch (*text)
2445 {
2446 case ATTR_COLOR:
2447 xtext_do_chunk (&c);
2448 rcol = 2;
2449 break;
2450 case ATTR_BEEP:
2451 case ATTR_RESET:
2452 case ATTR_REVERSE:
2453 case ATTR_BOLD:
2454 case ATTR_UNDERLINE:
2455 case ATTR_STRIKETHROUGH:
2456 case ATTR_ITALICS:
2457 xtext_do_chunk (&c);
2458 if (*text == ATTR_RESET)
2459 c.emph = 0;
2460 if (*text == ATTR_ITALICS)
2461 c.emph ^= EMPH_ITAL;
2462 if (*text == ATTR_BOLD)
2463 c.emph ^= EMPH_BOLD;
2464 break;
2465 case ATTR_HIDDEN:
2466 xtext_do_chunk (&c);
2467 c.emph ^= EMPH_HIDDEN;
2468 hidden = !hidden;
2469 break;
2470 default:
2471 if (strip_hidden == 2 || (!(hidden && strip_hidden)))
2472 {
2473 if (c.len1 == 0)
2474 c.off1 = text - text0;
2475 memcpy (new_str + i, text, mbl);
2476 i += mbl;
2477 c.len1 += mbl;
2478 }
2479 }
2480 }
2481 text += mbl;
2482 len -= mbl;
2483 }
2484
2485 bad_utf8: /* Normal ending sequence, and give up if bad utf8 */
2486 xtext_do_chunk (&c);
2487
2488 new_str[i] = 0;
2489
2490 if (newlen != NULL)
2491 *newlen = i;
2492
2493 if (slpp)
2494 *slpp = c.slp;
2495 else
2496 g_slist_free_full (c.slp, g_free);
2497
2498 return new_str;
2499 }
2500
2501 /* gives width of a string, excluding the mIRC codes */
2502
2503 static int
gtk_xtext_text_width_ent(GtkXText * xtext,textentry * ent)2504 gtk_xtext_text_width_ent (GtkXText *xtext, textentry *ent)
2505 {
2506 unsigned char *new_buf;
2507 GSList *slp0, *slp;
2508 int width;
2509
2510 if (ent->slp)
2511 {
2512 g_slist_free_full (ent->slp, g_free);
2513 ent->slp = NULL;
2514 }
2515
2516 new_buf = gtk_xtext_strip_color (ent->str, ent->str_len, xtext->scratch_buffer,
2517 NULL, &slp0, 2);
2518
2519 width = backend_get_text_width_slp (xtext, new_buf, slp0);
2520 ent->slp = slp0;
2521
2522 for (slp = slp0; slp; slp = g_slist_next (slp))
2523 {
2524 offlen_t *meta;
2525
2526 meta = slp->data;
2527 meta->width = backend_get_text_width_emph (xtext, ent->str + meta->off, meta->len, meta->emph);
2528 }
2529 return width;
2530 }
2531
2532 static int
gtk_xtext_text_width(GtkXText * xtext,unsigned char * text,int len)2533 gtk_xtext_text_width (GtkXText *xtext, unsigned char *text, int len)
2534 {
2535 unsigned char *new_buf;
2536 int new_len;
2537 GSList *slp;
2538 int width;
2539
2540 new_buf = gtk_xtext_strip_color (text, len, xtext->scratch_buffer,
2541 &new_len, &slp, !xtext->ignore_hidden);
2542
2543 width = backend_get_text_width_slp (xtext, new_buf, slp);
2544 g_slist_free_full (slp, g_free);
2545
2546 return width;
2547 }
2548
2549 /* actually draw text to screen (one run with the same color/attribs) */
2550
2551 static int
gtk_xtext_render_flush(GtkXText * xtext,int x,int y,unsigned char * str,int len,GdkGC * gc,int * emphasis)2552 gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
2553 int len, GdkGC *gc, int *emphasis)
2554 {
2555 int str_width, dofill;
2556 GdkDrawable *pix = NULL;
2557 int dest_x = 0, dest_y = 0;
2558
2559 if (xtext->dont_render || len < 1 || xtext->hidden)
2560 return 0;
2561
2562 str_width = backend_get_text_width_emph (xtext, str, len, *emphasis);
2563
2564 if (xtext->dont_render2)
2565 return str_width;
2566
2567 /* roll-your-own clipping (avoiding XftDrawString is always good!) */
2568 if (x > xtext->clip_x2 || x + str_width < xtext->clip_x)
2569 return str_width;
2570 if (y - xtext->font->ascent > xtext->clip_y2 || (y - xtext->font->ascent) + xtext->fontsize < xtext->clip_y)
2571 return str_width;
2572
2573 if (xtext->render_hilights_only)
2574 {
2575 if (!xtext->in_hilight) /* is it a hilight prefix? */
2576 return str_width;
2577 if (!xtext->un_hilight) /* doing a hilight? no need to draw the text */
2578 goto dounder;
2579 }
2580
2581 pix = gdk_pixmap_new (xtext->draw_buf, str_width, xtext->fontsize, xtext->depth);
2582 if (pix)
2583 {
2584 dest_x = x;
2585 dest_y = y - xtext->font->ascent;
2586
2587 gdk_gc_set_ts_origin (xtext->bgc, xtext->ts_x - x, xtext->ts_y - dest_y);
2588
2589 x = 0;
2590 y = xtext->font->ascent;
2591 xtext->draw_buf = pix;
2592 }
2593
2594 dofill = TRUE;
2595
2596 /* backcolor is always handled by XDrawImageString */
2597 if (!xtext->backcolor && xtext->pixmap)
2598 {
2599 /* draw the background pixmap behind the text - CAUSES FLICKER HERE!! */
2600 xtext_draw_bg (xtext, x, y - xtext->font->ascent, str_width,
2601 xtext->fontsize);
2602 dofill = FALSE; /* already drawn the background */
2603 }
2604
2605 backend_draw_text_emph (xtext, dofill, gc, x, y, str, len, str_width, *emphasis);
2606
2607 if (pix)
2608 {
2609 GdkRectangle clip;
2610 GdkRectangle dest;
2611
2612 gdk_gc_set_ts_origin (xtext->bgc, xtext->ts_x, xtext->ts_y);
2613 xtext->draw_buf = GTK_WIDGET (xtext)->window;
2614 clip.x = xtext->clip_x;
2615 clip.y = xtext->clip_y;
2616 clip.width = xtext->clip_x2 - xtext->clip_x;
2617 clip.height = xtext->clip_y2 - xtext->clip_y;
2618
2619 dest.x = dest_x;
2620 dest.y = dest_y;
2621 dest.width = str_width;
2622 dest.height = xtext->fontsize;
2623
2624 if (gdk_rectangle_intersect (&clip, &dest, &dest))
2625 /* dump the DB to window, but only within the clip_x/x2/y/y2 */
2626 gdk_draw_drawable (xtext->draw_buf, xtext->bgc, pix,
2627 dest.x - dest_x, dest.y - dest_y,
2628 dest.x, dest.y, dest.width, dest.height);
2629 g_object_unref (pix);
2630 }
2631
2632 if (xtext->strikethrough)
2633 {
2634 /* pango_attr_strikethrough_new does not render in the custom widget so we need to reinvent the wheel */
2635 y = dest_y + (xtext->fontsize / 2);
2636 gdk_draw_line (xtext->draw_buf, gc, dest_x, y, dest_x + str_width - 1, y);
2637 }
2638
2639 if (xtext->underline)
2640 {
2641 dounder:
2642
2643 if (pix)
2644 y = dest_y + xtext->font->ascent + 1;
2645 else
2646 {
2647 y++;
2648 dest_x = x;
2649 }
2650 /* draw directly to window, it's out of the range of our DB */
2651 gdk_draw_line (xtext->draw_buf, gc, dest_x, y, dest_x + str_width - 1, y);
2652 }
2653
2654 return str_width;
2655 }
2656
2657 static void
gtk_xtext_reset(GtkXText * xtext,int mark,int attribs)2658 gtk_xtext_reset (GtkXText * xtext, int mark, int attribs)
2659 {
2660 if (attribs)
2661 {
2662 xtext->underline = FALSE;
2663 xtext->strikethrough = FALSE;
2664 xtext->hidden = FALSE;
2665 }
2666 if (!mark)
2667 {
2668 xtext->backcolor = FALSE;
2669 if (xtext->col_fore != XTEXT_FG)
2670 xtext_set_fg (xtext, xtext->fgc, XTEXT_FG);
2671 if (xtext->col_back != XTEXT_BG)
2672 xtext_set_bg (xtext, xtext->fgc, XTEXT_BG);
2673 }
2674 xtext->col_fore = XTEXT_FG;
2675 xtext->col_back = XTEXT_BG;
2676 xtext->parsing_color = FALSE;
2677 xtext->parsing_backcolor = FALSE;
2678 xtext->nc = 0;
2679 }
2680
2681 /*
2682 * gtk_xtext_search_offset (buf, ent, off) --
2683 * Look for arg offset in arg textentry
2684 * Return one or more flags:
2685 * GTK_MATCH_MID if we are in a match
2686 * GTK_MATCH_START if we're at the first byte of it
2687 * GTK_MATCH_END if we're at the first byte past it
2688 * GTK_MATCH_CUR if it is the current match
2689 */
2690 #define GTK_MATCH_START 1
2691 #define GTK_MATCH_MID 2
2692 #define GTK_MATCH_END 4
2693 #define GTK_MATCH_CUR 8
2694 static int
gtk_xtext_search_offset(xtext_buffer * buf,textentry * ent,unsigned int off)2695 gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off)
2696 {
2697 GList *gl;
2698 offsets_t o;
2699 int flags = 0;
2700
2701 for (gl = g_list_first (ent->marks); gl; gl = g_list_next (gl))
2702 {
2703 o.u = GPOINTER_TO_UINT (gl->data);
2704 if (off < o.o.start || off > o.o.end)
2705 continue;
2706 flags = GTK_MATCH_MID;
2707 if (off == o.o.start)
2708 flags |= GTK_MATCH_START;
2709 if (off == o.o.end)
2710 {
2711 gl = g_list_next (gl);
2712 if (gl)
2713 {
2714 o.u = GPOINTER_TO_UINT (gl->data);
2715 if (off == o.o.start) /* If subseq match is adjacent */
2716 {
2717 flags |= (gl == buf->curmark)? GTK_MATCH_CUR: 0;
2718 }
2719 else /* If subseq match is not adjacent */
2720 {
2721 flags |= GTK_MATCH_END;
2722 }
2723 }
2724 else /* If there is no subseq match */
2725 {
2726 flags |= GTK_MATCH_END;
2727 }
2728 }
2729 else if (gl == buf->curmark) /* If not yet at the end of this match */
2730 {
2731 flags |= GTK_MATCH_CUR;
2732 }
2733 break;
2734 }
2735 return flags;
2736 }
2737
2738 /* render a single line, which WONT wrap, and parse mIRC colors */
2739
2740 #define RENDER_FLUSH x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, emphasis)
2741
2742 static int
gtk_xtext_render_str(GtkXText * xtext,int y,textentry * ent,unsigned char * str,int len,int win_width,int indent,int line,int left_only,int * x_size_ret,int * emphasis)2743 gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
2744 unsigned char *str, int len, int win_width, int indent,
2745 int line, int left_only, int *x_size_ret, int *emphasis)
2746 {
2747 GdkGC *gc;
2748 int i = 0, x = indent, j = 0;
2749 unsigned char *pstr = str;
2750 int col_num, tmp;
2751 int offset;
2752 int mark = FALSE;
2753 int ret = 1;
2754 int k;
2755 int srch_underline = FALSE;
2756 int srch_mark = FALSE;
2757
2758 xtext->in_hilight = FALSE;
2759
2760 offset = str - ent->str;
2761
2762 gc = xtext->fgc; /* our foreground GC */
2763
2764 if (ent->mark_start != -1 &&
2765 ent->mark_start <= i + offset && ent->mark_end > i + offset)
2766 {
2767 xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
2768 xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
2769 xtext->backcolor = TRUE;
2770 mark = TRUE;
2771 }
2772 if (xtext->hilight_ent == ent &&
2773 xtext->hilight_start <= i + offset && xtext->hilight_end > i + offset)
2774 {
2775 if (!xtext->un_hilight)
2776 {
2777 xtext->underline = TRUE;
2778 }
2779 xtext->in_hilight = TRUE;
2780 }
2781
2782 if (!xtext->skip_border_fills && !xtext->dont_render)
2783 {
2784 /* draw background to the left of the text */
2785 if (str == ent->str && indent > MARGIN && xtext->buffer->time_stamp)
2786 {
2787 /* don't overwrite the timestamp */
2788 if (indent > xtext->stamp_width)
2789 {
2790 xtext_draw_bg (xtext, xtext->stamp_width, y - xtext->font->ascent,
2791 indent - xtext->stamp_width, xtext->fontsize);
2792 }
2793 } else
2794 {
2795 /* fill the indent area with background gc */
2796 if (indent >= xtext->clip_x)
2797 {
2798 xtext_draw_bg (xtext, 0, y - xtext->font->ascent,
2799 MIN (indent, xtext->clip_x2), xtext->fontsize);
2800 }
2801 }
2802 }
2803
2804 if (xtext->jump_in_offset > 0 && offset < xtext->jump_in_offset)
2805 xtext->dont_render2 = TRUE;
2806
2807 while (i < len)
2808 {
2809
2810 if (xtext->hilight_ent == ent && xtext->hilight_start == (i + offset))
2811 {
2812 RENDER_FLUSH;
2813 pstr += j;
2814 j = 0;
2815 if (!xtext->un_hilight)
2816 {
2817 xtext->underline = TRUE;
2818 }
2819
2820 xtext->in_hilight = TRUE;
2821 }
2822
2823 if ((xtext->parsing_color && isdigit (str[i]) && xtext->nc < 2) ||
2824 (xtext->parsing_color && str[i] == ',' && isdigit (str[i+1]) && xtext->nc < 3 && !xtext->parsing_backcolor))
2825 {
2826 pstr++;
2827 if (str[i] == ',')
2828 {
2829 xtext->parsing_backcolor = TRUE;
2830 if (xtext->nc)
2831 {
2832 xtext->num[xtext->nc] = 0;
2833 xtext->nc = 0;
2834 col_num = atoi (xtext->num);
2835 if (col_num == 99) /* mIRC lameness */
2836 col_num = XTEXT_FG;
2837 else
2838 if (col_num > XTEXT_MAX_COLOR)
2839 col_num = col_num % XTEXT_MIRC_COLS;
2840 xtext->col_fore = col_num;
2841 if (!mark)
2842 xtext_set_fg (xtext, gc, col_num);
2843 }
2844 } else
2845 {
2846 xtext->num[xtext->nc] = str[i];
2847 if (xtext->nc < 7)
2848 xtext->nc++;
2849 }
2850 } else
2851 {
2852 if (xtext->parsing_color)
2853 {
2854 xtext->parsing_color = FALSE;
2855 if (xtext->nc)
2856 {
2857 xtext->num[xtext->nc] = 0;
2858 xtext->nc = 0;
2859 col_num = atoi (xtext->num);
2860 if (xtext->parsing_backcolor)
2861 {
2862 if (col_num == 99) /* mIRC lameness */
2863 col_num = XTEXT_BG;
2864 else
2865 if (col_num > XTEXT_MAX_COLOR)
2866 col_num = col_num % XTEXT_MIRC_COLS;
2867 if (col_num == XTEXT_BG)
2868 xtext->backcolor = FALSE;
2869 else
2870 xtext->backcolor = TRUE;
2871 if (!mark)
2872 xtext_set_bg (xtext, gc, col_num);
2873 xtext->col_back = col_num;
2874 } else
2875 {
2876 if (col_num == 99) /* mIRC lameness */
2877 col_num = XTEXT_FG;
2878 else
2879 if (col_num > XTEXT_MAX_COLOR)
2880 col_num = col_num % XTEXT_MIRC_COLS;
2881 if (!mark)
2882 xtext_set_fg (xtext, gc, col_num);
2883 xtext->col_fore = col_num;
2884 }
2885 xtext->parsing_backcolor = FALSE;
2886 } else
2887 {
2888 /* got a \003<non-digit>... i.e. reset colors */
2889 RENDER_FLUSH;
2890 pstr += j;
2891 j = 0;
2892 gtk_xtext_reset (xtext, mark, FALSE);
2893 }
2894 }
2895
2896 if (!left_only && !mark &&
2897 (k = gtk_xtext_search_offset (xtext->buffer, ent, offset + i)))
2898 {
2899 RENDER_FLUSH;
2900 pstr += j;
2901 j = 0;
2902 if (!(xtext->buffer->search_flags & highlight))
2903 {
2904 if (k & GTK_MATCH_CUR)
2905 {
2906 xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
2907 xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
2908 xtext->backcolor = TRUE;
2909 srch_mark = TRUE;
2910 } else
2911 {
2912 xtext_set_bg (xtext, gc, xtext->col_back);
2913 xtext_set_fg (xtext, gc, xtext->col_fore);
2914 xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE;
2915 srch_mark = FALSE;
2916 }
2917 }
2918 else
2919 {
2920 xtext->underline = (k & GTK_MATCH_CUR)? TRUE: FALSE;
2921 if (k & (GTK_MATCH_START | GTK_MATCH_MID))
2922 {
2923 xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
2924 xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
2925 xtext->backcolor = TRUE;
2926 srch_mark = TRUE;
2927 }
2928 if (k & GTK_MATCH_END)
2929 {
2930 xtext_set_bg (xtext, gc, xtext->col_back);
2931 xtext_set_fg (xtext, gc, xtext->col_fore);
2932 xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE;
2933 srch_mark = FALSE;
2934 xtext->underline = FALSE;
2935 }
2936 srch_underline = xtext->underline;
2937 }
2938 }
2939
2940 switch (str[i])
2941 {
2942 case '\n':
2943 /*case ATTR_BEEP:*/
2944 break;
2945 case ATTR_REVERSE:
2946 RENDER_FLUSH;
2947 pstr += j + 1;
2948 j = 0;
2949 tmp = xtext->col_fore;
2950 xtext->col_fore = xtext->col_back;
2951 xtext->col_back = tmp;
2952 if (!mark)
2953 {
2954 xtext_set_fg (xtext, gc, xtext->col_fore);
2955 xtext_set_bg (xtext, gc, xtext->col_back);
2956 }
2957 if (xtext->col_back != XTEXT_BG)
2958 xtext->backcolor = TRUE;
2959 else
2960 xtext->backcolor = FALSE;
2961 break;
2962 case ATTR_BOLD:
2963 RENDER_FLUSH;
2964 *emphasis ^= EMPH_BOLD;
2965 pstr += j + 1;
2966 j = 0;
2967 break;
2968 case ATTR_UNDERLINE:
2969 RENDER_FLUSH;
2970 xtext->underline = !xtext->underline;
2971 pstr += j + 1;
2972 j = 0;
2973 break;
2974 case ATTR_STRIKETHROUGH:
2975 RENDER_FLUSH;
2976 xtext->strikethrough = !xtext->strikethrough;
2977 pstr += j + 1;
2978 j = 0;
2979 break;
2980 case ATTR_ITALICS:
2981 RENDER_FLUSH;
2982 *emphasis ^= EMPH_ITAL;
2983 pstr += j + 1;
2984 j = 0;
2985 break;
2986 case ATTR_HIDDEN:
2987 RENDER_FLUSH;
2988 xtext->hidden = (!xtext->hidden) & (!xtext->ignore_hidden);
2989 pstr += j + 1;
2990 j = 0;
2991 break;
2992 case ATTR_RESET:
2993 RENDER_FLUSH;
2994 *emphasis = 0;
2995 pstr += j + 1;
2996 j = 0;
2997 gtk_xtext_reset (xtext, mark, !xtext->in_hilight);
2998 break;
2999 case ATTR_COLOR:
3000 RENDER_FLUSH;
3001 xtext->parsing_color = TRUE;
3002 pstr += j + 1;
3003 j = 0;
3004 break;
3005 default:
3006 tmp = charlen (str + i);
3007 /* invalid utf8 safe guard */
3008 if (tmp + i > len)
3009 tmp = len - i;
3010 j += tmp; /* move to the next utf8 char */
3011 }
3012 }
3013 i += charlen (str + i); /* move to the next utf8 char */
3014 /* invalid utf8 safe guard */
3015 if (i > len)
3016 i = len;
3017
3018 /* Separate the left part, the space and the right part
3019 into separate runs, and reset bidi state inbetween.
3020 Perform this only on the first line of the message.
3021 */
3022 if (offset == 0)
3023 {
3024 /* we've reached the end of the left part? */
3025 if ((pstr-str)+j == ent->left_len)
3026 {
3027 RENDER_FLUSH;
3028 pstr += j;
3029 j = 0;
3030 }
3031 else if ((pstr-str)+j == ent->left_len+1)
3032 {
3033 RENDER_FLUSH;
3034 pstr += j;
3035 j = 0;
3036 }
3037 }
3038
3039 /* have we been told to stop rendering at this point? */
3040 if (xtext->jump_out_offset > 0 && xtext->jump_out_offset <= (i + offset))
3041 {
3042 gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, emphasis);
3043 ret = 0; /* skip the rest of the lines, we're done. */
3044 j = 0;
3045 break;
3046 }
3047
3048 if (xtext->jump_in_offset > 0 && xtext->jump_in_offset == (i + offset))
3049 {
3050 RENDER_FLUSH;
3051 pstr += j;
3052 j = 0;
3053 xtext->dont_render2 = FALSE;
3054 }
3055
3056 if (xtext->hilight_ent == ent && xtext->hilight_end == (i + offset))
3057 {
3058 RENDER_FLUSH;
3059 pstr += j;
3060 j = 0;
3061 xtext->underline = FALSE;
3062 xtext->in_hilight = FALSE;
3063 if (xtext->render_hilights_only)
3064 {
3065 /* stop drawing this ent */
3066 ret = 0;
3067 break;
3068 }
3069 }
3070
3071 if (!mark && ent->mark_start == (i + offset))
3072 {
3073 RENDER_FLUSH;
3074 pstr += j;
3075 j = 0;
3076 xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
3077 xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
3078 xtext->backcolor = TRUE;
3079 if (srch_underline)
3080 {
3081 xtext->underline = FALSE;
3082 srch_underline = FALSE;
3083 }
3084 mark = TRUE;
3085 }
3086
3087 if (mark && ent->mark_end == (i + offset))
3088 {
3089 RENDER_FLUSH;
3090 pstr += j;
3091 j = 0;
3092 xtext_set_bg (xtext, gc, xtext->col_back);
3093 xtext_set_fg (xtext, gc, xtext->col_fore);
3094 if (xtext->col_back != XTEXT_BG)
3095 xtext->backcolor = TRUE;
3096 else
3097 xtext->backcolor = FALSE;
3098 mark = FALSE;
3099 }
3100
3101 }
3102
3103 if (j)
3104 RENDER_FLUSH;
3105
3106 if (mark || srch_mark)
3107 {
3108 xtext_set_bg (xtext, gc, xtext->col_back);
3109 xtext_set_fg (xtext, gc, xtext->col_fore);
3110 if (xtext->col_back != XTEXT_BG)
3111 xtext->backcolor = TRUE;
3112 else
3113 xtext->backcolor = FALSE;
3114 }
3115
3116 /* draw background to the right of the text */
3117 if (!left_only && !xtext->dont_render)
3118 {
3119 /* draw separator now so it doesn't appear to flicker */
3120 gtk_xtext_draw_sep (xtext, y - xtext->font->ascent);
3121 if (!xtext->skip_border_fills && xtext->clip_x2 >= x)
3122 {
3123 int xx = MAX (x, xtext->clip_x);
3124
3125 xtext_draw_bg (xtext,
3126 xx, /* x */
3127 y - xtext->font->ascent, /* y */
3128 MIN (xtext->clip_x2 - xx, (win_width + MARGIN) - xx), /* width */
3129 xtext->fontsize); /* height */
3130 }
3131 }
3132
3133 xtext->dont_render2 = FALSE;
3134
3135 /* return how much we drew in the x direction */
3136 if (x_size_ret)
3137 *x_size_ret = x - indent;
3138
3139 return ret;
3140 }
3141
3142 /* walk through str until this line doesn't fit anymore */
3143
3144 static int
find_next_wrap(GtkXText * xtext,textentry * ent,unsigned char * str,int win_width,int indent)3145 find_next_wrap (GtkXText * xtext, textentry * ent, unsigned char *str,
3146 int win_width, int indent)
3147 {
3148 unsigned char *last_space = str;
3149 unsigned char *orig_str = str;
3150 int str_width = indent;
3151 int rcol = 0, bgcol = 0;
3152 int hidden = FALSE;
3153 int mbl;
3154 int char_width;
3155 int ret;
3156 int limit_offset = 0;
3157 int emphasis = 0;
3158 GSList *lp;
3159
3160 /* single liners */
3161 if (win_width >= ent->str_width + ent->indent)
3162 return ent->str_len;
3163
3164 /* it does happen! */
3165 if (win_width < 1)
3166 {
3167 ret = ent->str_len - (str - ent->str);
3168 goto done;
3169 }
3170
3171 /* Find emphasis value for the offset that is the first byte of our string */
3172 for (lp = ent->slp; lp; lp = g_slist_next (lp))
3173 {
3174 offlen_t *meta = lp->data;
3175 unsigned char *start, *end;
3176
3177 start = ent->str + meta->off;
3178 end = start + meta->len;
3179 if (str >= start && str < end)
3180 {
3181 emphasis = meta->emph;
3182 break;
3183 }
3184 }
3185
3186 while (1)
3187 {
3188 if (rcol > 0 && (isdigit (*str) || (*str == ',' && isdigit (str[1]) && !bgcol)))
3189 {
3190 if (str[1] != ',') rcol--;
3191 if (*str == ',')
3192 {
3193 rcol = 2;
3194 bgcol = 1;
3195 }
3196 limit_offset++;
3197 str++;
3198 } else
3199 {
3200 rcol = bgcol = 0;
3201 switch (*str)
3202 {
3203 case ATTR_COLOR:
3204 rcol = 2;
3205 case ATTR_BEEP:
3206 case ATTR_RESET:
3207 case ATTR_REVERSE:
3208 case ATTR_BOLD:
3209 case ATTR_UNDERLINE:
3210 case ATTR_STRIKETHROUGH:
3211 case ATTR_ITALICS:
3212 if (*str == ATTR_RESET)
3213 emphasis = 0;
3214 if (*str == ATTR_ITALICS)
3215 emphasis ^= EMPH_ITAL;
3216 if (*str == ATTR_BOLD)
3217 emphasis ^= EMPH_BOLD;
3218 limit_offset++;
3219 str++;
3220 break;
3221 case ATTR_HIDDEN:
3222 if (xtext->ignore_hidden)
3223 goto def;
3224 hidden = !hidden;
3225 limit_offset++;
3226 str++;
3227 break;
3228 default:
3229 def:
3230 mbl = charlen (str);
3231 char_width = backend_get_text_width_emph (xtext, str, mbl, emphasis);
3232 if (!hidden) str_width += char_width;
3233 if (str_width > win_width)
3234 {
3235 if (xtext->wordwrap)
3236 {
3237 if (str - last_space > WORDWRAP_LIMIT + limit_offset)
3238 ret = str - orig_str; /* fall back to character wrap */
3239 else
3240 {
3241 if (*last_space == ' ')
3242 last_space++;
3243 ret = last_space - orig_str;
3244 if (ret == 0) /* fall back to character wrap */
3245 ret = str - orig_str;
3246 }
3247 goto done;
3248 }
3249 ret = str - orig_str;
3250 goto done;
3251 }
3252
3253 /* keep a record of the last space, for wordwrapping */
3254 if (is_del (*str))
3255 {
3256 last_space = str;
3257 limit_offset = 0;
3258 }
3259
3260 /* progress to the next char */
3261 str += mbl;
3262
3263 }
3264 }
3265
3266 if (str >= ent->str + ent->str_len)
3267 {
3268 ret = str - orig_str;
3269 goto done;
3270 }
3271 }
3272
3273 done:
3274
3275 /* must make progress */
3276 if (ret < 1)
3277 ret = 1;
3278
3279 return ret;
3280 }
3281
3282 /* find the offset, in bytes, that wrap number 'line' starts at */
3283
3284 static int
gtk_xtext_find_subline(GtkXText * xtext,textentry * ent,int line)3285 gtk_xtext_find_subline (GtkXText *xtext, textentry *ent, int line)
3286 {
3287 int rlen = 0;
3288
3289 if (line > 0)
3290 {
3291 rlen = GPOINTER_TO_UINT (g_slist_nth_data (ent->sublines, line - 1));
3292 if (rlen == 0)
3293 rlen = ent->str_len;
3294 }
3295 return rlen;
3296 }
3297
3298 /* horrible hack for drawing time stamps */
3299
3300 static void
gtk_xtext_render_stamp(GtkXText * xtext,textentry * ent,char * text,int len,int line,int win_width)3301 gtk_xtext_render_stamp (GtkXText * xtext, textentry * ent,
3302 char *text, int len, int line, int win_width)
3303 {
3304 textentry tmp_ent;
3305 int jo, ji, hs;
3306 int xsize, y, emphasis;
3307
3308 /* trashing ent here, so make a backup first */
3309 memcpy (&tmp_ent, ent, sizeof (tmp_ent));
3310 jo = xtext->jump_out_offset; /* back these up */
3311 ji = xtext->jump_in_offset;
3312 hs = xtext->hilight_start;
3313 xtext->jump_out_offset = 0;
3314 xtext->jump_in_offset = 0;
3315 xtext->hilight_start = 0xffff; /* temp disable */
3316 emphasis = 0;
3317
3318 if (xtext->mark_stamp)
3319 {
3320 /* if this line is marked, mark this stamp too */
3321 if (ent->mark_start == 0)
3322 {
3323 ent->mark_start = 0;
3324 ent->mark_end = len;
3325 }
3326 else
3327 {
3328 ent->mark_start = -1;
3329 ent->mark_end = -1;
3330 }
3331 ent->str = text;
3332 }
3333
3334 y = (xtext->fontsize * line) + xtext->font->ascent - xtext->pixel_offset;
3335 gtk_xtext_render_str (xtext, y, ent, text, len,
3336 win_width, 2, line, TRUE, &xsize, &emphasis);
3337
3338 /* restore everything back to how it was */
3339 memcpy (ent, &tmp_ent, sizeof (tmp_ent));
3340 xtext->jump_out_offset = jo;
3341 xtext->jump_in_offset = ji;
3342 xtext->hilight_start = hs;
3343
3344 /* with a non-fixed-width font, sometimes we don't draw enough
3345 background i.e. when this stamp is shorter than xtext->stamp_width */
3346 xsize += MARGIN;
3347 if (xsize < xtext->stamp_width)
3348 {
3349 y -= xtext->font->ascent;
3350 xtext_draw_bg (xtext,
3351 xsize, /* x */
3352 y, /* y */
3353 xtext->stamp_width - xsize, /* width */
3354 xtext->fontsize /* height */);
3355 }
3356 }
3357
3358 /* render a single line, which may wrap to more lines */
3359
3360 static int
gtk_xtext_render_line(GtkXText * xtext,textentry * ent,int line,int lines_max,int subline,int win_width)3361 gtk_xtext_render_line (GtkXText * xtext, textentry * ent, int line,
3362 int lines_max, int subline, int win_width)
3363 {
3364 unsigned char *str;
3365 int indent, taken, entline, len, y, start_subline;
3366 int emphasis = 0;
3367
3368 entline = taken = 0;
3369 str = ent->str;
3370 indent = ent->indent;
3371 start_subline = subline;
3372
3373 /* draw the timestamp */
3374 if (xtext->auto_indent && xtext->buffer->time_stamp &&
3375 (!xtext->skip_stamp || xtext->mark_stamp || xtext->force_stamp))
3376 {
3377 char *time_str;
3378 int len;
3379
3380 len = xtext_get_stamp_str (ent->stamp, &time_str);
3381 gtk_xtext_render_stamp (xtext, ent, time_str, len, line, win_width);
3382 g_free (time_str);
3383 }
3384
3385 /* draw each line one by one */
3386 do
3387 {
3388 if (entline > 0)
3389 len = GPOINTER_TO_INT (g_slist_nth_data (ent->sublines, entline)) - GPOINTER_TO_INT (g_slist_nth_data (ent->sublines, entline - 1));
3390 else
3391 len = GPOINTER_TO_INT (g_slist_nth_data (ent->sublines, entline));
3392
3393 entline++;
3394
3395 y = (xtext->fontsize * line) + xtext->font->ascent - xtext->pixel_offset;
3396 if (!subline)
3397 {
3398 if (!gtk_xtext_render_str (xtext, y, ent, str, len, win_width,
3399 indent, line, FALSE, NULL, &emphasis))
3400 {
3401 /* small optimization */
3402 gtk_xtext_draw_marker (xtext, ent, y - xtext->fontsize * (taken + start_subline + 1));
3403 return g_slist_length (ent->sublines) - subline;
3404 }
3405 } else
3406 {
3407 xtext->dont_render = TRUE;
3408 gtk_xtext_render_str (xtext, y, ent, str, len, win_width,
3409 indent, line, FALSE, NULL, &emphasis);
3410 xtext->dont_render = FALSE;
3411 subline--;
3412 line--;
3413 taken--;
3414 }
3415
3416 indent = xtext->buffer->indent;
3417 line++;
3418 taken++;
3419 str += len;
3420
3421 if (line >= lines_max)
3422 break;
3423
3424 }
3425 while (str < ent->str + ent->str_len);
3426
3427 gtk_xtext_draw_marker (xtext, ent, y - xtext->fontsize * (taken + start_subline));
3428
3429 return taken;
3430 }
3431
3432 void
gtk_xtext_set_palette(GtkXText * xtext,GdkColor palette[])3433 gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[])
3434 {
3435 int i;
3436
3437 for (i = (XTEXT_COLS-1); i >= 0; i--)
3438 {
3439 xtext->palette[i] = palette[i];
3440 }
3441
3442 if (gtk_widget_get_realized (GTK_WIDGET(xtext)))
3443 {
3444 xtext_set_fg (xtext, xtext->fgc, XTEXT_FG);
3445 xtext_set_bg (xtext, xtext->fgc, XTEXT_BG);
3446 xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
3447
3448 gdk_gc_set_foreground (xtext->marker_gc, &xtext->palette[XTEXT_MARKER]);
3449 }
3450 xtext->col_fore = XTEXT_FG;
3451 xtext->col_back = XTEXT_BG;
3452 }
3453
3454 static void
gtk_xtext_fix_indent(xtext_buffer * buf)3455 gtk_xtext_fix_indent (xtext_buffer *buf)
3456 {
3457 int j;
3458
3459 /* make indent a multiple of the space width */
3460 if (buf->indent && buf->xtext->space_width)
3461 {
3462 j = 0;
3463 while (j < buf->indent)
3464 {
3465 j += buf->xtext->space_width;
3466 }
3467 buf->indent = j;
3468 }
3469
3470 dontscroll (buf); /* force scrolling off */
3471 }
3472
3473 static void
gtk_xtext_recalc_widths(xtext_buffer * buf,int do_str_width)3474 gtk_xtext_recalc_widths (xtext_buffer *buf, int do_str_width)
3475 {
3476 textentry *ent;
3477
3478 /* since we have a new font, we have to recalc the text widths */
3479 ent = buf->text_first;
3480 while (ent)
3481 {
3482 if (do_str_width)
3483 {
3484 ent->str_width = gtk_xtext_text_width_ent (buf->xtext, ent);
3485 }
3486 if (ent->left_len != -1)
3487 {
3488 ent->indent =
3489 (buf->indent -
3490 gtk_xtext_text_width (buf->xtext, ent->str,
3491 ent->left_len)) - buf->xtext->space_width;
3492 if (ent->indent < MARGIN)
3493 ent->indent = MARGIN;
3494 }
3495 ent = ent->next;
3496 }
3497
3498 gtk_xtext_calc_lines (buf, FALSE);
3499 }
3500
3501 int
gtk_xtext_set_font(GtkXText * xtext,char * name)3502 gtk_xtext_set_font (GtkXText *xtext, char *name)
3503 {
3504
3505 if (xtext->font)
3506 backend_font_close (xtext);
3507
3508 /* realize now, so that font_open has a XDisplay */
3509 gtk_widget_realize (GTK_WIDGET (xtext));
3510
3511 backend_font_open (xtext, name);
3512 if (xtext->font == NULL)
3513 return FALSE;
3514
3515 {
3516 char *time_str;
3517 int stamp_size = xtext_get_stamp_str (time(0), &time_str);
3518 xtext->stamp_width =
3519 gtk_xtext_text_width (xtext, time_str, stamp_size) + MARGIN;
3520 g_free (time_str);
3521 }
3522
3523 gtk_xtext_fix_indent (xtext->buffer);
3524
3525 if (gtk_widget_get_realized (GTK_WIDGET(xtext)))
3526 gtk_xtext_recalc_widths (xtext->buffer, TRUE);
3527
3528 return TRUE;
3529 }
3530
3531 void
gtk_xtext_set_background(GtkXText * xtext,GdkPixmap * pixmap)3532 gtk_xtext_set_background (GtkXText * xtext, GdkPixmap * pixmap)
3533 {
3534 GdkGCValues val;
3535
3536 if (xtext->pixmap)
3537 {
3538 g_object_unref (xtext->pixmap);
3539 xtext->pixmap = NULL;
3540 }
3541
3542 dontscroll (xtext->buffer);
3543 xtext->pixmap = pixmap;
3544
3545 if (pixmap != 0)
3546 {
3547 g_object_ref (pixmap);
3548 if (gtk_widget_get_realized (GTK_WIDGET(xtext)))
3549 {
3550 gdk_gc_set_tile (xtext->bgc, pixmap);
3551 gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
3552 xtext->ts_x = xtext->ts_y = 0;
3553 gdk_gc_set_fill (xtext->bgc, GDK_TILED);
3554 }
3555 } else if (gtk_widget_get_realized (GTK_WIDGET(xtext)))
3556 {
3557 g_object_unref (xtext->bgc);
3558 val.subwindow_mode = GDK_INCLUDE_INFERIORS;
3559 val.graphics_exposures = 0;
3560 xtext->bgc = gdk_gc_new_with_values (GTK_WIDGET (xtext)->window,
3561 &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
3562 xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
3563 }
3564 }
3565
3566 void
gtk_xtext_save(GtkXText * xtext,int fh)3567 gtk_xtext_save (GtkXText * xtext, int fh)
3568 {
3569 textentry *ent;
3570 int newlen;
3571 char *buf;
3572
3573 ent = xtext->buffer->text_first;
3574 while (ent)
3575 {
3576 buf = gtk_xtext_strip_color (ent->str, ent->str_len, NULL,
3577 &newlen, NULL, FALSE);
3578 write (fh, buf, newlen);
3579 write (fh, "\n", 1);
3580 g_free (buf);
3581 ent = ent->next;
3582 }
3583 }
3584
3585 /* count how many lines 'ent' will take (with wraps) */
3586
3587 static int
gtk_xtext_lines_taken(xtext_buffer * buf,textentry * ent)3588 gtk_xtext_lines_taken (xtext_buffer *buf, textentry * ent)
3589 {
3590 unsigned char *str;
3591 int indent, len;
3592 int win_width;
3593
3594 g_slist_free (ent->sublines);
3595 ent->sublines = NULL;
3596 win_width = buf->window_width - MARGIN;
3597
3598 if (win_width >= ent->indent + ent->str_width)
3599 {
3600 ent->sublines = g_slist_append (ent->sublines, GINT_TO_POINTER (ent->str_len));
3601 return 1;
3602 }
3603
3604 indent = ent->indent;
3605 str = ent->str;
3606
3607 do
3608 {
3609 len = find_next_wrap (buf->xtext, ent, str, win_width, indent);
3610 ent->sublines = g_slist_append (ent->sublines, GINT_TO_POINTER (str + len - ent->str));
3611 indent = buf->indent;
3612 str += len;
3613 }
3614 while (str < ent->str + ent->str_len);
3615
3616 return g_slist_length (ent->sublines);
3617 }
3618
3619 /* Calculate number of actual lines (with wraps), to set adj->lower. *
3620 * This should only be called when the window resizes. */
3621
3622 static void
gtk_xtext_calc_lines(xtext_buffer * buf,int fire_signal)3623 gtk_xtext_calc_lines (xtext_buffer *buf, int fire_signal)
3624 {
3625 textentry *ent;
3626 int width;
3627 int height;
3628 int lines;
3629
3630 height = gdk_window_get_height (gtk_widget_get_window (GTK_WIDGET (buf->xtext)));
3631 width = gdk_window_get_width (gtk_widget_get_window (GTK_WIDGET (buf->xtext)));
3632 width -= MARGIN;
3633
3634 if (width < 30 || height < buf->xtext->fontsize || width < buf->indent + 30)
3635 return;
3636
3637 lines = 0;
3638 ent = buf->text_first;
3639 while (ent)
3640 {
3641 lines += gtk_xtext_lines_taken (buf, ent);
3642 ent = ent->next;
3643 }
3644
3645 buf->pagetop_ent = NULL;
3646 buf->num_lines = lines;
3647 gtk_xtext_adjustment_set (buf, fire_signal);
3648 }
3649
3650 /* find the n-th line in the linked list, this includes wrap calculations */
3651
3652 static textentry *
gtk_xtext_nth(GtkXText * xtext,int line,int * subline)3653 gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
3654 {
3655 int lines = 0;
3656 textentry *ent;
3657
3658 ent = xtext->buffer->text_first;
3659
3660 /* -- optimization -- try to make a short-cut using the pagetop ent */
3661 if (xtext->buffer->pagetop_ent)
3662 {
3663 if (line == xtext->buffer->pagetop_line)
3664 {
3665 *subline = xtext->buffer->pagetop_subline;
3666 return xtext->buffer->pagetop_ent;
3667 }
3668 if (line > xtext->buffer->pagetop_line)
3669 {
3670 /* lets start from the pagetop instead of the absolute beginning */
3671 ent = xtext->buffer->pagetop_ent;
3672 lines = xtext->buffer->pagetop_line - xtext->buffer->pagetop_subline;
3673 }
3674 else if (line > xtext->buffer->pagetop_line - line)
3675 {
3676 /* move backwards from pagetop */
3677 ent = xtext->buffer->pagetop_ent;
3678 lines = xtext->buffer->pagetop_line - xtext->buffer->pagetop_subline;
3679 while (1)
3680 {
3681 if (lines <= line)
3682 {
3683 *subline = line - lines;
3684 return ent;
3685 }
3686 ent = ent->prev;
3687 if (!ent)
3688 break;
3689 lines -= g_slist_length (ent->sublines);
3690 }
3691 return NULL;
3692 }
3693 }
3694 /* -- end of optimization -- */
3695
3696 while (ent)
3697 {
3698 lines += g_slist_length (ent->sublines);
3699 if (lines > line)
3700 {
3701 *subline = g_slist_length (ent->sublines) - (lines - line);
3702 return ent;
3703 }
3704 ent = ent->next;
3705 }
3706 return NULL;
3707 }
3708
3709 /* render enta (or an inclusive range enta->entb) */
3710
3711 static int
gtk_xtext_render_ents(GtkXText * xtext,textentry * enta,textentry * entb)3712 gtk_xtext_render_ents (GtkXText * xtext, textentry * enta, textentry * entb)
3713 {
3714 textentry *ent, *orig_ent, *tmp_ent;
3715 int line;
3716 int lines_max;
3717 int width;
3718 int height;
3719 int subline;
3720 int drawing = FALSE;
3721
3722 if (xtext->buffer->indent < MARGIN)
3723 xtext->buffer->indent = MARGIN; /* 2 pixels is our left margin */
3724
3725 height = gdk_window_get_height (gtk_widget_get_window (GTK_WIDGET (xtext)));
3726 width = gdk_window_get_width (gtk_widget_get_window (GTK_WIDGET (xtext)));
3727 width -= MARGIN;
3728
3729 if (width < 32 || height < xtext->fontsize || width < xtext->buffer->indent + 30)
3730 return 0;
3731
3732 lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1;
3733 line = 0;
3734 orig_ent = xtext->buffer->pagetop_ent;
3735 subline = xtext->buffer->pagetop_subline;
3736
3737 /* used before a complete page is in buffer */
3738 if (orig_ent == NULL)
3739 orig_ent = xtext->buffer->text_first;
3740
3741 /* check if enta is before the start of this page */
3742 if (entb)
3743 {
3744 tmp_ent = orig_ent;
3745 while (tmp_ent)
3746 {
3747 if (tmp_ent == enta)
3748 break;
3749 if (tmp_ent == entb)
3750 {
3751 drawing = TRUE;
3752 break;
3753 }
3754 tmp_ent = tmp_ent->next;
3755 }
3756 }
3757
3758 ent = orig_ent;
3759 while (ent)
3760 {
3761 if (entb && ent == enta)
3762 drawing = TRUE;
3763
3764 if (drawing || ent == entb || ent == enta)
3765 {
3766 gtk_xtext_reset (xtext, FALSE, TRUE);
3767 line += gtk_xtext_render_line (xtext, ent, line, lines_max,
3768 subline, width);
3769 subline = 0;
3770 xtext->jump_in_offset = 0; /* jump_in_offset only for the 1st */
3771 } else
3772 {
3773 if (ent == orig_ent)
3774 {
3775 line -= subline;
3776 subline = 0;
3777 }
3778 line += g_slist_length (ent->sublines);
3779 }
3780
3781 if (ent == entb)
3782 break;
3783
3784 if (line >= lines_max)
3785 break;
3786
3787 ent = ent->next;
3788 }
3789
3790 /* space below last line */
3791 return (xtext->fontsize * line) - xtext->pixel_offset;
3792 }
3793
3794 /* render a whole page/window, starting from 'startline' */
3795
3796 static void
gtk_xtext_render_page(GtkXText * xtext)3797 gtk_xtext_render_page (GtkXText * xtext)
3798 {
3799 textentry *ent;
3800 int line;
3801 int lines_max;
3802 int width;
3803 int height;
3804 int subline;
3805 int startline = xtext->adj->value;
3806 int pos, overlap;
3807
3808 if(!gtk_widget_get_realized(GTK_WIDGET(xtext)))
3809 return;
3810
3811 if (xtext->buffer->indent < MARGIN)
3812 xtext->buffer->indent = MARGIN; /* 2 pixels is our left margin */
3813
3814 gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
3815
3816 if (width < 34 || height < xtext->fontsize || width < xtext->buffer->indent + 32)
3817 return;
3818
3819 xtext->pixel_offset = (xtext->adj->value - startline) * xtext->fontsize;
3820
3821 subline = line = 0;
3822 ent = xtext->buffer->text_first;
3823
3824 if (startline > 0)
3825 ent = gtk_xtext_nth (xtext, startline, &subline);
3826
3827 xtext->buffer->pagetop_ent = ent;
3828 xtext->buffer->pagetop_subline = subline;
3829 xtext->buffer->pagetop_line = startline;
3830
3831 if (xtext->buffer->num_lines <= xtext->adj->page_size)
3832 dontscroll (xtext->buffer);
3833
3834 pos = xtext->adj->value * xtext->fontsize;
3835 overlap = xtext->buffer->last_pixel_pos - pos;
3836 xtext->buffer->last_pixel_pos = pos;
3837
3838 #ifndef __APPLE__
3839 if (!xtext->pixmap && abs (overlap) < height)
3840 {
3841 GdkRectangle area;
3842
3843 /* so the obscured regions are exposed */
3844 gdk_gc_set_exposures (xtext->fgc, TRUE);
3845 if (overlap < 1) /* DOWN */
3846 {
3847 int remainder;
3848
3849 gdk_draw_drawable (xtext->draw_buf, xtext->fgc, xtext->draw_buf,
3850 0, -overlap, 0, 0, width, height + overlap);
3851 remainder = ((height - xtext->font->descent) % xtext->fontsize) +
3852 xtext->font->descent;
3853 area.y = (height + overlap) - remainder;
3854 area.height = remainder - overlap;
3855 } else
3856 {
3857 gdk_draw_drawable (xtext->draw_buf, xtext->fgc, xtext->draw_buf,
3858 0, 0, 0, overlap, width, height - overlap);
3859 area.y = 0;
3860 area.height = overlap;
3861 }
3862 gdk_gc_set_exposures (xtext->fgc, FALSE);
3863
3864 if (area.height > 0)
3865 {
3866 area.x = 0;
3867 area.width = width;
3868 gtk_xtext_paint (GTK_WIDGET (xtext), &area);
3869 }
3870
3871 return;
3872 }
3873 #endif
3874
3875 width -= MARGIN;
3876 lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1;
3877
3878 while (ent)
3879 {
3880 gtk_xtext_reset (xtext, FALSE, TRUE);
3881 line += gtk_xtext_render_line (xtext, ent, line, lines_max,
3882 subline, width);
3883 subline = 0;
3884
3885 if (line >= lines_max)
3886 break;
3887
3888 ent = ent->next;
3889 }
3890
3891 line = (xtext->fontsize * line) - xtext->pixel_offset;
3892 /* fill any space below the last line with our background GC */
3893 xtext_draw_bg (xtext, 0, line, width + MARGIN, height - line);
3894
3895 /* draw the separator line */
3896 gtk_xtext_draw_sep (xtext, -1);
3897 }
3898
3899 void
gtk_xtext_refresh(GtkXText * xtext)3900 gtk_xtext_refresh (GtkXText * xtext)
3901 {
3902 if (gtk_widget_get_realized (GTK_WIDGET (xtext)))
3903 {
3904 gtk_xtext_render_page (xtext);
3905 }
3906 }
3907
3908 static int
gtk_xtext_kill_ent(xtext_buffer * buffer,textentry * ent)3909 gtk_xtext_kill_ent (xtext_buffer *buffer, textentry *ent)
3910 {
3911 int visible;
3912
3913 /* Set visible to TRUE if this is the current buffer */
3914 /* and this ent shows up on the screen now */
3915 visible = buffer->xtext->buffer == buffer &&
3916 gtk_xtext_check_ent_visibility (buffer->xtext, ent, 0);
3917
3918 if (ent == buffer->pagetop_ent)
3919 buffer->pagetop_ent = NULL;
3920
3921 if (ent == buffer->last_ent_start)
3922 {
3923 buffer->last_ent_start = ent->next;
3924 buffer->last_offset_start = 0;
3925 }
3926
3927 if (ent == buffer->last_ent_end)
3928 {
3929 buffer->last_ent_start = NULL;
3930 buffer->last_ent_end = NULL;
3931 }
3932
3933 if (buffer->marker_pos == ent)
3934 {
3935 /* Allow for "Marker line reset because exceeded scrollback limit. to appear. */
3936 buffer->marker_pos = ent->next;
3937 buffer->marker_state = MARKER_RESET_BY_KILL;
3938 }
3939
3940 if (ent->marks)
3941 {
3942 gtk_xtext_search_textentry_del (buffer, ent);
3943 }
3944
3945 g_slist_free_full (ent->slp, g_free);
3946 g_slist_free (ent->sublines);
3947
3948 g_free (ent);
3949 return visible;
3950 }
3951
3952 /* remove the topline from the list */
3953
3954 static void
gtk_xtext_remove_top(xtext_buffer * buffer)3955 gtk_xtext_remove_top (xtext_buffer *buffer)
3956 {
3957 textentry *ent;
3958
3959 ent = buffer->text_first;
3960 if (!ent)
3961 return;
3962 buffer->num_lines -= g_slist_length (ent->sublines);
3963 buffer->pagetop_line -= g_slist_length (ent->sublines);
3964 buffer->last_pixel_pos -= (g_slist_length (ent->sublines) * buffer->xtext->fontsize);
3965 buffer->text_first = ent->next;
3966 if (buffer->text_first)
3967 buffer->text_first->prev = NULL;
3968 else
3969 buffer->text_last = NULL;
3970
3971 buffer->old_value -= g_slist_length (ent->sublines);
3972 if (buffer->xtext->buffer == buffer) /* is it the current buffer? */
3973 {
3974 buffer->xtext->adj->value -= g_slist_length (ent->sublines);
3975 buffer->xtext->select_start_adj -= g_slist_length (ent->sublines);
3976 }
3977
3978 if (gtk_xtext_kill_ent (buffer, ent))
3979 {
3980 if (!buffer->xtext->add_io_tag)
3981 {
3982 /* remove scrolling events */
3983 if (buffer->xtext->io_tag)
3984 {
3985 g_source_remove (buffer->xtext->io_tag);
3986 buffer->xtext->io_tag = 0;
3987 }
3988 buffer->xtext->force_render = TRUE;
3989 buffer->xtext->add_io_tag = g_timeout_add (REFRESH_TIMEOUT * 2,
3990 (GSourceFunc)
3991 gtk_xtext_render_page_timeout,
3992 buffer->xtext);
3993 }
3994 }
3995 }
3996
3997 static void
gtk_xtext_remove_bottom(xtext_buffer * buffer)3998 gtk_xtext_remove_bottom (xtext_buffer *buffer)
3999 {
4000 textentry *ent;
4001
4002 ent = buffer->text_last;
4003 if (!ent)
4004 return;
4005 buffer->num_lines -= g_slist_length (ent->sublines);
4006 buffer->text_last = ent->prev;
4007 if (buffer->text_last)
4008 buffer->text_last->next = NULL;
4009 else
4010 buffer->text_first = NULL;
4011
4012 if (gtk_xtext_kill_ent (buffer, ent))
4013 {
4014 if (!buffer->xtext->add_io_tag)
4015 {
4016 /* remove scrolling events */
4017 if (buffer->xtext->io_tag)
4018 {
4019 g_source_remove (buffer->xtext->io_tag);
4020 buffer->xtext->io_tag = 0;
4021 }
4022 buffer->xtext->force_render = TRUE;
4023 buffer->xtext->add_io_tag = g_timeout_add (REFRESH_TIMEOUT * 2,
4024 (GSourceFunc)
4025 gtk_xtext_render_page_timeout,
4026 buffer->xtext);
4027 }
4028 }
4029 }
4030
4031 /* If lines=0 => clear all */
4032
4033 void
gtk_xtext_clear(xtext_buffer * buf,int lines)4034 gtk_xtext_clear (xtext_buffer *buf, int lines)
4035 {
4036 textentry *next;
4037 int marker_reset = FALSE;
4038
4039 if (lines != 0)
4040 {
4041 if (lines < 0)
4042 {
4043 /* delete lines from bottom */
4044 lines *= -1;
4045 while (lines)
4046 {
4047 if (buf->text_last == buf->marker_pos)
4048 marker_reset = TRUE;
4049 gtk_xtext_remove_bottom (buf);
4050 lines--;
4051 }
4052 }
4053 else
4054 {
4055 /* delete lines from top */
4056 while (lines)
4057 {
4058 if (buf->text_first == buf->marker_pos)
4059 marker_reset = TRUE;
4060 gtk_xtext_remove_top (buf);
4061 lines--;
4062 }
4063 }
4064 }
4065 else
4066 {
4067 /* delete all */
4068 if (buf->search_found)
4069 gtk_xtext_search_fini (buf);
4070 if (buf->xtext->auto_indent)
4071 buf->indent = MARGIN;
4072 buf->scrollbar_down = TRUE;
4073 buf->last_ent_start = NULL;
4074 buf->last_ent_end = NULL;
4075 buf->marker_pos = NULL;
4076 if (buf->text_first)
4077 marker_reset = TRUE;
4078 dontscroll (buf);
4079
4080 while (buf->text_first)
4081 {
4082 next = buf->text_first->next;
4083 g_free (buf->text_first);
4084 buf->text_first = next;
4085 }
4086 buf->text_last = NULL;
4087 }
4088
4089 if (buf->xtext->buffer == buf)
4090 {
4091 gtk_xtext_calc_lines (buf, TRUE);
4092 gtk_xtext_refresh (buf->xtext);
4093 } else
4094 {
4095 gtk_xtext_calc_lines (buf, FALSE);
4096 }
4097
4098 if (marker_reset)
4099 buf->marker_state = MARKER_RESET_BY_CLEAR;
4100 }
4101
4102 static gboolean
gtk_xtext_check_ent_visibility(GtkXText * xtext,textentry * find_ent,int add)4103 gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add)
4104 {
4105 textentry *ent;
4106 int lines;
4107 xtext_buffer *buf = xtext->buffer;
4108 int height;
4109
4110 if (find_ent == NULL)
4111 {
4112 return FALSE;
4113 }
4114
4115 height = gdk_window_get_height (gtk_widget_get_window (GTK_WIDGET (xtext)));
4116
4117 ent = buf->pagetop_ent;
4118 /* If top line not completely displayed return FALSE */
4119 if (ent == find_ent && buf->pagetop_subline > 0)
4120 {
4121 return FALSE;
4122 }
4123 /* Loop through line positions looking for find_ent */
4124 lines = ((height + xtext->pixel_offset) / xtext->fontsize) + buf->pagetop_subline + add;
4125 while (ent)
4126 {
4127 lines -= g_slist_length (ent->sublines);
4128 if (lines <= 0)
4129 {
4130 return FALSE;
4131 }
4132 if (ent == find_ent)
4133 {
4134 return TRUE;
4135 }
4136 ent = ent->next;
4137 }
4138
4139 return FALSE;
4140 }
4141
4142 void
gtk_xtext_check_marker_visibility(GtkXText * xtext)4143 gtk_xtext_check_marker_visibility (GtkXText * xtext)
4144 {
4145 if (gtk_xtext_check_ent_visibility (xtext, xtext->buffer->marker_pos, 1))
4146 xtext->buffer->marker_seen = TRUE;
4147 }
4148
4149 static void
gtk_xtext_unstrip_color(gint start,gint end,GSList * slp,GList ** gl,gint maxo)4150 gtk_xtext_unstrip_color (gint start, gint end, GSList *slp, GList **gl, gint maxo)
4151 {
4152 gint off1, off2, curlen;
4153 GSList *cursl;
4154 offsets_t marks;
4155 offlen_t *meta;
4156
4157 off1 = 0;
4158 curlen = 0;
4159 cursl = slp;
4160 while (cursl)
4161 {
4162 meta = cursl->data;
4163 if (start < meta->len)
4164 {
4165 off1 = meta->off + start;
4166 break;
4167 }
4168 curlen += meta->len;
4169 start -= meta->len;
4170 end -= meta->len;
4171 cursl = g_slist_next (cursl);
4172 }
4173
4174 off2 = off1;
4175 while (cursl)
4176 {
4177 meta = cursl->data;
4178 if (end < meta->len)
4179 {
4180 off2 = meta->off + end;
4181 break;
4182 }
4183 curlen += meta->len;
4184 end -= meta->len;
4185 cursl = g_slist_next (cursl);
4186 }
4187 if (!cursl)
4188 {
4189 off2 = maxo;
4190 }
4191
4192 marks.o.start = off1;
4193 marks.o.end = off2;
4194 *gl = g_list_append (*gl, GUINT_TO_POINTER (marks.u));
4195 }
4196
4197 /* Search a single textentry for occurrence(s) of search arg string */
4198 static GList *
gtk_xtext_search_textentry(xtext_buffer * buf,textentry * ent)4199 gtk_xtext_search_textentry (xtext_buffer *buf, textentry *ent)
4200 {
4201 gchar *str; /* text string to be searched */
4202 GList *gl = NULL;
4203 GSList *slp;
4204 gint lstr;
4205
4206 if (buf->search_text == NULL)
4207 {
4208 return gl;
4209 }
4210
4211 str = gtk_xtext_strip_color (ent->str, ent->str_len, buf->xtext->scratch_buffer,
4212 &lstr, &slp, !buf->xtext->ignore_hidden);
4213
4214 /* Regular-expression matching --- */
4215 if (buf->search_flags & regexp)
4216 {
4217 GMatchInfo *gmi;
4218 gint start, end;
4219
4220 if (buf->search_re == NULL)
4221 {
4222 return gl;
4223 }
4224 g_regex_match (buf->search_re, str, 0, &gmi);
4225 while (g_match_info_matches (gmi))
4226 {
4227 g_match_info_fetch_pos (gmi, 0, &start, &end);
4228 gtk_xtext_unstrip_color (start, end, slp, &gl, ent->str_len);
4229 g_match_info_next (gmi, NULL);
4230 }
4231 g_match_info_free (gmi);
4232
4233 /* Non-regular-expression matching --- */
4234 } else {
4235 gchar *hay, *pos;
4236 gint lhay, off, len;
4237 gint match = buf->search_flags & case_match;
4238
4239 hay = match? g_strdup (str): g_utf8_casefold (str, lstr);
4240 lhay = strlen (hay);
4241
4242 for (pos = hay, len = lhay; len;
4243 off += buf->search_lnee, pos = hay + off, len = lhay - off)
4244 {
4245 str = g_strstr_len (pos, len, buf->search_nee);
4246 if (str == NULL)
4247 {
4248 break;
4249 }
4250 off = str - hay;
4251 gtk_xtext_unstrip_color (off, off + buf->search_lnee,
4252 slp, &gl, ent->str_len);
4253 }
4254
4255 g_free (hay);
4256 }
4257
4258 /* Common processing --- */
4259 g_slist_free_full (slp, g_free);
4260 return gl;
4261 }
4262
4263 /* Add a list of found search results to an entry, maybe NULL */
4264 static void
gtk_xtext_search_textentry_add(xtext_buffer * buf,textentry * ent,GList * gl,gboolean pre)4265 gtk_xtext_search_textentry_add (xtext_buffer *buf, textentry *ent, GList *gl, gboolean pre)
4266 {
4267 ent->marks = gl;
4268 if (gl)
4269 {
4270 buf->search_found = (pre? g_list_prepend: g_list_append) (buf->search_found, ent);
4271 if (pre == FALSE && buf->hintsearch == NULL)
4272 {
4273 buf->hintsearch = ent;
4274 }
4275 }
4276 }
4277
4278 /* Free all search information for a textentry */
4279 static void
gtk_xtext_search_textentry_del(xtext_buffer * buf,textentry * ent)4280 gtk_xtext_search_textentry_del (xtext_buffer *buf, textentry *ent)
4281 {
4282 g_list_free (ent->marks);
4283 ent->marks = NULL;
4284 if (buf->cursearch && buf->cursearch->data == ent)
4285 {
4286 buf->cursearch = NULL;
4287 buf->curmark = NULL;
4288 buf->curdata.u = 0;
4289 }
4290 if (buf->pagetop_ent == ent)
4291 {
4292 buf->pagetop_ent = NULL;
4293 }
4294 if (buf->hintsearch == ent)
4295 {
4296 buf->hintsearch = NULL;
4297 }
4298 buf->search_found = g_list_remove (buf->search_found, ent);
4299 }
4300
4301 /* Used only by glist_foreach */
4302 static void
gtk_xtext_search_textentry_fini(gpointer entp,gpointer dummy)4303 gtk_xtext_search_textentry_fini (gpointer entp, gpointer dummy)
4304 {
4305 textentry *ent = entp;
4306
4307 g_list_free (ent->marks);
4308 ent->marks = NULL;
4309 }
4310
4311 /* Free all search information for all textentrys and the xtext_buffer */
4312 static void
gtk_xtext_search_fini(xtext_buffer * buf)4313 gtk_xtext_search_fini (xtext_buffer *buf)
4314 {
4315 g_list_foreach (buf->search_found, gtk_xtext_search_textentry_fini, 0);
4316 g_list_free (buf->search_found);
4317 buf->search_found = NULL;
4318 g_free (buf->search_text);
4319 buf->search_text = NULL;
4320 g_free (buf->search_nee);
4321 buf->search_nee = NULL;
4322 buf->search_flags = 0;
4323 buf->cursearch = NULL;
4324 buf->curmark = NULL;
4325 /* but leave buf->curdata.u alone! */
4326 if (buf->search_re)
4327 {
4328 g_regex_unref (buf->search_re);
4329 buf->search_re = NULL;
4330 }
4331 }
4332
4333 /* Returns TRUE if the base search information exists and is still okay to use */
4334 static gboolean
gtk_xtext_search_init(xtext_buffer * buf,const gchar * text,gtk_xtext_search_flags flags,GError ** perr)4335 gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gtk_xtext_search_flags flags, GError **perr)
4336 {
4337 /* Of the five flags, backward and highlight_all do not need a new search */
4338 if (buf->search_found &&
4339 strcmp (buf->search_text, text) == 0 &&
4340 (buf->search_flags & case_match) == (flags & case_match) &&
4341 (buf->search_flags & follow) == (flags & follow) &&
4342 (buf->search_flags & regexp) == (flags & regexp))
4343 {
4344 return TRUE;
4345 }
4346 buf->hintsearch = buf->cursearch? buf->cursearch->data: NULL;
4347 gtk_xtext_search_fini (buf);
4348 buf->search_text = g_strdup (text);
4349 if (flags & regexp)
4350 {
4351 buf->search_re = g_regex_new (text, (flags & case_match)? 0: G_REGEX_CASELESS, 0, perr);
4352 if (perr && *perr)
4353 {
4354 return FALSE;
4355 }
4356 }
4357 else
4358 {
4359 if (flags & case_match)
4360 {
4361 buf->search_nee = g_strdup (text);
4362 }
4363 else
4364 {
4365 buf->search_nee = g_utf8_casefold (text, strlen (text));
4366 }
4367 buf->search_lnee = strlen (buf->search_nee);
4368 }
4369 buf->search_flags = flags;
4370 buf->cursearch = NULL;
4371 buf->curmark = NULL;
4372 /* but leave buf->curdata.u alone! */
4373 return FALSE;
4374 }
4375
4376 #define BACKWARD (flags & backward)
4377 #define FIRSTLAST(lp) (BACKWARD? g_list_last(lp): g_list_first(lp))
4378 #define NEXTPREVIOUS(lp) (BACKWARD? g_list_previous(lp): g_list_next(lp))
4379 textentry *
gtk_xtext_search(GtkXText * xtext,const gchar * text,gtk_xtext_search_flags flags,GError ** perr)4380 gtk_xtext_search (GtkXText * xtext, const gchar *text, gtk_xtext_search_flags flags, GError **perr)
4381 {
4382 textentry *ent = NULL;
4383 xtext_buffer *buf = xtext->buffer;
4384 GList *gl;
4385
4386 if (buf->text_first == NULL)
4387 {
4388 return NULL;
4389 }
4390
4391 /* If the text arg is NULL, one of these has been toggled: highlight follow */
4392 if (text == NULL) /* Here on highlight or follow toggle */
4393 {
4394 gint oldfollow = buf->search_flags & follow;
4395 gint newfollow = flags & follow;
4396
4397 /* If "Follow" has just been checked, search possible new textentries --- */
4398 if (newfollow && (newfollow != oldfollow))
4399 {
4400 gl = g_list_last (buf->search_found);
4401 ent = gl? gl->data: buf->text_first;
4402 for (; ent; ent = ent->next)
4403 {
4404 GList *gl;
4405
4406 gl = gtk_xtext_search_textentry (buf, ent);
4407 gtk_xtext_search_textentry_add (buf, ent, gl, FALSE);
4408 }
4409 }
4410 buf->search_flags = flags;
4411 ent = buf->pagetop_ent;
4412 }
4413
4414 /* if the text arg is "", the reset button has been clicked or Control-Shift-F has been hit */
4415 else if (text[0] == 0) /* Let a null string do a reset. */
4416 {
4417 gtk_xtext_search_fini (buf);
4418 }
4419
4420 /* If the text arg is neither NULL nor "", it's the search string */
4421 else
4422 {
4423 if (gtk_xtext_search_init (buf, text, flags, perr) == FALSE) /* If a new search: */
4424 {
4425 if (perr && *perr)
4426 {
4427 return NULL;
4428 }
4429 for (ent = buf->text_first; ent; ent = ent->next)
4430 {
4431 GList *gl;
4432
4433 gl = gtk_xtext_search_textentry (buf, ent);
4434 gtk_xtext_search_textentry_add (buf, ent, gl, TRUE);
4435 }
4436 buf->search_found = g_list_reverse (buf->search_found);
4437 }
4438
4439 /* Now base search results are in place. */
4440
4441 if (buf->search_found)
4442 {
4443 /* If we're in the midst of moving among found items */
4444 if (buf->cursearch)
4445 {
4446 ent = buf->cursearch->data;
4447 buf->curmark = NEXTPREVIOUS (buf->curmark);
4448 if (buf->curmark == NULL)
4449 {
4450 /* We've returned all the matches for this textentry. */
4451 buf->cursearch = NEXTPREVIOUS (buf->cursearch);
4452 if (buf->cursearch)
4453 {
4454 ent = buf->cursearch->data;
4455 buf->curmark = FIRSTLAST (ent->marks);
4456 }
4457 else /* We've returned all the matches for all textentries */
4458 {
4459 ent = NULL;
4460 }
4461 }
4462 }
4463
4464 /* If user changed the search, let's look starting where he was */
4465 else if (buf->hintsearch)
4466 {
4467 GList *mark;
4468 offsets_t last, this;
4469 /*
4470 * If we already have a 'current' item from the last search, and if
4471 * the first character of an occurrence on this line for this new search
4472 * is within that former item, use the occurrence as current.
4473 */
4474 ent = buf->hintsearch;
4475 last.u = buf->curdata.u;
4476 for (mark = ent->marks; mark; mark = mark->next)
4477 {
4478 this.u = GPOINTER_TO_UINT (mark->data);
4479 if (this.o.start >= last.o.start && this.o.start < last.o.end)
4480 break;
4481 }
4482 if (mark == NULL)
4483 {
4484 for (ent = buf->hintsearch; ent; ent = BACKWARD? ent->prev: ent->next)
4485 if (ent->marks)
4486 break;
4487 mark = ent? FIRSTLAST (ent->marks): NULL;
4488 }
4489 buf->cursearch = g_list_find (buf->search_found, ent);
4490 buf->curmark = mark;
4491 }
4492
4493 /* This is a fresh search */
4494 else
4495 {
4496 buf->cursearch = FIRSTLAST (buf->search_found);
4497 ent = buf->cursearch->data;
4498 buf->curmark = FIRSTLAST (ent->marks);
4499 }
4500 buf->curdata.u = (buf->curmark)? GPOINTER_TO_UINT (buf->curmark->data): 0;
4501 }
4502 }
4503 buf->hintsearch = ent;
4504
4505 if (!gtk_xtext_check_ent_visibility (xtext, ent, 1))
4506 {
4507 GtkAdjustment *adj = xtext->adj;
4508 float value;
4509
4510 buf->pagetop_ent = NULL;
4511 for (value = 0, ent = buf->text_first;
4512 ent && ent != buf->hintsearch; ent = ent->next)
4513 {
4514 value += g_slist_length (ent->sublines);
4515 }
4516 if (value > adj->upper - adj->page_size)
4517 {
4518 value = adj->upper - adj->page_size;
4519 }
4520 else if ((flags & backward) && ent)
4521 {
4522 value -= adj->page_size - g_slist_length (ent->sublines);
4523 if (value < 0)
4524 {
4525 value = 0;
4526 }
4527 }
4528 gtk_adjustment_set_value (adj, value);
4529 }
4530
4531 gtk_widget_queue_draw (GTK_WIDGET (xtext));
4532
4533 return buf->hintsearch;
4534 }
4535 #undef BACKWARD
4536 #undef FIRSTLAST
4537 #undef NEXTPREVIOUS
4538
4539 static int
gtk_xtext_render_page_timeout(GtkXText * xtext)4540 gtk_xtext_render_page_timeout (GtkXText * xtext)
4541 {
4542 GtkAdjustment *adj = xtext->adj;
4543
4544 xtext->add_io_tag = 0;
4545
4546 /* less than a complete page? */
4547 if (xtext->buffer->num_lines <= adj->page_size)
4548 {
4549 xtext->buffer->old_value = 0;
4550 adj->value = 0;
4551 gtk_xtext_render_page (xtext);
4552 } else if (xtext->buffer->scrollbar_down)
4553 {
4554 g_signal_handler_block (xtext->adj, xtext->vc_signal_tag);
4555 gtk_xtext_adjustment_set (xtext->buffer, FALSE);
4556 gtk_adjustment_set_value (adj, adj->upper - adj->page_size);
4557 g_signal_handler_unblock (xtext->adj, xtext->vc_signal_tag);
4558 xtext->buffer->old_value = adj->value;
4559 gtk_xtext_render_page (xtext);
4560 } else
4561 {
4562 gtk_xtext_adjustment_set (xtext->buffer, TRUE);
4563 if (xtext->force_render)
4564 {
4565 xtext->force_render = FALSE;
4566 gtk_xtext_render_page (xtext);
4567 }
4568 }
4569
4570 return 0;
4571 }
4572
4573 /* append a textentry to our linked list */
4574
4575 static void
gtk_xtext_append_entry(xtext_buffer * buf,textentry * ent,time_t stamp)4576 gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp)
4577 {
4578 int i;
4579
4580 /* we don't like tabs */
4581 i = 0;
4582 while (i < ent->str_len)
4583 {
4584 if (ent->str[i] == '\t')
4585 ent->str[i] = ' ';
4586 i++;
4587 }
4588
4589 ent->stamp = stamp;
4590 if (stamp == 0)
4591 ent->stamp = time (0);
4592 ent->slp = NULL;
4593 ent->str_width = gtk_xtext_text_width_ent (buf->xtext, ent);
4594 ent->mark_start = -1;
4595 ent->mark_end = -1;
4596 ent->next = NULL;
4597 ent->marks = NULL;
4598
4599 if (ent->indent < MARGIN)
4600 ent->indent = MARGIN; /* 2 pixels is the left margin */
4601
4602 /* append to our linked list */
4603 if (buf->text_last)
4604 buf->text_last->next = ent;
4605 else
4606 buf->text_first = ent;
4607 ent->prev = buf->text_last;
4608 buf->text_last = ent;
4609
4610 ent->sublines = NULL;
4611 buf->num_lines += gtk_xtext_lines_taken (buf, ent);
4612
4613 if ((buf->marker_pos == NULL || buf->marker_seen) && (buf->xtext->buffer != buf ||
4614 !gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (buf->xtext))))))
4615 {
4616 buf->marker_pos = ent;
4617 buf->marker_state = MARKER_IS_SET;
4618 dontscroll (buf); /* force scrolling off */
4619 buf->marker_seen = FALSE;
4620 }
4621
4622 if (buf->xtext->max_lines > 2 && buf->xtext->max_lines < buf->num_lines)
4623 {
4624 gtk_xtext_remove_top (buf);
4625 }
4626
4627 if (buf->xtext->buffer == buf)
4628 {
4629 /* this could be improved */
4630 if ((buf->num_lines - 1) <= buf->xtext->adj->page_size)
4631 dontscroll (buf);
4632
4633 if (!buf->xtext->add_io_tag)
4634 {
4635 /* remove scrolling events */
4636 if (buf->xtext->io_tag)
4637 {
4638 g_source_remove (buf->xtext->io_tag);
4639 buf->xtext->io_tag = 0;
4640 }
4641 buf->xtext->add_io_tag = g_timeout_add (REFRESH_TIMEOUT * 2,
4642 (GSourceFunc)
4643 gtk_xtext_render_page_timeout,
4644 buf->xtext);
4645 }
4646 }
4647 if (buf->scrollbar_down)
4648 {
4649 buf->old_value = buf->num_lines - buf->xtext->adj->page_size;
4650 if (buf->old_value < 0)
4651 buf->old_value = 0;
4652 }
4653 if (buf->search_flags & follow)
4654 {
4655 GList *gl;
4656
4657 gl = gtk_xtext_search_textentry (buf, ent);
4658 gtk_xtext_search_textentry_add (buf, ent, gl, FALSE);
4659 }
4660 }
4661
4662 /* the main two public functions */
4663
4664 void
gtk_xtext_append_indent(xtext_buffer * buf,unsigned char * left_text,int left_len,unsigned char * right_text,int right_len,time_t stamp)4665 gtk_xtext_append_indent (xtext_buffer *buf,
4666 unsigned char *left_text, int left_len,
4667 unsigned char *right_text, int right_len,
4668 time_t stamp)
4669 {
4670 textentry *ent;
4671 unsigned char *str;
4672 int space;
4673 int tempindent;
4674 int left_width;
4675
4676 if (left_len == -1)
4677 left_len = strlen (left_text);
4678
4679 if (right_len == -1)
4680 right_len = strlen (right_text);
4681
4682 if (left_len + right_len + 2 >= sizeof (buf->xtext->scratch_buffer))
4683 right_len = sizeof (buf->xtext->scratch_buffer) - left_len - 2;
4684
4685 if (right_text[right_len-1] == '\n')
4686 right_len--;
4687
4688 ent = g_malloc (left_len + right_len + 2 + sizeof (textentry));
4689 str = (unsigned char *) ent + sizeof (textentry);
4690
4691 if (left_len)
4692 memcpy (str, left_text, left_len);
4693 str[left_len] = ' ';
4694 if (right_len)
4695 memcpy (str + left_len + 1, right_text, right_len);
4696 str[left_len + 1 + right_len] = 0;
4697
4698 left_width = gtk_xtext_text_width (buf->xtext, left_text, left_len);
4699
4700 ent->left_len = left_len;
4701 ent->str = str;
4702 ent->str_len = left_len + 1 + right_len;
4703 ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
4704
4705 /* This is copied into the scratch buffer later, double check math */
4706 g_assert (ent->str_len < sizeof (buf->xtext->scratch_buffer));
4707
4708 if (buf->time_stamp)
4709 space = buf->xtext->stamp_width;
4710 else
4711 space = 0;
4712
4713 /* do we need to auto adjust the separator position? */
4714 if (buf->xtext->auto_indent &&
4715 buf->indent < buf->xtext->max_auto_indent &&
4716 ent->indent < MARGIN + space)
4717 {
4718 tempindent = MARGIN + space + buf->xtext->space_width + left_width;
4719
4720 if (tempindent > buf->indent)
4721 buf->indent = tempindent;
4722
4723 if (buf->indent > buf->xtext->max_auto_indent)
4724 buf->indent = buf->xtext->max_auto_indent;
4725
4726 gtk_xtext_fix_indent (buf);
4727 gtk_xtext_recalc_widths (buf, FALSE);
4728
4729 ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
4730 buf->xtext->force_render = TRUE;
4731 }
4732
4733 gtk_xtext_append_entry (buf, ent, stamp);
4734 }
4735
4736 void
gtk_xtext_append(xtext_buffer * buf,unsigned char * text,int len,time_t stamp)4737 gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len, time_t stamp)
4738 {
4739 textentry *ent;
4740 gboolean truncate = FALSE;
4741
4742 if (len == -1)
4743 len = strlen (text);
4744
4745 if (text[len-1] == '\n')
4746 len--;
4747
4748 if (len >= sizeof (buf->xtext->scratch_buffer))
4749 {
4750 len = sizeof (buf->xtext->scratch_buffer) - 1;
4751 truncate = TRUE;
4752 }
4753
4754 ent = g_malloc (len + 1 + sizeof (textentry));
4755 ent->str = (unsigned char *) ent + sizeof (textentry);
4756 ent->str_len = len;
4757 if (len)
4758 {
4759 if (!truncate)
4760 {
4761 memcpy (ent->str, text, len);
4762 ent->str[len] = '\0';
4763 }
4764 else
4765 {
4766 safe_strcpy (ent->str, text, sizeof (buf->xtext->scratch_buffer));
4767 ent->str_len = strlen (ent->str);
4768 }
4769 }
4770 ent->indent = 0;
4771 ent->left_len = -1;
4772
4773 gtk_xtext_append_entry (buf, ent, stamp);
4774 }
4775
4776 gboolean
gtk_xtext_is_empty(xtext_buffer * buf)4777 gtk_xtext_is_empty (xtext_buffer *buf)
4778 {
4779 return buf->text_first == NULL;
4780 }
4781
4782
4783 int
gtk_xtext_lastlog(xtext_buffer * out,xtext_buffer * search_area)4784 gtk_xtext_lastlog (xtext_buffer *out, xtext_buffer *search_area)
4785 {
4786 textentry *ent;
4787 int matches;
4788 GList *gl;
4789
4790 ent = search_area->text_first;
4791 matches = 0;
4792
4793 while (ent)
4794 {
4795 gl = gtk_xtext_search_textentry (out, ent);
4796 if (gl)
4797 {
4798 matches++;
4799 /* copy the text over */
4800 if (search_area->xtext->auto_indent)
4801 {
4802 gtk_xtext_append_indent (out, ent->str, ent->left_len,
4803 ent->str + ent->left_len + 1,
4804 ent->str_len - ent->left_len - 1, 0);
4805 }
4806 else
4807 {
4808 gtk_xtext_append (out, ent->str, ent->str_len, 0);
4809 }
4810
4811 if (out->text_last)
4812 {
4813 out->text_last->stamp = ent->stamp;
4814 gtk_xtext_search_textentry_add (out, out->text_last, gl, TRUE);
4815 }
4816 }
4817 ent = ent->next;
4818 }
4819 out->search_found = g_list_reverse (out->search_found);
4820
4821 return matches;
4822 }
4823
4824 void
gtk_xtext_foreach(xtext_buffer * buf,GtkXTextForeach func,void * data)4825 gtk_xtext_foreach (xtext_buffer *buf, GtkXTextForeach func, void *data)
4826 {
4827 textentry *ent = buf->text_first;
4828
4829 while (ent)
4830 {
4831 (*func) (buf->xtext, ent->str, data);
4832 ent = ent->next;
4833 }
4834 }
4835
4836 void
gtk_xtext_set_indent(GtkXText * xtext,gboolean indent)4837 gtk_xtext_set_indent (GtkXText *xtext, gboolean indent)
4838 {
4839 xtext->auto_indent = indent;
4840 }
4841
4842 void
gtk_xtext_set_max_indent(GtkXText * xtext,int max_auto_indent)4843 gtk_xtext_set_max_indent (GtkXText *xtext, int max_auto_indent)
4844 {
4845 xtext->max_auto_indent = max_auto_indent;
4846 }
4847
4848 void
gtk_xtext_set_max_lines(GtkXText * xtext,int max_lines)4849 gtk_xtext_set_max_lines (GtkXText *xtext, int max_lines)
4850 {
4851 xtext->max_lines = max_lines;
4852 }
4853
4854 void
gtk_xtext_set_show_marker(GtkXText * xtext,gboolean show_marker)4855 gtk_xtext_set_show_marker (GtkXText *xtext, gboolean show_marker)
4856 {
4857 xtext->marker = show_marker;
4858 }
4859
4860 void
gtk_xtext_set_show_separator(GtkXText * xtext,gboolean show_separator)4861 gtk_xtext_set_show_separator (GtkXText *xtext, gboolean show_separator)
4862 {
4863 xtext->separator = show_separator;
4864 }
4865
4866 void
gtk_xtext_set_thin_separator(GtkXText * xtext,gboolean thin_separator)4867 gtk_xtext_set_thin_separator (GtkXText *xtext, gboolean thin_separator)
4868 {
4869 xtext->thinline = thin_separator;
4870 }
4871
4872 void
gtk_xtext_set_time_stamp(xtext_buffer * buf,gboolean time_stamp)4873 gtk_xtext_set_time_stamp (xtext_buffer *buf, gboolean time_stamp)
4874 {
4875 buf->time_stamp = time_stamp;
4876 }
4877
4878 void
gtk_xtext_set_urlcheck_function(GtkXText * xtext,int (* urlcheck_function)(GtkWidget *,char *))4879 gtk_xtext_set_urlcheck_function (GtkXText *xtext, int (*urlcheck_function) (GtkWidget *, char *))
4880 {
4881 xtext->urlcheck_function = urlcheck_function;
4882 }
4883
4884 void
gtk_xtext_set_wordwrap(GtkXText * xtext,gboolean wordwrap)4885 gtk_xtext_set_wordwrap (GtkXText *xtext, gboolean wordwrap)
4886 {
4887 xtext->wordwrap = wordwrap;
4888 }
4889
4890 void
gtk_xtext_set_marker_last(session * sess)4891 gtk_xtext_set_marker_last (session *sess)
4892 {
4893 xtext_buffer *buf = sess->res->buffer;
4894
4895 buf->marker_pos = buf->text_last;
4896 buf->marker_state = MARKER_IS_SET;
4897 }
4898
4899 void
gtk_xtext_reset_marker_pos(GtkXText * xtext)4900 gtk_xtext_reset_marker_pos (GtkXText *xtext)
4901 {
4902 if (xtext->buffer->marker_pos)
4903 {
4904 xtext->buffer->marker_pos = NULL;
4905 dontscroll (xtext->buffer); /* force scrolling off */
4906 gtk_xtext_render_page (xtext);
4907 xtext->buffer->marker_state = MARKER_RESET_MANUALLY;
4908 }
4909 }
4910
4911 int
gtk_xtext_moveto_marker_pos(GtkXText * xtext)4912 gtk_xtext_moveto_marker_pos (GtkXText *xtext)
4913 {
4914 gdouble value = 0;
4915 xtext_buffer *buf = xtext->buffer;
4916 textentry *ent = buf->text_first;
4917 GtkAdjustment *adj = xtext->adj;
4918
4919 if (buf->marker_pos == NULL)
4920 return buf->marker_state;
4921
4922 if (gtk_xtext_check_ent_visibility (xtext, buf->marker_pos, 1) == FALSE)
4923 {
4924 while (ent)
4925 {
4926 if (ent == buf->marker_pos)
4927 break;
4928 value += g_slist_length (ent->sublines);
4929 ent = ent->next;
4930 }
4931 if (value >= adj->value && value < adj->value + adj->page_size)
4932 return MARKER_IS_SET;
4933 value -= adj->page_size / 2;
4934 if (value < 0)
4935 value = 0;
4936 if (value > adj->upper - adj->page_size)
4937 value = adj->upper - adj->page_size;
4938 gtk_adjustment_set_value (adj, value);
4939 gtk_xtext_render_page (xtext);
4940 }
4941
4942 /* If we previously lost marker position to scrollback limit -- */
4943 if (buf->marker_pos == buf->text_first &&
4944 buf->marker_state == MARKER_RESET_BY_KILL)
4945 return MARKER_RESET_BY_KILL;
4946 else
4947 return MARKER_IS_SET;
4948 }
4949
4950 void
gtk_xtext_buffer_show(GtkXText * xtext,xtext_buffer * buf,int render)4951 gtk_xtext_buffer_show (GtkXText *xtext, xtext_buffer *buf, int render)
4952 {
4953 int w, h;
4954
4955 buf->xtext = xtext;
4956
4957 if (xtext->buffer == buf)
4958 return;
4959
4960 /*printf("text_buffer_show: xtext=%p buffer=%p\n", xtext, buf);*/
4961
4962 if (xtext->add_io_tag)
4963 {
4964 g_source_remove (xtext->add_io_tag);
4965 xtext->add_io_tag = 0;
4966 }
4967
4968 if (xtext->io_tag)
4969 {
4970 g_source_remove (xtext->io_tag);
4971 xtext->io_tag = 0;
4972 }
4973
4974 if (!gtk_widget_get_realized (GTK_WIDGET (xtext)))
4975 gtk_widget_realize (GTK_WIDGET (xtext));
4976
4977 h = gdk_window_get_height (gtk_widget_get_window (GTK_WIDGET (xtext)));
4978 w = gdk_window_get_width (gtk_widget_get_window (GTK_WIDGET (xtext)));
4979
4980 /* after a font change */
4981 if (buf->needs_recalc)
4982 {
4983 buf->needs_recalc = FALSE;
4984 gtk_xtext_recalc_widths (buf, TRUE);
4985 }
4986
4987 /* now change to the new buffer */
4988 xtext->buffer = buf;
4989 dontscroll (buf); /* force scrolling off */
4990 xtext->adj->value = buf->old_value;
4991 xtext->adj->upper = buf->num_lines;
4992
4993 /* if the scrollbar was down, keep it down */
4994 if (xtext->buffer->scrollbar_down && xtext->adj->value <
4995 xtext->adj->upper - xtext->adj->page_size)
4996 {
4997 xtext->adj->value = xtext->adj->upper - xtext->adj->page_size;
4998 }
4999
5000 if (xtext->adj->upper == 0)
5001 xtext->adj->upper = 1;
5002 /* sanity check */
5003 else if (xtext->adj->value > xtext->adj->upper - xtext->adj->page_size)
5004 {
5005 /*buf->pagetop_ent = NULL;*/
5006 xtext->adj->value = xtext->adj->upper - xtext->adj->page_size;
5007 if (xtext->adj->value < 0)
5008 xtext->adj->value = 0;
5009 }
5010
5011 if (render)
5012 {
5013 /* did the window change size since this buffer was last shown? */
5014 if (buf->window_width != w)
5015 {
5016 buf->window_width = w;
5017 buf->window_height = h;
5018 gtk_xtext_calc_lines (buf, FALSE);
5019 if (buf->scrollbar_down)
5020 gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
5021 xtext->adj->page_size);
5022 } else if (buf->window_height != h)
5023 {
5024 buf->window_height = h;
5025 buf->pagetop_ent = NULL;
5026 if (buf->scrollbar_down)
5027 xtext->adj->value = xtext->adj->upper;
5028 gtk_xtext_adjustment_set (buf, FALSE);
5029 }
5030
5031 gtk_xtext_render_page (xtext);
5032 gtk_adjustment_changed (xtext->adj);
5033 }
5034 }
5035
5036 xtext_buffer *
gtk_xtext_buffer_new(GtkXText * xtext)5037 gtk_xtext_buffer_new (GtkXText *xtext)
5038 {
5039 xtext_buffer *buf;
5040
5041 buf = g_new0 (xtext_buffer, 1);
5042 buf->old_value = -1;
5043 buf->xtext = xtext;
5044 buf->scrollbar_down = TRUE;
5045 buf->indent = xtext->space_width * 2;
5046 dontscroll (buf);
5047
5048 return buf;
5049 }
5050
5051 void
gtk_xtext_buffer_free(xtext_buffer * buf)5052 gtk_xtext_buffer_free (xtext_buffer *buf)
5053 {
5054 textentry *ent, *next;
5055
5056 if (buf->xtext->buffer == buf)
5057 buf->xtext->buffer = buf->xtext->orig_buffer;
5058
5059 if (buf->xtext->selection_buffer == buf)
5060 buf->xtext->selection_buffer = NULL;
5061
5062 if (buf->search_found)
5063 {
5064 gtk_xtext_search_fini (buf);
5065 }
5066
5067 ent = buf->text_first;
5068 while (ent)
5069 {
5070 next = ent->next;
5071 g_free (ent);
5072 ent = next;
5073 }
5074
5075 g_free (buf);
5076 }
5077