1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 
3 /*
4  * Copyright (C) 2004 Red Hat, Inc
5  * Copyright (c) 2007 Novell, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (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 GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
20  * Boston, MA 02110-1335, USA.
21  *
22  * Author: Alexander Larsson <alexl@redhat.com>
23  * XMP support by Hubert Figuiere <hfiguiere@novell.com>
24  */
25 
26 #include <config.h>
27 #include "nemo-image-properties-page.h"
28 
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31 #include <gio/gio.h>
32 #include <eel/eel-vfs-extensions.h>
33 #include <libnemo-extension/nemo-property-page-provider.h>
34 #include <libnemo-private/nemo-module.h>
35 #include <string.h>
36 
37 #ifdef HAVE_EXIF
38   #include <libexif/exif-data.h>
39   #include <libexif/exif-ifd.h>
40   #include <libexif/exif-loader.h>
41 #endif
42 #ifdef HAVE_EXEMPI
43   #include <exempi/xmp.h>
44   #include <exempi/xmpconsts.h>
45 #endif
46 
47 #define LOAD_BUFFER_SIZE 8192
48 
49 struct NemoImagePropertiesPageDetails {
50 	GCancellable *cancellable;
51 	GtkWidget *grid;
52 	GdkPixbufLoader *loader;
53 	gboolean got_size;
54 	gboolean pixbuf_still_loading;
55 	char buffer[LOAD_BUFFER_SIZE];
56 	int width;
57 	int height;
58 #ifdef HAVE_EXIF
59 	ExifLoader *exifldr;
60 #endif /*HAVE_EXIF*/
61 #ifdef HAVE_EXEMPI
62 	XmpPtr     xmp;
63 #endif
64 };
65 
66 #ifdef HAVE_EXIF
67 struct ExifAttribute {
68 	ExifTag tag;
69 	char *value;
70 	gboolean found;
71 };
72 #endif /*HAVE_EXIF*/
73 
74 enum {
75 	PROP_URI
76 };
77 
78 typedef struct {
79         GObject parent;
80 } NemoImagePropertiesPageProvider;
81 
82 typedef struct {
83         GObjectClass parent;
84 } NemoImagePropertiesPageProviderClass;
85 
86 
87 static GType nemo_image_properties_page_provider_get_type (void);
88 static void  property_page_provider_iface_init                (NemoPropertyPageProviderIface *iface);
89 
90 
91 G_DEFINE_TYPE (NemoImagePropertiesPage, nemo_image_properties_page, GTK_TYPE_BOX);
92 
93 G_DEFINE_TYPE_WITH_CODE (NemoImagePropertiesPageProvider, nemo_image_properties_page_provider, G_TYPE_OBJECT,
94 			 G_IMPLEMENT_INTERFACE (NEMO_TYPE_PROPERTY_PAGE_PROVIDER,
95 						property_page_provider_iface_init));
96 
97 static void
nemo_image_properties_page_finalize(GObject * object)98 nemo_image_properties_page_finalize (GObject *object)
99 {
100 	NemoImagePropertiesPage *page;
101 
102 	page = NEMO_IMAGE_PROPERTIES_PAGE (object);
103 
104 	if (page->details->cancellable) {
105 		g_cancellable_cancel (page->details->cancellable);
106 		g_object_unref (page->details->cancellable);
107 		page->details->cancellable = NULL;
108 	}
109 
110 	G_OBJECT_CLASS (nemo_image_properties_page_parent_class)->finalize (object);
111 }
112 
113 static void
file_close_callback(GObject * object,GAsyncResult * res,gpointer data)114 file_close_callback (GObject      *object,
115 		     GAsyncResult *res,
116 		     gpointer      data)
117 {
118 	NemoImagePropertiesPage *page;
119 	GInputStream *stream;
120 
121 	page = NEMO_IMAGE_PROPERTIES_PAGE (data);
122 	stream = G_INPUT_STREAM (object);
123 
124 	g_input_stream_close_finish (stream, res, NULL);
125 
126 	g_object_unref (page->details->cancellable);
127 	page->details->cancellable = NULL;
128 }
129 
130 static void
append_item(NemoImagePropertiesPage * page,const char * name,const char * value)131 append_item (NemoImagePropertiesPage *page,
132 	     const char                  *name,
133 	     const char                  *value)
134 {
135 	GtkWidget *name_label;
136 	GtkWidget *label;
137 	PangoAttrList *attrs;
138 
139 	name_label = gtk_label_new (name);
140 	attrs = pango_attr_list_new ();
141 	pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
142 	gtk_label_set_attributes (GTK_LABEL (name_label), attrs);
143 	pango_attr_list_unref (attrs);
144 	gtk_misc_set_alignment (GTK_MISC (name_label), 0, 0);
145 	gtk_container_add (GTK_CONTAINER (page->details->grid), name_label);
146 	gtk_widget_show (name_label);
147 
148 	if (value != NULL) {
149 		label = gtk_label_new (value);
150 		gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
151 		gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
152 		gtk_grid_attach_next_to (GTK_GRID (page->details->grid), label,
153 					 name_label, GTK_POS_RIGHT,
154 					 1, 1);
155 		gtk_widget_show (label);
156 	}
157 }
158 
159 #ifdef HAVE_EXIF
160 static char *
exif_string_to_utf8(const char * exif_str)161 exif_string_to_utf8 (const char *exif_str)
162 {
163 	char *utf8_str;
164 
165 	if (g_utf8_validate (exif_str, -1, NULL)) {
166 		return g_strdup (exif_str);
167 	}
168 
169 	utf8_str = g_locale_to_utf8 (exif_str, -1, NULL, NULL, NULL);
170 	if (utf8_str != NULL) {
171 		return utf8_str;
172 	}
173 
174 	return eel_make_valid_utf8 (exif_str);
175 }
176 
177 static void
exif_content_callback(ExifContent * content,gpointer data)178 exif_content_callback (ExifContent *content, gpointer data)
179 {
180 	struct ExifAttribute *attribute;
181 	char b[1024];
182 
183 	attribute = (struct ExifAttribute *)data;
184 	if (attribute->found) {
185 		return;
186 	}
187 
188         attribute->value = g_strdup (exif_content_get_value (content, attribute->tag, b, sizeof(b)));
189 
190 	if (attribute->value != NULL) {
191 		attribute->found = TRUE;
192 	}
193 }
194 
195 static char *
exifdata_get_tag_name_utf8(ExifTag tag)196 exifdata_get_tag_name_utf8 (ExifTag tag)
197 {
198 	return exif_string_to_utf8 (exif_tag_get_name (tag));
199 }
200 
201 static char *
exifdata_get_tag_value_utf8(ExifData * data,ExifTag tag)202 exifdata_get_tag_value_utf8 (ExifData *data, ExifTag tag)
203 {
204 	struct ExifAttribute attribute;
205 	char *utf8_value;
206 
207 	attribute.tag = tag;
208 	attribute.value = NULL;
209 	attribute.found = FALSE;
210 
211 	exif_data_foreach_content (data, exif_content_callback, &attribute);
212 
213 	if (attribute.found) {
214 		utf8_value = exif_string_to_utf8 (attribute.value);
215 		g_free (attribute.value);
216 	} else {
217 		utf8_value = NULL;
218 	}
219 
220 	return utf8_value;
221 }
222 
223 static gboolean
append_tag_value_pair(NemoImagePropertiesPage * page,ExifData * data,ExifTag tag,char * description)224 append_tag_value_pair (NemoImagePropertiesPage *page,
225 		       ExifData *data,
226 		       ExifTag   tag,
227 		       char     *description)
228 {
229         char *utf_attribute;
230         char *utf_value;
231 
232 	utf_attribute = exifdata_get_tag_name_utf8 (tag);
233 	utf_value = exifdata_get_tag_value_utf8 (data, tag);
234 
235 	if ((utf_attribute == NULL) || (utf_value == NULL)) {
236 		g_free (utf_attribute);
237 		g_free (utf_value);
238    		return FALSE;
239 	}
240 
241 	append_item (page,
242 		     description ? description : utf_attribute,
243 		     utf_value);
244 
245         g_free (utf_attribute);
246         g_free (utf_value);
247 
248 	return TRUE;
249 }
250 #endif /*HAVE_EXIF*/
251 
252 
253 #ifdef HAVE_EXEMPI
254 static void
append_xmp_value_pair(NemoImagePropertiesPage * page,XmpPtr xmp,const char * ns,const char * propname,char * descr)255 append_xmp_value_pair (NemoImagePropertiesPage *page,
256 		       XmpPtr      xmp,
257 		       const char *ns,
258 		       const char *propname,
259 		       char       *descr)
260 {
261 	uint32_t options;
262 	XmpStringPtr value;
263 
264 	value = xmp_string_new();
265 	if (xmp_get_property (xmp, ns, propname, value, &options)) {
266 		if (XMP_IS_PROP_SIMPLE (options)) {
267 			append_item (page, descr, xmp_string_cstr (value));
268 		}
269 		else if (XMP_IS_PROP_ARRAY (options)) {
270 			XmpIteratorPtr iter;
271 
272 			iter = xmp_iterator_new (xmp, ns, propname, XMP_ITER_JUSTLEAFNODES);
273 			if (iter) {
274 				GString *str;
275 				gboolean first = TRUE;
276 
277 				str = g_string_new (NULL);
278 
279 				while (xmp_iterator_next (iter, NULL, NULL, value, &options)
280 				       && !XMP_IS_PROP_QUALIFIER(options)) {
281 					if (!first) {
282 						g_string_append_printf (str, ", ");
283 					}
284 					else {
285 						first = FALSE;
286 					}
287 					g_string_append_printf (str,
288 								"%s",
289 								xmp_string_cstr(value));
290 				}
291 				xmp_iterator_free(iter);
292 				append_item (page, descr, g_string_free (str, FALSE));
293 			}
294 		}
295 	}
296 	xmp_string_free(value);
297 }
298 #endif /*HAVE EXEMPI*/
299 
300 static gboolean
append_option_value_pair(NemoImagePropertiesPage * page,GdkPixbuf * pixbuf,const char * key,char * description)301 append_option_value_pair (NemoImagePropertiesPage *page,
302 			  GdkPixbuf                   *pixbuf,
303 			  const char                  *key,
304 			  char                        *description)
305 {
306 	const char *value;
307 
308 	value = gdk_pixbuf_get_option (pixbuf, key);
309 	if (value == NULL)
310 		return FALSE;
311 
312 	append_item (page, description, value);
313 	return TRUE;
314 }
315 
316 static void
append_basic_info(NemoImagePropertiesPage * page)317 append_basic_info (NemoImagePropertiesPage *page)
318 {
319 	GdkPixbufFormat *format;
320 	char *name;
321 	char *desc;
322 	char *value;
323 
324 	format = gdk_pixbuf_loader_get_format (page->details->loader);
325 
326 	name = gdk_pixbuf_format_get_name (format);
327 	desc = gdk_pixbuf_format_get_description (format);
328 	value = g_strdup_printf ("%s (%s)", name, desc);
329 	g_free (name);
330 	g_free (desc);
331 	append_item (page, _("Image Type"), value);
332 	g_free (value);
333 	value = g_strdup_printf (ngettext ("%d pixel",
334 					   "%d pixels",
335 					   page->details->width),
336 				 page->details->width);
337 	append_item (page, _("Width"), value);
338 	g_free (value);
339 	value = g_strdup_printf (ngettext ("%d pixel",
340 					   "%d pixels",
341 					   page->details->height),
342 				 page->details->height);
343 	append_item (page, _("Height"), value);
344 	g_free (value);
345 }
346 
347 static void
append_options_info(NemoImagePropertiesPage * page)348 append_options_info (NemoImagePropertiesPage *page)
349 {
350 	GdkPixbuf *pixbuf;
351 
352 	pixbuf = gdk_pixbuf_loader_get_pixbuf (page->details->loader);
353 	if (pixbuf == NULL)
354 		return;
355 
356 	if (!append_option_value_pair (page, pixbuf, "Title", _("Title")))
357 		append_option_value_pair (page, pixbuf, "tEXt::Title", _("Title"));
358 	if (!append_option_value_pair (page, pixbuf, "Author", _("Author")))
359 		append_option_value_pair (page, pixbuf, "tEXt::Author", _("Author"));
360 
361 	append_option_value_pair (page, pixbuf, "tEXt::Description", _("Description"));
362 	append_option_value_pair (page, pixbuf, "tEXt::Copyright", _("Copyright"));
363 	append_option_value_pair (page, pixbuf, "tEXt::Creation Time", _("Created On"));
364 	append_option_value_pair (page, pixbuf, "tEXt::Software", _("Created By"));
365 	append_option_value_pair (page, pixbuf, "tEXt::Disclaimer", _("Disclaimer"));
366 	append_option_value_pair (page, pixbuf, "tEXt::Warning", _("Warning"));
367 	append_option_value_pair (page, pixbuf, "tEXt::Source", _("Source"));
368 	append_option_value_pair (page, pixbuf, "tEXt::Comment", _("Comment"));
369 }
370 
371 static void
append_exif_info(NemoImagePropertiesPage * page)372 append_exif_info (NemoImagePropertiesPage *page)
373 {
374 #ifdef HAVE_EXIF
375 	ExifData *exifdata;
376 
377 	exifdata = exif_loader_get_data (page->details->exifldr);
378 	if (exifdata == NULL)
379 		return;
380 
381 	if (exifdata->ifd[0] && exifdata->ifd[0]->count) {
382                 append_tag_value_pair (page, exifdata, EXIF_TAG_MAKE, _("Camera Brand"));
383                 append_tag_value_pair (page, exifdata, EXIF_TAG_MODEL, _("Camera Model"));
384 
385                 /* Choose which date to show in order of relevance */
386                 if (!append_tag_value_pair (page, exifdata, EXIF_TAG_DATE_TIME_ORIGINAL, _("Date Taken"))) {
387 			if (!append_tag_value_pair (page, exifdata, EXIF_TAG_DATE_TIME_DIGITIZED, _("Date Digitized"))) {
388 				append_tag_value_pair (page, exifdata, EXIF_TAG_DATE_TIME, _("Date Modified"));
389 			}
390 		}
391 
392                 append_tag_value_pair (page, exifdata, EXIF_TAG_EXPOSURE_TIME, _("Exposure Time"));
393                 append_tag_value_pair (page, exifdata, EXIF_TAG_APERTURE_VALUE, _("Aperture Value"));
394                 append_tag_value_pair (page, exifdata, EXIF_TAG_ISO_SPEED_RATINGS, _("ISO Speed Rating"));
395                 append_tag_value_pair (page, exifdata, EXIF_TAG_FLASH,_("Flash Fired"));
396                 append_tag_value_pair (page, exifdata, EXIF_TAG_METERING_MODE, _("Metering Mode"));
397                 append_tag_value_pair (page, exifdata, EXIF_TAG_EXPOSURE_PROGRAM, _("Exposure Program"));
398                 append_tag_value_pair (page, exifdata, EXIF_TAG_FOCAL_LENGTH,_("Focal Length"));
399                 append_tag_value_pair (page, exifdata, EXIF_TAG_SOFTWARE, _("Software"));
400 	}
401 
402 	exif_data_unref (exifdata);
403 #endif
404 }
405 
406 static void
append_xmp_info(NemoImagePropertiesPage * page)407 append_xmp_info (NemoImagePropertiesPage *page)
408 {
409 #ifdef HAVE_EXEMPI
410 	if (page->details->xmp == NULL)
411 		return;
412 
413 	append_xmp_value_pair (page, page->details->xmp, NS_IPTC4XMP, "Location", _("Location"));
414 	append_xmp_value_pair (page, page->details->xmp, NS_DC, "description", _("Description"));
415 	append_xmp_value_pair (page, page->details->xmp, NS_DC, "subject", _("Keywords"));
416 	append_xmp_value_pair (page, page->details->xmp, NS_DC, "creator", _("Creator"));
417 	append_xmp_value_pair (page, page->details->xmp, NS_DC, "rights", _("Copyright"));
418 	append_xmp_value_pair (page, page->details->xmp, NS_XAP,"Rating", _("Rating"));
419 	/* TODO add CC licenses */
420 #endif /*HAVE EXEMPI*/
421 }
422 
423 static void
load_finished(NemoImagePropertiesPage * page)424 load_finished (NemoImagePropertiesPage *page)
425 {
426 	GtkWidget *label;
427 
428 	label = gtk_grid_get_child_at (GTK_GRID (page->details->grid), 0, 0);
429 	gtk_container_remove (GTK_CONTAINER (page->details->grid), label);
430 
431 	if (page->details->loader != NULL) {
432 		gdk_pixbuf_loader_close (page->details->loader, NULL);
433 	}
434 
435 	if (page->details->got_size) {
436 		append_basic_info (page);
437 		append_options_info (page);
438 		append_exif_info (page);
439 		append_xmp_info (page);
440 	} else {
441 		append_item (page, _("Failed to load image information"), NULL);
442 	}
443 
444 	if (page->details->loader != NULL) {
445 		g_object_unref (page->details->loader);
446 		page->details->loader = NULL;
447 	}
448 #ifdef HAVE_EXIF
449 	if (page->details->exifldr != NULL) {
450 		exif_loader_unref (page->details->exifldr);
451 		page->details->exifldr = NULL;
452 	}
453 #endif /*HAVE_EXIF*/
454 #ifdef HAVE_EXEMPI
455 	if (page->details->xmp != NULL) {
456 		xmp_free (page->details->xmp);
457 		page->details->xmp = NULL;
458 	}
459 #endif
460 }
461 
462 static void
file_read_callback(GObject * object,GAsyncResult * res,gpointer data)463 file_read_callback (GObject      *object,
464 		    GAsyncResult *res,
465 		    gpointer      data)
466 {
467 	NemoImagePropertiesPage *page;
468 	GInputStream *stream;
469 	gssize count_read;
470 	GError *error;
471 	int exif_still_loading;
472 	gboolean done_reading;
473 
474 	page = NEMO_IMAGE_PROPERTIES_PAGE (data);
475 	stream = G_INPUT_STREAM (object);
476 
477 	error = NULL;
478 	done_reading = FALSE;
479 	count_read = g_input_stream_read_finish (stream, res, &error);
480 
481 	if (count_read > 0) {
482 
483 		g_assert (count_read <= (int)sizeof(page->details->buffer));
484 
485 #ifdef HAVE_EXIF
486 		exif_still_loading = exif_loader_write (page->details->exifldr,
487 				  		        (guchar *) page->details->buffer,
488 				  			count_read);
489 #else
490 		exif_still_loading = 0;
491 #endif
492 
493 		if (page->details->pixbuf_still_loading) {
494 			if (!gdk_pixbuf_loader_write (page->details->loader,
495 					      	      (const guchar *) page->details->buffer,
496 					      	      count_read,
497 					      	      NULL)) {
498 				page->details->pixbuf_still_loading = FALSE;
499 			}
500 		}
501 
502 		if (page->details->pixbuf_still_loading ||
503 		    (exif_still_loading == 1)) {
504 			g_input_stream_read_async (G_INPUT_STREAM (stream),
505 						   page->details->buffer,
506 						   sizeof (page->details->buffer),
507 						   0,
508 						   page->details->cancellable,
509 						   file_read_callback,
510 						   page);
511 		}
512 		else {
513 			done_reading = TRUE;
514 		}
515 	}
516 	else {
517 		/* either EOF, cancelled or an error occurred */
518 		done_reading = TRUE;
519 	}
520 
521 	if (error != NULL) {
522 		char *uri = g_file_get_uri (G_FILE (object));
523 		g_warning ("Error reading %s: %s", uri, error->message);
524 		g_free (uri);
525 		g_clear_error (&error);
526 	}
527 
528 	if (done_reading) {
529 		load_finished (page);
530 		g_input_stream_close_async (stream,
531 					    0,
532 					    page->details->cancellable,
533 					    file_close_callback,
534 					    page);
535 	}
536 }
537 
538 static void
size_prepared_callback(GdkPixbufLoader * loader,int width,int height,gpointer callback_data)539 size_prepared_callback (GdkPixbufLoader *loader,
540 			int              width,
541 			int              height,
542 			gpointer         callback_data)
543 {
544 	NemoImagePropertiesPage *page;
545 
546 	page = NEMO_IMAGE_PROPERTIES_PAGE (callback_data);
547 
548 	page->details->height = height;
549 	page->details->width = width;
550 	page->details->got_size = TRUE;
551 	page->details->pixbuf_still_loading = FALSE;
552 }
553 
554 typedef struct {
555 	NemoImagePropertiesPage *page;
556 	NemoFileInfo            *info;
557 } FileOpenData;
558 
559 static void
file_open_callback(GObject * object,GAsyncResult * res,gpointer user_data)560 file_open_callback (GObject      *object,
561 		    GAsyncResult *res,
562 		    gpointer      user_data)
563 {
564 	FileOpenData *data = user_data;
565 	NemoImagePropertiesPage *page = data->page;
566 	GFile *file;
567 	GFileInputStream *stream;
568 	GError *error;
569 	char *uri;
570 
571 	file = G_FILE (object);
572 	uri = g_file_get_uri (file);
573 
574 	error = NULL;
575 	stream = g_file_read_finish (file, res, &error);
576 	if (stream) {
577 		char *mime_type;
578 
579 		mime_type = nemo_file_info_get_mime_type (data->info);
580 		page->details->loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, &error);
581 		if (error != NULL) {
582 			g_warning ("Error creating loader for %s: %s", uri, error->message);
583 			g_clear_error (&error);
584 		}
585 		page->details->pixbuf_still_loading = TRUE;
586 		page->details->width = 0;
587 		page->details->height = 0;
588 #ifdef HAVE_EXIF
589 		page->details->exifldr = exif_loader_new ();
590 #endif /*HAVE_EXIF*/
591 		g_free (mime_type);
592 
593 		g_signal_connect (page->details->loader,
594 				  "size_prepared",
595 				  G_CALLBACK (size_prepared_callback),
596 				  page);
597 
598 		g_input_stream_read_async (G_INPUT_STREAM (stream),
599 					   page->details->buffer,
600 					   sizeof (page->details->buffer),
601 					   0,
602 					   page->details->cancellable,
603 					   file_read_callback,
604 					   page);
605 
606 		g_object_unref (stream);
607 	} else {
608 		g_warning ("Error reading %s: %s", uri, error->message);
609 		g_clear_error (&error);
610 		load_finished (page);
611 	}
612 
613 	g_free (uri);
614 	g_free (data);
615 }
616 
617 static void
load_location(NemoImagePropertiesPage * page,NemoFileInfo * info)618 load_location (NemoImagePropertiesPage *page,
619 	       NemoFileInfo	       *info)
620 {
621 	GFile *file;
622 	char *uri;
623 	FileOpenData *data;
624 
625 	g_assert (NEMO_IS_IMAGE_PROPERTIES_PAGE (page));
626 	g_assert (info != NULL);
627 
628 	page->details->cancellable = g_cancellable_new ();
629 
630 	uri = nemo_file_info_get_uri (info);
631 	file = g_file_new_for_uri (uri);
632 
633 #ifdef HAVE_EXEMPI
634 	{
635 		/* Current Exempi does not support setting custom IO to be able to use Gnome-vfs */
636 		/* So it will only work with local files. Future version might remove this limitation */
637 		XmpFilePtr xf;
638 		char *localname;
639 
640 		localname = g_filename_from_uri (uri, NULL, NULL);
641 		if (localname) {
642 			xf = xmp_files_open_new (localname, 0);
643 			page->details->xmp = xmp_files_get_new_xmp (xf); /* only load when loading */
644 			xmp_files_close (xf, 0);
645 			g_free (localname);
646 		}
647 		else {
648 			page->details->xmp = NULL;
649 		}
650 	}
651 #endif /*HAVE_EXEMPI*/
652 
653 	data = g_new0 (FileOpenData, 1);
654 	data->page = page;
655 	data->info = info;
656 
657 	g_file_read_async (file,
658 			   0,
659 			   page->details->cancellable,
660 			   file_open_callback,
661 			   data);
662 
663 	g_object_unref (file);
664 	g_free (uri);
665 }
666 
667 static void
nemo_image_properties_page_class_init(NemoImagePropertiesPageClass * class)668 nemo_image_properties_page_class_init (NemoImagePropertiesPageClass *class)
669 {
670 	GObjectClass *object_class;
671 
672 	object_class = G_OBJECT_CLASS (class);
673 
674 	object_class->finalize = nemo_image_properties_page_finalize;
675 
676 	g_type_class_add_private (object_class, sizeof(NemoImagePropertiesPageDetails));
677 }
678 
679 static void
nemo_image_properties_page_init(NemoImagePropertiesPage * page)680 nemo_image_properties_page_init (NemoImagePropertiesPage *page)
681 {
682 	GtkWidget *sw;
683 
684 	page->details = G_TYPE_INSTANCE_GET_PRIVATE (page,
685 						     NEMO_TYPE_IMAGE_PROPERTIES_PAGE,
686 						     NemoImagePropertiesPageDetails);
687 
688 	gtk_orientable_set_orientation (GTK_ORIENTABLE (page), GTK_ORIENTATION_VERTICAL);
689 	gtk_box_set_homogeneous (GTK_BOX (page), FALSE);
690 	gtk_box_set_spacing (GTK_BOX (page), 0);
691 	gtk_container_set_border_width (GTK_CONTAINER (page), 0);
692 
693 	sw = gtk_scrolled_window_new (NULL, NULL);
694 	gtk_container_set_border_width (GTK_CONTAINER (sw), 0);
695 	gtk_widget_set_vexpand (GTK_WIDGET (sw), TRUE);
696 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
697 	                                GTK_POLICY_NEVER,
698 	                                GTK_POLICY_AUTOMATIC);
699 	gtk_box_pack_start (GTK_BOX (page), sw, FALSE, TRUE, 2);
700 
701 	page->details->grid = gtk_grid_new ();
702 	gtk_container_set_border_width (GTK_CONTAINER (page->details->grid), 6);
703 	gtk_orientable_set_orientation (GTK_ORIENTABLE (page->details->grid), GTK_ORIENTATION_VERTICAL);
704 	gtk_grid_set_row_spacing (GTK_GRID (page->details->grid), 6);
705 	gtk_grid_set_column_spacing (GTK_GRID (page->details->grid), 20);
706 	append_item (page, _("Loading..."), NULL);
707 	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), page->details->grid);
708 
709 	gtk_widget_show_all (GTK_WIDGET (page));
710 }
711 
712 static gboolean
is_mime_type_supported(const char * mime_type)713 is_mime_type_supported (const char *mime_type)
714 {
715 	gboolean supported;
716 	GSList *formats;
717 	GSList *l;
718 
719 	supported = FALSE;
720 	formats = gdk_pixbuf_get_formats ();
721 
722 	for (l = formats; supported == FALSE && l != NULL; l = l->next) {
723 		GdkPixbufFormat *format = l->data;
724 		char **mime_types = gdk_pixbuf_format_get_mime_types (format);
725 		int i;
726 
727 		for (i = 0; mime_types[i] != NULL; i++) {
728 			if (strcmp (mime_types[i], mime_type) == 0) {
729 				supported = TRUE;
730 				break;
731 			}
732 		}
733 		g_strfreev (mime_types);
734 	}
735 	g_slist_free (formats);
736 
737 	return supported;
738 }
739 
740 static GList *
get_property_pages(NemoPropertyPageProvider * provider,GList * files)741 get_property_pages (NemoPropertyPageProvider *provider,
742                     GList *files)
743 {
744 	GList *pages;
745 	NemoFileInfo *file;
746 
747 	char *mime_type;
748 
749 	/* Only show the property page if 1 file is selected */
750 	if (!files || files->next != NULL) {
751 		return NULL;
752 	}
753 
754 	pages = NULL;
755 
756         file = NEMO_FILE_INFO (files->data);
757 
758 	mime_type = nemo_file_info_get_mime_type (file);
759 	if (mime_type != NULL && is_mime_type_supported (mime_type)) {
760 		NemoImagePropertiesPage *page;
761 		NemoPropertyPage *real_page;
762 
763 		page = g_object_new (nemo_image_properties_page_get_type (), NULL);
764 		load_location (page, file);
765 
766 		real_page = nemo_property_page_new ("NemoImagePropertiesPage::property_page",
767 		                            	    gtk_label_new (_("Image")),
768 		                                    GTK_WIDGET (page));
769 		pages = g_list_append (pages, real_page);
770 	}
771 
772         g_free (mime_type);
773 
774 	return pages;
775 }
776 
777 static void
property_page_provider_iface_init(NemoPropertyPageProviderIface * iface)778 property_page_provider_iface_init (NemoPropertyPageProviderIface *iface)
779 {
780 	iface->get_pages = get_property_pages;
781 }
782 
783 
784 static void
nemo_image_properties_page_provider_init(NemoImagePropertiesPageProvider * sidebar)785 nemo_image_properties_page_provider_init (NemoImagePropertiesPageProvider *sidebar)
786 {
787 }
788 
789 static void
nemo_image_properties_page_provider_class_init(NemoImagePropertiesPageProviderClass * class)790 nemo_image_properties_page_provider_class_init (NemoImagePropertiesPageProviderClass *class)
791 {
792 }
793 
794 void
nemo_image_properties_page_register(void)795 nemo_image_properties_page_register (void)
796 {
797         nemo_module_add_type (nemo_image_properties_page_provider_get_type ());
798 }
799 
800