[gnome-photos/wip/rishi/collection: 26/40] Add PhotosDeviceItem



commit 882c43b18caeacf7527affb17dd21ab31e65105e
Author: Debarshi Ray <debarshir gnome org>
Date:   Wed Jan 24 11:56:53 2018 +0100

    Add PhotosDeviceItem
    
    https://bugzilla.gnome.org/show_bug.cgi?id=751212

 configure.ac             |   2 +
 src/Makefile.am          |   4 +
 src/photos-device-item.c | 580 +++++++++++++++++++++++++++++++++++++++++++++++
 src/photos-device-item.h |  35 +++
 src/photos-utils.c       |   2 +
 5 files changed, 623 insertions(+)
---
diff --git a/configure.ac b/configure.ac
index e4fd24e7..33a250bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -45,6 +45,7 @@ GLIB_MIN_VERSION=2.44.0
 GOA_MIN_VERSION=3.8.0
 GRILO_MIN_VERSION=0.3.0
 GTK_MIN_VERSION=3.22.16
+GUDEV_MIN_VERSION=232
 
 G_LOG_DOMAIN=AC_PACKAGE_TARNAME
 AC_DEFINE_UNQUOTED([G_LOG_DOMAIN], ["$G_LOG_DOMAIN"], [Log domain])
@@ -115,6 +116,7 @@ PKG_CHECK_MODULES(GRILO, [grilo-0.3 >= $GRILO_MIN_VERSION])
 PKG_CHECK_MODULES(GSETTINGS_DESKTOP_SCHEMAS, [gsettings-desktop-schemas])
 PKG_CHECK_MODULES(GTK, [gtk+-3.0 >= $GTK_MIN_VERSION])
 PKG_CHECK_MODULES(GTK_UNIX_PRINT, [gtk+-unix-print-3.0])
+PKG_CHECK_MODULES(GUDEV, [gudev-1.0 >= $GUDEV_MIN_VERSION])
 PKG_CHECK_MODULES(PNG, [libpng16])
 PKG_CHECK_MODULES(TRACKER, [tracker-control-2.0 tracker-sparql-2.0])
 
diff --git a/src/Makefile.am b/src/Makefile.am
index dc3d8a86..10a1c67a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -70,6 +70,8 @@ gnome_photos_SOURCES = \
        photos-delete-item-job.h \
        photos-delete-notification.c \
        photos-delete-notification.h \
+       photos-device-item.c \
+       photos-device-item.h \
        photos-dlna-renderer.c \
        photos-dlna-renderer.h \
        photos-dlna-renderers-dialog.c \
@@ -424,6 +426,7 @@ AM_CPPFLAGS = \
        $(GSETTINGS_DESKTOP_SCHEMAS_CFLAGS) \
        $(GTK_CFLAGS) \
        $(GTK_UNIX_PRINT_CFLAGS) \
+       $(GUDEV_CFLAGS) \
        $(PNG_CFLAGS) \
        $(TRACKER_CFLAGS) \
        -I$(top_srcdir)/libgd \
@@ -452,6 +455,7 @@ gnome_photos_LDADD = \
        $(GRILO_LIBS) \
        $(GTK_LIBS) \
        $(GTK_UNIX_PRINT_LIBS) \
+       $(GUDEV_LIBS) \
        $(JPEG_LIBS) \
        $(PNG_LIBS) \
        $(TRACKER_LIBS) \
