1 /* Eye Of Gnome - Image Properties Dialog
2  *
3  * Copyright (C) 2006 The Free Software Foundation
4  *
5  * Author: Lucas Rocha <lucasr@gnome.org>
6  *         Hubert Figuiere <hub@figuiere.net> (XMP support)
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "eog-properties-dialog.h"
28 #include "eog-image.h"
29 #include "eog-util.h"
30 #include "eog-thumb-view.h"
31 
32 #ifdef HAVE_EXIF
33 #include "eog-exif-util.h"
34 #endif
35 
36 #include <glib.h>
37 #include <glib/gi18n.h>
38 #include <glib-object.h>
39 #include <gio/gio.h>
40 #include <gtk/gtk.h>
41 
42 #ifdef HAVE_EXEMPI
43 #include <exempi/xmp.h>
44 #include <exempi/xmpconsts.h>
45 #endif
46 #if defined(HAVE_EXIF) || defined(HAVE_EXEMPI)
47 #define HAVE_METADATA
48 #endif
49 
50 #ifdef HAVE_METADATA
51 #include "eog-metadata-details.h"
52 #endif
53 
54 enum {
55         PROP_0,
56         PROP_THUMBVIEW,
57         PROP_NETBOOK_MODE,
58         PROP_NEXT_ACTION,
59         PROP_PREV_ACTION
60 };
61 
62 struct _EogPropertiesDialogPrivate {
63 	EogThumbView   *thumbview;
64 
65 	gboolean        update_page;
66 	EogPropertiesDialogPage current_page;
67 
68 	GtkWidget      *notebook;
69 	GtkWidget      *next_button;
70 	GtkWidget      *previous_button;
71 
72 	GtkWidget      *general_box;
73 	GtkWidget      *thumbnail_image;
74 	GtkWidget      *name_label;
75 	GtkWidget      *width_label;
76 	GtkWidget      *height_label;
77 	GtkWidget      *type_label;
78 	GtkWidget      *bytes_label;
79 	GtkWidget      *folder_button;
80 	gchar          *folder_button_uri;
81 
82 #ifdef HAVE_EXIF
83 	GtkWidget      *exif_aperture_label;
84 	GtkWidget      *exif_exposure_label;
85 	GtkWidget      *exif_focal_label;
86 	GtkWidget      *exif_flash_label;
87 	GtkWidget      *exif_iso_label;
88 	GtkWidget      *exif_metering_label;
89 	GtkWidget      *exif_model_label;
90 	GtkWidget      *exif_date_label;
91 #endif
92 #ifdef HAVE_EXEMPI
93 	GtkWidget      *xmp_location_label;
94 	GtkWidget      *xmp_description_label;
95 	GtkWidget      *xmp_keywords_label;
96 	GtkWidget      *xmp_creator_label;
97 	GtkWidget      *xmp_rights_label;
98 #else
99 	GtkWidget      *xmp_box;
100 	GtkWidget      *xmp_box_label;
101 #endif
102 #ifdef HAVE_METADATA
103 	GtkWidget      *metadata_box;
104 	GtkWidget      *metadata_details_expander;
105 	GtkWidget      *metadata_details;
106 	GtkWidget      *metadata_details_box;
107 	GtkWidget      *metadata_details_sw;
108 #endif
109 
110 	gboolean        netbook_mode;
111 };
112 
113 G_DEFINE_TYPE_WITH_PRIVATE (EogPropertiesDialog, eog_properties_dialog, GTK_TYPE_DIALOG);
114 
115 static void
parent_file_display_name_query_info_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)116 parent_file_display_name_query_info_cb (GObject *source_object,
117 					GAsyncResult *res,
118 					gpointer user_data)
119 {
120 	EogPropertiesDialog *prop_dlg = EOG_PROPERTIES_DIALOG (user_data);
121 	GFile *parent_file = G_FILE (source_object);
122 	GFileInfo *file_info;
123 	gchar *display_name;
124 
125 
126 	file_info = g_file_query_info_finish (parent_file, res, NULL);
127 	if (file_info == NULL) {
128 		display_name = g_file_get_basename (parent_file);
129 	} else {
130 		display_name = g_strdup (
131 			g_file_info_get_display_name (file_info));
132 		g_object_unref (file_info);
133 	}
134 	gtk_button_set_label (GTK_BUTTON (prop_dlg->priv->folder_button),
135 			      display_name);
136 	gtk_widget_set_sensitive (prop_dlg->priv->folder_button, TRUE);
137 
138 	g_free (display_name);
139 	g_object_unref (prop_dlg);
140 }
141 
142 static void
pd_update_general_tab(EogPropertiesDialog * prop_dlg,EogImage * image)143 pd_update_general_tab (EogPropertiesDialog *prop_dlg,
144 		       EogImage            *image)
145 {
146 	gchar *bytes_str;
147 	gchar *width_str, *height_str;
148 	GFile *file, *parent_file;
149 	GFileInfo *file_info;
150 	const char *mime_str;
151 	char *type_str;
152 	gint width, height;
153 	goffset bytes;
154 
155 	g_object_set (G_OBJECT (prop_dlg->priv->thumbnail_image),
156 		      "pixbuf", eog_image_get_thumbnail (image),
157 		      NULL);
158 
159 	gtk_label_set_text (GTK_LABEL (prop_dlg->priv->name_label),
160 			    eog_image_get_caption (image));
161 
162 	eog_image_get_size (image, &width, &height);
163 
164 	width_str = g_strdup_printf ("%d %s", width,
165 				     ngettext ("pixel", "pixels", width));
166 	height_str = g_strdup_printf ("%d %s", height,
167 				      ngettext ("pixel", "pixels", height));
168 
169 	gtk_label_set_text (GTK_LABEL (prop_dlg->priv->width_label), width_str);
170 
171 	gtk_label_set_text (GTK_LABEL (prop_dlg->priv->height_label),
172 			    height_str);
173 
174 	g_free (height_str);
175 	g_free (width_str);
176 
177 	file = eog_image_get_file (image);
178 	file_info = g_file_query_info (file,
179 				       G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
180 				       0, NULL, NULL);
181 	if (file_info == NULL) {
182 		type_str = g_strdup (_("Unknown"));
183 	} else {
184 		mime_str = g_file_info_get_content_type (file_info);
185 		type_str = g_content_type_get_description (mime_str);
186 		g_object_unref (file_info);
187 	}
188 
189 	gtk_label_set_text (GTK_LABEL (prop_dlg->priv->type_label), type_str);
190 
191 	bytes = eog_image_get_bytes (image);
192 	bytes_str = g_format_size (bytes);
193 
194 	gtk_label_set_text (GTK_LABEL (prop_dlg->priv->bytes_label), bytes_str);
195 
196 	parent_file = g_file_get_parent (file);
197 	if (parent_file == NULL) {
198 		/* file is root directory itself */
199 		parent_file = g_object_ref (file);
200 	}
201 
202 	gtk_widget_set_sensitive (prop_dlg->priv->folder_button, FALSE);
203 	gtk_button_set_label (GTK_BUTTON (prop_dlg->priv->folder_button), NULL);
204 	g_free (prop_dlg->priv->folder_button_uri);
205 	prop_dlg->priv->folder_button_uri = g_file_get_uri (parent_file);
206 
207 	g_file_query_info_async (parent_file,
208 				 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
209 				 G_FILE_QUERY_INFO_NONE,
210 				 G_PRIORITY_DEFAULT,
211 				 NULL,
212 				 parent_file_display_name_query_info_cb,
213 				 g_object_ref (prop_dlg));
214 
215 	g_object_unref (parent_file);
216 	g_free (type_str);
217 	g_free (bytes_str);
218 }
219 
220 #ifdef HAVE_EXEMPI
221 static void
eog_xmp_set_label(XmpPtr xmp,const char * ns,const char * propname,GtkWidget * w)222 eog_xmp_set_label (XmpPtr xmp,
223 		   const char *ns,
224 		   const char *propname,
225 		   GtkWidget *w)
226 {
227 	uint32_t options;
228 
229 	XmpStringPtr value = xmp_string_new ();
230 
231 	if (xmp_get_property (xmp, ns, propname, value, &options)) {
232 		if (XMP_IS_PROP_SIMPLE (options)) {
233 			gtk_label_set_text (GTK_LABEL (w), xmp_string_cstr (value));
234 		} else if (XMP_IS_PROP_ARRAY (options)) {
235 			XmpIteratorPtr iter = xmp_iterator_new (xmp,
236 							        ns,
237 								propname,
238 								XMP_ITER_JUSTLEAFNODES);
239 
240 			GString *string = g_string_new ("");
241 
242 			if (iter) {
243 				gboolean first = TRUE;
244 
245 				while (xmp_iterator_next (iter, NULL, NULL, value, &options)
246 				       && !XMP_IS_PROP_QUALIFIER (options)) {
247 
248 					if (!first) {
249 						g_string_append_printf(string, ", ");
250 					} else {
251 						first = FALSE;
252 					}
253 
254 					g_string_append_printf (string,
255 								"%s",
256 								xmp_string_cstr (value));
257 				}
258 
259 				xmp_iterator_free (iter);
260 			}
261 
262 			gtk_label_set_text (GTK_LABEL (w), string->str);
263 			g_string_free (string, TRUE);
264 		}
265 	} else {
266 		/* Property was not found */
267 		/* Clear label so it won't show bogus data */
268 		gtk_label_set_text (GTK_LABEL (w), NULL);
269 	}
270 
271 	xmp_string_free (value);
272 }
273 #endif
274 
275 #ifdef HAVE_METADATA
276 static void
pd_update_metadata_tab(EogPropertiesDialog * prop_dlg,EogImage * image)277 pd_update_metadata_tab (EogPropertiesDialog *prop_dlg,
278 			EogImage            *image)
279 {
280 	EogPropertiesDialogPrivate *priv;
281 	GtkNotebook *notebook;
282 #ifdef HAVE_EXIF
283 	ExifData    *exif_data;
284 #endif
285 #ifdef HAVE_EXEMPI
286 	XmpPtr      xmp_data;
287 #endif
288 
289 	g_return_if_fail (EOG_IS_PROPERTIES_DIALOG (prop_dlg));
290 
291 	priv = prop_dlg->priv;
292 
293 	notebook = GTK_NOTEBOOK (priv->notebook);
294 
295 	if (TRUE
296 #ifdef HAVE_EXIF
297 	    && !eog_image_has_data (image, EOG_IMAGE_DATA_EXIF)
298 #endif
299 #ifdef HAVE_EXEMPI
300 	    && !eog_image_has_data (image, EOG_IMAGE_DATA_XMP)
301 #endif
302 	    ) {
303 		if (gtk_notebook_get_current_page (notebook) ==	EOG_PROPERTIES_DIALOG_PAGE_EXIF) {
304 			gtk_notebook_prev_page (notebook);
305 		} else if (gtk_notebook_get_current_page (notebook) == EOG_PROPERTIES_DIALOG_PAGE_DETAILS) {
306 			gtk_notebook_set_current_page (notebook, EOG_PROPERTIES_DIALOG_PAGE_GENERAL);
307 		}
308 
309 		if (gtk_widget_get_visible (priv->metadata_box)) {
310 			gtk_widget_hide (priv->metadata_box);
311 		}
312 		if (gtk_widget_get_visible (priv->metadata_details_box)) {
313 			gtk_widget_hide (priv->metadata_details_box);
314 		}
315 
316 		return;
317 	} else {
318 		if (!gtk_widget_get_visible (priv->metadata_box))
319 			gtk_widget_show_all (priv->metadata_box);
320 		if (priv->netbook_mode &&
321 		    !gtk_widget_get_visible (priv->metadata_details_box)) {
322 			gtk_widget_show_all (priv->metadata_details_box);
323 			gtk_widget_hide (priv->metadata_details_expander);
324 		}
325 	}
326 
327 #ifdef HAVE_EXIF
328 	exif_data = (ExifData *) eog_image_get_exif_info (image);
329 
330 	eog_exif_util_set_label_text (GTK_LABEL (priv->exif_aperture_label),
331 				      exif_data, EXIF_TAG_FNUMBER);
332 
333 	eog_exif_util_set_label_text (GTK_LABEL (priv->exif_exposure_label),
334 				      exif_data, EXIF_TAG_EXPOSURE_TIME);
335 
336 	eog_exif_util_set_focal_length_label_text (GTK_LABEL (priv->exif_focal_label), exif_data);
337 
338 	eog_exif_util_set_label_text (GTK_LABEL (priv->exif_flash_label),
339 				      exif_data, EXIF_TAG_FLASH);
340 
341 	eog_exif_util_set_label_text (GTK_LABEL (priv->exif_iso_label),
342 				      exif_data, EXIF_TAG_ISO_SPEED_RATINGS);
343 
344 
345 	eog_exif_util_set_label_text (GTK_LABEL (priv->exif_metering_label),
346 				      exif_data, EXIF_TAG_METERING_MODE);
347 
348 	eog_exif_util_set_label_text (GTK_LABEL (priv->exif_model_label),
349 				      exif_data, EXIF_TAG_MODEL);
350 
351 	eog_exif_util_set_label_text (GTK_LABEL (priv->exif_date_label),
352 				      exif_data, EXIF_TAG_DATE_TIME_ORIGINAL);
353 
354 	eog_metadata_details_update (EOG_METADATA_DETAILS (priv->metadata_details),
355 				 exif_data);
356 
357 	/* exif_data_unref can handle NULL-values */
358 	exif_data_unref(exif_data);
359 #endif
360 
361 #ifdef HAVE_EXEMPI
362 	xmp_data = (XmpPtr) eog_image_get_xmp_info (image);
363 
364  	if (xmp_data != NULL) {
365 		eog_xmp_set_label (xmp_data,
366 				   NS_IPTC4XMP,
367 				   "Location",
368 				   priv->xmp_location_label);
369 
370 		eog_xmp_set_label (xmp_data,
371 				   NS_DC,
372 				   "description",
373 				   priv->xmp_description_label);
374 
375 		eog_xmp_set_label (xmp_data,
376 				   NS_DC,
377 				   "subject",
378 				   priv->xmp_keywords_label);
379 
380 		eog_xmp_set_label (xmp_data,
381 				   NS_DC,
382         	                   "creator",
383 				   priv->xmp_creator_label);
384 
385 		eog_xmp_set_label (xmp_data,
386 				   NS_DC,
387 				   "rights",
388 				   priv->xmp_rights_label);
389 
390 		eog_metadata_details_xmp_update (EOG_METADATA_DETAILS (priv->metadata_details), xmp_data);
391 
392 		xmp_free (xmp_data);
393 	} else {
394 		/* Image has no XMP data */
395 
396 		/* Clear the labels so they won't display foreign data.*/
397 
398 		gtk_label_set_text (GTK_LABEL (priv->xmp_location_label), NULL);
399 		gtk_label_set_text (GTK_LABEL (priv->xmp_description_label),
400 				    NULL);
401 		gtk_label_set_text (GTK_LABEL (priv->xmp_keywords_label), NULL);
402 		gtk_label_set_text (GTK_LABEL (priv->xmp_creator_label), NULL);
403 		gtk_label_set_text (GTK_LABEL (priv->xmp_rights_label), NULL);
404 	}
405 #endif
406 }
407 
408 static gboolean
pd_resize_dialog(gpointer user_data)409 pd_resize_dialog (gpointer user_data)
410 {
411 	gint width, height;
412 
413 	gtk_window_get_size (GTK_WINDOW (user_data),
414 			     &width,
415 			     &height);
416 
417 	gtk_window_resize (GTK_WINDOW (user_data), width, 1);
418 
419 	return FALSE;
420 }
421 
422 static void
pd_exif_details_activated_cb(GtkExpander * expander,GParamSpec * param_spec,GtkWidget * dialog)423 pd_exif_details_activated_cb (GtkExpander *expander,
424 			      GParamSpec *param_spec,
425 			      GtkWidget *dialog)
426 {
427 	gboolean expanded;
428 
429 	expanded = gtk_expander_get_expanded (expander);
430 
431 	/*FIXME: this is depending on the expander animation
432          * duration. Need to find a safer way for doing that. */
433 	if (!expanded)
434 		g_timeout_add (150, pd_resize_dialog, dialog);
435 }
436 #endif
437 
438 static void
pd_folder_button_clicked_cb(GtkButton * button,gpointer data)439 pd_folder_button_clicked_cb (GtkButton *button, gpointer data)
440 {
441 	EogPropertiesDialogPrivate *priv = EOG_PROPERTIES_DIALOG (data)->priv;
442 	GtkWindow *window;
443 	guint32 timestamp;
444 
445 	if (!priv->folder_button_uri)
446 		return;
447 
448 	timestamp = gtk_get_current_event_time ();
449 
450 	window = GTK_WINDOW (data);
451 	gtk_show_uri_on_window (window, priv->folder_button_uri, timestamp, NULL);
452 }
453 
454 static gboolean
eog_properties_dialog_page_switch(GtkNotebook * notebook,GtkWidget * page,gint page_index,EogPropertiesDialog * prop_dlg)455 eog_properties_dialog_page_switch (GtkNotebook     *notebook,
456 				   GtkWidget       *page,
457 				   gint             page_index,
458 				   EogPropertiesDialog *prop_dlg)
459 {
460 	if (prop_dlg->priv->update_page)
461 		prop_dlg->priv->current_page = page_index;
462 
463 	return TRUE;
464 }
465 
466 void
eog_properties_dialog_set_netbook_mode(EogPropertiesDialog * dlg,gboolean enable)467 eog_properties_dialog_set_netbook_mode (EogPropertiesDialog *dlg,
468 					gboolean enable)
469 {
470 	EogPropertiesDialogPrivate *priv;
471 
472 	g_return_if_fail (EOG_IS_PROPERTIES_DIALOG (dlg));
473 
474 	priv = dlg->priv;
475 
476 	if (priv->netbook_mode == enable)
477 		return;
478 
479 	priv->netbook_mode = enable;
480 
481 #ifdef HAVE_METADATA
482 	if (enable) {
483 		g_object_ref (priv->metadata_details_sw);
484 		gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->metadata_details_sw)),
485 				      priv->metadata_details_sw);
486 		gtk_container_add (GTK_CONTAINER (priv->metadata_details_box), priv->metadata_details_sw);
487 		g_object_unref (priv->metadata_details_sw);
488 		// Only show details box if metadata is being displayed
489 		if (gtk_widget_get_visible (priv->metadata_box))
490 			gtk_widget_show_all (priv->metadata_details_box);
491 
492 		gtk_widget_hide (priv->metadata_details_expander);
493 	} else {
494 		g_object_ref (priv->metadata_details_sw);
495 		gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->metadata_details_sw)),
496 				      priv->metadata_details_sw);
497 		gtk_container_add (GTK_CONTAINER (priv->metadata_details_expander), priv->metadata_details_sw);
498 		g_object_unref (priv->metadata_details_sw);
499 		gtk_widget_show_all (priv->metadata_details_expander);
500 
501 		if (gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)) == EOG_PROPERTIES_DIALOG_PAGE_DETAILS)
502 			gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
503 		gtk_widget_hide (priv->metadata_details_box);
504 	}
505 #endif
506 }
507 
508 static void
eog_properties_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)509 eog_properties_dialog_set_property (GObject      *object,
510 				    guint         prop_id,
511 				    const GValue *value,
512 				    GParamSpec   *pspec)
513 {
514 	EogPropertiesDialog *prop_dlg = EOG_PROPERTIES_DIALOG (object);
515 
516 	switch (prop_id) {
517 		case PROP_THUMBVIEW:
518 			prop_dlg->priv->thumbview = g_value_get_object (value);
519 			break;
520 		case PROP_NETBOOK_MODE:
521 			eog_properties_dialog_set_netbook_mode (prop_dlg,
522 						   g_value_get_boolean (value));
523 			break;
524 		case PROP_NEXT_ACTION:
525 			gtk_actionable_set_action_name (GTK_ACTIONABLE (prop_dlg->priv->next_button),
526 							g_value_get_string (value));
527 			gtk_button_set_always_show_image(GTK_BUTTON(prop_dlg->priv->next_button), TRUE);
528 			break;
529 		case PROP_PREV_ACTION:
530 			gtk_actionable_set_action_name (GTK_ACTIONABLE (prop_dlg->priv->previous_button),
531 							g_value_get_string (value));
532 			gtk_button_set_always_show_image(GTK_BUTTON(prop_dlg->priv->previous_button), TRUE);
533 			break;
534 		default:
535 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id,
536 							   pspec);
537 			break;
538 	}
539 }
540 
541 static void
eog_properties_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)542 eog_properties_dialog_get_property (GObject    *object,
543 				    guint       prop_id,
544 				    GValue     *value,
545 				    GParamSpec *pspec)
546 {
547 	EogPropertiesDialog *prop_dlg = EOG_PROPERTIES_DIALOG (object);
548 
549 	switch (prop_id) {
550 		case PROP_THUMBVIEW:
551 			g_value_set_object (value, prop_dlg->priv->thumbview);
552 			break;
553 		case PROP_NETBOOK_MODE:
554 			g_value_set_boolean (value,
555 					     prop_dlg->priv->netbook_mode);
556 			break;
557 		case PROP_NEXT_ACTION:
558 		{
559 			const gchar* action = gtk_actionable_get_action_name (
560 						      GTK_ACTIONABLE (prop_dlg->priv->next_button));
561 			g_value_set_string (value, action);
562 			break;
563 		}
564 		case PROP_PREV_ACTION:
565 		{
566 			const gchar* action = gtk_actionable_get_action_name (
567 						      GTK_ACTIONABLE (prop_dlg->priv->previous_button));
568 			g_value_set_string (value, action);
569 			break;
570 		}
571 		default:
572 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id,
573 							   pspec);
574 			break;
575 	}
576 }
577 
578 static void
eog_properties_dialog_dispose(GObject * object)579 eog_properties_dialog_dispose (GObject *object)
580 {
581 	EogPropertiesDialog *prop_dlg;
582 	EogPropertiesDialogPrivate *priv;
583 
584 	g_return_if_fail (object != NULL);
585 	g_return_if_fail (EOG_IS_PROPERTIES_DIALOG (object));
586 
587 	prop_dlg = EOG_PROPERTIES_DIALOG (object);
588 	priv = prop_dlg->priv;
589 
590 	if (priv->thumbview) {
591 		g_object_unref (priv->thumbview);
592 		priv->thumbview = NULL;
593 	}
594 
595 	g_free (priv->folder_button_uri);
596 	priv->folder_button_uri = NULL;
597 
598 	G_OBJECT_CLASS (eog_properties_dialog_parent_class)->dispose (object);
599 }
600 
601 static void
eog_properties_dialog_class_init(EogPropertiesDialogClass * klass)602 eog_properties_dialog_class_init (EogPropertiesDialogClass *klass)
603 {
604 	GObjectClass *g_object_class = (GObjectClass *) klass;
605 
606 	g_object_class->dispose = eog_properties_dialog_dispose;
607 	g_object_class->set_property = eog_properties_dialog_set_property;
608 	g_object_class->get_property = eog_properties_dialog_get_property;
609 
610 	g_object_class_install_property (g_object_class,
611 					 PROP_THUMBVIEW,
612 					 g_param_spec_object ("thumbview",
613 							      "Thumbview",
614 							      "Thumbview",
615 							      EOG_TYPE_THUMB_VIEW,
616 							      G_PARAM_READWRITE |
617 							      G_PARAM_CONSTRUCT_ONLY |
618 							      G_PARAM_STATIC_STRINGS));
619 	g_object_class_install_property (g_object_class, PROP_NETBOOK_MODE,
620 					 g_param_spec_boolean ("netbook-mode",
621 					 		      "Netbook Mode",
622 							      "Netbook Mode",
623 							      FALSE,
624 							      G_PARAM_READWRITE |
625 							      G_PARAM_STATIC_STRINGS));
626 	g_object_class_install_property (g_object_class,
627 					 PROP_NEXT_ACTION,
628 					 g_param_spec_string ("next-action",
629 							      "Next Action",
630 							      "Action for Next button",
631 							      NULL,
632 							      G_PARAM_READWRITE |
633 							      G_PARAM_CONSTRUCT_ONLY |
634 							      G_PARAM_STATIC_STRINGS));
635 	g_object_class_install_property (g_object_class,
636 					 PROP_PREV_ACTION,
637 					 g_param_spec_string ("prev-action",
638 							      "Prev Action",
639 							      "Action for Prev button",
640 							      NULL,
641 							      G_PARAM_READWRITE |
642 							      G_PARAM_CONSTRUCT_ONLY |
643 							      G_PARAM_STATIC_STRINGS));
644 
645 	gtk_widget_class_set_template_from_resource ((GtkWidgetClass *) klass, "/org/gnome/eog/ui/eog-image-properties-dialog.ui");
646 
647 	GtkWidgetClass *wklass = (GtkWidgetClass*) klass;
648 	gtk_widget_class_bind_template_child_private(wklass,
649 						     EogPropertiesDialog,
650 						     notebook);
651 	gtk_widget_class_bind_template_child_private(wklass,
652 						     EogPropertiesDialog,
653 						     previous_button);
654 	gtk_widget_class_bind_template_child_private(wklass,
655 						     EogPropertiesDialog,
656 						     next_button);
657 	gtk_widget_class_bind_template_child_private(wklass,
658 						     EogPropertiesDialog,
659 						     thumbnail_image);
660 	gtk_widget_class_bind_template_child_private(wklass,
661 						     EogPropertiesDialog,
662 						     general_box);
663 	gtk_widget_class_bind_template_child_private(wklass,
664 						     EogPropertiesDialog,
665 						     name_label);
666 	gtk_widget_class_bind_template_child_private(wklass,
667 						     EogPropertiesDialog,
668 						     width_label);
669 	gtk_widget_class_bind_template_child_private(wklass,
670 						     EogPropertiesDialog,
671 						     height_label);
672 	gtk_widget_class_bind_template_child_private(wklass,
673 						     EogPropertiesDialog,
674 						     type_label);
675 	gtk_widget_class_bind_template_child_private(wklass,
676 						     EogPropertiesDialog,
677 						     bytes_label);
678 	gtk_widget_class_bind_template_child_private(wklass,
679 						     EogPropertiesDialog,
680 						     folder_button);
681 
682 #ifdef HAVE_EXIF
683 	gtk_widget_class_bind_template_child_private(wklass,
684 						     EogPropertiesDialog,
685 						     exif_aperture_label);
686 	gtk_widget_class_bind_template_child_private(wklass,
687 						     EogPropertiesDialog,
688 						     exif_exposure_label);
689 	gtk_widget_class_bind_template_child_private(wklass,
690 						     EogPropertiesDialog,
691 						     exif_focal_label);
692 	gtk_widget_class_bind_template_child_private(wklass,
693 						     EogPropertiesDialog,
694 						     exif_flash_label);
695 	gtk_widget_class_bind_template_child_private(wklass,
696 						     EogPropertiesDialog,
697 						     exif_iso_label);
698 	gtk_widget_class_bind_template_child_private(wklass,
699 						     EogPropertiesDialog,
700 						     exif_metering_label);
701 	gtk_widget_class_bind_template_child_private(wklass,
702 						     EogPropertiesDialog,
703 						     exif_model_label);
704 	gtk_widget_class_bind_template_child_private(wklass,
705 						     EogPropertiesDialog,
706 						     exif_date_label);
707 #endif
708 #ifdef HAVE_EXEMPI
709 	gtk_widget_class_bind_template_child_private(wklass,
710 						     EogPropertiesDialog,
711 						     xmp_location_label);
712 	gtk_widget_class_bind_template_child_private(wklass,
713 						     EogPropertiesDialog,
714 						     xmp_description_label);
715 	gtk_widget_class_bind_template_child_private(wklass,
716 						     EogPropertiesDialog,
717 						     xmp_keywords_label);
718 	gtk_widget_class_bind_template_child_private(wklass,
719 						     EogPropertiesDialog,
720 						     xmp_creator_label);
721 	gtk_widget_class_bind_template_child_private(wklass,
722 						     EogPropertiesDialog,
723 						     xmp_rights_label);
724 #else
725 	gtk_widget_class_bind_template_child_private(wklass,
726 						     EogPropertiesDialog,
727 						     xmp_box);
728 	gtk_widget_class_bind_template_child_private(wklass,
729 						     EogPropertiesDialog,
730 						     xmp_box_label);
731 #endif
732 #ifdef HAVE_METADATA
733 	gtk_widget_class_bind_template_child_private(wklass,
734 						     EogPropertiesDialog,
735 						     metadata_box);
736 	gtk_widget_class_bind_template_child_private(wklass,
737 						     EogPropertiesDialog,
738 						     metadata_details_expander);
739 	gtk_widget_class_bind_template_child_private(wklass,
740 						     EogPropertiesDialog,
741 						     metadata_details_box);
742 
743 	gtk_widget_class_bind_template_callback(wklass,
744 						pd_exif_details_activated_cb);
745 #endif
746 	gtk_widget_class_bind_template_callback(wklass,
747 						eog_properties_dialog_page_switch);
748 	gtk_widget_class_bind_template_callback(wklass,
749 						pd_folder_button_clicked_cb);
750 
751 }
752 
753 static void
eog_properties_dialog_init(EogPropertiesDialog * prop_dlg)754 eog_properties_dialog_init (EogPropertiesDialog *prop_dlg)
755 {
756 	EogPropertiesDialogPrivate *priv;
757 #ifdef HAVE_METADATA
758 	GtkWidget *sw;
759 #endif
760 
761 	prop_dlg->priv = eog_properties_dialog_get_instance_private (prop_dlg);
762 
763 	priv = prop_dlg->priv;
764 
765 	priv->update_page = FALSE;
766 
767 	gtk_widget_init_template (GTK_WIDGET (prop_dlg));
768 
769 
770 	g_signal_connect (prop_dlg,
771 			  "delete-event",
772 			  G_CALLBACK (gtk_widget_hide_on_delete),
773 			  prop_dlg);
774 
775 	gtk_widget_set_tooltip_text (GTK_WIDGET (priv->folder_button),
776 				     _("Show the folder which contains this "
777 				       "file in the file manager"));
778 	priv->folder_button_uri = NULL;
779 
780 	gtk_widget_set_size_request (priv->thumbnail_image, 100, 100);
781 
782 #ifdef HAVE_METADATA
783  	sw = gtk_scrolled_window_new (NULL, NULL);
784 
785 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
786 					     GTK_SHADOW_IN);
787 
788 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
789 					GTK_POLICY_AUTOMATIC,
790 					GTK_POLICY_AUTOMATIC);
791 
792 	priv->metadata_details = eog_metadata_details_new ();
793 	gtk_widget_set_size_request (priv->metadata_details, -1, 170);
794 	gtk_widget_set_vexpand (priv->metadata_details, TRUE);
795 	gtk_container_set_border_width (GTK_CONTAINER (sw), 6);
796 
797 	gtk_container_add (GTK_CONTAINER (sw), priv->metadata_details);
798 	gtk_widget_show_all (sw);
799 
800 	priv->metadata_details_sw = sw;
801 
802 	if (priv->netbook_mode) {
803 		gtk_widget_hide (priv->metadata_details_expander);
804 		gtk_box_pack_start (GTK_BOX (priv->metadata_details_box),
805 				    sw, TRUE, TRUE, 6);
806 	} else {
807 		gtk_container_add (GTK_CONTAINER (priv->metadata_details_expander),
808 				   sw);
809 	}
810 
811 #ifndef HAVE_EXEMPI
812 	gtk_widget_hide (priv->xmp_box);
813 	gtk_widget_hide (priv->xmp_box_label);
814 #endif
815 
816 #else
817 	/* Remove pages from back to front. Otherwise the page index
818 	 * needs to be adjusted when deleting the next page. */
819 	gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook),
820 				  EOG_PROPERTIES_DIALOG_PAGE_DETAILS);
821 	gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook),
822 				  EOG_PROPERTIES_DIALOG_PAGE_EXIF);
823 #endif
824 }
825 
826 /**
827  * eog_properties_dialog_new:
828  * @parent: the dialog's parent window
829  * @thumbview:
830  * @next_image_action:
831  * @previous_image_action:
832  *
833  * If %parent implements #GActionMap its actions will be automatically
834  * inserted in the "win" namespace.
835  *
836  * Returns: (transfer full) (type EogPropertiesDialog): a new #EogPropertiesDialog
837  **/
838 GtkWidget *
eog_properties_dialog_new(GtkWindow * parent,EogThumbView * thumbview,const gchar * next_image_action,const gchar * previous_image_action)839 eog_properties_dialog_new (GtkWindow    *parent,
840 			   EogThumbView *thumbview,
841 			   const gchar  *next_image_action,
842 			   const gchar  *previous_image_action)
843 {
844 	GObject *prop_dlg;
845 
846 	g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
847 	g_return_val_if_fail (EOG_IS_THUMB_VIEW (thumbview), NULL);
848 
849 	prop_dlg = g_object_new (EOG_TYPE_PROPERTIES_DIALOG,
850 			     	 "thumbview", thumbview,
851 				 "next-action", next_image_action,
852 				 "prev-action", previous_image_action,
853 				 "use-header-bar", TRUE,
854 			     	 NULL);
855 
856 	gtk_window_set_transient_for (GTK_WINDOW (prop_dlg), parent);
857 
858 	if (G_LIKELY (G_IS_ACTION_GROUP (parent))) {
859 		gtk_widget_insert_action_group (GTK_WIDGET (prop_dlg),
860 						"win",
861 						G_ACTION_GROUP (parent));
862 	}
863 
864 	return GTK_WIDGET (prop_dlg);
865 }
866 
867 void
eog_properties_dialog_update(EogPropertiesDialog * prop_dlg,EogImage * image)868 eog_properties_dialog_update (EogPropertiesDialog *prop_dlg,
869 			      EogImage            *image)
870 {
871 	g_return_if_fail (EOG_IS_PROPERTIES_DIALOG (prop_dlg));
872 
873 	prop_dlg->priv->update_page = FALSE;
874 
875 	pd_update_general_tab (prop_dlg, image);
876 
877 #ifdef HAVE_METADATA
878 	pd_update_metadata_tab (prop_dlg, image);
879 #endif
880 	gtk_notebook_set_current_page (GTK_NOTEBOOK (prop_dlg->priv->notebook),
881 				       prop_dlg->priv->current_page);
882 
883 	prop_dlg->priv->update_page = TRUE;
884 }
885 
886 void
eog_properties_dialog_set_page(EogPropertiesDialog * prop_dlg,EogPropertiesDialogPage page)887 eog_properties_dialog_set_page (EogPropertiesDialog *prop_dlg,
888 			        EogPropertiesDialogPage page)
889 {
890 	g_return_if_fail (EOG_IS_PROPERTIES_DIALOG (prop_dlg));
891 
892 	prop_dlg->priv->current_page = page;
893 
894 	gtk_notebook_set_current_page (GTK_NOTEBOOK (prop_dlg->priv->notebook),
895 				       page);
896 }
897