Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

added Message Overview plugin: view all message activity in a single …

…buffer.
  • Loading branch information...
commit 670198a5d590f033c1d3a872b7965a599c5e8d31 1 parent bdcb67f
Mitchell Cooper authored April 09, 2012
3,949  src/fe-gtk/xtext.c.1
... ...
@@ -0,0 +1,3949 @@
  1
+/* Conspire
  2
+ * Copyright (c) 2007,2008 William Pitcock
  3
+ *
  4
+ * X-Chat
  5
+ * Copyright (C) 1998 Peter Zelezny
  6
+ *
  7
+ * This program is free software; you can redistribute it and/or modify
  8
+ * it under the terms of the GNU General Public License as published by
  9
+ * the Free Software Foundation; either version 2 of the License, or
  10
+ * (at your option) any later version.
  11
+ *
  12
+ * This program is distributed in the hope that it will be useful,
  13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15
+ * GNU General Public License for more details.
  16
+ *
  17
+ * You should have received a copy of the GNU General Public License
  18
+ * along with this program; if not, write to the Free Software
  19
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  20
+ */
  21
+
  22
+/*
  23
+ * This is XText-NG. It is a refactored version of XText, which was quite
  24
+ * ancient. It was written to address the following issues with the original
  25
+ * XText:
  26
+ *
  27
+ *   - direct manipulation of video memory
  28
+ *   - overall speed
  29
+ *   - other things
  30
+ */
  31
+
  32
+/*
  33
+ * MARGIN defines the margin used for the widget. Use the default of 2 unless
  34
+ * you know what you're doing.
  35
+ */
  36
+#define MARGIN 2
  37
+
  38
+/*
  39
+ * REFRESH_TIMEOUT is the value used for refreshing the XText buffer. This will
  40
+ * eventually be removed.
  41
+ */
  42
+#define REFRESH_TIMEOUT 20
  43
+
  44
+/*
  45
+ * WORDWRAP_LIMIT is the default limit in pixels of when to cut off and start
  46
+ * a new line of text.
  47
+ */
  48
+#define WORDWRAP_LIMIT 24
  49
+
  50
+#include <string.h>
  51
+#include <ctype.h>
  52
+#include <stdlib.h>
  53
+#ifndef _WIN32
  54
+# include <unistd.h>
  55
+#endif
  56
+#include <time.h>
  57
+
  58
+#include <cairo.h>
  59
+#include <gtk/gtk.h>
  60
+#include <pango/pangocairo.h>
  61
+
  62
+#include "xtext.h"
  63
+
  64
+#define charlen(str) g_utf8_skip[*(guchar *)(str)]
  65
+
  66
+/* is delimiter */
  67
+#define is_del(c) \
  68
+	(c == ' ' || c == '\n' || c == ')' || c == '(' || \
  69
+	 c == '>' || c == '<' || c == ATTR_RESET || c == ATTR_BOLD || c == 0)
  70
+
  71
+/* force scrolling off */
  72
+#define dontscroll(buf) (buf)->last_pixel_pos = 0x7fffffff
  73
+
  74
+static GtkWidgetClass *parent_class = NULL;
  75
+
  76
+struct textentry
  77
+{
  78
+	struct textentry *next;
  79
+	struct textentry *prev;
  80
+	unsigned char *str;
  81
+	time_t stamp;
  82
+	gint16 str_width;
  83
+	gint16 str_len;
  84
+	gint16 mark_start;
  85
+	gint16 mark_end;
  86
+	gint16 indent;
  87
+	gint16 left_len;
  88
+	gint16 lines_taken;
  89
+#define RECORD_WRAPS 4
  90
+	guint16 wrap_offset[RECORD_WRAPS];
  91
+	unsigned int mb:1;	/* is multibyte? */
  92
+};
  93
+
  94
+enum
  95
+{
  96
+	WORD_CLICK,
  97
+	LAST_SIGNAL
  98
+};
  99
+
  100
+/* values for selection info */
  101
+enum
  102
+{
  103
+	TARGET_UTF8_STRING,
  104
+	TARGET_STRING,
  105
+	TARGET_TEXT,
  106
+	TARGET_COMPOUND_TEXT
  107
+};
  108
+
  109
+static guint xtext_signals[LAST_SIGNAL];
  110
+
  111
+char *nocasestrstr (const char *text, const char *tofind);	/* util.c */
  112
+int xtext_get_stamp_str (time_t, char **);
  113
+static void gtk_xtext_render_page (GtkXText * xtext);
  114
+static void gtk_xtext_calc_lines (xtext_buffer *buf, int);
  115
+static char *gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret);
  116
+static textentry *gtk_xtext_nth (GtkXText *xtext, int line, int *subline);
  117
+static void gtk_xtext_adjustment_changed (GtkAdjustment * adj,
  118
+														GtkXText * xtext);
  119
+static int gtk_xtext_render_ents (GtkXText * xtext, textentry *, textentry *);
  120
+static void gtk_xtext_recalc_widths (xtext_buffer *buf, int);
  121
+static void gtk_xtext_fix_indent (xtext_buffer *buf);
  122
+static int gtk_xtext_find_subline (GtkXText *xtext, textentry *ent, int line);
  123
+static unsigned char *
  124
+gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
  125
+							  int *newlen, int *mb_ret, int strip_hidden);
  126
+
  127
+static cairo_t *
  128
+gtk_xtext_create_cairo_handle (GtkXText *xtext)
  129
+{
  130
+	cairo_t *cr;
  131
+
  132
+	g_return_val_if_fail(xtext != NULL, NULL);
  133
+	g_return_val_if_fail(xtext->draw_buf != NULL, NULL);
  134
+
  135
+	cr = gdk_cairo_create(GDK_DRAWABLE(xtext->draw_buf));
  136
+#if GTK_CHECK_VERSION(2,18,0)
  137
+	gdk_cairo_reset_clip(cr, GDK_DRAWABLE(xtext->draw_buf));
  138
+#endif
  139
+
  140
+	return cr;
  141
+}
  142
