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