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