+
  143
+/* gives width of a 8bit string - with no mIRC codes in it */
  144
+
  145
+static int
  146
+gtk_xtext_text_width_8bit (GtkXText *xtext, unsigned char *str, int len)
  147
+{
  148
+	int width = 0;
  149
+
  150
+	while (len)
  151
+	{
  152
+		width += xtext->fontwidth[*str];
  153
+		str++;
  154
+		len--;
  155
+	}
  156
+
  157
+	return width;
  158
+}
  159
+
  160
+static void
  161
+xtext_draw_bg(GtkXText *xtext, gint x, gint y, gint width, gint height)
  162
+{
  163
+	cairo_t *cr;
  164
+
  165
+	g_return_if_fail(xtext != NULL);
  166
+
  167
+	cr = gtk_xtext_create_cairo_handle(xtext);
  168
+	cairo_rectangle(cr, x, y, width, height);
  169
+
  170
+        cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
  171
+	cairo_set_source(cr, xtext->bg_pattern);
  172
+
  173
+	cairo_fill(cr);
  174
+
  175
+	cairo_destroy(cr);
  176
+}
  177
+
  178
+static void
  179
+xtext_set_bg(GtkXText *xtext, gint color)
  180
+{
  181
+	GdkColor *bgcol;
  182
+
  183
+	g_return_if_fail(xtext != NULL);
  184
+
  185
+	xtext->bgcol = color;
  186
+	bgcol = &xtext->palette[xtext->bgcol];
  187
+
  188
+	if (xtext->bg_pattern != NULL)
  189
+		cairo_pattern_destroy(xtext->bg_pattern);
  190
+
  191
+	xtext->bg_pattern = cairo_pattern_create_rgba (bgcol->red / 65535.,
  192
+						       bgcol->green / 65535.,
  193
+						       bgcol->blue / 65535.,
  194
+						       xtext->transparency);
  195
+}
  196
+
  197
+static void
  198
+xtext_set_fg(GtkXText *xtext, gint color)
  199
+{
  200
+	g_return_if_fail(xtext != NULL);
  201
+
  202
+	xtext->fgcol = color;
  203
+}
  204
+
  205
+/* ======================================= */
  206
+/* ============ PANGO BACKEND ============ */
  207
+/* ======================================= */
  208
+
  209
+static void
  210
+backend_font_close (GtkXText *xtext)
  211
+{
  212
+	pango_font_description_free (xtext->font->font);
  213
+	pango_font_description_free (xtext->font->ifont);
  214
+}
  215
+
  216
+static void
  217
+backend_init (GtkXText *xtext)
  218
+{
  219
+	if (xtext->layout == NULL)
  220
+	{
  221
+		xtext->layout = gtk_widget_create_pango_layout (GTK_WIDGET (xtext), 0); 
  222
+		if (xtext->font)
  223
+			pango_layout_set_font_description (xtext->layout, xtext->font->font);
  224
+	}
  225
+}
  226
+
  227
+static void
  228
+backend_deinit (GtkXText *xtext)
  229
+{
  230
+	if (xtext->layout)
  231
+	{
  232
+		g_object_unref (xtext->layout);
  233
+		xtext->layout = NULL;
  234
+	}
  235
+}
  236
+
  237
+static PangoFontDescription *
  238
+backend_font_open_real (char *name)
  239
+{
  240
+	PangoFontDescription *font;
  241
+
  242
+	font = pango_font_description_from_string (name);
  243
+	if (font && pango_font_description_get_size (font) == 0)
  244
+	{
  245
+		pango_font_description_free (font);
  246
+		font = pango_font_description_from_string ("sans 11");
  247
+	}
  248
+	if (!font)
  249
+		font = pango_font_description_from_string ("sans 11");
  250
+
  251
+	return font;
  252
+}
  253
+
  254
+static void
  255
+backend_font_open (GtkXText *xtext, char *name)
  256
+{
  257
+	PangoLanguage *lang;
  258
+	PangoContext *context;
  259
+	PangoFontMetrics *metrics;
  260
+
  261
+	xtext->font = &xtext->pango_font;
  262
+	xtext->font->font = backend_font_open_real (name);
  263
+	if (!xtext->font->font)
  264
+	{
  265
+		xtext->font = NULL;
  266
+		return;
  267
+	}
  268
+
  269
+	xtext->font->ifont = backend_font_open_real (name);
  270
+	pango_font_description_set_style (xtext->font->ifont, PANGO_STYLE_ITALIC);
  271
+
  272
+	backend_init (xtext);
  273
+	pango_layout_set_font_description (xtext->layout, xtext->font->font);
  274
+
  275
+	/* vte does it this way */
  276
+	context = gtk_widget_get_pango_context (GTK_WIDGET (xtext));
  277
+	lang = pango_context_get_language (context);
  278
+	metrics = pango_context_get_metrics (context, xtext->font->font, lang);
  279
+	xtext->font->ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
  280
+	xtext->font->descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
  281
+	pango_font_metrics_unref (metrics);
  282
+}
  283
+
  284
+static int
  285
+backend_get_text_width (GtkXText *xtext, guchar *str, int len, int is_mb)
  286
+{
  287
+	int width;
  288
+
  289
+	if (!is_mb)
  290
+		return gtk_xtext_text_width_8bit (xtext, str, len);
  291
+
  292
+	if (*str == 0)
  293
+		return 0;
  294
+
  295
+	pango_layout_set_text (xtext->layout, str, len);
  296
+	pango_layout_get_pixel_size (xtext->layout, &width, NULL);
  297
+
  298
+	return width;
  299
+}
  300
+
  301
+inline static int
  302
+backend_get_char_width (GtkXText *xtext, unsigned char *str, int *mbl_ret)
  303
