1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
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>
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>
34 #include <eel/eel-vfs-extensions.h>
35 #include <libnautilus-extension/nautilus-property-page-provider.h>
36 #include <libnautilus-private/nautilus-module.h>
40 #include <libexif/exif-data.h>
41 #include <libexif/exif-ifd.h>
42 #include <libexif/exif-loader.h>
45 #include <exempi/xmp.h>
46 #include <exempi/xmpconsts.h>
49 #define LOAD_BUFFER_SIZE 8192
51 struct NautilusImagePropertiesPageDetails
{
52 GCancellable
*cancellable
;
53 GtkWidget
*resolution
;
54 GdkPixbufLoader
*loader
;
56 gboolean pixbuf_still_loading
;
57 char buffer
[LOAD_BUFFER_SIZE
];
69 struct ExifAttribute
{
82 } NautilusImagePropertiesPageProvider
;
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
));
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
);
116 file_close_callback (GObject
*object
,
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
;
134 exif_string_to_utf8 (const char *exif_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
) {
147 return eel_make_valid_utf8 (exif_str
);
151 exif_content_callback (ExifContent
*content
, gpointer data
)
153 struct ExifAttribute
*attribute
;
154 #ifndef HAVE_OLD_EXIF
158 attribute
= (struct ExifAttribute
*)data
;
159 if (attribute
->found
) {
164 attribute
->value
= g_strdup (exif_content_get_value (content
, attribute
->tag
));
166 attribute
->value
= g_strdup (exif_content_get_value (content
, attribute
->tag
, b
, sizeof(b
)));
168 if (attribute
->value
!= NULL
) {
169 attribute
->found
= TRUE
;
174 exifdata_get_tag_name_utf8 (ExifTag tag
)
176 return exif_string_to_utf8 (exif_tag_get_name (tag
));
180 exifdata_get_tag_value_utf8 (ExifData
*data
, ExifTag tag
)
182 struct ExifAttribute attribute
;
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
);
202 append_tag_value_pair (GString
*string
,
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
);
219 g_string_append_printf (string
, "<b>%s:</b> %s\n",
220 description
? description
: utf_attribute
,
223 g_free (utf_attribute
);
229 append_exifdata_string (ExifData
*exifdata
, GString
*string
)
231 if (exifdata
&& 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"));
258 append_xmp_value_pair (GString
*string
,
261 const char *propname
,
267 value
= xmp_string_new();
268 #ifdef HAVE_EXEMPI_NEW_API
269 if (xmp_get_property (xmp
, ns
, propname
, value
, &options
)) {
271 if (xmp_get_property_and_bits (xmp
, ns
, propname
, value
, &options
)) {
273 if (XMP_IS_PROP_SIMPLE (options
)) {
274 g_string_append_printf (string
,
277 xmp_string_cstr (value
));
279 else if (XMP_IS_PROP_ARRAY (options
)) {
282 iter
= xmp_iterator_new (xmp
, ns
, propname
, XMP_ITER_JUSTLEAFNODES
);
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
)) {
289 g_string_append_printf (string
, ", ");
294 g_string_append_printf (string
,
296 xmp_string_cstr(value
));
298 xmp_iterator_free(iter
);
299 g_string_append_printf(string
, "\n");
303 xmp_string_free(value
);
307 append_xmpdata_string(XmpPtr xmp
, GString
*string
)
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 */
322 load_finished (NautilusImagePropertiesPage
*page
)
324 GdkPixbufFormat
*format
;
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
);
348 append_exifdata_string (exif_loader_get_data (page
->details
->exifldr
), str
);
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
);
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
;
367 if (page
->details
->exifldr
!= NULL
) {
368 exif_loader_unref (page
->details
->exifldr
);
369 page
->details
->exifldr
= NULL
;
373 if (page
->details
->xmp
!= NULL
) {
374 xmp_free(page
->details
->xmp
);
375 page
->details
->xmp
= NULL
;
381 file_read_callback (GObject
*object
,
385 NautilusImagePropertiesPage
*page
;
386 GInputStream
*stream
;
389 int exif_still_loading
;
390 gboolean done_reading
;
392 page
= NAUTILUS_IMAGE_PROPERTIES_PAGE (data
);
393 stream
= G_INPUT_STREAM (object
);
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
));
404 exif_still_loading
= exif_loader_write (page
->details
->exifldr
,
405 page
->details
->buffer
,
408 exif_still_loading
= 0;
411 if (page
->details
->pixbuf_still_loading
) {
412 if (!gdk_pixbuf_loader_write (page
->details
->loader
,
413 page
->details
->buffer
,
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
,
435 /* either EOF, cancelled or an error occurred */
440 load_finished (page
);
441 g_input_stream_close_async (stream
,
443 page
->details
->cancellable
,
450 size_prepared_callback (GdkPixbufLoader
*loader
,
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
;
466 file_open_callback (GObject
*object
,
470 NautilusImagePropertiesPage
*page
;
472 GFileInputStream
*stream
;
475 page
= NAUTILUS_IMAGE_PROPERTIES_PAGE (data
);
476 file
= G_FILE (object
);
479 stream
= g_file_read_finish (file
, res
, &error
);
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;
486 page
->details
->exifldr
= exif_loader_new ();
489 g_signal_connect (page
->details
->loader
,
491 G_CALLBACK (size_prepared_callback
),
494 g_input_stream_read_async (G_INPUT_STREAM (stream
),
495 page
->details
->buffer
,
496 sizeof (page
->details
->buffer
),
498 page
->details
->cancellable
,
502 g_object_unref (stream
);
507 load_location (NautilusImagePropertiesPage
*page
,
508 const char *location
)
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
);
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 */
525 localname
= g_filename_from_uri (location
, NULL
, NULL
);
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);
533 page
->details
->xmp
= NULL
;
536 #endif /*HAVE_EXEMPI*/
538 g_file_read_async (file
,
540 page
->details
->cancellable
,
544 g_object_unref (file
);
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
));
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
,
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
594 get_property_pages (NautilusPropertyPageProvider
*provider
,
598 NautilusPropertyPage
*real_page
;
599 NautilusFileInfo
*file
;
601 NautilusImagePropertiesPage
*page
;
603 /* Only show the property page if 1 file is selected */
604 if (!files
|| files
->next
!= NULL
) {
608 file
= NAUTILUS_FILE_INFO (files
->data
);
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"))) {
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
);
635 real_page
= nautilus_property_page_new
636 ("NautilusImagePropertiesPage::property_page",
637 gtk_label_new (_("Image")),
639 pages
= g_list_append (pages
, real_page
);
645 property_page_provider_iface_init (NautilusPropertyPageProviderIface
*iface
)
647 iface
->get_pages
= get_property_pages
;
652 nautilus_image_properties_page_provider_init (NautilusImagePropertiesPageProvider
*sidebar
)
657 nautilus_image_properties_page_provider_class_init (NautilusImagePropertiesPageProviderClass
*class)
662 nautilus_image_properties_page_register (void)
664 nautilus_module_add_type (nautilus_image_properties_page_provider_get_type ());