diff --git a/src/photos-device-item.c b/src/photos-device-item.c
new file mode 100644
index 00000000..3f92b3bb
--- /dev/null
+++ b/src/photos-device-item.c
@@ -0,0 +1,580 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2018 Red Hat, Inc.
+ *
+ * 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 3 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/>.
+ */
+
+/* Based on code from:
+ *   + Documents
+ */
+
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <gudev/gudev.h>
+#include <tracker-sparql.h>
+
+#include "photos-base-manager.h"
+#include "photos-device-item.h"
+#include "photos-error.h"
+#include "photos-glib.h"
+#include "photos-query.h"
+#include "photos-search-context.h"
+#include "photos-source.h"
+#include "photos-utils.h"
+
+
+struct _PhotosDeviceItem
+{
+  PhotosBaseItem parent_instance;
+  GCancellable *cancellable;
+  GVolume *volume;
+  PhotosBaseManager *src_mngr;
+};
+
+
+G_DEFINE_TYPE_WITH_CODE (PhotosDeviceItem, photos_device_item, PHOTOS_TYPE_BASE_ITEM,
+                         photos_utils_ensure_extension_points ();
+                         g_io_extension_point_implement (PHOTOS_BASE_ITEM_EXTENSION_POINT_NAME,
+                                                         g_define_type_id,
+                                                         "device",
+                                                         0));
+
+
+typedef struct _PhotosDeviceItemPopulateFromCursorData PhotosDeviceItemPopulateFromCursorData;
+
+struct _PhotosDeviceItemPopulateFromCursorData
+{
+  PhotosDeviceItem *item;
+  TrackerSparqlCursor *cursor;
+};
+
+
+static PhotosDeviceItemPopulateFromCursorData *
+photos_device_item_populate_from_cursor_data_new (PhotosDeviceItem *item, TrackerSparqlCursor *cursor)
+{
+  PhotosDeviceItemPopulateFromCursorData *data;
+
+  data = g_slice_new0 (PhotosDeviceItemPopulateFromCursorData);
+  data->item = item;
+  data->cursor = g_object_ref (cursor);
+
+  return data;
+}
+
+
+static void
+photos_device_item_populate_from_cursor_data_free (PhotosDeviceItemPopulateFromCursorData *data)
+{
+  g_object_unref (data->cursor);
+  g_slice_free (PhotosDeviceItemPopulateFromCursorData, data);
+}
+
+
+static GVolume *
+photos_device_item_get_volume_from_enclosing_mount (PhotosDeviceItem *self, GMount *enclosing_mount)
+{
+  g_autoptr (GVolume) volume = NULL;
+  GVolume *ret_val = NULL;
+
+  volume = g_mount_get_volume (enclosing_mount);
+  if (volume == NULL)
+    {
+      g_autoptr (GFile) enclosing_root = NULL;
+      guint i;
+      guint n_items;
+
+      enclosing_root = g_mount_get_root (enclosing_mount);
+
+      n_items = g_list_model_get_n_items (G_LIST_MODEL (self->src_mngr));
+      for (i = 0; i < n_items; i++)
+        {
+          g_autoptr (GFile) root = NULL;
+          GMount *mount;
+          g_autoptr (PhotosSource) source = NULL;
+
+          source = PHOTOS_SOURCE (g_list_model_get_object (G_LIST_MODEL (self->src_mngr), i));
+          mount = photos_source_get_mount (source);
+          if (mount == NULL)
+            continue;
+
+          root = g_mount_get_root (mount);
+          if (g_file_equal (enclosing_root, root))
+            {
+              volume = g_mount_get_volume (mount);
+              if (volume != NULL)
+                break;
+            }
+        }
+
+      if (volume == NULL)
+        {
+          for (i = 0; i < n_items; i++)
+            {
+              g_autoptr (GFile) root = NULL;
+              GMount *mount;
+              g_autoptr (PhotosSource) source = NULL;
+
+              source = PHOTOS_SOURCE (g_list_model_get_object (G_LIST_MODEL (self->src_mngr), i));
+              mount = photos_source_get_mount (source);
+              if (mount == NULL)
+                continue;
+
+              root = g_mount_get_root (mount);
+              if (g_file_has_prefix (root, enclosing_root))
+                {
+                  volume = g_mount_get_volume (mount);
+                  if (volume != NULL)
+                    break;
+                }
+            }
+        }
+    }
+
+  ret_val = g_object_ref (volume);
+  return ret_val;
+}
+
+
+static void
+photos_device_item_populate_from_cursor_find_enclosing_mount (GObject *source_object,
+                                                              GAsyncResult *res,
+                                                              gpointer user_data)
+{
+  PhotosDeviceItemPopulateFromCursorData *data = (PhotosDeviceItemPopulateFromCursorData *) user_data;
+  PhotosDeviceItem *self;
+  GFile *file = G_FILE (source_object);
+  g_autoptr (GMount) mount = NULL;
+
+  {
+    g_autoptr (GError) error = NULL;
+
+    mount = g_file_find_enclosing_mount_finish (file, res, &error);
+    if (error != NULL)
+      {
+        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+          goto out;
+
+        g_warning ("Unable to find enclosing mount: %s", error->message);
+      }
+  }
+
+  self = data->item;
+
+  g_clear_object (&self->volume);
+  if (mount != NULL)
+    {
+      self->volume = photos_device_item_get_volume_from_enclosing_mount (self, mount);
+      if (self->volume == NULL)
+        g_warning ("Unable to find volume");
+    }
+
+  PHOTOS_BASE_ITEM_CLASS (photos_device_item_parent_class)->populate_from_cursor (PHOTOS_BASE_ITEM (self),
+                                                                                  data->cursor);
+
+ out:
+  photos_device_item_populate_from_cursor_data_free (data);
+  return;
+}
+
+
+static gboolean
+photos_device_item_source_widget_activate_link (GtkLinkButton *button, gpointer user_data)
+{
+  g_autoptr (GAppInfo) default_app = NULL;
+  g_autoptr (GAppInfo) nautilus_app = NULL;
+  g_autoptr (GAppLaunchContext) ctx = NULL;
+  PhotosDeviceItem *self;
+  gboolean ret_val = GDK_EVENT_PROPAGATE;
+  const gchar *commandline = "nautilus --select";
+  const gchar *default_app_id;
+  const gchar *source_uri;
+  const gchar *uri;
+  g_autofree gchar *command_line = NULL;
+  g_autofree gchar *source_uri_scheme = NULL;
+
+  g_return_val_if_fail (GTK_IS_LINK_BUTTON (button), GDK_EVENT_PROPAGATE);
+  g_return_val_if_fail (PHOTOS_IS_DEVICE_ITEM (user_data), GDK_EVENT_PROPAGATE);
+
+  self = PHOTOS_DEVICE_ITEM (user_data);
+
+  source_uri = gtk_link_button_get_uri (button);
+
+  /* Even though g_file_query_default_handler calls
+   * g_app_info_get_default_for_uri_scheme, we have to do it here in
+   * case GFile can't parse source_uri correctly.
+   *
+   * See glib/gio/gappinfo.c
+   */
+
+  source_uri_scheme = g_uri_parse_scheme (source_uri);
+  if (source_uri_scheme != NULL && source_uri_scheme[0] != '\0')
+    default_app = g_app_info_get_default_for_uri_scheme (source_uri_scheme);
+
+  if (default_app == NULL)
+    {
+      g_autoptr (GFile) source_link = NULL;
+
+      source_link = g_file_new_for_uri (source_uri);
+
+      {
+        g_autoptr (GError) error = NULL;
+
+        default_app = g_file_query_default_handler (source_link, NULL, &error);
+        if (error != NULL)
+          {
+            g_warning ("Unable to query default handler for %s: %s", source_uri, error->message);
+            goto out;
+          }
+      }
+    }
+
+  g_return_val_if_fail (G_IS_APP_INFO (default_app), GDK_EVENT_PROPAGATE);
+
+  default_app_id = g_app_info_get_id (default_app);
+  if (g_strcmp0 (default_app_id, "org.gnome.Nautilus.desktop") != 0)
+    goto out;
+
+  {
+    g_autoptr (GError) error = NULL;
+
+    nautilus_app = g_app_info_create_from_commandline (commandline, NULL, G_APP_INFO_CREATE_NONE, &error);
+    if (error != NULL)
+      {
+        g_warning ("Unable to create GAppInfo from '%s': %s", commandline, error->message);
+        goto out;
+      }
+  }
+
+  uri = photos_base_item_get_uri (PHOTOS_BASE_ITEM (self));
+  ctx = photos_utils_new_app_launch_context_from_widget (GTK_WIDGET (button));
+
+  {
+    g_autoptr (GError) error = NULL;
+
+    if (!photos_glib_app_info_launch_uri (nautilus_app, uri, ctx, &error))
+      {
+        g_warning ("Unable to launch '%s': %s", commandline, error->message);
+        goto out;
+      }
+  }
+
+  ret_val = GDK_EVENT_STOP;
+
+ out:
+  return ret_val;
+}
+
+
+static gchar *
+photos_device_item_create_filename_fallback (PhotosBaseItem *item)
+{
+  g_warn_if_reached ();
+  return NULL;
+}
+
+
+static gchar *
+photos_device_item_create_name_fallback (PhotosBaseItem *item)
+{
+  const gchar *filename;
+  gchar *ret_val;
+
+  filename = photos_base_item_get_filename (item);
+  ret_val = photos_glib_filename_strip_extension (filename);
+  return ret_val;
+}
+
+
+static gboolean
+photos_device_item_create_thumbnail (PhotosBaseItem *item, GCancellable *cancellable, GError **error)
+{
+  PhotosDeviceItem *self = PHOTOS_DEVICE_ITEM (item);
+  g_autoptr (GFile) file = NULL;
+  GQuark orientation;
+  gboolean ret_val = FALSE;
+  g_autofree gchar *thumbnail_path = NULL;
+  const gchar *mime_type;
+  const gchar *uri;
+  gint64 height;
+  gint64 mtime;
+  gint64 width;
+
+  uri = photos_base_item_get_uri (PHOTOS_BASE_ITEM (self));
+  file = g_file_new_for_uri (uri);
+  mime_type = photos_base_item_get_mime_type (PHOTOS_BASE_ITEM (self));
+  mtime = photos_base_item_get_mtime (PHOTOS_BASE_ITEM (self));
+  orientation = photos_base_item_get_orientation (PHOTOS_BASE_ITEM (self));
+  height = photos_base_item_get_height (PHOTOS_BASE_ITEM (self));
+  width = photos_base_item_get_width (PHOTOS_BASE_ITEM (self));
+  thumbnail_path = photos_base_item_create_thumbnail_path (PHOTOS_BASE_ITEM (self));
+
+  if (!photos_utils_create_thumbnail (file,
+                                      mime_type,
+                                      mtime,
+                                      orientation,
+                                      height,
+                                      width,
+                                      "",
+                                      thumbnail_path,
+                                      cancellable,
+                                      error))
+    goto out;
+
+  ret_val = TRUE;
+
+ out:
+  return ret_val;
+}
+
+
+static gchar *
+photos_device_item_create_thumbnail_path (PhotosBaseItem *item)
+{
+  PhotosDeviceItem *self = PHOTOS_DEVICE_ITEM (item);
+  g_autoptr (GUdevClient) client = NULL;
+  g_autoptr (GUdevDevice) device = NULL;
+  const gchar *model_id;
+  const gchar *serial_short;
+  const gchar *vendor_id;
+  g_autofree gchar *device_dir = NULL;
+  g_autofree gchar *path_default = NULL;
+  g_autofree gchar *path_default_basename = NULL;
+  g_autofree gchar *path_default_dir = NULL;
+  g_autofree gchar *unix_device = NULL;
+  g_autofree gchar *uuid = NULL;
+  gchar *path = NULL;
+  const gchar *subsystems[] = { NULL };
+
+  path_default
+    = PHOTOS_BASE_ITEM_CLASS (photos_device_item_parent_class)->create_thumbnail_path (PHOTOS_BASE_ITEM 
(self));
+
+  path_default_basename = g_path_get_basename (path_default);
+  path_default_dir = g_path_get_dirname (path_default);
+
+  if (self->volume == NULL)
+    {
+      device_dir = g_strdup ("unknown");
+      goto out;
+    }
+
+  uuid = g_volume_get_uuid (self->volume);
+  if (uuid != NULL && uuid[0] != '\0')
+    {
+      device_dir = g_steal_pointer (&uuid);
+      goto out;
+    }
+
+  unix_device = g_volume_get_identifier (self->volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+
+  client = g_udev_client_new (subsystems);
+  device = g_udev_client_query_by_device_file (client, unix_device);
+  if (device == NULL)
+    {
+      device_dir = g_strdup ("unknown");
+      goto out;
+    }
+
+  model_id = g_udev_device_get_property (device, "ID_MODEL_ID");
+  serial_short = g_udev_device_get_property (device, "ID_SERIAL_SHORT");
+  vendor_id = g_udev_device_get_property (device, "ID_VENDOR_ID");
+
+  if (model_id != NULL
+      && model_id[0] != '\0'
+      && serial_short != NULL
+      && serial_short[0] != '\0'
+      && vendor_id != NULL
+      && vendor_id[0] != '\0')
+    {
+      device_dir = g_strconcat (vendor_id, "-", model_id, "-", serial_short, NULL);
+    }
+  else if (model_id != NULL
+           && model_id[0] != '\0'
+           && vendor_id != NULL
+           && vendor_id[0] != '\0')
+    {
+      device_dir = g_strconcat (vendor_id, "-", model_id, NULL);
+    }
+  else
+    {
+      device_dir = g_strdup ("unknown");
+    }
+
+ out:
+  path = g_build_filename (path_default_dir, "devices", device_dir, path_default_basename, NULL);
+  return path;
+}
+
+
+static gchar *
+photos_device_item_download (PhotosBaseItem *item, GCancellable *cancellable, GError **error)
+{
+  g_assert_not_reached ();
+  return NULL;
+}
+
+
+static GtkWidget *
+photos_device_item_get_source_widget (PhotosBaseItem *item)
+{
+  g_autoptr (GFile) file = NULL;
+  g_autoptr (GFile) source_link = NULL;
+  GtkWidget *label;
+  GtkWidget *source_widget;
+  const gchar *uri;
+  g_autofree gchar *source_path = NULL;
+  g_autofree gchar *source_uri = NULL;
+
+  g_return_val_if_fail (!photos_base_item_is_collection (item), NULL);
+
+  uri = photos_base_item_get_uri (item);
+  file = g_file_new_for_uri (uri);
+  source_link = g_file_get_parent (file);
+  source_path = g_file_get_path (source_link);
+  source_uri = g_file_get_uri (source_link);
+
+  source_widget = gtk_link_button_new_with_label (source_uri, source_path);
+  gtk_widget_set_halign (source_widget, GTK_ALIGN_START);
+  g_signal_connect_object (source_widget,
+                           "activate-link",
+                           G_CALLBACK (photos_device_item_source_widget_activate_link),
+                           item,
+                           0);
+
+  label = gtk_bin_get_child (GTK_BIN (source_widget));
+  gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+  gtk_label_set_max_width_chars (GTK_LABEL (label), 40);
+
+  return source_widget;
+}
+
+
+static gboolean
+photos_device_item_metadata_add_shared (PhotosBaseItem  *item,
+                                        const gchar     *provider_type,
+                                        const gchar     *account_identity,
+                                        const gchar     *shared_id,
+                                        GCancellable    *cancellable,
+                                        GError         **error)
+{
+  g_assert_not_reached ();
+  return FALSE;
+}
+
+
+static void
+photos_device_item_populate_from_cursor (PhotosBaseItem *item, TrackerSparqlCursor *cursor)
+{
+  PhotosDeviceItem *self = PHOTOS_DEVICE_ITEM (item);
+  g_autoptr (GFile) file = NULL;
+  PhotosDeviceItemPopulateFromCursorData *data;
+  const gchar *uri;
+
+  g_cancellable_cancel (self->cancellable);
+  g_clear_object (&self->cancellable);
+  self->cancellable = g_cancellable_new ();
+
+  uri = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URI, NULL);
+  if (uri == NULL)
+    uri = "";
+
+  file = g_file_new_for_uri (uri);
+  data = photos_device_item_populate_from_cursor_data_new (self, cursor);
+  g_file_find_enclosing_mount_async (file,
+                                     G_PRIORITY_DEFAULT,
+                                     self->cancellable,
+                                     photos_device_item_populate_from_cursor_find_enclosing_mount,
+                                     data);
+}
+
+
+static void
+photos_device_item_trash (PhotosBaseItem *item)
+{
+  g_assert_not_reached ();
+}
+
+
+static void
+photos_device_item_constructed (GObject *object)
+{
+  PhotosDeviceItem *self = PHOTOS_DEVICE_ITEM (object);
+  const gchar *mime_type;
+
+  G_OBJECT_CLASS (photos_device_item_parent_class)->constructed (object);
+
+  mime_type = photos_base_item_get_mime_type (PHOTOS_BASE_ITEM (self));
+  if (mime_type != NULL)
+    {
+      g_autoptr (GAppInfo) default_app = NULL;
+
+      default_app = g_app_info_get_default_for_type (mime_type, FALSE);
+      if (default_app != NULL)
+        photos_base_item_set_default_app (PHOTOS_BASE_ITEM (self), default_app);
+    }
+}
+
+
+static void
+photos_device_item_dispose (GObject *object)
+{
+  PhotosDeviceItem *self = PHOTOS_DEVICE_ITEM (object);
+
+  if (self->cancellable != NULL)
+    {
+      g_cancellable_cancel (self->cancellable);
+      g_clear_object (&self->cancellable);
+    }
+
+  g_clear_object (&self->volume);
+  g_clear_object (&self->src_mngr);
+
+  G_OBJECT_CLASS (photos_device_item_parent_class)->dispose (object);
+}
+
+
+static void
+photos_device_item_init (PhotosDeviceItem *self)
+{
+  GApplication *app;
+  PhotosSearchContextState *state;
+
+  app = g_application_get_default ();
+  state = photos_search_context_get_state (PHOTOS_SEARCH_CONTEXT (app));
+
+  self->cancellable = g_cancellable_new ();
+  self->src_mngr = g_object_ref (state->src_mngr);
+}
+
+
+static void
+photos_device_item_class_init (PhotosDeviceItemClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  PhotosBaseItemClass *base_item_class = PHOTOS_BASE_ITEM_CLASS (class);
+
+  object_class->constructed = photos_device_item_constructed;
+  object_class->dispose = photos_device_item_dispose;
+  base_item_class->create_filename_fallback = photos_device_item_create_filename_fallback;
+  base_item_class->create_name_fallback = photos_device_item_create_name_fallback;
+  base_item_class->create_thumbnail = photos_device_item_create_thumbnail;
+  base_item_class->create_thumbnail_path = photos_device_item_create_thumbnail_path;
+  base_item_class->download = photos_device_item_download;
+  base_item_class->get_source_widget = photos_device_item_get_source_widget;
+  base_item_class->metadata_add_shared = photos_device_item_metadata_add_shared;
+  base_item_class->populate_from_cursor = photos_device_item_populate_from_cursor;
+  base_item_class->trash = photos_device_item_trash;
+}
diff --git a/src/photos-device-item.h b/src/photos-device-item.h
new file mode 100644
index 00000000..b5f27c06
--- /dev/null
+++ b/src/photos-device-item.h
@@ -0,0 +1,35 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2018 Red Hat, Inc.
+ *
+ * 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 3 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/>.
+ */
+
+/* Based on code from:
+ *   + Documents
+ */
+
+#ifndef PHOTOS_DEVICE_ITEM_H
+#define PHOTOS_DEVICE_ITEM_H
+
+#include "photos-base-item.h"
+
+G_BEGIN_DECLS
+
+#define PHOTOS_TYPE_DEVICE_ITEM (photos_device_item_get_type ())
+G_DECLARE_FINAL_TYPE (PhotosDeviceItem, photos_device_item, PHOTOS, DEVICE_ITEM, PhotosBaseItem);
+
+G_END_DECLS
+
+#endif /* PHOTOS_DEVICE_ITEM_H */
diff --git a/src/photos-utils.c b/src/photos-utils.c
index cc2f8e78..e31db31e 100644
--- a/src/photos-utils.c
+++ b/src/photos-utils.c
@@ -34,6 +34,7 @@
 #include <libgd/gd.h>
 
 #include "photos-application.h"
+#include "photos-device-item.h"
 #include "photos-enums.h"
 #include "photos-error.h"
 #include "photos-facebook-item.h"
@@ -704,6 +705,7 @@ photos_utils_ensure_builtins (void)
 
   if (g_once_init_enter (&once_init_value))
     {
+      g_type_ensure (PHOTOS_TYPE_DEVICE_ITEM);
       g_type_ensure (PHOTOS_TYPE_FACEBOOK_ITEM);
       g_type_ensure (PHOTOS_TYPE_FLICKR_ITEM);
       g_type_ensure (PHOTOS_TYPE_GOOGLE_ITEM);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]