+{
  304
+	int width;
  305
+
  306
+	if (*str < 128)
  307
+	{
  308
+		*mbl_ret = 1;
  309
+		return xtext->fontwidth[*str];
  310
+	}
  311
+
  312
+	*mbl_ret = charlen (str);
  313
+	pango_layout_set_text (xtext->layout, str, *mbl_ret);
  314
+	pango_layout_get_pixel_size (xtext->layout, &width, NULL);
  315
+
  316
+	return width;
  317
+}
  318
+
  319
+static void
  320
+backend_draw_text (GtkXText *xtext, int dofill, int x, int y, const gchar *str, int len, int str_width, int is_mb)
  321
+{
  322
+	cairo_t *cr;
  323
+
  324
+	cr = gtk_xtext_create_cairo_handle(xtext);
  325
+	cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
  326
+
  327
+	if (xtext->italics)
  328
+		pango_layout_set_font_description (xtext->layout, xtext->font->ifont);
  329
+
  330
+	pango_layout_set_text (xtext->layout, str, len);
  331
+
  332
+	if (dofill)
  333
+		xtext_draw_bg(xtext, x, y, str_width, xtext->fontsize);
  334
+
  335
+	gdk_cairo_set_source_color(cr, &xtext->palette[xtext->fgcol]);
  336
+
  337
+	cairo_move_to(cr, x, y);
  338
+	pango_cairo_show_layout(cr, xtext->layout);
  339
+
  340
+	if (xtext->bold)
  341
+	{
  342
+		cairo_rel_move_to(cr, 1, 0);
  343
+		pango_cairo_show_layout(cr, xtext->layout);
  344
+	}
  345
+
  346
+	if (xtext->italics)
  347
+		pango_layout_set_font_description (xtext->layout, xtext->font->font);
  348
+
  349
+	cairo_destroy(cr);
  350
+}
  351
+
  352
+static void
  353
+gtk_xtext_init (GtkXText * xtext)
  354
+{
  355
+	xtext->io_tag = 0;
  356
+	xtext->add_io_tag = 0;
  357
+	xtext->scroll_tag = 0;
  358
+	xtext->max_lines = 0;
  359
+	xtext->col_back = XTEXT_BG;
  360
+	xtext->col_fore = XTEXT_FG;
  361
+	xtext->nc = 0;
  362
+	xtext->pixel_offset = 0;
  363
+	xtext->bold = FALSE;
  364
+	xtext->underline = FALSE;
  365
+	xtext->hidden = FALSE;
  366
+	xtext->font = NULL;
  367
+	xtext->layout = NULL;
  368
+	xtext->italics = FALSE;
  369
+	xtext->jump_out_offset = 0;
  370
+	xtext->jump_in_offset = 0;
  371
+	xtext->ts_x = 0;
  372
+	xtext->ts_y = 0;
  373
+	xtext->clip_x = 0;
  374
+	xtext->clip_x2 = 1000000;
  375
+	xtext->clip_y = 0;
  376
+	xtext->clip_y2 = 1000000;
  377
+	xtext->urlcheck_function = NULL;
  378
+	xtext->color_paste = FALSE;
  379
+	xtext->skip_border_fills = FALSE;
  380
+	xtext->skip_stamp = FALSE;
  381
+	xtext->render_hilights_only = FALSE;
  382
+	xtext->un_hilight = FALSE;
  383
+	xtext->recycle = FALSE;
  384
+	xtext->dont_render = FALSE;
  385
+	xtext->dont_render2 = FALSE;
  386
+	xtext->transparency = 1.0;
  387
+
  388
+	xtext->adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 1, 1, 1, 1);
  389
+	g_object_ref (G_OBJECT (xtext->adj));
  390
+	g_object_ref_sink (G_OBJECT (xtext->adj));
  391
+	g_object_unref (G_OBJECT (xtext->adj));
  392
+
  393
+	xtext->vc_signal_tag = g_signal_connect (G_OBJECT (xtext->adj),
  394
+				"value_changed", G_CALLBACK (gtk_xtext_adjustment_changed), xtext);
  395
+	{
  396
+		static const GtkTargetEntry targets[] = {
  397
+			{ "UTF8_STRING", 0, TARGET_UTF8_STRING },
  398
+			{ "STRING", 0, TARGET_STRING },
  399
+			{ "TEXT",   0, TARGET_TEXT }, 
  400
+			{ "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }
  401
+		};
  402
+		static const gint n_targets = sizeof (targets) / sizeof (targets[0]);
  403
+
  404
+		gtk_selection_add_targets (GTK_WIDGET (xtext), GDK_SELECTION_PRIMARY,
  405
+											targets, n_targets);
  406
+	}
  407
+}
  408
+
  409
+static void
  410
+gtk_xtext_adjustment_set (xtext_buffer *buf, int fire_signal)
  411
+{
  412
+	GtkAdjustment *adj = buf->xtext->adj;
  413
+
  414
+	if (buf->xtext->buffer == buf)
  415
+	{
  416
+		adj->lower = 0;
  417
+		adj->upper = buf->num_lines;
  418
+
  419
+		if (adj->upper == 0)
  420
+			adj->upper = 1;
  421
+
  422
+		adj->page_size =
  423
+			(GTK_WIDGET (buf->xtext)->allocation.height -
  424
+			 buf->xtext->font->descent) / buf->xtext->fontsize;
  425
+		adj->page_increment = adj->page_size;
  426
+
  427
+		if (adj->value > adj->upper - adj->page_size)
  428
+			adj->value = adj->upper - adj->page_size;
  429
+
  430
+		if (adj->value < 0)
  431
+			adj->value = 0;
  432
+
  433
+		if (fire_signal)
  434
+			gtk_adjustment_changed (adj);
  435
+	}
  436
+}
  437
+
  438
+static gint
  439
+gtk_xtext_adjustment_timeout (GtkXText * xtext)
  440
+{
  441
+	gtk_xtext_render_page (xtext);
  442
+	xtext->io_tag = 0;
  443
+	return 0;
  444
+}
  445
+
  446
+static void
  447
+gtk_xtext_adjustment_changed (GtkAdjustment * adj, GtkXText * xtext)
  448
