1 /*
2  * Copyright (C) 2010 Stefan Walter
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as
6  * published by the Free Software Foundation; either version 2.1 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include "gcr-display-view.h"
21 #include "gcr-renderer.h"
22 #include "gcr-viewer.h"
23 
24 #include "egg/egg-oid.h"
25 #include "egg/egg-hex.h"
26 
27 #include <gdk/gdk.h>
28 #include <glib/gi18n-lib.h>
29 
30 static void _gcr_display_view_viewer_iface (GcrViewerIface *iface);
31 
32 #define ZWSP "\342\200\213"
33 #define NORMAL_MARGIN 10
34 #define FIELD_MARGIN 17
35 #define COLUMN_MARGIN 6
36 #define ICON_MARGIN 8
37 #define MESSAGE_PADDING 8
38 
39 typedef struct _GcrDisplayItem {
40 	GcrDisplayView *display_view;
41 	GcrRenderer *renderer;
42 	gboolean expanded;
43 	gboolean details;
44 	GtkTextMark *beginning;
45 	GtkTextMark *ending;
46 	GtkWidget *details_widget;
47 	GtkTextChildAnchor *area_anchor;
48 	GtkTextTag *extra_tag;
49 	gint field_width;
50 	GdkPixbuf *pixbuf;
51 	GtkTextTag *field_tag;
52 	GtkTextTag *details_tag;
53 	gulong data_changed_id;
54 } GcrDisplayItem;
55 
56 struct _GcrDisplayViewPrivate {
57 	GtkTextBuffer *buffer;
58 	GPtrArray *renderers;
59 	GHashTable *items;
60 	GtkTextTag *title_tag;
61 	GtkTextTag *content_tag;
62 	GtkTextTag *heading_tag;
63 	GtkTextTag *message_tag;
64 	GtkTextTag *monospace_tag;
65 	GtkTextTag *area_tag;
66 	GcrDisplayItem *current_item;
67 	gint text_height;
68 	GdkCursor *cursor;
69 
70 	gboolean have_measurements;
71 	gint minimal_width;
72 	gint natural_width;
73 	gint minimal_height;
74 	gint natural_height;
75 };
76 
77 G_DEFINE_TYPE_WITH_CODE (GcrDisplayView, _gcr_display_view, GTK_TYPE_TEXT_VIEW,
78                          G_ADD_PRIVATE (GcrDisplayView);
79                          G_IMPLEMENT_INTERFACE (GCR_TYPE_VIEWER, _gcr_display_view_viewer_iface));
80 
81 /* -----------------------------------------------------------------------------
82  * INTERNAL
83  */
84 
85 static void
ensure_measurements(GcrDisplayView * self)86 ensure_measurements (GcrDisplayView *self)
87 {
88 	PangoLayout *layout;
89 	PangoRectangle extents;
90 	gint icon_width;
91 	gint icon_height;
92 	GHashTableIter iter;
93 	GcrDisplayItem *item;
94 	gpointer value;
95 	gboolean expanded;
96 
97 	if (self->pv->have_measurements)
98 		return;
99 
100 	/* See if anything is expanded? */
101 	expanded = FALSE;
102 	g_hash_table_iter_init (&iter, self->pv->items);
103 	while (g_hash_table_iter_next (&iter, NULL, &value)) {
104 		item = value;
105 		if (item->expanded) {
106 			expanded = TRUE;
107 			break;
108 		}
109 	}
110 
111 	/*
112 	 * We use a string in our widget font as the basis for our measurements.
113 	 * These are just estimates of what we need, and what looks goodish.
114 	 * There's room here for improvement. If this is causes problems for
115 	 * you or bothers you, scratch that itch:
116 	 */
117 
118 	layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "0123456789");
119 	pango_layout_get_extents (layout, NULL, &extents);
120 	pango_extents_to_pixels (&extents, NULL);
121 	g_object_unref (layout);
122 
123 	if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &icon_width, &icon_height)) {
124 		icon_width = 48;
125 		icon_height = 48;
126 	}
127 
128 	if (expanded) {
129 		/* If expanded, display more 10 lines at least */
130 		self->pv->minimal_height = extents.height * 14;
131 		self->pv->natural_height = extents.height * 25;
132 	} else {
133 		/* If not expanded we can get by with 9 lines */
134 		self->pv->minimal_height = extents.height * 8;
135 		self->pv->natural_height = extents.height * 9;
136 	}
137 
138 	self->pv->minimal_width = icon_width + (extents.width * 5);
139 	self->pv->natural_width = icon_width + (extents.width * 8);
140 	self->pv->have_measurements = TRUE;
141 }
142 
143 static void
ensure_text_height(GcrDisplayView * self)144 ensure_text_height (GcrDisplayView *self)
145 {
146 	PangoRectangle extents;
147 	PangoLayout *layout;
148 
149 	if (self->pv->text_height > 0)
150 		return;
151 
152 	layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "Wp");
153 	pango_layout_get_extents (layout, NULL, &extents);
154 	pango_extents_to_pixels (&extents, NULL);
155 	g_object_unref (layout);
156 
157 	self->pv->text_height = extents.height;
158 }
159 
160 static void
recalculate_and_resize(GcrDisplayView * self)161 recalculate_and_resize (GcrDisplayView *self)
162 {
163 	self->pv->have_measurements = FALSE;
164 	gtk_widget_queue_resize (GTK_WIDGET (self));
165 }
166 
167 static GtkTextTagTable*
create_tag_table(GcrDisplayView * self)168 create_tag_table (GcrDisplayView *self)
169 {
170 	GtkTextTagTable *tags;
171 	gint width, height;
172 
173 	g_assert (GCR_IS_DISPLAY_VIEW (self));
174 
175 	tags = gtk_text_tag_table_new ();
176 
177 	if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &width, &height))
178 		width = 48;
179 
180 	self->pv->title_tag = g_object_new (GTK_TYPE_TEXT_TAG,
181 	                                    "name", "title",
182 	                                    "scale", PANGO_SCALE_LARGE,
183 	                                    "right-margin", (ICON_MARGIN * 2) + width,
184 	                                    "pixels-below-lines", 6,
185 	                                    "weight", PANGO_WEIGHT_BOLD,
186 	                                    NULL);
187 	gtk_text_tag_table_add (tags, self->pv->title_tag);
188 
189 	self->pv->content_tag = g_object_new (GTK_TYPE_TEXT_TAG,
190 	                                      "name", "content",
191 	                                      "right-margin", (ICON_MARGIN * 2) + width,
192 	                                      "left-margin", FIELD_MARGIN,
193 	                                      "pixels-below-lines", 3,
194 	                                      "wrap-mode", GTK_WRAP_WORD,
195 	                                      NULL);
196 	gtk_text_tag_table_add (tags, self->pv->content_tag);
197 
198 	self->pv->message_tag = g_object_new (GTK_TYPE_TEXT_TAG,
199 	                                      "name", "message",
200 	                                      "right-margin", (ICON_MARGIN * 2) + width,
201 	                                      "rise", 3 * PANGO_SCALE,
202 	                                      "pixels-below-lines", 3,
203 	                                      "wrap-mode", GTK_WRAP_WORD,
204 	                                      NULL);
205 	gtk_text_tag_table_add (tags, self->pv->message_tag);
206 
207 	self->pv->heading_tag = g_object_new (GTK_TYPE_TEXT_TAG,
208 	                                      "name", "heading",
209 	                                      "pixels-above-lines", 9,
210 	                                      "pixels-below-lines", 3,
211 	                                      "weight", PANGO_WEIGHT_BOLD,
212 	                                      NULL);
213 	gtk_text_tag_table_add (tags, self->pv->heading_tag);
214 
215 	self->pv->monospace_tag = g_object_new (GTK_TYPE_TEXT_TAG,
216 	                                        "name", "monospace",
217 	                                        "family", "monospace",
218 	                                        NULL);
219 	gtk_text_tag_table_add (tags, self->pv->monospace_tag);
220 
221 	self->pv->area_tag = g_object_new (GTK_TYPE_TEXT_TAG,
222 	                                   "name", "area",
223 	                                   "justification", GTK_JUSTIFY_CENTER,
224 	                                   NULL);
225 	gtk_text_tag_table_add (tags, self->pv->area_tag);
226 
227 	return tags;
228 }
229 
230 static void
on_expander_realize(GtkWidget * widget,gpointer user_data)231 on_expander_realize (GtkWidget *widget, gpointer user_data)
232 {
233 	GdkCursor *cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), GDK_ARROW);
234 	gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
235 	g_object_unref (cursor);
236 }
237 
238 static void
on_expander_expanded(GObject * object,GParamSpec * param_spec,gpointer user_data)239 on_expander_expanded (GObject *object, GParamSpec *param_spec, gpointer user_data)
240 {
241 	GtkExpander *expander = GTK_EXPANDER (object);
242 	GcrDisplayItem *item = user_data;
243 	item->expanded = gtk_expander_get_expanded (expander);
244 	gcr_renderer_render_view (item->renderer, GCR_VIEWER (item->display_view));
245 	recalculate_and_resize (item->display_view);
246 }
247 
248 static GcrDisplayItem*
create_display_item(GcrDisplayView * self,GcrRenderer * renderer)249 create_display_item (GcrDisplayView *self, GcrRenderer *renderer)
250 {
251 	GcrDisplayItem *item;
252 	GtkTextTagTable *tags;
253 	GtkTextIter iter;
254 	GtkWidget *widget;
255 	GtkWidget *label;
256 	GtkStyleContext *style;
257 	gchar *text;
258 
259 	item = g_new0 (GcrDisplayItem, 1);
260 	item->display_view = self;
261 	item->renderer = renderer;
262 
263 	tags = gtk_text_buffer_get_tag_table (self->pv->buffer);
264 
265 	g_assert (!item->field_tag);
266 	item->field_width = 0;
267 	item->field_tag = g_object_new (GTK_TYPE_TEXT_TAG,
268 	                                "left-margin", item->field_width + FIELD_MARGIN,
269 	                                "indent", item->field_width,
270 	                                "pixels-below-lines", 3,
271 	                                "wrap-mode", GTK_WRAP_WORD_CHAR,
272 	                                NULL);
273 	gtk_text_tag_table_add (tags, item->field_tag);
274 
275 	g_assert (!item->details_tag);
276 	item->details_tag = g_object_new (GTK_TYPE_TEXT_TAG, NULL);
277 	gtk_text_tag_table_add (tags, item->details_tag);
278 
279 	/*
280 	 * Add two lines space that delimit this from later items. The
281 	 * item will live between the two zero width spaces.
282 	 */
283 	gtk_text_buffer_get_end_iter (self->pv->buffer, &iter);
284 	gtk_text_buffer_insert (self->pv->buffer, &iter, "\n\n", -1);
285 	if (!gtk_text_iter_backward_char (&iter))
286 		g_assert_not_reached ();
287 
288 	/* The mark that determines the beginning of this item, with left gravity. */
289 	item->beginning = gtk_text_buffer_create_mark (self->pv->buffer, NULL, &iter, TRUE);
290 	g_object_ref (item->beginning);
291 
292 	/* The mark that determines the end of this item, with right gravity. */
293 	item->ending = gtk_text_buffer_create_mark (self->pv->buffer, NULL, &iter, FALSE);
294 	g_object_ref (item->ending);
295 
296 	widget = gtk_expander_new_with_mnemonic ("");
297 	label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
298 	text = g_strdup_printf ("<b>%s</b>", _("_Details"));
299 	gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), text);
300 	g_signal_connect (widget, "notify::expanded", G_CALLBACK (on_expander_expanded), item);
301 	g_signal_connect (widget, "realize", G_CALLBACK (on_expander_realize), NULL);
302 	item->expanded = gtk_expander_get_expanded (GTK_EXPANDER (widget));
303 	g_free (text);
304 
305 	gtk_widget_set_halign (widget, 0.5);
306 	gtk_widget_set_valign (widget, 0.5);
307 	gtk_widget_set_margin_top (widget, 6);
308 	gtk_widget_set_margin_bottom (widget, 9);
309 	gtk_widget_show_all (widget);
310 
311 	item->details_widget = gtk_event_box_new ();
312 	gtk_event_box_set_visible_window (GTK_EVENT_BOX (item->details_widget), FALSE);
313 	gtk_container_add (GTK_CONTAINER (item->details_widget), widget);
314 	g_signal_connect (item->details_widget, "realize", G_CALLBACK (on_expander_realize), NULL);
315 	style = gtk_widget_get_style_context (GTK_WIDGET (item->details_widget));
316 	gtk_style_context_add_class (style, "gcr-red");
317 	g_object_ref (item->details_widget);
318 
319 	return item;
320 }
321 
322 static void
destroy_display_item(gpointer data)323 destroy_display_item (gpointer data)
324 {
325 	GcrDisplayItem *item = data;
326 	GtkTextIter iter, end;
327 	GtkTextTagTable *tags;
328 	GcrDisplayView *self;
329 
330 	g_assert (item);
331 
332 	g_assert (GCR_IS_DISPLAY_VIEW (item->display_view));
333 	self = item->display_view;
334 
335 	tags = gtk_text_buffer_get_tag_table (self->pv->buffer);
336 	gtk_text_tag_table_remove (tags, item->field_tag);
337 	gtk_text_tag_table_remove (tags, item->details_tag);
338 
339 	g_object_unref (item->field_tag);
340 	g_object_unref (item->details_tag);
341 
342 	if (item->pixbuf)
343 		g_object_unref (item->pixbuf);
344 	item->pixbuf = NULL;
345 
346 	g_assert (item->details_widget);
347 	g_object_unref (item->details_widget);
348 	item->details_widget = NULL;
349 
350 	g_clear_object (&item->area_anchor);
351 
352 	g_return_if_fail (!gtk_text_mark_get_deleted (item->beginning));
353 	g_return_if_fail (!gtk_text_mark_get_deleted (item->ending));
354 
355 	/* Setup iters to encompass our delemiter characters see create_display_item() */
356 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->beginning);
357 	gtk_text_iter_backward_char (&iter);
358 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &end, item->ending);
359 	gtk_text_iter_forward_char (&end);
360 	gtk_text_buffer_delete (self->pv->buffer, &iter, &end);
361 
362 	gtk_text_buffer_delete_mark (self->pv->buffer, item->beginning);
363 	gtk_text_buffer_delete_mark (self->pv->buffer, item->ending);
364 
365 	g_object_unref (item->beginning);
366 	g_object_unref (item->ending);
367 
368 	g_free (item);
369 }
370 
371 static GcrDisplayItem*
lookup_display_item(GcrDisplayView * self,GcrRenderer * renderer)372 lookup_display_item (GcrDisplayView *self, GcrRenderer *renderer)
373 {
374 	GcrDisplayItem *item = g_hash_table_lookup (self->pv->items, renderer);
375 	g_return_val_if_fail (item, NULL);
376 	g_assert (item->display_view == self);
377 	return item;
378 }
379 
380 static GcrDisplayItem*
find_item_at_iter(GcrDisplayView * self,GtkTextIter * iter)381 find_item_at_iter (GcrDisplayView *self, GtkTextIter *iter)
382 {
383 	GHashTableIter hi;
384 	GcrDisplayItem *item;
385 	gpointer value;
386 	GtkTextIter start, end;
387 
388 	g_hash_table_iter_init (&hi, self->pv->items);
389 	while (g_hash_table_iter_next (&hi, NULL, &value)) {
390 		item = value;
391 
392 		gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &start, item->beginning);
393 		gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &end, item->ending);
394 
395 		if (gtk_text_iter_compare (iter, &start) >= 0 &&
396 		    gtk_text_iter_compare (iter, &end) < 0)
397 			return item;
398 	}
399 
400 	return NULL;
401 }
402 
403 static void
on_renderer_data_changed(GcrRenderer * renderer,gpointer user_data)404 on_renderer_data_changed (GcrRenderer *renderer,
405                           gpointer user_data)
406 {
407 	GcrDisplayView *self = GCR_DISPLAY_VIEW (user_data);
408 
409 	/* Item may be removed, but not yet destroyed */
410 	if (!g_hash_table_lookup (self->pv->items, renderer))
411 		return;
412 
413 	/* Just ask the renderer to render itself on us */
414 	gcr_renderer_render_view (renderer, GCR_VIEWER (self));
415 }
416 
417 static void
paint_item_icon(GcrDisplayView * self,GcrDisplayItem * item,GdkRectangle * visible,cairo_t * cr)418 paint_item_icon (GcrDisplayView *self,
419                  GcrDisplayItem *item,
420                  GdkRectangle *visible,
421                  cairo_t *cr)
422 {
423 	GtkTextIter iter;
424 	GdkRectangle location;
425 	GtkTextView *view;
426 
427 	if (item->pixbuf == NULL)
428 		return;
429 
430 	view = GTK_TEXT_VIEW (self);
431 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->beginning);
432 	gtk_text_view_get_iter_location (view, &iter, &location);
433 
434 	location.height = gdk_pixbuf_get_height (item->pixbuf);
435 	location.width = gdk_pixbuf_get_width (item->pixbuf);
436 	location.x = visible->width - location.width - ICON_MARGIN;
437 
438 	if (!gdk_rectangle_intersect (visible, &location, NULL))
439 		return;
440 
441 	gtk_text_view_buffer_to_window_coords (view, GTK_TEXT_WINDOW_TEXT,
442 	                                       location.x, location.y,
443 	                                       &location.x, &location.y);
444 
445 	cairo_save (cr);
446 	gdk_cairo_set_source_pixbuf (cr, item->pixbuf, location.x, location.y);
447 	cairo_rectangle (cr, location.x, location.y, location.width, location.height);
448 	cairo_fill (cr);
449 	cairo_restore (cr);
450 }
451 
452 static void
paint_item_border(GcrDisplayView * self,GcrDisplayItem * item,GtkStyleContext * context,GdkRectangle * visible,gint index,cairo_t * cr)453 paint_item_border (GcrDisplayView *self,
454                    GcrDisplayItem *item,
455                    GtkStyleContext *context,
456                    GdkRectangle *visible,
457                    gint index,
458                    cairo_t *cr)
459 {
460 	GtkTextView *view;
461 	GtkTextIter iter, end;
462 	GdkRectangle location;
463 
464 	if (index == 0)
465 		return;
466 
467 	view = GTK_TEXT_VIEW (self);
468 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->beginning);
469 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &end, item->ending);
470 
471 	/* Don't paint for non-visible items */
472 	if (gtk_text_iter_compare (&iter, &end) == 0)
473 		return;
474 
475 	ensure_text_height (self);
476 
477 	gtk_text_view_get_iter_location (view, &iter, &location);
478 
479 	location.height = 2;
480 	location.width = visible->width - (NORMAL_MARGIN * 2);
481 	location.x = NORMAL_MARGIN;
482 	location.y -= self->pv->text_height / 2;
483 
484 	if (!gdk_rectangle_intersect (visible, &location, NULL))
485 		return;
486 
487 	gtk_render_background (context, cr, location.x, location.y - 0.5, location.width, 1);
488 }
489 
490 static void
paint_extras(GcrDisplayView * self,cairo_t * cr)491 paint_extras (GcrDisplayView *self, cairo_t *cr)
492 {
493 	GdkRectangle visible;
494 	GcrDisplayItem *item;
495 	GtkStyleContext *context;
496 	guint i;
497 
498 	gtk_text_view_get_visible_rect (GTK_TEXT_VIEW (self), &visible);
499 	context = gtk_widget_get_style_context (GTK_WIDGET (self));
500 
501 	for (i = 0; i < self->pv->renderers->len; i++) {
502 		item = g_hash_table_lookup (self->pv->items, self->pv->renderers->pdata[i]);
503 		g_assert (item != NULL);
504 		paint_item_icon (self, item, &visible, cr);
505 		paint_item_border (self, item, context, &visible, i, cr);
506 	}
507 }
508 
509 /* -----------------------------------------------------------------------------
510  * OBJECT
511  */
512 
513 static GObject*
_gcr_display_view_constructor(GType type,guint n_props,GObjectConstructParam * props)514 _gcr_display_view_constructor (GType type, guint n_props, GObjectConstructParam *props)
515 {
516 	GObject *obj = G_OBJECT_CLASS (_gcr_display_view_parent_class)->constructor (type, n_props, props);
517 	GcrDisplayView *self = NULL;
518 	GtkTextView *view = NULL;
519 	GtkTextTagTable *tags;
520 
521 	g_return_val_if_fail (obj, NULL);
522 
523 	self = GCR_DISPLAY_VIEW (obj);
524 	view = GTK_TEXT_VIEW (obj);
525 
526 	tags = create_tag_table (self);
527 	self->pv->buffer = gtk_text_buffer_new (tags);
528 	g_object_unref (tags);
529 
530 	gtk_text_view_set_buffer (view, self->pv->buffer);
531 	gtk_text_view_set_editable (view, FALSE);
532 	gtk_text_view_set_left_margin (view, NORMAL_MARGIN);
533 	gtk_text_view_set_right_margin (view, NORMAL_MARGIN);
534 	gtk_text_view_set_cursor_visible (view, FALSE);
535 
536 	return obj;
537 }
538 
539 static void
_gcr_display_view_init(GcrDisplayView * self)540 _gcr_display_view_init (GcrDisplayView *self)
541 {
542 	self->pv = _gcr_display_view_get_instance_private (self);
543 	self->pv->items = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, destroy_display_item);
544 	self->pv->renderers = g_ptr_array_new_with_free_func (g_object_unref);
545 }
546 
547 static void
_gcr_display_view_dispose(GObject * obj)548 _gcr_display_view_dispose (GObject *obj)
549 {
550 	GcrDisplayView *self = GCR_DISPLAY_VIEW (obj);
551 	GcrRenderer *renderer;
552 	GcrDisplayItem *item;
553 
554 	while (self->pv->renderers->len) {
555 		renderer = g_ptr_array_index (self->pv->renderers, 0);
556 		item = g_hash_table_lookup (self->pv->items, renderer);
557 		g_return_if_fail (item);
558 		g_signal_handler_disconnect (renderer, item->data_changed_id);
559 		if (!g_hash_table_remove (self->pv->items, renderer))
560 			g_return_if_reached ();
561 		g_ptr_array_remove_index_fast (self->pv->renderers, 0);
562 	}
563 
564 	if (self->pv->buffer)
565 		g_object_unref (self->pv->buffer);
566 	self->pv->buffer = NULL;
567 
568 	g_assert (g_hash_table_size (self->pv->items) == 0);
569 
570 	G_OBJECT_CLASS (_gcr_display_view_parent_class)->dispose (obj);
571 }
572 
573 static void
_gcr_display_view_finalize(GObject * obj)574 _gcr_display_view_finalize (GObject *obj)
575 {
576 	GcrDisplayView *self = GCR_DISPLAY_VIEW (obj);
577 
578 	if (self->pv->buffer)
579 		g_object_unref (self->pv->buffer);
580 	self->pv->buffer = NULL;
581 
582 	g_assert (g_hash_table_size (self->pv->items) == 0);
583 	g_hash_table_destroy (self->pv->items);
584 	self->pv->items = NULL;
585 
586 	g_assert (self->pv->renderers);
587 	g_assert (self->pv->renderers->len == 0);
588 	g_ptr_array_free (self->pv->renderers, TRUE);
589 	self->pv->renderers = NULL;
590 
591 	g_assert (self->pv->content_tag);
592 	g_object_unref (self->pv->content_tag);
593 	self->pv->content_tag = NULL;
594 
595 	g_assert (self->pv->heading_tag);
596 	g_object_unref (self->pv->heading_tag);
597 	self->pv->heading_tag = NULL;
598 
599 	g_assert (self->pv->monospace_tag);
600 	g_object_unref (self->pv->monospace_tag);
601 	self->pv->monospace_tag = NULL;
602 
603 	g_assert (self->pv->title_tag);
604 	g_object_unref (self->pv->title_tag);
605 	self->pv->title_tag = NULL;
606 
607 	g_clear_object (&self->pv->cursor);
608 
609 	G_OBJECT_CLASS (_gcr_display_view_parent_class)->finalize (obj);
610 }
611 
612 static void
_gcr_display_view_realize(GtkWidget * widget)613 _gcr_display_view_realize (GtkWidget *widget)
614 {
615 	GcrDisplayView *self = GCR_DISPLAY_VIEW (widget);
616 	GdkDisplay *display;
617 
618 	if (GTK_WIDGET_CLASS (_gcr_display_view_parent_class)->realize)
619 		GTK_WIDGET_CLASS (_gcr_display_view_parent_class)->realize (widget);
620 
621 	if (!self->pv->cursor) {
622 		display = gtk_widget_get_display (GTK_WIDGET (self));
623 		self->pv->cursor = gdk_cursor_new_for_display (display, GDK_ARROW);
624 	}
625 
626 	gdk_window_set_cursor (gtk_text_view_get_window (GTK_TEXT_VIEW (self), GTK_TEXT_WINDOW_WIDGET),
627 	                       self->pv->cursor);
628 }
629 
630 static gboolean
_gcr_display_view_button_press_event(GtkWidget * widget,GdkEventButton * event)631 _gcr_display_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
632 {
633 	GtkTextView *text_view = GTK_TEXT_VIEW (widget);
634 	GcrDisplayView *self = GCR_DISPLAY_VIEW (widget);
635 	GcrDisplayItem *item;
636 	gboolean handled = FALSE;
637 	GtkTextIter iter;
638 	gint x, y;
639 
640 	if (GTK_WIDGET_CLASS (_gcr_display_view_parent_class)->button_press_event)
641 		handled = GTK_WIDGET_CLASS (_gcr_display_view_parent_class)->button_press_event (
642 				widget, event);
643 
644 	if (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)) {
645 		gtk_text_view_window_to_buffer_coords (text_view, GTK_TEXT_WINDOW_TEXT,
646 		                                       event->x, event->y, &x, &y);
647 		gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
648 
649 		item = find_item_at_iter (self, &iter);
650 		self->pv->current_item = item;
651 	}
652 
653 	return handled;
654 }
655 
656 static gboolean
_gcr_display_view_draw(GtkWidget * widget,cairo_t * cr)657 _gcr_display_view_draw (GtkWidget *widget, cairo_t *cr)
658 {
659 	GdkWindow *window;
660 	gboolean handled = TRUE;
661 
662 	/* Have GtkTextView draw the text first. */
663 	if (GTK_WIDGET_CLASS (_gcr_display_view_parent_class)->draw)
664 		handled = GTK_WIDGET_CLASS (_gcr_display_view_parent_class)->draw (widget, cr);
665 
666 	window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_TEXT);
667 	if (gtk_cairo_should_draw_window (cr, window))
668 		paint_extras (GCR_DISPLAY_VIEW (widget), cr);
669 
670 	return handled;
671 }
672 
673 static void
_gcr_display_get_preferred_height(GtkWidget * widget,gint * minimal_height,gint * natural_height)674 _gcr_display_get_preferred_height (GtkWidget *widget, gint *minimal_height,
675                                    gint *natural_height)
676 {
677 	GcrDisplayView *self = GCR_DISPLAY_VIEW (widget);
678 	ensure_measurements (self);
679 	*minimal_height = self->pv->minimal_height;
680 	*natural_height = self->pv->natural_height;
681 }
682 
683 static void
_gcr_display_get_preferred_width(GtkWidget * widget,gint * minimal_width,gint * natural_width)684 _gcr_display_get_preferred_width (GtkWidget *widget, gint *minimal_width,
685                                   gint *natural_width)
686 {
687 	GcrDisplayView *self = GCR_DISPLAY_VIEW (widget);
688 	ensure_measurements (self);
689 	*minimal_width = self->pv->minimal_width;
690 	*natural_width = self->pv->natural_width;
691 }
692 
693 static void
_gcr_display_view_populate_popup(GtkTextView * text_view,GtkWidget * menu)694 _gcr_display_view_populate_popup (GtkTextView *text_view,
695 #if GTK_CHECK_VERSION (3, 8, 0)
696                                   GtkWidget *menu
697 #else
698                                   GtkMenu *menu
699 #endif
700                                   )
701 {
702 	GcrDisplayView *self = GCR_DISPLAY_VIEW (text_view);
703 
704 	if (GTK_TEXT_VIEW_CLASS (_gcr_display_view_parent_class)->populate_popup)
705 		GTK_TEXT_VIEW_CLASS (_gcr_display_view_parent_class)->populate_popup (text_view, menu);
706 
707 	/* Ask the current renderer to add menu items */
708 	if (self->pv->current_item)
709 		gcr_renderer_popuplate_popup (self->pv->current_item->renderer,
710 		                              GCR_VIEWER (self), GTK_MENU (menu));
711 }
712 
713 static void
_gcr_display_view_class_init(GcrDisplayViewClass * klass)714 _gcr_display_view_class_init (GcrDisplayViewClass *klass)
715 {
716 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
717 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
718 	GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS (klass);
719 
720 	_gcr_display_view_parent_class = g_type_class_peek_parent (klass);
721 
722 	gobject_class->constructor = _gcr_display_view_constructor;
723 	gobject_class->dispose = _gcr_display_view_dispose;
724 	gobject_class->finalize = _gcr_display_view_finalize;
725 
726 	widget_class->realize = _gcr_display_view_realize;
727 	widget_class->button_press_event = _gcr_display_view_button_press_event;
728 	widget_class->get_preferred_height = _gcr_display_get_preferred_height;
729 	widget_class->get_preferred_width = _gcr_display_get_preferred_width;
730 	widget_class->draw = _gcr_display_view_draw;
731 
732 	text_view_class->populate_popup = _gcr_display_view_populate_popup;
733 
734 	/* Load a CSS once */
735 	do {
736 		GtkCssProvider* provider = gtk_css_provider_new ();
737 		GdkDisplay* display = gdk_display_get_default ();
738 		GdkScreen* screen = gdk_display_get_default_screen (display);
739 		GError *err = NULL;
740 
741 		gtk_style_context_add_provider_for_screen (screen,
742 							   GTK_STYLE_PROVIDER(provider),
743 							   GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
744 
745 		if (!gtk_css_provider_load_from_data (GTK_CSS_PROVIDER(provider),
746 						      ".gcr-red * { background-color: red; }\n", -1, &err)) {
747 			g_warning ("couldn't load style: %s",
748 				  err && err->message ? err->message : "");
749 		}
750 		g_object_unref (provider);
751 	} while (0);
752 }
753 
754 static void
_gcr_display_view_real_insert_renderer(GcrViewer * viewer,GcrRenderer * renderer,GcrRenderer * before)755 _gcr_display_view_real_insert_renderer (GcrViewer *viewer,
756                                         GcrRenderer *renderer,
757                                         GcrRenderer *before)
758 {
759 	GcrDisplayView *self = GCR_DISPLAY_VIEW (viewer);
760 	GcrDisplayItem *item;
761 	guint i;
762 
763 	if (before != NULL)
764 		g_return_if_fail (g_hash_table_lookup (self->pv->items, before) != NULL);
765 
766 	item = create_display_item (self, renderer);
767 	g_object_ref (renderer);
768 
769 	/* Insert it at the right place */
770 	if (before != NULL) {
771 		g_ptr_array_add (self->pv->renderers, NULL);
772 		for (i = self->pv->renderers->len; i > 0; i--) {
773 			self->pv->renderers->pdata[i] = self->pv->renderers->pdata[i - 1];
774 			if (self->pv->renderers->pdata[i] == before) {
775 				self->pv->renderers->pdata[i - 1] = renderer;
776 				break;
777 			}
778 		}
779 
780 		/* Must have been found */
781 		g_assert (i > 0);
782 
783 	/* No before, just add to end */
784 	} else {
785 		g_ptr_array_add (self->pv->renderers, renderer);
786 	}
787 
788 	g_hash_table_insert (self->pv->items, renderer, item);
789 
790 	gcr_renderer_render_view (renderer, viewer);
791 	item->data_changed_id = g_signal_connect (renderer, "data-changed",
792 	                                          G_CALLBACK (on_renderer_data_changed), self);
793 }
794 
795 static void
_gcr_display_view_real_add_renderer(GcrViewer * viewer,GcrRenderer * renderer)796 _gcr_display_view_real_add_renderer (GcrViewer *viewer, GcrRenderer *renderer)
797 {
798 	_gcr_display_view_real_insert_renderer (viewer, renderer, NULL);
799 }
800 
801 static void
_gcr_display_view_real_remove_renderer(GcrViewer * viewer,GcrRenderer * renderer)802 _gcr_display_view_real_remove_renderer (GcrViewer *viewer, GcrRenderer *renderer)
803 {
804 	GcrDisplayView *self = GCR_DISPLAY_VIEW (viewer);
805 	GcrDisplayItem *item;
806 
807 	item = lookup_display_item (self, renderer);
808 	g_return_if_fail (item);
809 
810 	/* Unhook the callback */
811 	g_signal_handler_disconnect (renderer, item->data_changed_id);
812 
813 	/* Destroys the display item */
814 	g_assert (item->display_view == self);
815 	g_hash_table_remove (self->pv->items, renderer);
816 
817 	/* Unrefs the renderer */
818 	if (!g_ptr_array_remove (self->pv->renderers, renderer))
819 		g_return_if_reached ();
820 }
821 
822 static guint
_gcr_display_view_real_count_renderers(GcrViewer * viewer)823 _gcr_display_view_real_count_renderers (GcrViewer *viewer)
824 {
825 	GcrDisplayView *self = GCR_DISPLAY_VIEW (viewer);
826 	return self->pv->renderers->len;
827 }
828 
829 static GcrRenderer*
_gcr_display_view_real_get_renderer(GcrViewer * viewer,guint index_)830 _gcr_display_view_real_get_renderer (GcrViewer *viewer, guint index_)
831 {
832 	GcrDisplayView *self = GCR_DISPLAY_VIEW (viewer);
833 	g_return_val_if_fail (index_ < self->pv->renderers->len, NULL);
834 	return g_ptr_array_index (self->pv->renderers, index_);
835 }
836 
837 static void
_gcr_display_view_viewer_iface(GcrViewerIface * iface)838 _gcr_display_view_viewer_iface (GcrViewerIface *iface)
839 {
840 	iface->add_renderer = (gpointer)_gcr_display_view_real_add_renderer;
841 	iface->insert_renderer = (gpointer)_gcr_display_view_real_insert_renderer;
842 	iface->remove_renderer = (gpointer)_gcr_display_view_real_remove_renderer;
843 	iface->count_renderers = (gpointer)_gcr_display_view_real_count_renderers;
844 	iface->get_renderer = (gpointer)_gcr_display_view_real_get_renderer;
845 }
846 
847 /* -----------------------------------------------------------------------------
848  * PUBLIC
849  */
850 
851 GcrDisplayView*
_gcr_display_view_new(void)852 _gcr_display_view_new (void)
853 {
854 	return g_object_new (GCR_TYPE_DISPLAY_VIEW, NULL);
855 }
856 
857 void
_gcr_display_view_begin(GcrDisplayView * self,GcrRenderer * renderer)858 _gcr_display_view_begin (GcrDisplayView *self,
859                          GcrRenderer *renderer)
860 {
861 	GtkTextIter start, iter;
862 	GcrDisplayItem *item;
863 	GList *widgets, *l;
864 
865 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
866 	item = lookup_display_item (self, renderer);
867 	g_return_if_fail (item);
868 
869 	/* Remove the details widget so it doesn't get destroyed */
870 	if (gtk_widget_get_parent (item->details_widget))
871 		gtk_container_remove (GTK_CONTAINER (self), item->details_widget);
872 
873 	/* Remove area widgets so they don't get destroyed unnecessarily */
874 	if (item->area_anchor) {
875 		g_assert (!gtk_text_child_anchor_get_deleted (item->area_anchor));
876 		widgets = gtk_text_child_anchor_get_widgets (item->area_anchor);
877 		for (l = widgets; l != NULL; l = g_list_next (l))
878 			gtk_container_remove (GTK_CONTAINER (self), l->data);
879 		g_list_free (widgets);
880 		g_object_unref (item->area_anchor);
881 		item->area_anchor = NULL;
882 	}
883 
884 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &start, item->beginning);
885 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->ending);
886 	gtk_text_buffer_delete (self->pv->buffer, &start, &iter);
887 
888 	item->extra_tag = NULL;
889 	item->field_width = 0;
890 	item->details = FALSE;
891 }
892 
893 void
_gcr_display_view_end(GcrDisplayView * self,GcrRenderer * renderer)894 _gcr_display_view_end (GcrDisplayView *self,
895                        GcrRenderer *renderer)
896 {
897 	GcrDisplayItem *item;
898 
899 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
900 	item = lookup_display_item (self, renderer);
901 	g_return_if_fail (item);
902 }
903 
904 void
_gcr_display_view_start_details(GcrDisplayView * self,GcrRenderer * renderer)905 _gcr_display_view_start_details (GcrDisplayView *self, GcrRenderer *renderer)
906 {
907 	GtkTextChildAnchor *anchor;
908 	GcrDisplayItem *item;
909 	GtkTextIter iter;
910 
911 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
912 	item = lookup_display_item (self, renderer);
913 	g_return_if_fail (item);
914 
915 	if (item->details) {
916 		g_warning ("A GcrRenderer implementation has called %s twice in one render",
917 		           G_STRFUNC);
918 		return;
919 	}
920 
921 	item->extra_tag = item->details_tag;
922 	item->details = TRUE;
923 
924 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->ending);
925 	anchor = gtk_text_buffer_create_child_anchor (self->pv->buffer, &iter);
926 	gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (self), item->details_widget, anchor);
927 	gtk_widget_show_all (item->details_widget);
928 	gtk_text_buffer_insert (self->pv->buffer, &iter, "\n", 1);
929 }
930 
931 void
_gcr_display_view_append_content(GcrDisplayView * self,GcrRenderer * renderer,const gchar * content,const gchar * details)932 _gcr_display_view_append_content (GcrDisplayView *self, GcrRenderer *renderer,
933                                   const gchar *content, const gchar *details)
934 {
935 	GcrDisplayItem *item;
936 	GtkTextIter iter;
937 	gchar *memory = NULL;
938 
939 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
940 	g_return_if_fail (content);
941 
942 	item = lookup_display_item (self, renderer);
943 	g_return_if_fail (item);
944 
945 	if (item->details && !item->expanded)
946 		return;
947 
948 	if (details)
949 		content = memory = g_strdup_printf ("%s: %s", content, details);
950 
951 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->ending);
952 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, content, -1,
953 	                                  self->pv->content_tag, item->extra_tag, NULL);
954 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, "\n", 1,
955 	                                  item->extra_tag, NULL);
956 
957 	g_free (memory);
958 }
959 
960 void
_gcr_display_view_append_value(GcrDisplayView * self,GcrRenderer * renderer,const gchar * field,const gchar * value,gboolean monospace)961 _gcr_display_view_append_value (GcrDisplayView *self, GcrRenderer *renderer, const gchar *field,
962                                 const gchar *value, gboolean monospace)
963 {
964 	GcrDisplayItem *item;
965 	PangoRectangle extents;
966 	PangoTabArray *tabs;
967 	PangoLayout *layout;
968 	GtkTextIter iter;
969 	gchar *text;
970 
971 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
972 	g_return_if_fail (field);
973 
974 	item = lookup_display_item (self, renderer);
975 	g_return_if_fail (item);
976 
977 	if (item->details && !item->expanded)
978 		return;
979 
980 	text = g_strdup_printf ("%s:", field);
981 	if (value == NULL)
982 		value = "";
983 
984 	/* Measure the width of the field */
985 	layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), text);
986 	pango_layout_get_extents (layout, NULL, &extents);
987 	pango_extents_to_pixels (&extents, NULL);
988 	g_object_unref (layout);
989 
990 	/* An estimate of the text height */
991 	self->pv->text_height = extents.height;
992 
993 	/* Make the tab wide enough to accomodate */
994 	if (extents.width > item->field_width) {
995 		item->field_width = extents.width + COLUMN_MARGIN;
996 		tabs = pango_tab_array_new (1, TRUE);
997 		pango_tab_array_set_tab (tabs, 0, PANGO_TAB_LEFT, item->field_width);
998 		g_object_set (item->field_tag,
999 		              "left-margin", FIELD_MARGIN,
1000 		              "indent", 0 - item->field_width,
1001 		              "tabs", tabs,
1002 		              NULL);
1003 		pango_tab_array_free (tabs);
1004 	}
1005 
1006 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->ending);
1007 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, text, -1,
1008 	                                  item->field_tag, item->extra_tag, NULL);
1009 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, "\t", 1,
1010 	                                  item->extra_tag, NULL);
1011 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, value, -1, item->field_tag,
1012 	                                  monospace ? self->pv->monospace_tag : item->extra_tag,
1013 	                                  monospace ? item->extra_tag : NULL, NULL);
1014 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, "\n", 1,
1015 	                                  item->extra_tag, NULL);
1016 
1017 	g_free (text);
1018 }
1019 
1020 void
_gcr_display_view_append_hex(GcrDisplayView * self,GcrRenderer * renderer,const gchar * field,gconstpointer value,gsize n_value)1021 _gcr_display_view_append_hex (GcrDisplayView *self, GcrRenderer *renderer,
1022                               const gchar *field, gconstpointer value, gsize n_value)
1023 {
1024 	gchar *display;
1025 
1026 	display = egg_hex_encode_full (value, n_value, TRUE, " ", 1);
1027 	_gcr_display_view_append_value (self, renderer, field, display, TRUE);
1028 	g_free (display);
1029 }
1030 
1031 void
_gcr_display_view_append_title(GcrDisplayView * self,GcrRenderer * renderer,const gchar * title)1032 _gcr_display_view_append_title (GcrDisplayView *self, GcrRenderer *renderer, const gchar *title)
1033 {
1034 	GcrDisplayItem *item;
1035 	GtkTextIter iter;
1036 
1037 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
1038 	g_return_if_fail (title);
1039 
1040 	item = lookup_display_item (self, renderer);
1041 	g_return_if_fail (item);
1042 
1043 	if (item->details && !item->expanded)
1044 		return;
1045 
1046 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->ending);
1047 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, title, -1,
1048 	                                  self->pv->title_tag, item->extra_tag, NULL);
1049 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, "\n", 1,
1050 	                                  item->extra_tag, NULL);
1051 }
1052 
1053 void
_gcr_display_view_append_heading(GcrDisplayView * self,GcrRenderer * renderer,const gchar * heading)1054 _gcr_display_view_append_heading (GcrDisplayView *self, GcrRenderer *renderer, const gchar *heading)
1055 {
1056 	GcrDisplayItem *item;
1057 	GtkTextIter iter;
1058 
1059 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
1060 	g_return_if_fail (heading);
1061 
1062 	item = lookup_display_item (self, renderer);
1063 	g_return_if_fail (item);
1064 
1065 	if (item->details && !item->expanded)
1066 		return;
1067 
1068 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->ending);
1069 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, heading, -1,
1070 	                                  self->pv->heading_tag, item->extra_tag, NULL);
1071 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, "\n", 1,
1072 	                                  item->extra_tag, NULL);
1073 }
1074 
1075 void
_gcr_display_view_append_fingerprint(GcrDisplayView * self,GcrRenderer * renderer,const guchar * data,gsize n_data,const gchar * name,GChecksumType type)1076 _gcr_display_view_append_fingerprint (GcrDisplayView *self, GcrRenderer *renderer, const guchar *data,
1077                                       gsize n_data, const gchar *name, GChecksumType type)
1078 {
1079 	GChecksum *checksum;
1080 	guint8 *buffer;
1081 	gsize n_buffer;
1082 
1083 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
1084 
1085 	checksum = g_checksum_new (type);
1086 	g_return_if_fail (checksum);
1087 	g_checksum_update (checksum, data, n_data);
1088 
1089 	n_buffer = g_checksum_type_get_length (type);
1090 	g_return_if_fail (n_buffer);
1091 	buffer = g_malloc0 (n_buffer);
1092 
1093 	g_checksum_get_digest (checksum, buffer, &n_buffer);
1094 	g_checksum_free (checksum);
1095 
1096 	_gcr_display_view_append_hex (self, renderer, name, buffer, n_buffer);
1097 
1098 	g_free (buffer);
1099 }
1100 
1101 void
_gcr_display_view_append_message(GcrDisplayView * self,GcrRenderer * renderer,GtkMessageType message_type,const gchar * message)1102 _gcr_display_view_append_message (GcrDisplayView *self,
1103                                   GcrRenderer *renderer,
1104                                   GtkMessageType message_type,
1105                                   const gchar *message)
1106 {
1107 	const gchar *name = NULL;
1108 	GtkWidget *image = NULL;
1109 	GcrDisplayItem *item;
1110 	GtkTextChildAnchor *anchor;
1111 	GtkTextIter iter;
1112 
1113 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
1114 	g_return_if_fail (GCR_IS_RENDERER (renderer));
1115 
1116 	item = lookup_display_item (self, renderer);
1117 	g_return_if_fail (item);
1118 
1119 	switch (message_type) {
1120 	case GTK_MESSAGE_INFO:
1121 		name = "dialog-information";
1122 		break;
1123 
1124 	case GTK_MESSAGE_QUESTION:
1125 		name = "dialog-question";
1126 		break;
1127 
1128 	case GTK_MESSAGE_WARNING:
1129 		name = "dialog-warning";
1130 		break;
1131 
1132 	case GTK_MESSAGE_ERROR:
1133 		name = "dialog-error";
1134 		break;
1135 
1136 	case GTK_MESSAGE_OTHER:
1137 		break;
1138 
1139 	default:
1140 		g_warning ("unknown GtkMessageType: %u", message_type);
1141 		break;
1142 	}
1143 
1144 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->ending);
1145 
1146 	if (name != NULL) {
1147 		image = gtk_image_new_from_icon_name (name, GTK_ICON_SIZE_MENU);
1148 #if GTK_CHECK_VERSION (3, 12, 0)
1149 		gtk_widget_set_margin_start (image, MESSAGE_PADDING);
1150 		gtk_widget_set_margin_end (image, MESSAGE_PADDING);
1151 #else
1152 		gtk_widget_set_margin_left (image, MESSAGE_PADDING);
1153 		gtk_widget_set_margin_right (image, MESSAGE_PADDING);
1154 #endif
1155 		gtk_widget_show (image);
1156 
1157 		anchor = gtk_text_buffer_create_child_anchor (self->pv->buffer, &iter);
1158 		gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (self), image, anchor);
1159 	}
1160 
1161 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, message, -1,
1162 	                                  self->pv->message_tag, item->extra_tag, NULL);
1163 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, "\n", 1,
1164 	                                  item->extra_tag, NULL);
1165 }
1166 
1167 void
_gcr_display_view_set_icon(GcrDisplayView * self,GcrRenderer * renderer,GIcon * icon)1168 _gcr_display_view_set_icon (GcrDisplayView *self, GcrRenderer *renderer, GIcon *icon)
1169 {
1170 	GcrDisplayItem *item;
1171 	GdkScreen *screen;
1172 	GtkIconTheme *icon_theme;
1173 	gint width, height;
1174 	GtkIconInfo *info;
1175 
1176 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
1177 	item = lookup_display_item (self, renderer);
1178 	g_return_if_fail (item);
1179 
1180 	if (item->pixbuf)
1181 		g_object_unref (item->pixbuf);
1182 	item->pixbuf = NULL;
1183 
1184 	if (!icon)
1185 		return;
1186 
1187 	screen = gtk_widget_get_screen (GTK_WIDGET (self));
1188 	icon_theme = gtk_icon_theme_get_for_screen (screen);
1189 
1190 	if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &width, &height))
1191 		g_return_if_reached ();
1192 
1193 	info = gtk_icon_theme_lookup_by_gicon (icon_theme, icon, MIN (width, height),
1194 	                                       GTK_ICON_LOOKUP_USE_BUILTIN);
1195 
1196 	if (info) {
1197 		GtkStyleContext *style = gtk_widget_get_style_context (GTK_WIDGET (self));
1198 		item->pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, FALSE, NULL);
1199 #if GTK_CHECK_VERSION(3, 8, 0)
1200 		g_object_unref (info);
1201 #else
1202 		gtk_icon_info_free (info);
1203 #endif
1204 	}
1205 }
1206 
1207 void
_gcr_display_view_add_widget_area(GcrDisplayView * self,GcrRenderer * renderer,GtkWidget * area)1208 _gcr_display_view_add_widget_area (GcrDisplayView *self,
1209                                    GcrRenderer *renderer,
1210                                    GtkWidget *area)
1211 {
1212 	GtkTextIter iter, start;
1213 	GcrDisplayItem *item;
1214 
1215 	g_return_if_fail (GCR_IS_DISPLAY_VIEW (self));
1216 	g_return_if_fail (GTK_IS_WIDGET (area));
1217 
1218 	item = lookup_display_item (self, renderer);
1219 	g_return_if_fail (item != NULL);
1220 	g_return_if_fail (item->area_anchor == NULL);
1221 
1222 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &start, item->ending);
1223 	iter = start;
1224 
1225 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, "\n" ZWSP, -1, self->pv->area_tag, NULL);
1226 	gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &iter, item->ending);
1227 
1228 	item->area_anchor = gtk_text_buffer_create_child_anchor (self->pv->buffer, &iter);
1229 	g_object_ref (item->area_anchor);
1230 	gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (self), area, item->area_anchor);
1231 	gtk_text_buffer_insert_with_tags (self->pv->buffer, &iter, ZWSP "\n", -1, self->pv->area_tag, NULL);
1232 }
1233