No issues found
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., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, 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 "nautilus-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 <libnautilus-extension/nautilus-property-page-provider.h>
34 #include <libnautilus-private/nautilus-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 NautilusImagePropertiesPageDetails {
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 } NautilusImagePropertiesPageProvider;
81
82 typedef struct {
83 GObjectClass parent;
84 } NautilusImagePropertiesPageProviderClass;
85
86
87 static GType nautilus_image_properties_page_provider_get_type (void);
88 static void property_page_provider_iface_init (NautilusPropertyPageProviderIface *iface);
89
90
91 G_DEFINE_TYPE (NautilusImagePropertiesPage, nautilus_image_properties_page, GTK_TYPE_BOX);
92
93 G_DEFINE_TYPE_WITH_CODE (NautilusImagePropertiesPageProvider, nautilus_image_properties_page_provider, G_TYPE_OBJECT,
94 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_PROPERTY_PAGE_PROVIDER,
95 property_page_provider_iface_init));
96
97 static void
98 nautilus_image_properties_page_finalize (GObject *object)
99 {
100 NautilusImagePropertiesPage *page;
101
102 page = NAUTILUS_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 (nautilus_image_properties_page_parent_class)->finalize (object);
111 }
112
113 static void
114 file_close_callback (GObject *object,
115 GAsyncResult *res,
116 gpointer data)
117 {
118 NautilusImagePropertiesPage *page;
119 GInputStream *stream;
120
121 page = NAUTILUS_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
131 append_item (NautilusImagePropertiesPage *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 *
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
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 *
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 *
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
224 append_tag_value_pair (NautilusImagePropertiesPage *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
251 static void
252 append_exifdata_string (ExifData *exifdata, NautilusImagePropertiesPage *page)
253 {
254 if (exifdata && exifdata->ifd[0] && exifdata->ifd[0]->count) {
255 append_tag_value_pair (page, exifdata, EXIF_TAG_MAKE, _("Camera Brand"));
256 append_tag_value_pair (page, exifdata, EXIF_TAG_MODEL, _("Camera Model"));
257
258 /* Choose which date to show in order of relevance */
259 if (!append_tag_value_pair (page, exifdata, EXIF_TAG_DATE_TIME_ORIGINAL, _("Date Taken")))
260 {
261 if (!append_tag_value_pair (page, exifdata, EXIF_TAG_DATE_TIME_DIGITIZED, _("Date Digitized")))
262 {
263 append_tag_value_pair (page, exifdata, EXIF_TAG_DATE_TIME, _("Date Modified"));
264 }
265 }
266
267 append_tag_value_pair (page, exifdata, EXIF_TAG_EXPOSURE_TIME, _("Exposure Time"));
268 append_tag_value_pair (page, exifdata, EXIF_TAG_APERTURE_VALUE, _("Aperture Value"));
269 append_tag_value_pair (page, exifdata, EXIF_TAG_ISO_SPEED_RATINGS, _("ISO Speed Rating"));
270 append_tag_value_pair (page, exifdata, EXIF_TAG_FLASH,_("Flash Fired"));
271 append_tag_value_pair (page, exifdata, EXIF_TAG_METERING_MODE, _("Metering Mode"));
272 append_tag_value_pair (page, exifdata, EXIF_TAG_EXPOSURE_PROGRAM, _("Exposure Program"));
273 append_tag_value_pair (page, exifdata, EXIF_TAG_FOCAL_LENGTH,_("Focal Length"));
274 append_tag_value_pair (page, exifdata, EXIF_TAG_SOFTWARE, _("Software"));
275 }
276 }
277 #endif /*HAVE_EXIF*/
278
279 #ifdef HAVE_EXEMPI
280 static void
281 append_xmp_value_pair (NautilusImagePropertiesPage *page,
282 XmpPtr xmp,
283 const char *ns,
284 const char *propname,
285 char *descr)
286 {
287 uint32_t options;
288 XmpStringPtr value;
289
290 value = xmp_string_new();
291 if (xmp_get_property (xmp, ns, propname, value, &options)) {
292 if (XMP_IS_PROP_SIMPLE (options)) {
293 append_item (page, descr, xmp_string_cstr (value));
294 }
295 else if (XMP_IS_PROP_ARRAY (options)) {
296 XmpIteratorPtr iter;
297
298 iter = xmp_iterator_new (xmp, ns, propname, XMP_ITER_JUSTLEAFNODES);
299 if (iter) {
300 GString *str;
301 gboolean first = TRUE;
302
303 str = g_string_new (NULL);
304
305 while (xmp_iterator_next (iter, NULL, NULL, value, &options)
306 && !XMP_IS_PROP_QUALIFIER(options)) {
307 if (!first) {
308 g_string_append_printf (str, ", ");
309 }
310 else {
311 first = FALSE;
312 }
313 g_string_append_printf (str,
314 "%s",
315 xmp_string_cstr(value));
316 }
317 xmp_iterator_free(iter);
318 append_item (page, descr, g_string_free (str, FALSE));
319 }
320 }
321 }
322 xmp_string_free(value);
323 }
324
325 static void
326 append_xmpdata_string (XmpPtr xmp, NautilusImagePropertiesPage *page)
327 {
328 if (xmp != NULL) {
329 append_xmp_value_pair (page, xmp, NS_IPTC4XMP, "Location", _("Location"));
330 append_xmp_value_pair (page, xmp, NS_DC, "description", _("Description"));
331 append_xmp_value_pair (page, xmp, NS_DC, "subject", _("Keywords"));
332 append_xmp_value_pair (page, xmp, NS_DC, "creator", _("Creator"));
333 append_xmp_value_pair (page, xmp, NS_DC, "rights", _("Copyright"));
334 append_xmp_value_pair (page, xmp, NS_XAP,"Rating", _("Rating"));
335 /* TODO add CC licenses */
336 }
337 }
338 #endif
339
340 static void
341 load_finished (NautilusImagePropertiesPage *page)
342 {
343 GdkPixbufFormat *format;
344 GtkWidget *label;
345 char *name, *desc;
346 char *value;
347
348 label = gtk_grid_get_child_at (GTK_GRID (page->details->grid), 0, 0);
349 gtk_container_remove (GTK_CONTAINER (page->details->grid), label);
350
351 if (page->details->loader != NULL) {
352 gdk_pixbuf_loader_close (page->details->loader, NULL);
353 }
354
355 if (page->details->got_size) {
356 #ifdef HAVE_EXIF
357 ExifData *exif_data;
358 #endif
359
360 format = gdk_pixbuf_loader_get_format (page->details->loader);
361
362 name = gdk_pixbuf_format_get_name (format);
363 desc = gdk_pixbuf_format_get_description (format);
364 value = g_strdup_printf ("%s (%s)", name, desc);
365 g_free (name);
366 g_free (desc);
367 append_item (page, _("Image Type"), value);
368 g_free (value);
369 value = g_strdup_printf (ngettext ("%d pixel",
370 "%d pixels",
371 page->details->width),
372 page->details->width);
373 append_item (page, _("Width"), value);
374 g_free (value);
375 value = g_strdup_printf (ngettext ("%d pixel",
376 "%d pixels",
377 page->details->height),
378 page->details->height);
379 append_item (page, _("Height"), value);
380 g_free (value);
381 #ifdef HAVE_EXIF
382 exif_data = exif_loader_get_data (page->details->exifldr);
383 append_exifdata_string (exif_data, page);
384 exif_data_unref (exif_data);
385 #endif /*HAVE_EXIF*/
386 #ifdef HAVE_EXEMPI
387 append_xmpdata_string (page->details->xmp, page);
388 #endif /*HAVE EXEMPI*/
389 } else {
390 append_item (page, _("Failed to load image information"), NULL);
391 }
392
393 if (page->details->loader != NULL) {
394 g_object_unref (page->details->loader);
395 page->details->loader = NULL;
396 }
397 #ifdef HAVE_EXIF
398 if (page->details->exifldr != NULL) {
399 exif_loader_unref (page->details->exifldr);
400 page->details->exifldr = NULL;
401 }
402 #endif /*HAVE_EXIF*/
403 #ifdef HAVE_EXEMPI
404 if (page->details->xmp != NULL) {
405 xmp_free (page->details->xmp);
406 page->details->xmp = NULL;
407 }
408 #endif
409 }
410
411 static void
412 file_read_callback (GObject *object,
413 GAsyncResult *res,
414 gpointer data)
415 {
416 NautilusImagePropertiesPage *page;
417 GInputStream *stream;
418 gssize count_read;
419 GError *error;
420 int exif_still_loading;
421 gboolean done_reading;
422
423 page = NAUTILUS_IMAGE_PROPERTIES_PAGE (data);
424 stream = G_INPUT_STREAM (object);
425
426 error = NULL;
427 done_reading = FALSE;
428 count_read = g_input_stream_read_finish (stream, res, &error);
429
430 if (count_read > 0) {
431
432 g_assert (count_read <= sizeof(page->details->buffer));
433
434 #ifdef HAVE_EXIF
435 exif_still_loading = exif_loader_write (page->details->exifldr,
436 (guchar *) page->details->buffer,
437 count_read);
438 #else
439 exif_still_loading = 0;
440 #endif
441
442 if (page->details->pixbuf_still_loading) {
443 if (!gdk_pixbuf_loader_write (page->details->loader,
444 (const guchar *) page->details->buffer,
445 count_read,
446 NULL)) {
447 page->details->pixbuf_still_loading = FALSE;
448 }
449 }
450
451 if (page->details->pixbuf_still_loading ||
452 (exif_still_loading == 1)) {
453 g_input_stream_read_async (G_INPUT_STREAM (stream),
454 page->details->buffer,
455 sizeof (page->details->buffer),
456 0,
457 page->details->cancellable,
458 file_read_callback,
459 page);
460 }
461 else {
462 done_reading = TRUE;
463 }
464 }
465 else {
466 /* either EOF, cancelled or an error occurred */
467 done_reading = TRUE;
468 }
469
470 if (error != NULL) {
471 char *uri = g_file_get_uri (G_FILE (object));
472 g_warning ("Error reading %s: %s", uri, error->message);
473 g_free (uri);
474 g_clear_error (&error);
475 }
476
477 if (done_reading) {
478 load_finished (page);
479 g_input_stream_close_async (stream,
480 0,
481 page->details->cancellable,
482 file_close_callback,
483 page);
484 }
485 }
486
487 static void
488 size_prepared_callback (GdkPixbufLoader *loader,
489 int width,
490 int height,
491 gpointer callback_data)
492 {
493 NautilusImagePropertiesPage *page;
494
495 page = NAUTILUS_IMAGE_PROPERTIES_PAGE (callback_data);
496
497 page->details->height = height;
498 page->details->width = width;
499 page->details->got_size = TRUE;
500 page->details->pixbuf_still_loading = FALSE;
501 }
502
503 typedef struct {
504 NautilusImagePropertiesPage *page;
505 NautilusFileInfo *info;
506 } FileOpenData;
507
508 static void
509 file_open_callback (GObject *object,
510 GAsyncResult *res,
511 gpointer user_data)
512 {
513 FileOpenData *data = user_data;
514 NautilusImagePropertiesPage *page = data->page;
515 GFile *file;
516 GFileInputStream *stream;
517 GError *error;
518 char *uri;
519
520 file = G_FILE (object);
521 uri = g_file_get_uri (file);
522
523 error = NULL;
524 stream = g_file_read_finish (file, res, &error);
525 if (stream) {
526 char *mime_type;
527
528 mime_type = nautilus_file_info_get_mime_type (data->info);
529 page->details->loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, &error);
530 if (error != NULL) {
531 g_warning ("Error creating loader for %s: %s", uri, error->message);
532 g_clear_error (&error);
533 }
534 page->details->pixbuf_still_loading = TRUE;
535 page->details->width = 0;
536 page->details->height = 0;
537 #ifdef HAVE_EXIF
538 page->details->exifldr = exif_loader_new ();
539 #endif /*HAVE_EXIF*/
540 g_free (mime_type);
541
542 g_signal_connect (page->details->loader,
543 "size_prepared",
544 G_CALLBACK (size_prepared_callback),
545 page);
546
547 g_input_stream_read_async (G_INPUT_STREAM (stream),
548 page->details->buffer,
549 sizeof (page->details->buffer),
550 0,
551 page->details->cancellable,
552 file_read_callback,
553 page);
554
555 g_object_unref (stream);
556 } else {
557 g_warning ("Error reading %s: %s", uri, error->message);
558 g_clear_error (&error);
559 load_finished (page);
560 }
561
562 g_free (uri);
563 g_free (data);
564 }
565
566 static void
567 load_location (NautilusImagePropertiesPage *page,
568 NautilusFileInfo *info)
569 {
570 GFile *file;
571 char *uri;
572 FileOpenData *data;
573
574 g_assert (NAUTILUS_IS_IMAGE_PROPERTIES_PAGE (page));
575 g_assert (info != NULL);
576
577 page->details->cancellable = g_cancellable_new ();
578
579 uri = nautilus_file_info_get_uri (info);
580 file = g_file_new_for_uri (uri);
581
582 #ifdef HAVE_EXEMPI
583 {
584 /* Current Exempi does not support setting custom IO to be able to use Gnome-vfs */
585 /* So it will only work with local files. Future version might remove this limitation */
586 XmpFilePtr xf;
587 char *localname;
588
589 localname = g_filename_from_uri (uri, NULL, NULL);
590 if (localname) {
591 xf = xmp_files_open_new (localname, 0);
592 page->details->xmp = xmp_files_get_new_xmp (xf); /* only load when loading */
593 xmp_files_close (xf, 0);
594 g_free (localname);
595 }
596 else {
597 page->details->xmp = NULL;
598 }
599 }
600 #endif /*HAVE_EXEMPI*/
601
602 data = g_new0 (FileOpenData, 1);
603 data->page = page;
604 data->info = info;
605
606 g_file_read_async (file,
607 0,
608 page->details->cancellable,
609 file_open_callback,
610 data);
611
612 g_object_unref (file);
613 g_free (uri);
614 }
615
616 static void
617 nautilus_image_properties_page_class_init (NautilusImagePropertiesPageClass *class)
618 {
619 GObjectClass *object_class;
620
621 object_class = G_OBJECT_CLASS (class);
622
623 object_class->finalize = nautilus_image_properties_page_finalize;
624
625 g_type_class_add_private (object_class, sizeof(NautilusImagePropertiesPageDetails));
626 }
627
628 static void
629 nautilus_image_properties_page_init (NautilusImagePropertiesPage *page)
630 {
631 GtkWidget *sw;
632
633 page->details = G_TYPE_INSTANCE_GET_PRIVATE (page,
634 NAUTILUS_TYPE_IMAGE_PROPERTIES_PAGE,
635 NautilusImagePropertiesPageDetails);
636
637 gtk_orientable_set_orientation (GTK_ORIENTABLE (page), GTK_ORIENTATION_VERTICAL);
638 gtk_box_set_homogeneous (GTK_BOX (page), FALSE);
639 gtk_box_set_spacing (GTK_BOX (page), 0);
640 gtk_container_set_border_width (GTK_CONTAINER (page), 0);
641
642 sw = gtk_scrolled_window_new (NULL, NULL);
643 gtk_container_set_border_width (GTK_CONTAINER (sw), 0);
644 gtk_widget_set_vexpand (GTK_WIDGET (sw), TRUE);
645 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
646 GTK_POLICY_NEVER,
647 GTK_POLICY_AUTOMATIC);
648 gtk_box_pack_start (GTK_BOX (page), sw, FALSE, TRUE, 2);
649
650 page->details->grid = gtk_grid_new ();
651 gtk_container_set_border_width (GTK_CONTAINER (page->details->grid), 6);
652 gtk_orientable_set_orientation (GTK_ORIENTABLE (page->details->grid), GTK_ORIENTATION_VERTICAL);
653 gtk_grid_set_row_spacing (GTK_GRID (page->details->grid), 6);
654 gtk_grid_set_column_spacing (GTK_GRID (page->details->grid), 20);
655 append_item (page, _("Loading..."), NULL);
656 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), page->details->grid);
657
658 gtk_widget_show_all (GTK_WIDGET (page));
659 }
660
661 static gboolean
662 is_mime_type_supported (const char *mime_type)
663 {
664 gboolean supported;
665 GSList *formats;
666 GSList *l;
667
668 supported = FALSE;
669 formats = gdk_pixbuf_get_formats ();
670
671 for (l = formats; supported == FALSE && l != NULL; l = l->next) {
672 GdkPixbufFormat *format = l->data;
673 char **mime_types = gdk_pixbuf_format_get_mime_types (format);
674 int i;
675
676 for (i = 0; mime_types[i] != NULL; i++) {
677 if (strcmp (mime_types[i], mime_type) == 0) {
678 supported = TRUE;
679 break;
680 }
681 }
682 g_strfreev (mime_types);
683 }
684 g_slist_free (formats);
685
686 return supported;
687 }
688
689 static GList *
690 get_property_pages (NautilusPropertyPageProvider *provider,
691 GList *files)
692 {
693 GList *pages;
694 NautilusFileInfo *file;
695 char *mime_type;
696
697 /* Only show the property page if 1 file is selected */
698 if (!files || files->next != NULL) {
699 return NULL;
700 }
701
702 pages = NULL;
703 file = NAUTILUS_FILE_INFO (files->data);
704
705 mime_type = nautilus_file_info_get_mime_type (file);
706 if (mime_type != NULL
707 && is_mime_type_supported (mime_type)) {
708 NautilusImagePropertiesPage *page;
709 NautilusPropertyPage *real_page;
710
711 page = g_object_new (nautilus_image_properties_page_get_type (), NULL);
712 load_location (page, file);
713
714 real_page = nautilus_property_page_new
715 ("NautilusImagePropertiesPage::property_page",
716 gtk_label_new (_("Image")),
717 GTK_WIDGET (page));
718 pages = g_list_append (pages, real_page);
719 }
720
721 g_free (mime_type);
722
723 return pages;
724 }
725
726 static void
727 property_page_provider_iface_init (NautilusPropertyPageProviderIface *iface)
728 {
729 iface->get_pages = get_property_pages;
730 }
731
732
733 static void
734 nautilus_image_properties_page_provider_init (NautilusImagePropertiesPageProvider *sidebar)
735 {
736 }
737
738 static void
739 nautilus_image_properties_page_provider_class_init (NautilusImagePropertiesPageProviderClass *class)
740 {
741 }
742
743 void
744 nautilus_image_properties_page_register (void)
745 {
746 nautilus_module_add_type (nautilus_image_properties_page_provider_get_type ());
747 }