+{
  449
+	if (xtext->buffer->old_value != xtext->adj->value)
  450
+	{
  451
+		if (xtext->adj->value >= xtext->adj->upper - xtext->adj->page_size)
  452
+			xtext->buffer->scrollbar_down = TRUE;
  453
+		else
  454
+			xtext->buffer->scrollbar_down = FALSE;
  455
+
  456
+		if (xtext->adj->value + 1 == xtext->buffer->old_value ||
  457
+			 xtext->adj->value - 1 == xtext->buffer->old_value)	/* clicked an arrow? */
  458
+		{
  459
+			if (xtext->io_tag)
  460
+			{
  461
+				g_source_remove (xtext->io_tag);
  462
+				xtext->io_tag = 0;
  463
+			}
  464
+			gtk_xtext_render_page (xtext);
  465
+		} else
  466
+		{
  467
+			if (!xtext->io_tag)
  468
+				xtext->io_tag = g_timeout_add (REFRESH_TIMEOUT,
  469
+															(GSourceFunc)
  470
+															gtk_xtext_adjustment_timeout,
  471
+															xtext);
  472
+		}
  473
+	}
  474
+	xtext->buffer->old_value = adj->value;
  475
+}
  476
+
  477
+GtkWidget *
  478
+gtk_xtext_new (GdkColor palette[], int separator)
  479
+{
  480
+	GtkXText *xtext;
  481
+
  482
+	xtext = g_object_new (gtk_xtext_get_type (), NULL);
  483
+	xtext->separator = separator;
  484
+	xtext->wordwrap = TRUE;
  485
+	xtext->buffer = gtk_xtext_buffer_new (xtext);
  486
+	xtext->orig_buffer = xtext->buffer;
  487
+
  488
+	gtk_xtext_set_palette (xtext, palette);
  489
+
  490
+	return GTK_WIDGET (xtext);
  491
+}
  492
+
  493
+static void
  494
+gtk_xtext_destroy (GtkObject * object)
  495
+{
  496
+	GtkXText *xtext = GTK_XTEXT (object);
  497
+
  498
+	if (xtext->add_io_tag)
  499
+	{
  500
+		g_source_remove (xtext->add_io_tag);
  501
+		xtext->add_io_tag = 0;
  502
+	}
  503
+
  504
+	if (xtext->scroll_tag)
  505
+	{
  506
+		g_source_remove (xtext->scroll_tag);
  507
+		xtext->scroll_tag = 0;
  508
+	}
  509
+
  510
+	if (xtext->io_tag)
  511
+	{
  512
+		g_source_remove (xtext->io_tag);
  513
+		xtext->io_tag = 0;
  514
+	}
  515
+
  516
+	if (xtext->font)
  517
+	{
  518
+		backend_font_close (xtext);
  519
+		xtext->font = NULL;
  520
+	}
  521
+
  522
+	if (xtext->adj)
  523
+	{
  524
+		g_signal_handlers_disconnect_matched (G_OBJECT (xtext->adj),
  525
+					G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xtext);
  526
+		g_object_unref (G_OBJECT (xtext->adj));
  527
+		xtext->adj = NULL;
  528
+	}
  529
+
  530
+	if (xtext->hand_cursor)
  531
+	{
  532
+		gdk_cursor_unref (xtext->hand_cursor);
  533
+		xtext->hand_cursor = NULL;
  534
+	}
  535
+
  536
+	if (xtext->resize_cursor)
  537
+	{
  538
+		gdk_cursor_unref (xtext->resize_cursor);
  539
+		xtext->resize_cursor = NULL;
  540
+	}
  541
+
  542
+	if (xtext->orig_buffer)
  543
+	{
  544
+		gtk_xtext_buffer_free (xtext->orig_buffer);
  545
+		xtext->orig_buffer = NULL;
  546
+	}
  547
+
  548
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
  549
+		(*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
  550
+}
  551
+
  552
+static void
  553
+gtk_xtext_unrealize (GtkWidget * widget)
  554
+{
  555
+	backend_deinit (GTK_XTEXT (widget));
  556
+
  557
+	/* if there are still events in the queue, this'll avoid segfault */
  558
+	gdk_window_set_user_data (widget->window, NULL);
  559
+
  560
+	if (parent_class->unrealize)
  561
+		(* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
  562
+}
  563
+
  564
+static void
  565
+gtk_xtext_realize (GtkWidget * widget)
  566
+{
  567
+	GtkXText *xtext;
  568
+	GdkWindowAttr attributes;
  569
+	GdkColormap *cmap;
  570
+
  571
+	GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  572
+	xtext = GTK_XTEXT (widget);
  573
+
  574
+	attributes.x = widget->allocation.x;
  575
+	attributes.y = widget->allocation.y;
  576
+	attributes.width = widget->allocation.width;
  577
+	attributes.height = widget->allocation.height;
  578
+	attributes.wclass = GDK_INPUT_OUTPUT;
  579
+	attributes.window_type = GDK_WINDOW_CHILD;
  580
+	attributes.event_mask = gtk_widget_get_events (widget) |
  581
+		GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
  582
+		| GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK;
  583
+
  584
+	cmap = gtk_widget_get_colormap (widget);
  585
+	attributes.colormap = cmap;
  586
+	attributes.visual = gtk_widget_get_visual (widget);
  587
+
  588
+	widget->window = gdk_window_new (gtk_widget_get_parent_window(widget), &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP);
  589
+	gdk_window_set_user_data (widget->window, xtext);
  590
+
  591
+#if GTK_CHECK_VERSION(2, 18, 0)
  592
+	gdk_window_ensure_native (widget->window);
  593
+#endif
  594
+
  595
+	xtext->depth = gdk_drawable_get_visual (widget->window)->depth;
  596
+
  597
+	xtext_set_fg(xtext, XTEXT_FG);
  598
+	xtext_set_bg(xtext, XTEXT_BG);
  599
+
  600
+	/* draw directly to window */
  601
+	xtext->draw_buf = widget->window;
  602
+
  603
+	xtext->hand_cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (widget->window), GDK_HAND1);
  604
+	xtext->resize_cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (widget->window), GDK_LEFT_SIDE);
  605
+
  606
+	xtext_draw_bg (xtext, attributes.x, attributes.y, attributes.width, attributes.height);
  607
+	widget->style = gtk_style_attach (widget->style, widget->window);
  608
+
  609
+	backend_init (xtext);
  610
+}
  611
