2008-04-30 Cosimo Cecchi <cosimoc@gnome.org>
[nautilus.git] / src / nautilus-image-properties-page.c
blobb6152d326419202409d1504b472e1da23413920f
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /*
4 * Copyright (C) 2004 Red Hat, Inc
5 * Copyright (c) 2007 Novell, Inc.
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.
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.
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., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
22 * Author: Alexander Larsson <alexl@redhat.com>
23 * XMP support by Hubert Figuiere <hfiguiere@novell.com>
26 #include <config.h>
27 #include "nautilus-image-properties-page.h"
29 #include <gtk/gtkvbox.h>
30 #include <gtk/gtklabel.h>
31 #include <libgnome/gnome-macros.h>
32 #include <glib/gi18n.h>
33 #include <gio/gio.h>
34 #include <eel/eel-vfs-extensions.h>
35 #include <libnautilus-extension/nautilus-property-page-provider.h>
36 #include <libnautilus-private/nautilus-module.h>
37 #include <string.h>
39 #ifdef HAVE_EXIF
40 #include <libexif/exif-data.h>
41 #include <libexif/exif-ifd.h>
42 #include <libexif/exif-loader.h>
43 #endif
44 #ifdef HAVE_EXEMPI
45 #include <exempi/xmp.h>
46 #include <exempi/xmpconsts.h>
47 #endif
49 #define LOAD_BUFFER_SIZE 8192
51 struct NautilusImagePropertiesPageDetails {
52 GCancellable *cancellable;
53 GtkWidget *resolution;
54 GdkPixbufLoader *loader;
55 gboolean got_size;
56 gboolean pixbuf_still_loading;
57 char buffer[LOAD_BUFFER_SIZE];
58 int width;
59 int height;
60 #ifdef HAVE_EXIF
61 ExifLoader *exifldr;
62 #endif /*HAVE_EXIF*/
63 #ifdef HAVE_EXEMPI
64 XmpPtr xmp;
65 #endif
68 #ifdef HAVE_EXIF
69 struct ExifAttribute {
70 ExifTag tag;
71 char *value;
72 gboolean found;
74 #endif /*HAVE_EXIF*/
76 enum {
77 PROP_URI
80 typedef struct {
81 GObject parent;
82 } NautilusImagePropertiesPageProvider;
84 typedef struct {
85 GObjectClass parent;
86 } NautilusImagePropertiesPageProviderClass;
89 static GType nautilus_image_properties_page_provider_get_type (void);
90 static void property_page_provider_iface_init (NautilusPropertyPageProviderIface *iface);
93 G_DEFINE_TYPE (NautilusImagePropertiesPage, nautilus_image_properties_page, GTK_TYPE_VBOX);
95 G_DEFINE_TYPE_WITH_CODE (NautilusImagePropertiesPageProvider, nautilus_image_properties_page_provider, G_TYPE_OBJECT,
96 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_PROPERTY_PAGE_PROVIDER,
97 property_page_provider_iface_init));
99 static void
100 nautilus_image_properties_page_finalize (GObject *object)
102 NautilusImagePropertiesPage *page;
104 page = NAUTILUS_IMAGE_PROPERTIES_PAGE (object);
106 if (page->details->cancellable) {
107 g_cancellable_cancel (page->details->cancellable);
108 g_object_unref (page->details->cancellable);
109 page->details->cancellable = NULL;
112 G_OBJECT_CLASS (nautilus_image_properties_page_parent_class)->finalize (object);
115 static void
116 file_close_callback (GObject *object,
117 GAsyncResult *res,
118 gpointer data)
120 NautilusImagePropertiesPage *page;
121 GInputStream *stream;
123 page = NAUTILUS_IMAGE_PROPERTIES_PAGE (data);
124 stream = G_INPUT_STREAM (object);
126 g_input_stream_close_finish (stream, res, NULL);
128 g_object_unref (page->details->cancellable);
129 page->details->cancellable = NULL;
132 #ifdef HAVE_EXIF
133 static char *
134 exif_string_to_utf8 (const char *exif_str)
136 char *utf8_str;
138 if (g_utf8_validate (exif_str, -1, NULL)) {
139 return g_strdup (exif_str);
142 utf8_str = g_locale_to_utf8 (exif_str, -1, NULL, NULL, NULL);
143 if (utf8_str != NULL) {
144 return utf8_str;
147 return eel_make_valid_utf8 (exif_str);
150 static void
151 exif_content_callback (ExifContent *content, gpointer data)
153 struct ExifAttribute *attribute;
154 #ifndef HAVE_OLD_EXIF
155 char b[1024];
156 #endif
158 attribute = (struct ExifAttribute *)data;
159 if (attribute->found) {
160 return;
163 #ifdef HAVE_OLD_EXIF
164 attribute->value = g_strdup (exif_content_get_value (content, attribute->tag));
165 #else
166 attribute->value = g_strdup (exif_content_get_value (content, attribute->tag, b, sizeof(b)));
167 #endif
168 if (attribute->value != NULL) {
169 attribute->found = TRUE;
173 static char *
174 exifdata_get_tag_name_utf8 (ExifTag tag)
176 return exif_string_to_utf8 (exif_tag_get_name (tag));
179 static char *
180 exifdata_get_tag_value_utf8 (ExifData *data, ExifTag tag)
182 struct ExifAttribute attribute;
183 char *utf8_value;
185 attribute.tag = tag;
186 attribute.value = NULL;
187 attribute.found = FALSE;
189 exif_data_foreach_content (data, exif_content_callback, &attribute);
191 if (attribute.found) {
192 utf8_value = exif_string_to_utf8 (attribute.value);
193 g_free (attribute.value);
194 } else {
195 utf8_value = NULL;
198 return utf8_value;
201 static gboolean
202 append_tag_value_pair (GString *string,
203 ExifData *data,
204 ExifTag tag,
205 char *description)
207 char *utf_attribute;
208 char *utf_value;
210 utf_attribute = exifdata_get_tag_name_utf8 (tag);
211 utf_value = exifdata_get_tag_value_utf8 (data, tag);
213 if ((utf_attribute == NULL) || (utf_value == NULL)) {
214 g_free (utf_attribute);
215 g_free (utf_value);
216 return FALSE;
219 g_string_append_printf (string, "<b>%s:</b> %s\n",
220 description ? description : utf_attribute,
221 utf_value);
223 g_free (utf_attribute);
224 g_free (utf_value);
225 return TRUE;
228 static void
229 append_exifdata_string (ExifData *exifdata, GString *string)
231 if (exifdata->ifd[0] && exifdata->ifd[0]->count) {
232 append_tag_value_pair (string, exifdata, EXIF_TAG_MAKE, _("Camera Brand"));
233 append_tag_value_pair (string, exifdata, EXIF_TAG_MODEL, _("Camera Model"));
235 /* Choose which date to show in order of relevance */
236 if (!append_tag_value_pair (string, exifdata, EXIF_TAG_DATE_TIME_ORIGINAL, _("Date Taken")))
238 if (!append_tag_value_pair (string, exifdata, EXIF_TAG_DATE_TIME_DIGITIZED, _("Date Digitized")))
240 append_tag_value_pair (string, exifdata, EXIF_TAG_DATE_TIME, _("Date Modified"));
244 append_tag_value_pair (string, exifdata, EXIF_TAG_EXPOSURE_TIME, _("Exposure Time"));
245 append_tag_value_pair (string, exifdata, EXIF_TAG_APERTURE_VALUE, _("Aperture Value"));
246 append_tag_value_pair (string, exifdata, EXIF_TAG_ISO_SPEED_RATINGS, _("ISO Speed Rating"));
247 append_tag_value_pair (string, exifdata, EXIF_TAG_FLASH,_("Flash Fired"));
248 append_tag_value_pair (string, exifdata, EXIF_TAG_METERING_MODE, _("Metering Mode"));
249 append_tag_value_pair (string, exifdata, EXIF_TAG_EXPOSURE_PROGRAM, _("Exposure Program"));
250 append_tag_value_pair (string, exifdata, EXIF_TAG_FOCAL_LENGTH,_("Focal Length"));
251 append_tag_value_pair (string, exifdata, EXIF_TAG_SOFTWARE, _("Software"));
254 #endif /*HAVE_EXIF*/
256 #ifdef HAVE_EXEMPI
257 static void
258 append_xmp_value_pair (GString *string,
259 XmpPtr xmp,
260 const char *ns,
261 const char *propname,
262 char *descr)
264 uint32_t options;
265 XmpStringPtr value;
267 value = xmp_string_new();
268 #ifdef HAVE_EXEMPI_NEW_API
269 if (xmp_get_property (xmp, ns, propname, value, &options)) {
270 #else
271 if (xmp_get_property_and_bits (xmp, ns, propname, value, &options)) {
272 #endif
273 if (XMP_IS_PROP_SIMPLE (options)) {
274 g_string_append_printf (string,
275 "<b>%s:</b> %s\n",
276 descr,
277 xmp_string_cstr (value));
279 else if (XMP_IS_PROP_ARRAY (options)) {
280 XmpIteratorPtr iter;
282 iter = xmp_iterator_new (xmp, ns, propname, XMP_ITER_JUSTLEAFNODES);
283 if (iter) {
284 gboolean first = TRUE;
285 g_string_append_printf (string, "<b>%s:</b> ", descr);
286 while (xmp_iterator_next (iter, NULL, NULL, value, &options)
287 && !XMP_IS_PROP_QUALIFIER(options)) {
288 if (!first) {
289 g_string_append_printf (string, ", ");
291 else {
292 first = FALSE;
294 g_string_append_printf (string,
295 "%s",
296 xmp_string_cstr(value));
298 xmp_iterator_free(iter);
299 g_string_append_printf(string, "\n");
303 xmp_string_free(value);
306 static void
307 append_xmpdata_string(XmpPtr xmp, GString *string)
309 if(xmp != NULL) {
310 append_xmp_value_pair(string, xmp, NS_IPTC4XMP, "Location", _("Location"));
311 append_xmp_value_pair(string, xmp, NS_DC, "description", _("Description"));
312 append_xmp_value_pair(string, xmp, NS_DC, "subject", _("Keywords"));
313 append_xmp_value_pair(string, xmp, NS_DC, "creator", _("Creator"));
314 append_xmp_value_pair(string, xmp, NS_DC, "rights", _("Copyright"));
315 append_xmp_value_pair(string, xmp, NS_XAP,"Rating", _("Rating"));
316 /* TODO add CC licenses */
319 #endif
321 static void
322 load_finished (NautilusImagePropertiesPage *page)
324 GdkPixbufFormat *format;
325 char *name, *desc;
326 GString *str;
328 if (page->details->got_size) {
329 str = g_string_new (NULL);
330 format = gdk_pixbuf_loader_get_format (page->details->loader);
332 name = gdk_pixbuf_format_get_name (format);
333 desc = gdk_pixbuf_format_get_description (format);
334 g_string_append_printf (str, "<b>%s</b> %s (%s)\n",
335 _("Image Type:"), name, desc);
336 g_string_append_printf (str, ngettext ("<b>Width:</b> %d pixel\n",
337 "<b>Width:</b> %d pixels\n",
338 page->details->width),
339 page->details->width);
340 g_string_append_printf (str, ngettext ("<b>Height:</b> %d pixel\n",
341 "<b>Height:</b> %d pixels\n",
342 page->details->height),
343 page->details->height);
344 g_free (name);
345 g_free (desc);
347 #ifdef HAVE_EXIF
348 append_exifdata_string (exif_loader_get_data (page->details->exifldr), str);
349 #endif /*HAVE_EXIF*/
350 #ifdef HAVE_EXEMPI
351 append_xmpdata_string(page->details->xmp, str);
352 #endif /*HAVE EXEMPI*/
354 gtk_label_set_markup (GTK_LABEL (page->details->resolution), str->str);
355 gtk_label_set_selectable (GTK_LABEL (page->details->resolution), TRUE);
356 g_string_free (str, TRUE);
357 } else {
358 gtk_label_set_text (GTK_LABEL (page->details->resolution), _("Failed to load image information"));
361 if (page->details->loader != NULL) {
362 gdk_pixbuf_loader_close (page->details->loader, NULL);
363 g_object_unref (page->details->loader);
364 page->details->loader = NULL;
366 #ifdef HAVE_EXIF
367 if (page->details->exifldr != NULL) {
368 exif_loader_unref (page->details->exifldr);
369 page->details->exifldr = NULL;
371 #endif /*HAVE_EXIF*/
372 #ifdef HAVE_EXEMPI
373 if (page->details->xmp != NULL) {
374 xmp_free(page->details->xmp);
375 page->details->xmp = NULL;
377 #endif
380 static void
381 file_read_callback (GObject *object,
382 GAsyncResult *res,
383 gpointer data)
385 NautilusImagePropertiesPage *page;
386 GInputStream *stream;
387 gssize count_read;
388 GError *error;
389 int exif_still_loading;
390 gboolean done_reading;
392 page = NAUTILUS_IMAGE_PROPERTIES_PAGE (data);
393 stream = G_INPUT_STREAM (object);
395 error = NULL;
396 done_reading = FALSE;
397 count_read = g_input_stream_read_finish (stream, res, &error);
399 if (count_read > 0) {
401 g_assert (count_read <= sizeof(page->details->buffer));
403 #ifdef HAVE_EXIF
404 exif_still_loading = exif_loader_write (page->details->exifldr,
405 page->details->buffer,
406 count_read);
407 #else
408 exif_still_loading = 0;
409 #endif
411 if (page->details->pixbuf_still_loading) {
412 if (!gdk_pixbuf_loader_write (page->details->loader,
413 page->details->buffer,
414 count_read,
415 NULL)) {
416 page->details->pixbuf_still_loading = FALSE;
420 if (page->details->pixbuf_still_loading ||
421 (exif_still_loading == 1)) {
422 g_input_stream_read_async (G_INPUT_STREAM (stream),
423 page->details->buffer,
424 sizeof (page->details->buffer),
426 page->details->cancellable,
427 file_read_callback,
428 page);
430 else {
431 done_reading = TRUE;
434 else {
435 /* either EOF, cancelled or an error occurred */
436 done_reading = TRUE;
439 if (done_reading) {
440 load_finished (page);
441 g_input_stream_close_async (stream,
443 page->details->cancellable,
444 file_close_callback,
445 page);
449 static void
450 size_prepared_callback (GdkPixbufLoader *loader,
451 int width,
452 int height,
453 gpointer callback_data)
455 NautilusImagePropertiesPage *page;
457 page = NAUTILUS_IMAGE_PROPERTIES_PAGE (callback_data);
459 page->details->height = height;
460 page->details->width = width;
461 page->details->got_size = TRUE;
462 page->details->pixbuf_still_loading = FALSE;
465 static void
466 file_open_callback (GObject *object,
467 GAsyncResult *res,
468 gpointer data)
470 NautilusImagePropertiesPage *page;
471 GFile *file;
472 GFileInputStream *stream;
473 GError *error;
475 page = NAUTILUS_IMAGE_PROPERTIES_PAGE (data);
476 file = G_FILE (object);
478 error = NULL;
479 stream = g_file_read_finish (file, res, &error);
480 if (stream) {
481 page->details->loader = gdk_pixbuf_loader_new ();
482 page->details->pixbuf_still_loading = TRUE;
483 page->details->width = 0;
484 page->details->height = 0;
485 #ifdef HAVE_EXIF
486 page->details->exifldr = exif_loader_new ();
487 #endif /*HAVE_EXIF*/
489 g_signal_connect (page->details->loader,
490 "size_prepared",
491 G_CALLBACK (size_prepared_callback),
492 page);
494 g_input_stream_read_async (G_INPUT_STREAM (stream),
495 page->details->buffer,
496 sizeof (page->details->buffer),
498 page->details->cancellable,
499 file_read_callback,
500 page);
502 g_object_unref (stream);
506 static void
507 load_location (NautilusImagePropertiesPage *page,
508 const char *location)
510 GFile *file;
512 g_assert (NAUTILUS_IS_IMAGE_PROPERTIES_PAGE (page));
513 g_assert (location != NULL);
515 page->details->cancellable = g_cancellable_new ();
516 file = g_file_new_for_uri (location);
518 #ifdef HAVE_EXEMPI
520 /* Current Exempi does not support setting custom IO to be able to use Gnome-vfs */
521 /* So it will only work with local files. Future version might remove this limitation */
522 XmpFilePtr xf;
523 char *localname;
525 localname = g_filename_from_uri (location, NULL, NULL);
526 if (localname) {
527 xf = xmp_files_open_new (localname, 0);
528 page->details->xmp = xmp_files_get_new_xmp (xf); /* only load when loading */
529 xmp_files_close (xf, 0);
530 g_free (localname);
532 else {
533 page->details->xmp = NULL;
536 #endif /*HAVE_EXEMPI*/
538 g_file_read_async (file,
540 page->details->cancellable,
541 file_open_callback,
542 page);
544 g_object_unref (file);
547 static void
548 nautilus_image_properties_page_class_init (NautilusImagePropertiesPageClass *class)
550 GObjectClass *object_class;
552 object_class = G_OBJECT_CLASS (class);
554 object_class->finalize = nautilus_image_properties_page_finalize;
556 g_type_class_add_private (object_class, sizeof(NautilusImagePropertiesPageDetails));
559 static void
560 nautilus_image_properties_page_init (NautilusImagePropertiesPage *page)
562 page->details = G_TYPE_INSTANCE_GET_PRIVATE (page,
563 NAUTILUS_TYPE_IMAGE_PROPERTIES_PAGE,
564 NautilusImagePropertiesPageDetails);
566 gtk_box_set_homogeneous (GTK_BOX (page), FALSE);
567 gtk_box_set_spacing (GTK_BOX (page), 2);
568 gtk_container_set_border_width (GTK_CONTAINER (page), 6);
570 page->details->resolution = gtk_label_new (_("loading..."));
571 gtk_misc_set_alignment (GTK_MISC (page->details->resolution),
575 gtk_box_pack_start (GTK_BOX (page),
576 page->details->resolution,
577 FALSE, TRUE, 2);
579 gtk_widget_show_all (GTK_WIDGET (page));
582 /* nautilus_property_page_provider_get_pages
584 * This function is called by Nautilus when it wants property page
585 * items from the extension.
587 * This function is called in the main thread before a property page
588 * is shown, so it should return quickly.
590 * The function should return a GList of allocated NautilusPropertyPage
591 * items.
593 static GList *
594 get_property_pages (NautilusPropertyPageProvider *provider,
595 GList *files)
597 GList *pages;
598 NautilusPropertyPage *real_page;
599 NautilusFileInfo *file;
600 char *uri;
601 NautilusImagePropertiesPage *page;
603 /* Only show the property page if 1 file is selected */
604 if (!files || files->next != NULL) {
605 return NULL;
608 file = NAUTILUS_FILE_INFO (files->data);
610 if (!
611 (nautilus_file_info_is_mime_type (file, "image/x-bmp") ||
612 nautilus_file_info_is_mime_type (file, "image/x-ico") ||
613 nautilus_file_info_is_mime_type (file, "image/jpeg") ||
614 nautilus_file_info_is_mime_type (file, "image/gif") ||
615 nautilus_file_info_is_mime_type (file, "image/png") ||
616 nautilus_file_info_is_mime_type (file, "image/pnm") ||
617 nautilus_file_info_is_mime_type (file, "image/ras") ||
618 nautilus_file_info_is_mime_type (file, "image/tga") ||
619 nautilus_file_info_is_mime_type (file, "image/tiff") ||
620 nautilus_file_info_is_mime_type (file, "image/wbmp") ||
621 nautilus_file_info_is_mime_type (file, "image/x-xbitmap") ||
622 nautilus_file_info_is_mime_type (file, "image/x-xpixmap"))) {
623 return NULL;
626 pages = NULL;
628 uri = nautilus_file_info_get_uri (file);
630 page = g_object_new (nautilus_image_properties_page_get_type (), NULL);
631 load_location (page, uri);
633 g_free (uri);
635 real_page = nautilus_property_page_new
636 ("NautilusImagePropertiesPage::property_page",
637 gtk_label_new (_("Image")),
638 GTK_WIDGET (page));
639 pages = g_list_append (pages, real_page);
641 return pages;
644 static void
645 property_page_provider_iface_init (NautilusPropertyPageProviderIface *iface)
647 iface->get_pages = get_property_pages;
651 static void
652 nautilus_image_properties_page_provider_init (NautilusImagePropertiesPageProvider *sidebar)
656 static void
657 nautilus_image_properties_page_provider_class_init (NautilusImagePropertiesPageProviderClass *class)
661 void
662 nautilus_image_properties_page_register (void)
664 nautilus_module_add_type (nautilus_image_properties_page_provider_get_type ());