summaryrefslogtreecommitdiff
path: root/extensions/image-properties/nautilus-image-properties-page-model.c
diff options
context:
space:
mode:
authorCarlos Soriano <csoriano@redhat.com>2018-05-22 15:10:47 +0200
committerCarlos Soriano <csoriano@gnome.org>2018-06-20 09:59:44 +0200
commit02d041d003ad497a07a964ee2fb5a6378073cb34 (patch)
tree5b320d1dc2e3d5163633148b4fef92d2b95639db /extensions/image-properties/nautilus-image-properties-page-model.c
parent813a469d445166e4b37e868e0960f30cde540976 (diff)
downloadnautilus-02d041d003ad497a07a964ee2fb5a6378073cb34.tar.gz
general: Make property extensions gtk version independentnew-properties-extension
Nautilus property extensions to add property pages to the property dialog was providing a GtkWidget to be modified by extensions. This makes the extension need to target a specific gtk version, which with the new gtk versioning might be hard to provide, and it's quite a bad practice since it requires everyone to be on top of any gtk update. This is currently holding the work for porting Nautilus to gtk4, since the Totem extension depends on us having the same gtk+ version, which is unlikely. This work makes the extension for providing property extensions not depend on gtk by providing a plain struct to be filled with data that later on Nautilus will layout on the UI. See https://gitlab.gnome.org/GNOME/nautilus/issues/276
Diffstat (limited to 'extensions/image-properties/nautilus-image-properties-page-model.c')
-rw-r--r--extensions/image-properties/nautilus-image-properties-page-model.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/extensions/image-properties/nautilus-image-properties-page-model.c b/extensions/image-properties/nautilus-image-properties-page-model.c
new file mode 100644
index 000000000..437f8abda
--- /dev/null
+++ b/extensions/image-properties/nautilus-image-properties-page-model.c
@@ -0,0 +1,515 @@
+/* Copyright (C) 2004 Red Hat, Inc
+ * Copyright (c) 2007 Novell, Inc.
+ * Copyright (c) 2017 Thomas Bechtold <thomasbechtold@jpberlin.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ * XMP support by Hubert Figuiere <hfiguiere@novell.com>
+ */
+
+#include "nautilus-image-properties-page-model.h"
+
+#include <gexiv2/gexiv2.h>
+#include <glib/gi18n.h>
+
+#define LOAD_BUFFER_SIZE 8192
+#define SECTION_ID 0
+
+struct _NautilusImagePropertiesPageModel
+{
+ NautilusPropertyPageModel parent;
+
+ GCancellable *cancellable;
+ GtkWidget *grid;
+ GdkPixbufLoader *loader;
+ gboolean got_size;
+ gboolean pixbuf_still_loading;
+ unsigned char buffer[LOAD_BUFFER_SIZE];
+ int width;
+ int height;
+
+ GExiv2Metadata *md;
+ gboolean md_ready;
+
+ GList *sections;
+ GList *items;
+};
+
+G_DEFINE_TYPE (NautilusImagePropertiesPageModel,
+ nautilus_image_properties_page_model,
+ NAUTILUS_TYPE_PROPERTY_PAGE_MODEL);
+
+
+static void
+free_section (gpointer data)
+{
+ NautilusPropertyPageModelSection *section = (NautilusPropertyPageModelSection *) data;
+
+ g_free(section->title);
+}
+
+static void
+free_item (gpointer data)
+{
+ NautilusPropertyPageModelItem *section = (NautilusPropertyPageModelItem *) data;
+
+ g_free(section->field);
+ g_free(section->value);
+}
+
+static void
+finalize (GObject *object)
+{
+ NautilusImagePropertiesPageModel *self;
+
+ self = NAUTILUS_IMAGE_PROPERTIES_PAGE_MODEL (object);
+
+ if (self->cancellable != NULL)
+ {
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ }
+
+ g_list_free_full (self->items, free_item);
+ g_list_free_full (self->sections, free_section);
+ self->items = NULL;
+ self->sections = NULL;
+
+ G_OBJECT_CLASS (nautilus_image_properties_page_model_parent_class)->finalize (object);
+}
+
+static void
+nautilus_image_properties_page_model_class_init (NautilusImagePropertiesPageModelClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = finalize;
+}
+
+static void
+append_item (NautilusImagePropertiesPageModel *self,
+ const char *name,
+ const char *value)
+{
+ NautilusPropertyPageModelItem *item;
+
+ if (value != NULL)
+ {
+ item = g_new (NautilusPropertyPageModelItem, 1);
+ item->section_id = SECTION_ID;
+ item->field = g_strdup (name);
+ item->value = g_strdup (value);
+ self->items = g_list_append (self->items, item);
+ }
+}
+
+static void
+nautilus_image_properties_page_model_init (NautilusImagePropertiesPageModel *self)
+{
+ NautilusPropertyPageModelSection *section;
+
+ section = g_new (NautilusPropertyPageModelSection, 1);
+ section->id = SECTION_ID;
+ section->title = g_strdup (_("Basic information"));
+ self->sections = g_list_append (NULL, section);
+}
+
+static void
+append_basic_info (NautilusImagePropertiesPageModel *page)
+{
+ GdkPixbufFormat *format;
+ g_autofree char *name = NULL;
+ g_autofree char *desc = NULL;
+ g_autofree char *value = NULL;
+
+ format = gdk_pixbuf_loader_get_format (page->loader);
+ name = gdk_pixbuf_format_get_name (format);
+ desc = gdk_pixbuf_format_get_description (format);
+ value = g_strdup_printf ("%s (%s)", name, desc);
+
+ append_item (page, _("Image Type"), value);
+
+ g_free (value);
+ value = g_strdup_printf (ngettext ("%d pixel",
+ "%d pixels",
+ page->width),
+ page->width);
+
+ append_item (page, _("Width"), value);
+
+ g_free (value);
+ value = g_strdup_printf (ngettext ("%d pixel",
+ "%d pixels",
+ page->height),
+ page->height);
+
+ append_item (page, _("Height"), value);
+}
+
+static void
+append_gexiv2_tag (NautilusImagePropertiesPageModel *page,
+ const char **tag_names,
+ const char *description)
+{
+ g_assert (tag_names != NULL);
+
+ for (const char **i = tag_names; *i != NULL; i++)
+ {
+ if (gexiv2_metadata_has_tag (page->md, *i))
+ {
+ g_autofree char *tag_value = NULL;
+
+ tag_value = gexiv2_metadata_get_tag_interpreted_string (page->md, *i);
+
+ if (description == NULL)
+ {
+ description = gexiv2_metadata_get_tag_description (*i);
+ }
+
+ /* don't add empty tags - try next one */
+ if (strlen (tag_value) > 0)
+ {
+ append_item (page, description, tag_value);
+ break;
+ }
+ }
+ }
+}
+
+static void
+append_gexiv2_info (NautilusImagePropertiesPageModel *page)
+{
+ double longitude;
+ double latitude;
+ double altitude;
+
+ /* define tags and its alternatives */
+ const char *title[] = { "Xmp.dc.title", NULL };
+ const char *camera_brand[] = { "Exif.Image.Make", NULL };
+ const char *camera_model[] = { "Exif.Image.Model", "Exif.Image.UniqueCameraModel", NULL };
+ const char *created_on[] = { "Exif.Photo.DateTimeOriginal", "Xmp.xmp.CreateDate", "Exif.Image.DateTime", NULL };
+ const char *exposure_time[] = { "Exif.Photo.ExposureTime", NULL };
+ const char *aperture_value[] = { "Exif.Photo.ApertureValue", NULL };
+ const char *iso_speed_ratings[] = { "Exif.Photo.ISOSpeedRatings", "Xmp.exifEX.ISOSpeed", NULL };
+ const char *flash[] = { "Exif.Photo.Flash", NULL };
+ const char *metering_mode[] = { "Exif.Photo.MeteringMode", NULL };
+ const char *exposure_mode[] = { "Exif.Photo.ExposureMode", NULL };
+ const char *focal_length[] = { "Exif.Photo.FocalLength", NULL };
+ const char *software[] = { "Exif.Image.Software", NULL };
+ const char *description[] = { "Xmp.dc.description", "Exif.Photo.UserComment", NULL };
+ const char *subject[] = { "Xmp.dc.subject", NULL };
+ const char *creator[] = { "Xmp.dc.creator", "Exif.Image.Artist", NULL };
+ const char *rights[] = { "Xmp.dc.rights", NULL };
+ const char *rating[] = { "Xmp.xmp.Rating", NULL };
+
+ if (!page->md_ready)
+ {
+ return;
+ }
+
+ append_gexiv2_tag (page, camera_brand, _("Camera Brand"));
+ append_gexiv2_tag (page, camera_model, _("Camera Model"));
+ append_gexiv2_tag (page, exposure_time, _("Exposure Time"));
+ append_gexiv2_tag (page, exposure_mode, _("Exposure Program"));
+ append_gexiv2_tag (page, aperture_value, _("Aperture Value"));
+ append_gexiv2_tag (page, iso_speed_ratings, _("ISO Speed Rating"));
+ append_gexiv2_tag (page, flash, _("Flash Fired"));
+ append_gexiv2_tag (page, metering_mode, _("Metering Mode"));
+ append_gexiv2_tag (page, focal_length, _("Focal Length"));
+ append_gexiv2_tag (page, software, _("Software"));
+ append_gexiv2_tag (page, title, _("Title"));
+ append_gexiv2_tag (page, description, _("Description"));
+ append_gexiv2_tag (page, subject, _("Keywords"));
+ append_gexiv2_tag (page, creator, _("Creator"));
+ append_gexiv2_tag (page, created_on, _("Created On"));
+ append_gexiv2_tag (page, rights, _("Copyright"));
+ append_gexiv2_tag (page, rating, _("Rating"));
+
+ if (gexiv2_metadata_get_gps_info (page->md, &longitude, &latitude, &altitude))
+ {
+ g_autofree char *gps_coords = NULL;
+
+ /* Translators: These are the coordinates of a position where a picture was taken. */
+ gps_coords = g_strdup_printf (_("%f N / %f W (%.0f m)"), latitude, longitude, altitude);
+
+ append_item (page, _("Coordinates"), gps_coords);
+ }
+}
+
+static void
+load_finished (NautilusImagePropertiesPageModel *page)
+{
+ if (page->loader != NULL)
+ {
+ gdk_pixbuf_loader_close (page->loader, NULL);
+ }
+
+ if (page->got_size)
+ {
+ append_basic_info (page);
+ append_gexiv2_info (page);
+ }
+ else
+ {
+ append_item (page, _("Failed to load image information"), NULL);
+ }
+
+ if (page->loader != NULL)
+ {
+ g_object_unref (page->loader);
+ page->loader = NULL;
+ }
+ page->md_ready = FALSE;
+ g_clear_object (&page->md);
+
+ nautilus_property_page_model_set_sections (NAUTILUS_PROPERTY_PAGE_MODEL (page),
+ page->sections);
+ nautilus_property_page_model_set_items (NAUTILUS_PROPERTY_PAGE_MODEL (page),
+ page->items);
+}
+
+static void
+file_close_callback (GObject *object,
+ GAsyncResult *res,
+ gpointer data)
+{
+ NautilusImagePropertiesPageModel *page;
+ GInputStream *stream;
+
+ page = data;
+ stream = G_INPUT_STREAM (object);
+
+ g_input_stream_close_finish (stream, res, NULL);
+
+ g_clear_object (&page->cancellable);
+}
+
+static void
+file_read_callback (GObject *object,
+ GAsyncResult *res,
+ gpointer data)
+{
+ NautilusImagePropertiesPageModel *page;
+ GInputStream *stream;
+ g_autoptr (GError) error = NULL;
+ gssize count_read;
+ gboolean done_reading;
+
+ page = data;
+ stream = G_INPUT_STREAM (object);
+ count_read = g_input_stream_read_finish (stream, res, &error);
+ done_reading = FALSE;
+
+ if (count_read > 0)
+ {
+ g_assert (count_read <= sizeof (page->buffer));
+
+ if (page->pixbuf_still_loading)
+ {
+ if (!gdk_pixbuf_loader_write (page->loader,
+ page->buffer,
+ count_read,
+ NULL))
+ {
+ page->pixbuf_still_loading = FALSE;
+ }
+ }
+
+ if (page->pixbuf_still_loading)
+ {
+ g_input_stream_read_async (G_INPUT_STREAM (stream),
+ page->buffer,
+ sizeof (page->buffer),
+ G_PRIORITY_DEFAULT,
+ page->cancellable,
+ file_read_callback,
+ page);
+ }
+ else
+ {
+ done_reading = TRUE;
+ }
+ }
+ else
+ {
+ /* either EOF, cancelled or an error occurred */
+ done_reading = TRUE;
+ }
+
+ if (error != NULL)
+ {
+ g_autofree char *uri = NULL;
+
+ uri = g_file_get_uri (G_FILE (object));
+
+ g_warning ("Error reading %s: %s", uri, error->message);
+ }
+
+ if (done_reading)
+ {
+ load_finished (page);
+ g_input_stream_close_async (stream,
+ G_PRIORITY_DEFAULT,
+ page->cancellable,
+ file_close_callback,
+ page);
+ }
+}
+
+static void
+size_prepared_callback (GdkPixbufLoader *loader,
+ int width,
+ int height,
+ gpointer callback_data)
+{
+ NautilusImagePropertiesPageModel *page;
+
+ page = callback_data;
+
+ page->height = height;
+ page->width = width;
+ page->got_size = TRUE;
+ page->pixbuf_still_loading = FALSE;
+}
+
+typedef struct
+{
+ NautilusImagePropertiesPageModel *page;
+ NautilusFileInfo *file_info;
+} FileOpenData;
+
+static void
+file_open_callback (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autofree FileOpenData *data = NULL;
+ NautilusImagePropertiesPageModel *page;
+ GFile *file;
+ g_autofree char *uri = NULL;
+ g_autoptr (GError) error = NULL;
+ g_autoptr (GFileInputStream) stream = NULL;
+
+ data = user_data;
+ page = data->page;
+ file = G_FILE (object);
+ uri = g_file_get_uri (file);
+ stream = g_file_read_finish (file, res, &error);
+ if (stream != NULL)
+ {
+ g_autofree char *mime_type = NULL;
+
+ mime_type = nautilus_file_info_get_mime_type (data->file_info);
+
+ page->loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, &error);
+ if (error != NULL)
+ {
+ g_warning ("Error creating loader for %s: %s", uri, error->message);
+ }
+ page->pixbuf_still_loading = TRUE;
+ page->width = 0;
+ page->height = 0;
+
+ g_signal_connect (page->loader,
+ "size-prepared",
+ G_CALLBACK (size_prepared_callback),
+ page);
+
+ g_input_stream_read_async (G_INPUT_STREAM (stream),
+ page->buffer,
+ sizeof (page->buffer),
+ G_PRIORITY_DEFAULT,
+ page->cancellable,
+ file_read_callback,
+ page);
+ }
+ else
+ {
+ g_warning ("Error reading %s: %s", uri, error->message);
+ load_finished (page);
+ }
+}
+
+void
+nautilus_image_properties_page_model_load_from_file_info (NautilusImagePropertiesPageModel *self,
+ NautilusFileInfo *file_info)
+{
+ g_autofree char *uri = NULL;
+ g_autoptr (GFile) file = NULL;
+ g_autofree char *path = NULL;
+ FileOpenData *data;
+
+ g_return_if_fail (NAUTILUS_IS_IMAGE_PROPERTIES_PAGE_MODEL (self));
+ g_return_if_fail (file_info != NULL);
+
+ self->cancellable = g_cancellable_new ();
+
+ g_print ("### removing old info\n");
+ /* Remove old information, if any */
+ g_list_free_full (self->items, free_item);
+ self->items = NULL;
+ nautilus_property_page_model_set_items (NAUTILUS_PROPERTY_PAGE_MODEL (self),
+ NULL);
+
+ uri = nautilus_file_info_get_uri (file_info);
+ file = g_file_new_for_uri (uri);
+ path = g_file_get_path (file);
+
+ /* gexiv2 metadata init */
+ self->md_ready = gexiv2_initialize ();
+ if (!self->md_ready)
+ {
+ g_warning ("Unable to initialize gexiv2");
+ }
+ else
+ {
+ self->md = gexiv2_metadata_new ();
+ if (path != NULL)
+ {
+ g_autoptr (GError) error = NULL;
+
+ if (!gexiv2_metadata_open_path (self->md, path, &error))
+ {
+ g_warning ("gexiv2 metadata not supported for '%s': %s", path, error->message);
+ self->md_ready = FALSE;
+ }
+ }
+ else
+ {
+ self->md_ready = FALSE;
+ }
+ }
+
+ data = g_new0 (FileOpenData, 1);
+
+ data->page = self;
+ data->file_info = file_info;
+
+ g_file_read_async (file,
+ G_PRIORITY_DEFAULT,
+ self->cancellable,
+ file_open_callback,
+ data);
+}
+
+NautilusImagePropertiesPageModel *
+nautilus_image_properties_page_model_new (void)
+{
+ return g_object_new (NAUTILUS_TYPE_IMAGE_PROPERTIES_PAGE_MODEL,
+ "title", _("Image"),
+ NULL);
+}