+
  612
+static void
  613
+gtk_xtext_size_request (GtkWidget * widget, GtkRequisition * requisition)
  614
+{
  615
+	requisition->width = 200;
  616
+	requisition->height = 90;
  617
+}
  618
+
  619
+static void
  620
+gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
  621
+{
  622
+	GtkXText *xtext = GTK_XTEXT (widget);
  623
+	int height_only = FALSE;
  624
+
  625
+	if (allocation->width == xtext->buffer->window_width)
  626
+		height_only = TRUE;
  627
+
  628
+	widget->allocation = *allocation;
  629
+	if (GTK_WIDGET_REALIZED (widget))
  630
+	{
  631
+		xtext->buffer->window_width = allocation->width;
  632
+		xtext->buffer->window_height = allocation->height;
  633
+
  634
+		gdk_window_move_resize (widget->window, allocation->x, allocation->y,
  635
+										allocation->width, allocation->height);
  636
+		dontscroll (xtext->buffer);	/* force scrolling off */
  637
+		if (!height_only)
  638
+			gtk_xtext_calc_lines (xtext->buffer, FALSE);
  639
+		else
  640
+		{
  641
+			xtext->buffer->pagetop_ent = NULL;
  642
+			gtk_xtext_adjustment_set (xtext->buffer, FALSE);
  643
+		}
  644
+		if (xtext->buffer->scrollbar_down)
  645
+			gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
  646
+											  xtext->adj->page_size);
  647
+	}
  648
+}
  649
+
  650
+static void
  651
+gtk_xtext_selection_clear_full (xtext_buffer *buf)
  652
+{
  653
+	textentry *ent = buf->text_first;
  654
+	while (ent)
  655
+	{
  656
+		ent->mark_start = -1;
  657
+		ent->mark_end = -1;
  658
+		ent = ent->next;
  659
+	}
  660
+}
  661
+
  662
+static int
  663
+gtk_xtext_selection_clear (xtext_buffer *buf)
  664
+{
  665
+	textentry *ent;
  666
+	int ret = 0;
  667
+
  668
+	ent = buf->last_ent_start;
  669
+	while (ent)
  670
+	{
  671
+		if (ent->mark_start != -1)
  672
+			ret = 1;
  673
+		ent->mark_start = -1;
  674
+		ent->mark_end = -1;
  675
+		if (ent == buf->last_ent_end)
  676
+			break;
  677
+		ent = ent->next;
  678
+	}
  679
+
  680
+	return ret;
  681
+}
  682
+
  683
+static int
  684
+find_x (GtkXText *xtext, textentry *ent, unsigned char *text, int x, int indent)
  685
+{
  686
+	int xx = indent;
  687
+	int i = 0;
  688
+	int rcol = 0, bgcol = 0;
  689
+	int hidden = FALSE;
  690
+	unsigned char *orig = text;
  691
+	int mbl;
  692
+	int char_width;
  693
+
  694
+	while (*text)
  695
+	{
  696
+		mbl = 1;
  697
+		if (rcol > 0 && (isdigit (*text) || (*text == ',' && isdigit (text[1]) && !bgcol)))
  698
+		{
  699
+			if (text[1] != ',') rcol--;
  700
+			if (*text == ',')
  701
+			{
  702
+				rcol = 2;
  703
+				bgcol = 1;
  704
+			}
  705
+			text++;
  706
+		} else
  707
+		{
  708
+			rcol = bgcol = 0;
  709
+			switch (*text)
  710
+			{
  711
+			case ATTR_COLOR:
  712
+				rcol = 2;
  713
+			case ATTR_BEEP:
  714
+			case ATTR_RESET:
  715
+			case ATTR_REVERSE:
  716
+			case ATTR_BOLD:
  717
+			case ATTR_UNDERLINE:
  718
+			case ATTR_ITALICS:
  719
+				text++;
  720
+				break;
  721
+			case ATTR_HIDDEN:
  722
+				if (xtext->ignore_hidden)
  723
+					goto def;
  724
+				hidden = !hidden;
  725
+				text++;
  726
+				break;
  727
+			default:
  728
+			def:
  729
+				char_width = backend_get_char_width (xtext, text, &mbl);
  730
+				if (!hidden) xx += char_width;
  731
+				text += mbl;
  732
+				if (xx >= x)
  733
+					return i + (orig - ent->str);
  734
+			}
  735
+		}
  736
+
  737
+		i += mbl;
  738
+		if (text - orig >= ent->str_len)
  739
+			return ent->str_len;
  740
+	}
  741
+
  742
+	return ent->str_len;
  743
+}
  744
+
  745
+static int
  746
+gtk_xtext_find_x (GtkXText * xtext, int x, textentry * ent, int subline,
  747
+						int line, int *out_of_bounds)
  748
+{
  749
+	int indent;
  750
+	unsigned char *str;
  751
+
  752
+	if (subline < 1)
  753
+		indent = ent->indent;
  754
+	else
  755
+		indent = xtext->buffer->indent;
  756
+
  757
+	if (line > xtext->adj->page_size || line < 0)
  758
+		return 0;
  759
+
  760
+	if (xtext->buffer->grid_dirty || line > 255)
  761
+	{
  762
+		str = ent->str + gtk_xtext_find_subline (xtext, ent, subline);
  763
+		if (str >= ent->str + ent->str_len)
  764
+			return 0;
  765
+	} else
  766
+	{
  767
+		if (xtext->buffer->grid_offset[line] > ent->str_len)
  768
+			return 0;
  769
+		str = ent->str + xtext->buffer->grid_offset[line];
  770
+	}
  771
+
  772
+	if (x < indent)
  773
+	{
  774
+		*out_of_bounds = 1;
  775
+		return (str - ent->str);
  776
+	}
  777
+
  778
+	*out_of_bounds = 0;
  779
+
  780
+	return find_x (xtext, ent, str, x, indent);
  781
+}
  782
