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