1 /* Exif-display -- display information about digital pictures
2  *
3  * Copyright (C) 2009 The Free Software Foundation
4  *
5  * Author: Emmanuel Touzery  <emmanuel.touzery@free.fr>
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 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <gtk/gtk.h>
27 
28 #include <glib/gi18n-lib.h>
29 #include <eog/eog-debug.h>
30 
31 // TODO: This is not cool. We need to find a better way to organize the API.
32 #ifndef HAVE_EXIF
33 #define HAVE_EXIF 1
34 #endif
35 #include <eog/eog-image.h>
36 
37 #include <eog/eog-thumb-view.h>
38 #include <eog/eog-job-scheduler.h>
39 #include <eog/eog-exif-util.h>
40 #include <eog/eog-sidebar.h>
41 #include <eog/eog-window-activatable.h>
42 
43 #include "eog-exif-display-plugin-settings.h"
44 #include "eog-exif-display-plugin-setup.h"
45 #include "eog-exif-display-plugin.h"
46 
47 #define GRESOURCE_PATH "/org/gnome/eog/plugins/exif-display/exif-display.ui"
48 
49 enum {
50 	PROP_O,
51 	PROP_DRAW_CHAN_HISTOGRAM,
52 	PROP_DRAW_RGB_HISTOGRAM,
53 	PROP_ENABLE_STATUSBAR,
54 	PROP_WINDOW
55 };
56 
57 static void
58 eog_window_activatable_iface_init (EogWindowActivatableInterface *iface);
59 
60 G_DEFINE_DYNAMIC_TYPE_EXTENDED (EogExifDisplayPlugin, eog_exif_display_plugin,
61                 PEAS_TYPE_EXTENSION_BASE, 0,
62                 G_IMPLEMENT_INTERFACE_DYNAMIC(EOG_TYPE_WINDOW_ACTIVATABLE,
63                                         eog_window_activatable_iface_init))
64 
65 static void
eog_exif_display_plugin_init(EogExifDisplayPlugin * plugin)66 eog_exif_display_plugin_init (EogExifDisplayPlugin *plugin)
67 {
68 }
69 
70 /* eog_util_make_valid_utf8 is not exported so it's duped here */
71 gchar *
_eog_util_make_valid_utf8(const gchar * str)72 _eog_util_make_valid_utf8 (const gchar *str)
73 {
74 	GString *string;
75 	const char *remainder, *invalid;
76 	int remaining_bytes, valid_bytes;
77 
78 	string = NULL;
79 	remainder = str;
80 	remaining_bytes = strlen (str);
81 
82 	while (remaining_bytes != 0) {
83 		if (g_utf8_validate (remainder, remaining_bytes, &invalid)) {
84 			break;
85 		}
86 
87 		valid_bytes = invalid - remainder;
88 
89 		if (string == NULL) {
90 			string = g_string_sized_new (remaining_bytes);
91 		}
92 
93 		g_string_append_len (string, remainder, valid_bytes);
94 		g_string_append_c (string, '?');
95 
96 		remaining_bytes -= valid_bytes + 1;
97 		remainder = invalid + 1;
98 	}
99 
100 	if (string == NULL) {
101 		return g_strdup (str);
102 	}
103 
104 	g_string_append (string, remainder);
105 	g_string_append (string, _(" (invalid Unicode)"));
106 
107 	g_assert (g_utf8_validate (string->str, -1, NULL));
108 
109 	return g_string_free (string, FALSE);
110 }
111 
112 /* stolen from eog-properties-dialog.c*/
113 static void
eog_exif_set_label(GtkWidget * w,ExifData * exif_data,gint tag_id)114 eog_exif_set_label (GtkWidget *w, ExifData *exif_data, gint tag_id)
115 {
116 	gchar exif_buffer[512];
117 	const gchar *buf_ptr;
118 	gchar *label_text = NULL;
119 
120 	if (exif_data) {
121 		buf_ptr = eog_exif_data_get_value (exif_data, tag_id,
122 						   exif_buffer, 512);
123 
124 		if (tag_id == EXIF_TAG_DATE_TIME_ORIGINAL && buf_ptr)
125 			label_text = eog_exif_util_format_date (buf_ptr);
126 		else
127 			label_text = _eog_util_make_valid_utf8 (buf_ptr);
128 	}
129 
130 	gtk_label_set_text (GTK_LABEL (w), label_text);
131 	g_free (label_text);
132 }
133 
set_exif_label(ExifData * exif_data,int exif_tag,GtkBuilder * gtk_builder,const gchar * gtk_builder_label_name,gboolean tooltip)134 static void set_exif_label (ExifData *exif_data, int exif_tag,
135 			    GtkBuilder *gtk_builder,
136 			    const gchar *gtk_builder_label_name,
137 			    gboolean tooltip)
138 {
139 	GtkWidget *widget = GTK_WIDGET (gtk_builder_get_object (
140 					gtk_builder, gtk_builder_label_name));
141 	eog_exif_set_label (widget, exif_data, exif_tag);
142 
143 	if (tooltip) {
144 		gtk_widget_set_tooltip_text (widget, gtk_label_get_label (GTK_LABEL (widget)));
145 	}
146 }
147 
148 /* stolen from eog-properties-dialog and slightly modified
149  * you must g_free () the gchar* that I return!
150  * */
151 static gchar*
eog_exif_get_focal_length_desc(ExifData * exif_data)152 eog_exif_get_focal_length_desc (ExifData *exif_data)
153 {
154 	ExifEntry *entry = NULL, *entry35mm = NULL;
155 	ExifByteOrder byte_order;
156 	gfloat f_val = 0.0;
157 	gchar *fl_text = NULL,*fl35_text = NULL;
158 	gchar *result;
159 
160 	/* If no ExifData is supplied the label will be
161 	 * cleared later as fl35_text is NULL. */
162 	if (exif_data != NULL) {
163 		entry = exif_data_get_entry (exif_data, EXIF_TAG_FOCAL_LENGTH);
164 		entry35mm = exif_data_get_entry (exif_data,
165 					    EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM);
166 		byte_order = exif_data_get_byte_order (exif_data);
167 	}
168 
169 	if (entry && G_LIKELY (entry->format == EXIF_FORMAT_RATIONAL)) {
170 		ExifRational value;
171 
172 		/* Decode value by hand as libexif is not necessarily returning
173 		 * it in the format we want it to be.
174 		 */
175 		value = exif_get_rational (entry->data, byte_order);
176 		/* Guard against div by zero */
177 		if (G_LIKELY(value.denominator != 0))
178 			f_val = (gfloat)value.numerator/
179 				(gfloat)value.denominator;
180 
181 		/* TRANSLATORS: This is the actual focal length used when
182 		   the image was taken.*/
183 		fl_text = g_strdup_printf (_("%.1fmm (lens)"), f_val);
184 
185 	}
186 	if (entry35mm && G_LIKELY (entry35mm->format == EXIF_FORMAT_SHORT)) {
187 		ExifShort s_val;
188 
189 		s_val = exif_get_short (entry35mm->data, byte_order);
190 
191 		/* Print as float to get a similar look as above. */
192 		/* TRANSLATORS: This is the equivalent focal length assuming
193 		   a 35mm film camera. */
194 		fl35_text = g_strdup_printf(_("%.1fmm (35mm film)"),(float)s_val);
195 	}
196 	if (fl_text) {
197 		if (fl35_text) {
198 			result = g_strconcat (fl35_text,", ", fl_text, NULL);
199 
200 			g_free (fl35_text);
201 			g_free (fl_text);
202 
203 		} else {
204 			result = fl_text;
205 			g_free (fl35_text);
206 		}
207 	} else {
208 		result = fl35_text;
209 		g_free (fl_text);
210 	}
211 
212 	return result;
213 }
214 
215 /* stolen from eog-properties-dialog and modified */
216 static void
eog_exif_set_focal_length_label(GtkWidget * w,ExifData * exif_data)217 eog_exif_set_focal_length_label (GtkWidget *w, ExifData *exif_data)
218 {
219 	gchar *focal_length_desc = eog_exif_get_focal_length_desc (exif_data);
220 	gtk_label_set_text (GTK_LABEL (w), focal_length_desc);
221 
222 	g_free (focal_length_desc);
223 }
224 
manage_exif_data(EogExifDisplayPlugin * plugin)225 static void manage_exif_data (EogExifDisplayPlugin *plugin)
226 {
227 	EogImage *image;
228 	ExifData *exif_data;
229 
230 	image = eog_thumb_view_get_first_selected_image (plugin->thumbview);
231 	g_return_if_fail (image != NULL);
232 
233 	exif_data = (ExifData *)eog_image_get_exif_info (image);
234 
235 	set_exif_label (exif_data, EXIF_TAG_DATE_TIME_ORIGINAL, plugin->sidebar_builder, "takenon_label", TRUE);
236 
237 	eog_exif_set_focal_length_label (GTK_WIDGET (gtk_builder_get_object (
238 			plugin->sidebar_builder, "focal_length_label")), exif_data);
239 
240 	set_exif_label (exif_data, EXIF_TAG_EXPOSURE_BIAS_VALUE,
241 			plugin->sidebar_builder, "exposure_bias_label", FALSE);
242 
243 	set_exif_label (exif_data, EXIF_TAG_EXPOSURE_TIME,
244 			plugin->sidebar_builder, "exposure_time_label", FALSE);
245 
246 	set_exif_label (exif_data, EXIF_TAG_MODEL,
247 			plugin->sidebar_builder, "camera_model_label", FALSE);
248 
249 	set_exif_label (exif_data, EXIF_TAG_FNUMBER,
250 			plugin->sidebar_builder, "aperture_label", FALSE);
251 
252 	set_exif_label (exif_data, EXIF_TAG_ISO_SPEED_RATINGS,
253 			plugin->sidebar_builder, "iso_label", FALSE);
254 
255 	set_exif_label (exif_data, EXIF_TAG_FLASH,
256 			plugin->sidebar_builder, "flash_label", TRUE);
257 
258 	set_exif_label (exif_data, EXIF_TAG_METERING_MODE,
259 			plugin->sidebar_builder, "metering_mode_label", TRUE);
260 
261 	set_exif_label (exif_data, EXIF_TAG_USER_COMMENT,
262 			plugin->sidebar_builder, "desc_label", TRUE);
263 
264 	set_exif_label (exif_data, EXIF_TAG_EXPOSURE_BIAS_VALUE,
265 			plugin->sidebar_builder, "exposure_bias_label", FALSE);
266 
267 	exif_data_unref (exif_data);
268 
269 	g_object_unref (image);
270 }
271 
manage_exif_data_cb(EogJob * job,gpointer data)272 static void manage_exif_data_cb (EogJob *job, gpointer data)
273 {
274 	if (!job->error) {
275 		manage_exif_data (EOG_EXIF_DISPLAY_PLUGIN(data));
276 	}
277 }
278 
279 static gboolean
calculate_histogram(EogExifDisplayPlugin * plugin,EogImage * eog_image)280 calculate_histogram (EogExifDisplayPlugin *plugin, EogImage *eog_image)
281 {
282 	int rowstride;
283 	int width, height;
284 	int row, col;
285 	GdkPixbuf *image_pixbuf;
286 	guchar *pixels;
287 	int array_sums_elt = 0;
288 
289 	/* for the red when we calculate we store
290 	 * the values in a temporary array.
291 	 * only when everything is calculated
292 	 * we copy the pointers to the real
293 	 * plugin->histogram_values_red.
294 	 * That way we'll try to display
295 	 * the histogram only once it's fully
296 	 * calculated.*/
297 	int *histogram_values_red_temp;
298 
299 	if (eog_image == NULL) {
300 		return FALSE;
301 	}
302 
303 	g_free (plugin->histogram_values_red);
304 	plugin->histogram_values_red = NULL;
305 
306 	g_free (plugin->histogram_values_green);
307 	plugin->histogram_values_green = NULL;
308 
309 	g_free (plugin->histogram_values_blue);
310 	plugin->histogram_values_blue = NULL;
311 
312 	g_free (plugin->histogram_values_rgb);
313 	plugin->histogram_values_rgb = NULL;
314 
315 	image_pixbuf = eog_image_get_pixbuf (eog_image);
316 	if (image_pixbuf == NULL) {
317 		return FALSE;
318 	}
319 
320 	if ((gdk_pixbuf_get_colorspace (image_pixbuf) != GDK_COLORSPACE_RGB)
321 		|| (gdk_pixbuf_get_bits_per_sample (image_pixbuf) > 8)) {
322 		g_object_unref (image_pixbuf);
323 		return FALSE;
324 	}
325 
326 	rowstride = gdk_pixbuf_get_rowstride (image_pixbuf);
327 
328 	width = gdk_pixbuf_get_width (image_pixbuf);
329 	height = gdk_pixbuf_get_height (image_pixbuf);
330 
331 	pixels = gdk_pixbuf_get_pixels (image_pixbuf);
332 
333 	histogram_values_red_temp = g_new0 (int, 256);
334 
335 	plugin->histogram_values_green = g_new0 (int, 256);
336 	plugin->histogram_values_blue = g_new0 (int, 256);
337 	plugin->max_of_array_sums = 0;
338 
339 	plugin->histogram_values_rgb = g_new0 (int, 256);
340 	plugin->max_of_array_sums_rgb = 0;
341 
342 	for (row = 0; row < height; row++) {
343 		guchar *row_cur_idx = pixels + row*rowstride;
344 		for (col = 0; col < width; col++) {
345 			guchar red = *row_cur_idx++;
346 			guchar green = *row_cur_idx++;
347 			guchar blue = *row_cur_idx++;
348 
349 			histogram_values_red_temp[red] += 1;
350 			plugin->histogram_values_green[green] += 1;
351 			plugin->histogram_values_blue[blue] += 1;
352 			plugin->histogram_values_rgb[MAX (red, MAX (green, blue))] += 1;
353 		}
354 	}
355 	for (array_sums_elt=0;array_sums_elt<256;array_sums_elt++) {
356 		if (histogram_values_red_temp[array_sums_elt] > plugin->max_of_array_sums) {
357 			plugin->max_of_array_sums = histogram_values_red_temp[array_sums_elt];
358 		}
359 		if (plugin->histogram_values_green[array_sums_elt] > plugin->max_of_array_sums) {
360 			plugin->max_of_array_sums = plugin->histogram_values_green[array_sums_elt];
361 		}
362 		if (plugin->histogram_values_blue[array_sums_elt] > plugin->max_of_array_sums) {
363 			plugin->max_of_array_sums = plugin->histogram_values_blue[array_sums_elt];
364 		}
365 	}
366 
367 	for (array_sums_elt=0;array_sums_elt<256;array_sums_elt++) {
368 		if (plugin->histogram_values_rgb[array_sums_elt] > plugin->max_of_array_sums_rgb) {
369 			plugin->max_of_array_sums_rgb = plugin->histogram_values_rgb[array_sums_elt];
370 		}
371 	}
372 
373 	plugin->histogram_values_red = histogram_values_red_temp;
374 
375 	g_object_unref (image_pixbuf);
376 
377 	return TRUE;
378 }
379 
380 static void
draw_histogram_graph(cairo_t * cr,int * histogram_values,int max_of_array_sums)381 draw_histogram_graph (cairo_t *cr, int *histogram_values, int max_of_array_sums)
382 {
383 	int i;
384 
385 	cairo_move_to (cr, 0, 1);
386 	for (i = 0; i < 256; i++) {
387 		cairo_line_to (cr, ((float)i)/(256.0), 1.0 - ((float)histogram_values[i])/max_of_array_sums);
388 	}
389 	cairo_line_to (cr, 1, 1);
390 	cairo_close_path (cr);
391 	cairo_fill (cr);
392 }
393 
394 static void
drawing_area_draw_cb(GtkDrawingArea * drawing_area,cairo_t * cr,EogExifDisplayPlugin * plugin)395 drawing_area_draw_cb (GtkDrawingArea *drawing_area, cairo_t *cr,
396 		     EogExifDisplayPlugin *plugin)
397 {
398 	gboolean draw_channels_histogram, draw_rgb_histogram;
399 	EogImage *eog_image;
400 	gint drawing_area_width, drawing_area_height;
401 	int scale_factor_y;
402 	GtkStyleContext *style_ctx;
403 
404 	if (!gtk_widget_get_realized (GTK_WIDGET (drawing_area)))
405 		return;
406 
407 	draw_channels_histogram = plugin->draw_chan_histogram;
408 	draw_rgb_histogram = plugin->draw_rgb_histogram;
409 
410 	eog_image = eog_thumb_view_get_first_selected_image (plugin->thumbview);
411 	g_return_if_fail (eog_image != NULL);
412 
413 	if (plugin->histogram_values_red == NULL) {
414 		/* when calculate_histogram was called previously,
415 		 * the picture was not loaded yet.
416 		 * Now it's loaded, let's ask to calculate the
417 		 * histogram again... */
418 		calculate_histogram (plugin, eog_image);
419 	}
420 
421 	drawing_area_width = gtk_widget_get_allocated_width (GTK_WIDGET (drawing_area));
422 	drawing_area_height = gtk_widget_get_allocated_height (GTK_WIDGET (drawing_area));
423 
424 	scale_factor_y = drawing_area_height;
425 	if (scale_factor_y > drawing_area_width/2) {
426 		/* histogram taller than it is wide looks ugly.
427 		 * it must be wider than it is tall for aesthetics.
428 		 */
429 		scale_factor_y = drawing_area_width/2;
430 	}
431 	cairo_scale (cr, drawing_area_width, scale_factor_y);
432 
433 	/* clear the display */
434 	style_ctx = gtk_widget_get_style_context (GTK_WIDGET (drawing_area));
435 	gtk_render_background (style_ctx, cr, 0, 0,
436 			       drawing_area_width, drawing_area_height);
437 
438 	if (plugin->histogram_values_red == NULL) {
439 		/* it's possible, if the image
440 		 * is not loaded and histogram
441 		 * can't be calculated, we go this
442 		 * far to clear the display.
443 		 * now exit, we won't draw any
444 		 * histogram without the data.
445 		 */
446 		return;
447 	}
448 
449 	if (draw_channels_histogram) {
450 		cairo_set_source_rgba (cr, 1, 0, 0, 0.5);
451 		draw_histogram_graph (cr, plugin->histogram_values_red,
452 				      plugin->max_of_array_sums);
453 
454 		cairo_set_source_rgba (cr, 0, 1, 0, 0.5);
455 		draw_histogram_graph (cr, plugin->histogram_values_green,
456 				      plugin->max_of_array_sums);
457 
458 		cairo_set_source_rgba (cr, 0, 0, 1, 0.5);
459 		draw_histogram_graph (cr, plugin->histogram_values_blue,
460 				      plugin->max_of_array_sums);
461 	}
462 	if (draw_rgb_histogram) {
463 		cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
464 		draw_histogram_graph (cr, plugin->histogram_values_rgb,
465 				      plugin->max_of_array_sums_rgb);
466 	}
467 
468 	g_object_unref (eog_image);
469 }
470 
calculate_histogram_cb(EogJob * job,gpointer data)471 static void calculate_histogram_cb (EogJob *job, gpointer data)
472 {
473 	EogExifDisplayPlugin *plugin = EOG_EXIF_DISPLAY_PLUGIN (data);
474 
475 	if (!job->error) {
476 		EogImage *eog_image =
477 			eog_thumb_view_get_first_selected_image (plugin->thumbview);
478 		calculate_histogram (plugin, eog_image);
479 		g_object_unref (eog_image);
480 		gtk_widget_queue_draw (GTK_WIDGET (plugin->drawing_area));
481 	}
482 }
483 
484 static void
statusbar_update_exif_data(GtkStatusbar * statusbar,EogThumbView * view)485 statusbar_update_exif_data (GtkStatusbar *statusbar, EogThumbView *view)
486 {
487 	EogImage *image;
488 	ExifData *exif_data;
489 	gchar *exif_desc = NULL;
490 
491 	if (eog_thumb_view_get_n_selected (view) == 0)
492 		return;
493 
494 	image = eog_thumb_view_get_first_selected_image (view);
495 
496 	gtk_statusbar_pop (statusbar, 0);
497 
498 	if (!eog_image_has_data (image, EOG_IMAGE_DATA_EXIF)) {
499 		if (!eog_image_load (image, EOG_IMAGE_DATA_EXIF, NULL, NULL)) {
500 			gtk_widget_hide (GTK_WIDGET (statusbar));
501 		}
502 	}
503 
504 	exif_data = (ExifData *) eog_image_get_exif_info (image);
505 	if (exif_data) {
506 		ExifEntry *exif_entry;
507 		gchar exposition_time[512];
508 		gchar aperture[512];
509 		gchar iso[512];
510 		gchar *focal_length;
511 
512 		exposition_time[0] = 0;
513 		exif_entry = exif_data_get_entry (exif_data, EXIF_TAG_EXPOSURE_TIME);
514 		exif_entry_get_value (exif_entry, exposition_time, sizeof(exposition_time));
515 
516 		aperture[0] = 0;
517 		exif_entry = exif_data_get_entry (exif_data, EXIF_TAG_FNUMBER);
518 		exif_entry_get_value (exif_entry, aperture, sizeof(aperture));
519 
520 		iso[0] = 0;
521 		exif_entry = exif_data_get_entry (exif_data, EXIF_TAG_ISO_SPEED_RATINGS);
522 		exif_entry_get_value (exif_entry, iso, sizeof(iso));
523 
524 		focal_length = eog_exif_get_focal_length_desc (exif_data);
525 
526 		exif_desc = g_strdup_printf ("ISO%s  %s  %s  %s",
527 				iso, exposition_time, aperture, focal_length);
528 
529 		g_free (focal_length);
530 
531 		exif_data_unref (exif_data);
532 	}
533 	g_object_unref (image);
534 
535 	if (exif_desc) {
536 		gtk_statusbar_push (statusbar, 0, exif_desc);
537 		gtk_widget_show (GTK_WIDGET (statusbar));
538 		g_free (exif_desc);
539 	} else {
540 		gtk_widget_hide (GTK_WIDGET (statusbar));
541 	}
542 }
543 
544 
545 static void
selection_changed_cb(EogThumbView * view,EogExifDisplayPlugin * plugin)546 selection_changed_cb (EogThumbView *view, EogExifDisplayPlugin *plugin)
547 {
548 	EogImage *image;
549 
550 	if (!eog_thumb_view_get_n_selected (view)) {
551 		return;
552 	}
553 
554 	image = eog_thumb_view_get_first_selected_image (view);
555 	g_return_if_fail (image != NULL);
556 
557 	if (plugin->enable_statusbar) {
558 		statusbar_update_exif_data (GTK_STATUSBAR (plugin->statusbar_exif), view);
559 	}
560 
561 	if (!eog_image_has_data (image, EOG_IMAGE_DATA_EXIF)) {
562 		EogJob *job;
563 
564 		job = eog_job_load_new (image, EOG_IMAGE_DATA_EXIF);
565 		g_signal_connect (G_OBJECT (job), "finished",
566 				  G_CALLBACK (manage_exif_data_cb),
567 				  plugin);
568 		eog_job_scheduler_add_job (job);
569 		g_object_unref (job);
570 	} else {
571 		manage_exif_data (plugin);
572 	}
573 
574 	/* the selected image changed, the histogram must
575 	 * be recalculated. */
576 	if (!eog_image_has_data (image, EOG_IMAGE_DATA_IMAGE)) {
577 		EogJob *job;
578 
579 		job = eog_job_load_new (image, EOG_IMAGE_DATA_IMAGE);
580 		g_signal_connect (G_OBJECT (job), "finished",
581 				  G_CALLBACK (calculate_histogram_cb),
582 				  plugin);
583 		eog_job_scheduler_add_job (job);
584 		g_object_unref (job);
585 	}
586 
587 	g_object_unref (image);
588 }
589 
590 static void
remove_statusbar_entry(EogExifDisplayPlugin * plugin)591 remove_statusbar_entry (EogExifDisplayPlugin *plugin)
592 {
593 	GtkWidget *statusbar = eog_window_get_statusbar (plugin->window);
594 
595 	if (plugin->statusbar_exif == NULL) {
596 		return;
597 	}
598 	gtk_container_remove (GTK_CONTAINER (statusbar),
599 			      plugin->statusbar_exif);
600 	plugin->statusbar_exif = NULL;
601 }
602 
603 static void
setup_statusbar_exif(EogExifDisplayPlugin * plugin)604 setup_statusbar_exif (EogExifDisplayPlugin *plugin)
605 {
606 	GtkWidget *statusbar = eog_window_get_statusbar (plugin->window);
607 
608 	if (plugin->enable_statusbar) {
609 		plugin->statusbar_exif = gtk_statusbar_new ();
610 		gtk_widget_set_size_request (plugin->statusbar_exif, 280, 10);
611 		gtk_box_pack_end (GTK_BOX (statusbar),
612 				  plugin->statusbar_exif,
613 				  FALSE, FALSE, 0);
614 
615 		statusbar_update_exif_data (GTK_STATUSBAR (plugin->statusbar_exif), plugin->thumbview);
616 	}
617 	else {
618 		remove_statusbar_entry (plugin);
619 	}
620 }
621 
622 static void
impl_activate(EogWindowActivatable * activatable)623 impl_activate (EogWindowActivatable *activatable)
624 {
625 	EogExifDisplayPlugin *plugin = EOG_EXIF_DISPLAY_PLUGIN (activatable);
626 	EogWindow *window = plugin->window;
627 	GSettings *settings;
628 	GtkWidget *thumbview;
629 	GtkWidget *sidebar;
630 	GtkWidget *drawing_area;
631 	GError* error = NULL;
632 
633 	settings = g_settings_new (EOG_EXIF_DISPLAY_CONF_SCHEMA_ID);
634 
635 	thumbview = eog_window_get_thumb_view (window);
636 	plugin->thumbview = EOG_THUMB_VIEW (thumbview);
637 
638 	plugin->histogram_values_red = NULL;
639 	plugin->histogram_values_green = NULL;
640 	plugin->histogram_values_blue = NULL;
641 	plugin->histogram_values_rgb = NULL;
642 
643 	plugin->statusbar_exif = NULL;
644 
645 	plugin->selection_changed_id = g_signal_connect (G_OBJECT (thumbview),
646 					"selection-changed",
647 					G_CALLBACK (selection_changed_cb),
648 					plugin);
649 
650 	sidebar = eog_window_get_sidebar (window);
651 
652 	plugin->sidebar_builder = gtk_builder_new ();
653 	gtk_builder_set_translation_domain (plugin->sidebar_builder,
654 					    GETTEXT_PACKAGE);
655 	if (!gtk_builder_add_from_resource (plugin->sidebar_builder,
656 					    GRESOURCE_PATH, &error))
657 	{
658 		g_warning ("Couldn't load UI resource: %s", error->message);
659 		g_error_free (error);
660 	}
661 	plugin->gtkbuilder_widget = GTK_WIDGET (gtk_builder_get_object (plugin->sidebar_builder, "viewport1"));
662 
663 	drawing_area = GTK_WIDGET (gtk_builder_get_object (plugin->sidebar_builder, "drawingarea1"));
664 	g_signal_connect (drawing_area, "draw",
665 			  G_CALLBACK (drawing_area_draw_cb), plugin);
666 	plugin->drawing_area = GTK_DRAWING_AREA (drawing_area);
667 
668 	eog_sidebar_add_page (EOG_SIDEBAR (sidebar), "Details",
669 			      plugin->gtkbuilder_widget);
670 	gtk_widget_show_all (plugin->gtkbuilder_widget);
671 
672 	g_settings_bind (settings, EOG_EXIF_DISPLAY_CONF_CHANNELS_HISTOGRAM,
673 			 plugin, "draw-chan-histogram", G_SETTINGS_BIND_GET);
674 	g_settings_bind (settings, EOG_EXIF_DISPLAY_CONF_RGB_HISTOGRAM,
675 			 plugin, "draw-rgb-histogram", G_SETTINGS_BIND_GET);
676 	g_settings_bind (settings, EOG_EXIF_DISPLAY_CONF_EXIF_IN_STATUSBAR,
677 			 plugin, "enable-statusbar", G_SETTINGS_BIND_GET);
678 
679 	setup_statusbar_exif (plugin);
680 
681 	/* force display of data now */
682 	selection_changed_cb (plugin->thumbview, plugin);
683 	if (plugin->enable_statusbar)
684 	{
685 		statusbar_update_exif_data (GTK_STATUSBAR (plugin->statusbar_exif),
686 					    EOG_THUMB_VIEW (thumbview));
687 	}
688 
689 	g_object_unref (settings);
690 }
691 
692 static void
impl_deactivate(EogWindowActivatable * activatable)693 impl_deactivate	(EogWindowActivatable *activatable)
694 {
695 	EogExifDisplayPlugin *plugin = EOG_EXIF_DISPLAY_PLUGIN (activatable);
696 	GtkWidget *sidebar, *thumbview;
697 
698 	remove_statusbar_entry (plugin);
699 
700 	sidebar = eog_window_get_sidebar (plugin->window);
701 	eog_sidebar_remove_page(EOG_SIDEBAR (sidebar),
702 				plugin->gtkbuilder_widget);
703 
704 	thumbview = eog_window_get_thumb_view (plugin->window);
705 	g_signal_handler_disconnect (thumbview, plugin->selection_changed_id);
706 
707 	g_free (plugin->histogram_values_red);
708 	plugin->histogram_values_red = NULL;
709 	g_free (plugin->histogram_values_green);
710 	plugin->histogram_values_green = NULL;
711 	g_free (plugin->histogram_values_blue);
712 	plugin->histogram_values_blue = NULL;
713 	g_free (plugin->histogram_values_rgb);
714 	plugin->histogram_values_rgb = NULL;
715 
716 	g_object_unref (plugin->sidebar_builder);
717 	plugin->sidebar_builder = NULL;
718 }
719 
720 
721 static void
eog_exif_display_plugin_set_draw_chan_histogram(EogExifDisplayPlugin * plugin,gboolean value)722 eog_exif_display_plugin_set_draw_chan_histogram (EogExifDisplayPlugin *plugin,
723 						 gboolean value)
724 {
725 	if (plugin->draw_chan_histogram == value)
726 		return;
727 
728 	plugin->draw_chan_histogram = value;
729 
730 	gtk_widget_queue_draw (GTK_WIDGET (plugin->drawing_area));
731 
732 	g_object_notify (G_OBJECT (plugin), "draw-chan-histogram");
733 }
734 
735 static void
eog_exif_display_plugin_set_draw_rgb_histogram(EogExifDisplayPlugin * plugin,gboolean value)736 eog_exif_display_plugin_set_draw_rgb_histogram (EogExifDisplayPlugin *plugin,
737 						gboolean value)
738 {
739 	if (plugin->draw_rgb_histogram == value)
740 		return;
741 
742 	plugin->draw_rgb_histogram = value;
743 
744 	gtk_widget_queue_draw (GTK_WIDGET (plugin->drawing_area));
745 
746 	g_object_notify (G_OBJECT (plugin), "draw-rgb-histogram");
747 }
748 
749 static void
eog_exif_display_plugin_enable_statusbar(EogExifDisplayPlugin * plugin,gboolean value)750 eog_exif_display_plugin_enable_statusbar (EogExifDisplayPlugin *plugin,
751 					  gboolean value)
752 {
753 	if (plugin->enable_statusbar == value)
754 		return;
755 
756 	plugin->enable_statusbar = value;
757 
758 	setup_statusbar_exif (plugin);
759 
760 	g_object_notify (G_OBJECT (plugin), "enable-statusbar");
761 }
762 
763 static void
eog_exif_display_plugin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)764 eog_exif_display_plugin_get_property (GObject    *object,
765 				      guint       prop_id,
766 				      GValue     *value,
767 				      GParamSpec *pspec)
768 {
769 	EogExifDisplayPlugin *plugin = EOG_EXIF_DISPLAY_PLUGIN (object);
770 
771 	switch (prop_id)
772 	{
773 	case PROP_DRAW_CHAN_HISTOGRAM:
774 		g_value_set_boolean (value, plugin->draw_chan_histogram);
775 		break;
776 	case PROP_DRAW_RGB_HISTOGRAM:
777 		g_value_set_boolean (value, plugin->draw_rgb_histogram);
778 		break;
779 	case PROP_ENABLE_STATUSBAR:
780 		g_value_set_boolean (value, plugin->enable_statusbar);
781 		break;
782 	case PROP_WINDOW:
783 		g_value_set_object (value, plugin->window);
784 		break;
785 
786 	default:
787 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
788 		break;
789 	}
790 }
791 
792 static void
eog_exif_display_plugin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)793 eog_exif_display_plugin_set_property (GObject      *object,
794 				      guint         prop_id,
795 				      const GValue *value,
796 				      GParamSpec   *pspec)
797 {
798 	EogExifDisplayPlugin *plugin = EOG_EXIF_DISPLAY_PLUGIN (object);
799 
800 	switch (prop_id)
801 	{
802 	case PROP_DRAW_CHAN_HISTOGRAM:
803 		eog_exif_display_plugin_set_draw_chan_histogram (plugin,
804 						g_value_get_boolean (value));
805 		break;
806 	case PROP_DRAW_RGB_HISTOGRAM:
807 		eog_exif_display_plugin_set_draw_rgb_histogram (plugin,
808 						g_value_get_boolean (value));
809 		break;
810 	case PROP_ENABLE_STATUSBAR:
811 		eog_exif_display_plugin_enable_statusbar (plugin,
812 						g_value_get_boolean (value));
813 		break;
814 	case PROP_WINDOW:
815 		plugin->window = EOG_WINDOW (g_value_dup_object (value));
816 		break;
817 
818 	default:
819 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
820 		break;
821 	}
822 }
823 
824 static void
eog_exif_display_plugin_dispose(GObject * object)825 eog_exif_display_plugin_dispose (GObject *object)
826 {
827 	EogExifDisplayPlugin *plugin = EOG_EXIF_DISPLAY_PLUGIN (object);
828 
829 	eog_debug_message (DEBUG_PLUGINS, "EogPostrPlugin disposing");
830 
831 	if (plugin->window != NULL) {
832 		g_object_unref (plugin->window);
833 		plugin->window = NULL;
834 	}
835 
836 	G_OBJECT_CLASS (eog_exif_display_plugin_parent_class)->dispose (object);
837 }
838 static void
eog_exif_display_plugin_class_init(EogExifDisplayPluginClass * klass)839 eog_exif_display_plugin_class_init (EogExifDisplayPluginClass *klass)
840 {
841 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
842 
843 	object_class->dispose = eog_exif_display_plugin_dispose;
844 	object_class->set_property = eog_exif_display_plugin_set_property;
845 	object_class->get_property = eog_exif_display_plugin_get_property;
846 
847 	g_object_class_install_property (object_class, PROP_DRAW_CHAN_HISTOGRAM,
848 		g_param_spec_boolean ("draw-chan-histogram", NULL, NULL, FALSE,
849 				      G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
850 
851 	g_object_class_install_property (object_class, PROP_DRAW_RGB_HISTOGRAM,
852 		g_param_spec_boolean ("draw-rgb-histogram", NULL, NULL, FALSE,
853 				      G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
854 
855 	g_object_class_install_property (object_class, PROP_ENABLE_STATUSBAR,
856 		g_param_spec_boolean ("enable-statusbar", NULL, NULL, FALSE,
857 				      G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
858 
859 	g_object_class_override_property (object_class, PROP_WINDOW, "window");
860 }
861 
862 static void
eog_window_activatable_iface_init(EogWindowActivatableInterface * iface)863 eog_window_activatable_iface_init (EogWindowActivatableInterface *iface)
864 {
865         iface->activate = impl_activate;
866         iface->deactivate = impl_deactivate;
867 }
868 
869 static void
eog_exif_display_plugin_class_finalize(EogExifDisplayPluginClass * klass)870 eog_exif_display_plugin_class_finalize (EogExifDisplayPluginClass *klass)
871 {
872 	/* Dummy needed for G_DEFINE_DYNAMIC_TYPE_EXTENDED */
873 }
874 
875 G_MODULE_EXPORT void
peas_register_types(PeasObjectModule * module)876 peas_register_types (PeasObjectModule *module)
877 {
878         eog_exif_display_plugin_register_type (G_TYPE_MODULE (module));
879         peas_object_module_register_extension_type (module,
880                                                     EOG_TYPE_WINDOW_ACTIVATABLE,
881                                                     EOG_TYPE_EXIF_DISPLAY_PLUGIN);
882 
883 	eog_exif_display_plugin_setup_register_types (module);
884 }
885