+
  783
+static textentry *
  784
+gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off,
  785
+							int *out_of_bounds)
  786
+{
  787
+	textentry *ent;
  788
+	int line;
  789
+	int subline;
  790
+
  791
+	line = (y + xtext->pixel_offset) / xtext->fontsize;
  792
+	ent = gtk_xtext_nth (xtext, line + (int)xtext->adj->value, &subline);
  793
+	if (!ent)
  794
+		return 0;
  795
+
  796
+	if (off)
  797
+		*off = gtk_xtext_find_x (xtext, x, ent, subline, line, out_of_bounds);
  798
+
  799
+	return ent;
  800
+}
  801
+
  802
+static void
  803
+gtk_xtext_draw_sep (GtkXText * xtext, int y)
  804
+{
  805
+	gint x, height;
  806
+	cairo_t *cr;
  807
+
  808
+	cr = gtk_xtext_create_cairo_handle(xtext);
  809
+	cairo_set_line_width(cr, 1.0);
  810
+	cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
  811
+
  812
+	if (y == -1)
  813
+	{
  814
+		y = 0;
  815
+		height = GTK_WIDGET (xtext)->allocation.height;
  816
+	} else
  817
+		height = xtext->fontsize;
  818
+
  819
+	/* draw the separator line */
  820
+	if (xtext->separator && xtext->buffer->indent)
  821
+	{
  822
+		x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
  823
+		if (x < 1)
  824
+			return;
  825
+
  826
+		if (xtext->moving_separator)
  827
+		{
  828
+			cairo_set_source_rgb(cr, 0.9, 0.9, 0.9);
  829
+
  830
+			cairo_move_to(cr, x, y + height);
  831
+			cairo_line_to(cr, x, y);
  832
+		}
  833
+		else
  834
+		{
  835
+			cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  836
+
  837
+			cairo_move_to(cr, x, y + height);
  838
+			cairo_line_to(cr, x, y);
  839
+		}
  840
+
  841
+		cairo_stroke(cr);
  842
+	}
  843
+
  844
+	cairo_destroy(cr);
  845
+}
  846
+
  847
+static void
  848
+gtk_xtext_draw_marker (GtkXText * xtext, textentry * ent, int y)
  849
+{
  850
+	int x, width, render_y;
  851
+	cairo_t *cr;
  852
+
  853
+	cr = gtk_xtext_create_cairo_handle(xtext);
  854
+	cairo_set_line_width(cr, 1.0);
  855
+	cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
  856
+
  857
+	if (!xtext->marker)
  858
+	{
  859
+		cairo_destroy(cr);
  860
+		return;
  861
+	}
  862
+
  863
+	if (xtext->buffer->marker_pos == ent)
  864
+	{
  865
+		render_y = y + xtext->font->descent;
  866
+	}
  867
+	else if (xtext->buffer->marker_pos == ent->next && ent->next != NULL)
  868
+	{
  869
+		render_y = y + xtext->font->descent + xtext->fontsize * ent->lines_taken;
  870
+	}
  871
+	else
  872
+	{
  873
+		cairo_destroy(cr);
  874
+		return;
  875
+	}
  876
+
  877
+	x = 0;
  878
+	width = GTK_WIDGET (xtext)->allocation.width;
  879
+
  880
+	gdk_cairo_set_source_color(cr, &xtext->palette[XTEXT_MARKER]);
  881
+
  882
+	cairo_move_to(cr, x, render_y);
  883
+	cairo_line_to(cr, x + width, render_y);
  884
+
  885
+	cairo_stroke(cr);
  886
+
  887
+	if (gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (xtext)))))
  888
+	{
  889
+		xtext->buffer->marker_seen = TRUE;
  890
+	}
  891
+
  892
+	cairo_destroy(cr);
  893
+}
  894
+
  895
+static void
  896
+gtk_xtext_paint (GtkWidget *widget, GdkRectangle *area)
  897
+{
  898
+	GtkXText *xtext = GTK_XTEXT (widget);
  899
+	textentry *ent_start, *ent_end;
  900
+	int x, y;
  901
+
  902
+	if (area->x == 0 && area->y == 0 &&
  903
+		 area->height == widget->allocation.height &&
  904
+		 area->width == widget->allocation.width)
  905
+	{
  906
+		dontscroll (xtext->buffer);	/* force scrolling off */
  907
+		gtk_xtext_render_page (xtext);
  908
+		return;
  909
+	}
  910
+
  911
+	ent_start = gtk_xtext_find_char (xtext, area->x, area->y, NULL, NULL);
  912
+	if (!ent_start)
  913
+	{
  914
+		xtext_draw_bg (xtext, area->x, area->y, area->width, area->height);
  915
+		goto xit;
  916
+	}
  917
+	ent_end = gtk_xtext_find_char (xtext, area->x + area->width,
  918
+											 area->y + area->height, NULL, NULL);
  919
+	if (!ent_end)
  920
+		ent_end = xtext->buffer->text_last;
  921
+
  922
+	xtext->clip_x = area->x;
  923
+	xtext->clip_x2 = area->x + area->width;
  924
+	xtext->clip_y = area->y;
  925
+	xtext->clip_y2 = area->y + area->height;
  926
+
  927
+	/* y is the last pixel y location it rendered text at */
  928
+	y = gtk_xtext_render_ents (xtext, ent_start, ent_end);
  929
+
  930
+	if (y && y < widget->allocation.height && !ent_end->next)
  931
+	{
  932
+		GdkRectangle rect;
  933
+
  934
+		rect.x = 0;
  935
+		rect.y = y;
  936
+		rect.width = widget->allocation.width;
  937
+		rect.height = widget->allocation.height - y;
  938
+
  939
+		/* fill any space below the last line that also intersects with
  940
+			the exposure rectangle */
  941
+		if (gdk_rectangle_intersect (area, &rect, &rect))
  942
+		{
  943
+			xtext_draw_bg (xtext, rect.x, rect.y, rect.width, rect.height);
  944
+		}
  945
+	}
  946
+
  947
+	/*backend_clear_clip (xtext);*/
  948
+	xtext->clip_x = 0;
  949
+	xtext->clip_x2 = 1000000;
  950
+	xtext->clip_y = 0;
  951
+	xtext->clip_y2 = 1000000;
  952
+
  953
+xit:
  954
+	x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
  955
+	if (area->x <= x)
  956
+		gtk_xtext_draw_sep (xtext, -1);
  957
+}
  958
+
  959
+static gboolean
  960
+gtk_xtext_expose (GtkWidget * widget, GdkEventExpose * event)
  961
+{
  962
+	gtk_xtext_paint (widget, &event->area);
  963
+	return FALSE;
  964
+}
  965
+
  966
+/* render a selection that has extended or contracted upward */
  967
+
  968
+static void
  969
+gtk_xtext_selection_up (GtkXText *xtext, textentry *start, textentry *end,
  970
+								int start_offset)
  971
+{
  972
+	/* render all the complete lines */
  973
+	if (start->next == end)
  974
+		gtk_xtext_render_ents (xtext, end, NULL);
  975
+	else
  976
+		gtk_xtext_render_ents (xtext, start->next, end);
  977
+
  978
+	/* now the incomplete upper line */
  979
+	if (start == xtext->buffer->last_ent_start)
  980
+		xtext->jump_in_offset = xtext->buffer->last_offset_start;
  981
+	else
  982
+		xtext->jump_in_offset = start_offset;
  983
+	gtk_xtext_render_ents (xtext, start, NULL);
  984
+	xtext->jump_in_offset = 0;
  985
+}
  986
+
  987
+/* render a selection that has extended or contracted downward */
  988
+
  989
+static void
  990
+gtk_xtext_selection_down (GtkXText *xtext, textentry *start, textentry *end,
  991
+								  int end_offset)
  992
+{
  993
+	/* render all the complete lines */
  994
+	if (end->prev == start)
  995
+		gtk_xtext_render_ents (xtext, start, NULL);
  996
+	else
  997
+		gtk_xtext_render_ents (xtext, start, end->prev);
  998
+
  999
+	/* now the incomplete bottom line */
  1000
+	if (end == xtext->buffer->last_ent_end)
  1001
+		xtext->jump_out_offset = xtext->buffer->last_offset_end;
  1002
+	else
  1003
+		xtext->jump_out_offset = end_offset;
  1004
+	gtk_xtext_render_ents (xtext, end, NULL);
  1005
+	xtext->jump_out_offset = 0;
  1006
+}
  1007
+
  1008
+static void
  1009
+gtk_xtext_selection_render (GtkXText *xtext,
  1010
+									 textentry *start_ent, int start_offset,
  1011
+									 textentry *end_ent, int end_offset)
  1012
+{
  1013
+	textentry *ent;
  1014
+	int start, end;
  1015
+
  1016
+	xtext->skip_border_fills = TRUE;
  1017
+	xtext->skip_stamp = TRUE;
  1018
+
  1019
+	/* force an optimized render if there was no previous selection */
  1020
+	if (xtext->buffer->last_ent_start == NULL && start_ent == end_ent)
  1021
+	{
  1022
+		xtext->buffer->last_offset_start = start_offset;
  1023
+		xtext->buffer->last_offset_end = end_offset;
  1024
+		goto lamejump;
  1025
+	}
  1026
+
  1027
+	/* mark changed within 1 ent only? */
  1028
+	if (xtext->buffer->last_ent_start == start_ent &&
  1029
+		 xtext->buffer->last_ent_end == end_ent)
  1030
+	{
  1031
+		/* when only 1 end of the selection is changed, we can really
  1032
+			save on rendering */
  1033
+		if (xtext->buffer->last_offset_start == start_offset ||
  1034
+			 xtext->buffer->last_offset_end == end_offset)
  1035
+		{
  1036
+lamejump:
  1037
+			ent = end_ent;
  1038
+			/* figure out where to start and end the rendering */
  1039
+			if (end_offset > xtext->buffer->last_offset_end)
  1040
+			{
  1041
+				end = end_offset;
  1042
+				start = xtext->buffer->last_offset_end;
  1043
+			} else if (end_offset < xtext->buffer->last_offset_end)
  1044
+			{
  1045
+				end = xtext->buffer->last_offset_end;
  1046
+				start = end_offset;
  1047
+			} else if (start_offset < xtext->buffer->last_offset_start)
  1048
+			{
  1049
+				end = xtext->buffer->last_offset_start;
  1050
+				start = start_offset;
  1051
+				ent = start_ent;
  1052
+			} else if (start_offset > xtext->buffer->last_offset_start)
  1053
+			{
  1054
+				end = start_offset;
  1055
+				start = xtext->buffer->last_offset_start;
  1056
+				ent = start_ent;
  1057
+			} else
  1058
+			{	/* WORD selects end up here */
  1059
+				end = end_offset;
  1060
+				start = start_offset;
  1061
+			}
  1062
+		} else
  1063
+		{
  1064
+			/* LINE selects end up here */
  1065
+			/* so which ent actually changed? */
  1066
+			ent = start_ent;
  1067
+			if (xtext->buffer->last_offset_start == start_offset)
  1068
+				ent = end_ent;
  1069
+
  1070
+			end = MAX (xtext->buffer->last_offset_end, end_offset);
  1071
+			start = MIN (xtext->buffer->last_offset_start, start_offset);
  1072
+		}
  1073
+
  1074
+		xtext->jump_out_offset = end;
  1075
+		xtext->jump_in_offset = start;
  1076
+		gtk_xtext_render_ents (xtext, ent, NULL);
  1077
+		xtext->jump_out_offset = 0;
  1078
+		xtext->jump_in_offset = 0;
  1079
+	}
  1080
+	/* marking downward? */
  1081
+	else if (xtext->buffer->last_ent_start == start_ent &&
  1082
+				xtext->buffer->last_offset_start == start_offset)
  1083
+	{
  1084
+		/* find the range that covers both old and new selection */
  1085
+		ent = start_ent;
  1086
+		while (ent)
  1087
+		{
  1088
+			if (ent == xtext->buffer->last_ent_end)
  1089
+			{
  1090
+				gtk_xtext_selection_down (xtext, ent, end_ent, end_offset);
  1091
+				/*gtk_xtext_render_ents (xtext, ent, end_ent);*/
  1092
+				break;
  1093
+			}
  1094
+			if (ent == end_ent)
  1095
+			{
  1096
+				gtk_xtext_selection_down (xtext, ent, xtext->buffer->last_ent_end, end_offset);
  1097
+				/*gtk_xtext_render_ents (xtext, ent, xtext->buffer->last_ent_end);*/
  1098
+				break;
  1099
+			}
  1100
+			ent = ent->next;
  1101
+		}
  1102
+	}
  1103
+	/* marking upward? */
  1104
+	else if (xtext->buffer->last_ent_end == end_ent &&
  1105
+				xtext->buffer->last_offset_end == end_offset)
  1106
+	{
  1107
+		ent = end_ent;
  1108
+		while (ent)
  1109
+		{
  1110
+			if (ent == start_ent)
  1111
+			{
  1112
+				gtk_xtext_selection_up (xtext, xtext->buffer->last_ent_start, ent, start_offset);
  1113
+				/*gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, ent);*/
  1114
+				break;
  1115
+			}
  1116
+			if (ent == xtext->buffer->last_ent_start)
  1117
+			{
  1118
+				gtk_xtext_selection_up (xtext, start_ent, ent, start_offset);
  1119
+				/*gtk_xtext_render_ents (xtext, start_ent, ent);*/
  1120
+				break;
  1121
+			}
  1122
+			ent = ent->prev;
  1123
+		}
  1124
+	}
  1125
+	else	/* cross-over mark (stretched or shrunk at both ends) */
  1126
+	{
  1127
+		/* unrender the old mark */
  1128
+		gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, xtext->buffer->last_ent_end);
  1129
+		/* now render the new mark, but skip overlaps */
  1130
+		if (start_ent == xtext->buffer->last_ent_start)
  1131
+		{
  1132
+			/* if the new mark is a sub-set of the old, do nothing */
  1133
+			if (start_ent != end_ent)
  1134
+				gtk_xtext_render_ents (xtext, start_ent->next, end_ent);
  1135
+		} else if (end_ent == xtext->buffer->last_ent_end)
  1136
+		{
  1137
+			/* if the new mark is a sub-set of the old, do nothing */
  1138
+			if (start_ent != end_ent)
  1139
+				gtk_xtext_render_ents (xtext, start_ent, end_ent->prev);
  1140
+		} else
  1141
+			gtk_xtext_render_ents (xtext, start_ent, end_ent);
  1142
+	}
  1143
+
  1144
+	xtext->buffer->last_ent_start = start_ent;
  1145
+	xtext->buffer->last_ent_end = end_ent;
  1146
+	xtext->buffer->last_offset_start = start_offset;
  1147
+	xtext->buffer->last_offset_end = end_offset;
  1148
+
  1149
+	xtext->skip_border_fills = FALSE;
  1150
+	xtext->skip_stamp = FALSE;
  1151
+}
  1152
+
  1153
+static void
  1154
+gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean render)
  1155
+{
  1156
+	textentry *ent;
  1157
+	textentry *ent_end;
  1158
+	textentry *ent_start;
  1159
+	int offset_start;
  1160
+	int offset_end;
  1161
+	int low_x;
  1162
+	int low_y;
  1163
+	int high_x;
  1164
+	int high_y;
  1165
+	int tmp;
  1166
+
  1167
+	if (xtext->select_start_y > xtext->select_end_y)
  1168
+	{
  1169
+		low_x = xtext->select_end_x;
  1170
+		low_y = xtext->select_end_y;
  1171
+		high_x = xtext->select_start_x;
  1172
+		high_y = xtext->select_start_y;
  1173
+	} else
  1174
+	{
  1175
+		low_x = xtext->select_start_x;
  1176
+		low_y = xtext->select_start_y;
  1177
+		high_x = xtext->select_end_x;
  1178
+		high_y = xtext->select_end_y;
  1179
+	}
  1180
+
  1181
+	ent_start = gtk_xtext_find_char (xtext, low_x, low_y, &offset_start, &tmp);
  1182
+	if (!ent_start)
  1183
+	{
  1184
+		if (xtext->adj->value != xtext->buffer->old_value)
  1185
+			gtk_xtext_render_page (xtext);
  1186
+		return;
  1187
+	}
  1188
+
  1189
+	ent_end = gtk_xtext_find_char (xtext, high_x, high_y, &offset_end, &tmp);
  1190
+	if (!ent_end)
  1191
+	{
  1192
+		ent_end = xtext->buffer->text_last;
  1193
+		if (!ent_end)
  1194
+		{
  1195
+			if (xtext->adj->value != xtext->buffer->old_value)
  1196
+				gtk_xtext_render_page (xtext);
  1197
+			return;
  1198
+		}
  1199
+		offset_end = ent_end->str_len;
  1200
+	}
  1201
+
  1202
+	/* marking less than a complete line? */
  1203
+	/* make sure "start" is smaller than "end" (swap them if need be) */
  1204
+	if (ent_start == ent_end && offset_start > offset_end)
  1205
+	{
  1206
+		tmp = offset_start;
  1207
+		offset_start = offset_end;
  1208
+		offset_end = tmp;