[gtk+/wip/csoriano/cloud-providers: 232/233] cloud providers



commit 6a6697c898efb343c335fae231b2d69ed7522006
Author: Carlos Soriano <csoriano gnome org>
Date:   Wed Jun 17 20:30:46 2015 +0200

    cloud providers

 docs/reference/gtk/gtk3-sections.txt               |    8 +
 gtk/Makefile.am                                    |    4 +
 gtk/gtk.h                                          |    2 +
 gtk/gtkcloudprovider.c                             |  432 +++++++++++++++++++
 gtk/gtkcloudprovider.h                             |   84 ++++
 gtk/gtkcloudprovidermanager.c                      |  310 ++++++++++++++
 gtk/gtkcloudprovidermanager.h                      |   61 +++
 gtk/gtkfilechooser.c                               |    3 +-
 tests/Makefile.am                                  |   20 +
 tests/org.gtk.CloudProviderServerExample.ini       |    4 +
 tests/org.gtk.CloudProviderServerExample.service   |    3 +
 .../org.gtk.CloudProviderServerExample.service.in  |    3 +
 tests/testcloudproviderclient.c                    |  131 ++++++
 tests/testcloudproviderserver.c                    |  437 ++++++++++++++++++++
 tests/testpopover.c                                |    1 +
 15 files changed, 1502 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index 7b29c13..ec27cc5 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -2631,6 +2631,14 @@ gtk_paned_get_type
 </SECTION>
 
 <SECTION>
+<FILE>gtkcloudprovidermanager</FILE>
+<TITLE>GtkCloudProviderManager</TITLE>
+GtkCloudProviderManager
+gtk_cloud_provider_manager_dup_singleton
+gtk_cloud_provider_manager_get_providers
+</SECTION>
+
+<SECTION>
 <FILE>gtkplacessidebar</FILE>
 <TITLE>GtkPlacesSidebar</TITLE>
 GtkPlacesSidebar
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 4795f84..b91020e 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -164,6 +164,8 @@ gtk_public_h_sources =              \
        gtkcontainer.h          \
        gtkcssprovider.h        \
        gtkcsssection.h         \
+       gtkcloudprovider.h      \
+       gtkcloudprovidermanager.h       \
        gtkdebug.h              \
        gtkdialog.h             \
        gtkdnd.h                \
@@ -666,6 +668,8 @@ gtk_base_c_sources =                \
        gtkcsstypes.c           \
        gtkcssvalue.c           \
        gtkcsswidgetnode.c      \
+       gtkcloudprovider.c      \
+       gtkcloudprovidermanager.c       \
        gtkdialog.c             \
        gtkdrawingarea.c        \
        gtkeditable.c           \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 3cbd13f..0ec9af3 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -71,6 +71,8 @@
 #include <gtk/gtkcheckbutton.h>
 #include <gtk/gtkcheckmenuitem.h>
 #include <gtk/gtkclipboard.h>
+#include <gtk/gtkcloudprovider.h>
+#include <gtk/gtkcloudprovidermanager.h>
 #include <gtk/gtkcolorbutton.h>
 #include <gtk/gtkcolorchooser.h>
 #include <gtk/gtkcolorchooserdialog.h>
diff --git a/gtk/gtkcloudprovider.c b/gtk/gtkcloudprovider.c
new file mode 100644
index 0000000..efab1c5
--- /dev/null
+++ b/gtk/gtkcloudprovider.c
@@ -0,0 +1,432 @@
+/* gtkcloudprovider.c
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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/>.
+ */
+
+#include "config.h"
+#include "gtkcloudprovider.h"
+#include "gtkmenu.h"
+#include <gio/gio.h>
+
+static const gchar provider_xml[] =
+  "<node>"
+  "  <interface name='org.gtk.CloudProvider'>"
+  "    <method name='GetName'>"
+  "      <arg type='s' name='name' direction='out'/>"
+  "    </method>"
+  "    <method name='GetStatus'>"
+  "      <arg type='i' name='name' direction='out'/>"
+  "    </method>"
+  "    <method name='GetIcon'>"
+  "      <arg type='v' name='icon' direction='out'/>"
+  "    </method>"
+  "    <method name='GetPath'>"
+  "      <arg type='s' name='path' direction='out'/>"
+  "    </method>"
+  "  </interface>"
+  "</node>";
+
+
+typedef struct
+{
+  gchar *name;
+  gchar *path;
+  GtkCloudProviderStatus status;
+  GIcon *icon;
+  GtkWidget *menu;
+  GMenuModel *menu_model;
+  GActionGroup *action_group;
+
+  GDBusConnection *bus;
+  GDBusProxy *proxy;
+  gchar *bus_name;
+  gchar *object_path;
+  GCancellable *cancellable;
+} GtkCloudProviderPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkCloudProvider, gtk_cloud_provider, G_TYPE_OBJECT)
+
+enum {
+  CHANGED,
+  LAST_SIGNAL
+};
+
+static guint gSignals [LAST_SIGNAL];
+
+static void
+on_get_icon (GObject      *source_object,
+             GAsyncResult *res,
+             gpointer      user_data)
+{
+  GtkCloudProvider *self = GTK_CLOUD_PROVIDER (user_data);
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+  GError *error = NULL;
+  GVariant *variant_tuple;
+  GVariant *variant_dict;
+  GVariant *variant;
+
+  g_clear_object (&priv->icon);
+
+  variant_tuple = g_dbus_proxy_call_finish (priv->proxy, res, &error);
+  g_print ("variant tuple %s\n", g_variant_print (variant_tuple, TRUE));
+  if (error != NULL)
+    {
+      g_warning ("Error getting the provider icon %s", error->message);
+      goto out;
+    }
+
+  variant_dict = g_variant_get_child_value (variant_tuple, 0);
+  variant = g_variant_get_child_value (variant_dict, 0);
+  priv->icon = g_icon_deserialize (variant);
+  g_variant_unref (variant);
+  g_variant_unref (variant_dict);
+
+out:
+  g_variant_unref (variant_tuple);
+  g_signal_emit_by_name (self, "changed");
+}
+
+static void
+on_get_name (GObject      *source_object,
+             GAsyncResult *res,
+             gpointer      user_data)
+{
+  GtkCloudProvider *self = GTK_CLOUD_PROVIDER (user_data);
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+  GError *error = NULL;
+  GVariant *variant_tuple;
+  GVariant *variant;
+
+  if (priv->name != NULL)
+    g_free (priv->name);
+
+  variant_tuple = g_dbus_proxy_call_finish (priv->proxy, res, &error);
+  if (error != NULL)
+    {
+      g_warning ("Error getting the provider name %s", error->message);
+      goto out;
+    }
+
+  variant = g_variant_get_child_value (variant_tuple, 0);
+  priv->name = g_variant_dup_string (variant, NULL);
+  g_variant_unref (variant);
+
+out:
+  g_variant_unref (variant_tuple);
+  g_signal_emit_by_name (self, "changed");
+}
+
+static void
+on_get_path (GObject      *source_object,
+             GAsyncResult *res,
+             gpointer      user_data)
+{
+  GtkCloudProvider *self = GTK_CLOUD_PROVIDER (user_data);
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+  GError *error = NULL;
+  GVariant *variant_tuple;
+  GVariant *variant;
+
+  if (priv->path != NULL)
+    g_free (priv->path);
+
+  variant_tuple = g_dbus_proxy_call_finish (priv->proxy, res, &error);
+  if (error != NULL)
+    {
+      g_warning ("Error getting the provider path %s", error->message);
+      goto out;
+    }
+
+  variant = g_variant_get_child_value (variant_tuple, 0);
+  priv->path = g_variant_dup_string (variant, NULL);
+  g_variant_unref (variant);
+
+out:
+  g_variant_unref (variant_tuple);
+  g_signal_emit_by_name (self, "changed");
+}
+
+static void
+on_get_status (GObject      *source_object,
+               GAsyncResult *res,
+               gpointer      user_data)
+{
+  GtkCloudProvider *self = GTK_CLOUD_PROVIDER (user_data);
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+  GError *error = NULL;
+  GVariant *variant_tuple;
+  GVariant *variant;
+
+  variant_tuple = g_dbus_proxy_call_finish (priv->proxy, res, &error);
+  if (error != NULL)
+    {
+      g_warning ("Error getting the provider status %s", error->message);
+      goto out;
+    }
+
+  variant = g_variant_get_child_value (variant_tuple, 0);
+  priv->status = g_variant_get_int32 (variant);
+  g_variant_unref (variant);
+
+out:
+  g_variant_unref (variant_tuple);
+  g_signal_emit_by_name (self, "changed");
+}
+
+void
+gtk_cloud_provider_update (GtkCloudProvider *self)
+{
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  if (priv->proxy != NULL)
+    {
+      g_dbus_proxy_call (priv->proxy,
+                         "GetName",
+                         g_variant_new ("()"),
+                         G_DBUS_CALL_FLAGS_NONE,
+                         -1,
+                         NULL,
+                         (GAsyncReadyCallback) on_get_name,
+                         self);
+
+      g_dbus_proxy_call (priv->proxy,
+                         "GetStatus",
+                         g_variant_new ("()"),
+                         G_DBUS_CALL_FLAGS_NONE,
+                         -1,
+                         NULL,
+                         (GAsyncReadyCallback) on_get_status,
+                         self);
+
+      g_dbus_proxy_call (priv->proxy,
+                         "GetIcon",
+                         g_variant_new ("()"),
+                         G_DBUS_CALL_FLAGS_NONE,
+                         -1,
+                         NULL,
+                         (GAsyncReadyCallback) on_get_icon,
+                         self);
+
+      g_dbus_proxy_call (priv->proxy,
+                         "GetPath",
+                         g_variant_new ("()"),
+                         G_DBUS_CALL_FLAGS_NONE,
+                         -1,
+                         NULL,
+                         (GAsyncReadyCallback) on_get_path,
+                         self);
+
+      priv->menu_model = (GMenuModel*) g_dbus_menu_model_get (priv->bus,
+                                                              priv->bus_name,
+                                                              priv->object_path);
+      priv->action_group = (GActionGroup*) g_dbus_action_group_get (priv->bus,
+                                                                    priv->bus_name,
+                                                                    priv->object_path);
+      /* The GMenuModel returned is lazily loaded, so it can't be used as a populated
+       * GMenuModel. To avoid confusion to GtkCloudProvider clients, use a GtkMenu,
+       * which will take care of loading the items */
+      priv->menu = gtk_menu_new_from_model (priv->menu_model);
+    }
+}
+
+static void
+on_proxy_created (GObject      *source_object,
+                  GAsyncResult *res,
+                  gpointer      user_data)
+{
+  GError *error = NULL;
+  GtkCloudProvider *self;
+  GtkCloudProviderPrivate *priv;
+  GDBusProxy *proxy;
+
+  proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+  if (error != NULL)
+    {
+      if (error->code != G_IO_ERROR_CANCELLED)
+        g_warning ("Error creating proxy for cloud provider %s", error->message);
+      return;
+    }
+  self = GTK_CLOUD_PROVIDER (user_data);
+  priv = gtk_cloud_provider_get_instance_private (self);
+
+  priv->proxy = proxy;
+
+  gtk_cloud_provider_update (self);
+}
+
+static void
+on_bus_acquired (GObject      *source_object,
+                 GAsyncResult *res,
+                 gpointer      user_data)
+{
+  GError *error = NULL;
+  GtkCloudProvider *self;
+  GDBusConnection *bus;
+  GtkCloudProviderPrivate *priv;
+  GDBusInterfaceInfo *interface_info;
+  GDBusNodeInfo *proxy_info;
+
+  bus = g_bus_get_finish (res, &error);
+  if (error != NULL)
+    {
+      if (error->code != G_IO_ERROR_CANCELLED)
+        g_warning ("Error acdquiring bus for cloud provider %s", error->message);
+      return;
+    }
+
+  self = GTK_CLOUD_PROVIDER (user_data);
+  priv = gtk_cloud_provider_get_instance_private (user_data);
+  priv->bus = bus;
+  proxy_info = g_dbus_node_info_new_for_xml (provider_xml, &error);
+  interface_info = g_dbus_node_info_lookup_interface (proxy_info, "org.gtk.CloudProvider");
+  g_clear_object (&priv->cancellable);
+  priv->cancellable = g_cancellable_new ();
+  g_dbus_proxy_new (priv->bus,
+                    G_DBUS_PROXY_FLAGS_NONE,
+                    interface_info,
+                    priv->bus_name,
+                    priv->object_path,
+                    "org.gtk.CloudProvider",
+                    priv->cancellable,
+                    on_proxy_created,
+                    self);
+}
+
+GtkCloudProvider*
+gtk_cloud_provider_new (const gchar *bus_name,
+                        const gchar *object_path)
+{
+  GtkCloudProvider *self;
+  GtkCloudProviderPrivate *priv;
+
+  self = g_object_new (GTK_TYPE_CLOUD_PROVIDER, NULL);
+  priv = gtk_cloud_provider_get_instance_private (self);
+
+  priv->bus_name = g_strdup (bus_name);
+  priv->object_path = g_strdup (object_path);
+  priv->cancellable = g_cancellable_new ();
+  priv->status = GTK_CLOUD_PROVIDER_STATUS_INVALID;
+  g_bus_get (G_BUS_TYPE_SESSION,
+             priv->cancellable,
+             on_bus_acquired,
+             self);
+
+  return self;
+}
+
+static void
+gtk_cloud_provider_finalize (GObject *object)
+{
+  GtkCloudProvider *self = (GtkCloudProvider *)object;
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  g_cancellable_cancel (priv->cancellable);
+  g_clear_object (&priv->cancellable);
+  g_free (priv->name);
+  g_free (priv->path);
+  g_clear_object (&priv->icon);
+  g_clear_object (&priv->menu);
+  g_clear_object (&priv->action_group);
+  g_clear_object (&priv->bus);
+  g_clear_object (&priv->proxy);
+  g_free (priv->bus_name);
+  g_free (priv->object_path);
+
+  G_OBJECT_CLASS (gtk_cloud_provider_parent_class)->finalize (object);
+}
+
+static void
+gtk_cloud_provider_class_init (GtkCloudProviderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_cloud_provider_finalize;
+
+  gSignals [CHANGED] =
+    g_signal_new ("changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL,
+                  NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE,
+                  0);
+}
+
+static void
+gtk_cloud_provider_init (GtkCloudProvider *self)
+{
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  priv->status = GTK_CLOUD_PROVIDER_STATUS_INVALID;
+}
+
+gchar*
+gtk_cloud_provider_get_name (GtkCloudProvider *self)
+{
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  return priv->name;
+}
+
+GtkCloudProviderStatus
+gtk_cloud_provider_get_status (GtkCloudProvider *self)
+{
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  return priv->status;
+}
+
+GIcon*
+gtk_cloud_provider_get_icon (GtkCloudProvider *self)
+{
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  return priv->icon;
+}
+
+GtkWidget*
+gtk_cloud_provider_get_menu (GtkCloudProvider *self)
+{
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  return priv->menu;
+}
+
+GMenuModel*
+gtk_cloud_provider_get_menu_model (GtkCloudProvider *self)
+{
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  return priv->menu_model;
+}
+
+GActionGroup*
+gtk_cloud_provider_get_action_group (GtkCloudProvider *self)
+{
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  return priv->action_group;
+}
+
+gchar *
+gtk_cloud_provider_get_path (GtkCloudProvider *self)
+{
+  GtkCloudProviderPrivate *priv = gtk_cloud_provider_get_instance_private (self);
+
+  return priv->path;
+}
diff --git a/gtk/gtkcloudprovider.h b/gtk/gtkcloudprovider.h
new file mode 100644
index 0000000..0036af6
--- /dev/null
+++ b/gtk/gtkcloudprovider.h
@@ -0,0 +1,84 @@
+/* gtkcloudprovider.h
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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/>.
+ */
+
+#ifndef GTK_CLOUD_PROVIDER_H
+#define GTK_CLOUD_PROVIDER_H
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gio/gio.h>
+#include <gdk/gdk.h>
+#include "gtkwidget.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+  GTK_CLOUD_PROVIDER_STATUS_INVALID,
+  GTK_CLOUD_PROVIDER_STATUS_IDLE,
+  GTK_CLOUD_PROVIDER_STATUS_SYNCING,
+  GTK_CLOUD_PROVIDER_STATUS_ERROR
+} GtkCloudProviderStatus;
+
+#define GTK_TYPE_CLOUD_PROVIDER             (gtk_cloud_provider_get_type())
+#define GTK_CLOUD_PROVIDER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CLOUD_PROVIDER, 
GtkCloudProvider))
+#define GTK_CLOUD_PROVIDER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CLOUD_PROVIDER, 
GtkCloudProviderClass))
+#define GTK_IS_CLOUD_PROVIDER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CLOUD_PROVIDER))
+#define GTK_IS_CLOUD_PROVIDER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CLOUD_PROVIDER))
+#define GTK_CLOUD_PROVIDER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CLOUD_PROVIDER, 
GtkCloudProviderClass))
+
+typedef struct _GtkCloudProvider GtkCloudProvider;
+typedef struct _GtkCloudProviderClass GtkCloudProviderClass;
+
+
+struct _GtkCloudProviderClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GtkCloudProvider
+{
+  GObject parent_instance;
+};
+
+GDK_AVAILABLE_IN_3_18
+GType          gtk_cloud_provider_get_type          (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_3_18
+GtkCloudProvider *gtk_cloud_provider_new (const gchar *bus_name,
+                                          const gchar *object_path);
+GDK_AVAILABLE_IN_3_18
+gchar* gtk_cloud_provider_get_name (GtkCloudProvider *self);
+GDK_AVAILABLE_IN_3_18
+GtkCloudProviderStatus gtk_cloud_provider_get_status (GtkCloudProvider *self);
+GDK_AVAILABLE_IN_3_18
+GIcon *gtk_cloud_provider_get_icon (GtkCloudProvider *self);
+GDK_AVAILABLE_IN_3_18
+GtkWidget *gtk_cloud_provider_get_menu (GtkCloudProvider *self);
+GDK_AVAILABLE_IN_3_18
+GMenuModel *gtk_cloud_provider_get_menu_model (GtkCloudProvider *self);
+GDK_AVAILABLE_IN_3_18
+GActionGroup* gtk_cloud_provider_get_action_group (GtkCloudProvider *self);
+GDK_AVAILABLE_IN_3_18
+gchar *gtk_cloud_provider_get_path (GtkCloudProvider *self);
+
+
+
+G_END_DECLS
+
+#endif /* GTK_CLOUD_PROVIDER_H */
diff --git a/gtk/gtkcloudprovidermanager.c b/gtk/gtkcloudprovidermanager.c
new file mode 100644
index 0000000..9a161b6
--- /dev/null
+++ b/gtk/gtkcloudprovidermanager.c
@@ -0,0 +1,310 @@
+/* gtkcloudprovidermanager.c
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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/>.
+ */
+
+#include "config.h"
+
+#include "gtkcloudprovidermanager.h"
+#include "gtkcloudprovider.h"
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#define KEY_FILE_GROUP "Gtk Cloud Provider"
+
+typedef struct
+{
+  GList *providers;
+  guint dbus_owner_id;
+  GDBusNodeInfo *dbus_node_info;
+} GtkCloudProviderManagerPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkCloudProviderManager, gtk_cloud_provider_manager, G_TYPE_OBJECT)
+
+enum
+{
+  CHANGED,
+  LAST_SIGNAL
+};
+
+static guint gSignals [LAST_SIGNAL];
+
+static const gchar manager_xml[] =
+  "<node>"
+  "  <interface name='org.gtk.CloudProviderManager'>"
+  "    <method name='CloudProviderChanged'>"
+  "    </method>"
+  "  </interface>"
+  "</node>";
+
+static void
+handle_method_call (GDBusConnection       *connection,
+                    const gchar           *sender,
+                    const gchar           *object_path,
+                    const gchar           *interface_name,
+                    const gchar           *method_name,
+                    GVariant              *parameters,
+                    GDBusMethodInvocation *invocation,
+                    gpointer               user_data)
+{
+  if (g_strcmp0 (method_name, "CloudProviderChanged") == 0)
+    {
+      gtk_cloud_provider_manager_update (GTK_CLOUD_PROVIDER_MANAGER (user_data));
+    }
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+  handle_method_call,
+};
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const gchar     *name,
+                 gpointer         user_data)
+{
+  GtkCloudProviderManager *self = user_data;
+  GtkCloudProviderManagerPrivate *priv = gtk_cloud_provider_manager_get_instance_private (self);
+  guint registration_id;
+
+  g_debug ("Registering cloud provider server 'MyCloud'\n");
+  registration_id = g_dbus_connection_register_object (connection,
+                                                       "/org/gtk/CloudProviderManager",
+                                                       priv->dbus_node_info->interfaces[0],
+                                                       &interface_vtable,
+                                                       self,
+                                                       NULL,  /* user_data_free_func */
+                                                       NULL); /* GError** */
+  g_assert (registration_id > 0);
+
+  /* In case some provider updated before adquiring the bus */
+  gtk_cloud_provider_manager_update (GTK_CLOUD_PROVIDER_MANAGER (user_data));
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar     *name,
+                  gpointer         user_data)
+{
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar     *name,
+              gpointer         user_data)
+{
+}
+
+/**
+ * gtk_cloud_provider_manager_dup_singleton
+ * Returns: (transfer none): A manager singleton
+ */
+GtkCloudProviderManager *
+gtk_cloud_provider_manager_dup_singleton (void)
+{
+  static GObject *self = NULL;
+
+  if (self == NULL)
+    {
+      GtkCloudProviderManagerPrivate *priv;
+
+      self = g_object_new (GTK_TYPE_CLOUD_PROVIDER_MANAGER, NULL);
+      priv = gtk_cloud_provider_manager_get_instance_private (GTK_CLOUD_PROVIDER_MANAGER (self));
+
+      /* Export the interface we listen to, so clients can request properties of
+       * the cloud provider such as name, status or icon */
+      priv->dbus_node_info = g_dbus_node_info_new_for_xml (manager_xml, NULL);
+      g_assert (priv->dbus_node_info != NULL);
+
+      priv->dbus_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                                            "org.gtk.CloudProviderManager",
+                                            G_BUS_NAME_OWNER_FLAGS_NONE,
+                                            on_bus_acquired,
+                                            on_name_acquired,
+                                            on_name_lost,
+                                            self,
+                                            NULL);
+      return GTK_CLOUD_PROVIDER_MANAGER (self);
+    }
+  else
+    {
+      return g_object_ref (self);
+    }
+}
+
+static void
+gtk_cloud_provider_manager_finalize (GObject *object)
+{
+  GtkCloudProviderManager *self = (GtkCloudProviderManager *)object;
+  GtkCloudProviderManagerPrivate *priv = gtk_cloud_provider_manager_get_instance_private (self);
+
+  g_list_free_full (priv->providers, g_object_unref);
+  g_bus_unown_name (priv->dbus_owner_id);
+  g_dbus_node_info_unref (priv->dbus_node_info);
+
+  G_OBJECT_CLASS (gtk_cloud_provider_manager_parent_class)->finalize (object);
+}
+
+static void
+gtk_cloud_provider_manager_class_init (GtkCloudProviderManagerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_cloud_provider_manager_finalize;
+
+  gSignals [CHANGED] =
+    g_signal_new ("changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL,
+                  NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE,
+                  0);
+}
+
+static void
+gtk_cloud_provider_manager_init (GtkCloudProviderManager *self)
+{
+}
+
+/**
+ * gtk_cloud_provider_manager_get_providers
+ * @manager: A GtkCloudProviderManager
+ * Returns: (transfer none): The list of providers.
+ */
+GList*
+gtk_cloud_provider_manager_get_providers (GtkCloudProviderManager *manager)
+{
+  GtkCloudProviderManagerPrivate *priv = gtk_cloud_provider_manager_get_instance_private (manager);
+
+  return priv->providers;
+}
+
+static void
+on_cloud_provider_changed (GtkCloudProvider        *cloud_provider,
+                           GtkCloudProviderManager *self)
+{
+  GIcon *icon;
+  gchar *name;
+  guint status;
+
+  name = gtk_cloud_provider_get_name (cloud_provider);
+  icon = gtk_cloud_provider_get_icon (cloud_provider);
+  status = gtk_cloud_provider_get_status (cloud_provider);
+  if (name == NULL || icon == NULL || status == GTK_CLOUD_PROVIDER_STATUS_INVALID)
+    return;
+
+  g_signal_emit_by_name (self, "changed", NULL);
+}
+
+static void
+load_cloud_provider (GtkCloudProviderManager *self,
+                     GFile                   *file)
+{
+  GtkCloudProviderManagerPrivate *priv = gtk_cloud_provider_manager_get_instance_private (self);
+  GKeyFile *key_file;
+  gchar *path;
+  GError *error = NULL;
+  gchar *bus_name;
+  gchar *object_path;
+  gboolean success = FALSE;
+  GtkCloudProvider *cloud_provider;
+
+  key_file = g_key_file_new ();
+  path = g_file_get_path (file);
+  g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &error);
+  if (error != NULL)
+    goto out;
+
+  if (!g_key_file_has_group (key_file, KEY_FILE_GROUP))
+    goto out;
+
+  bus_name = g_key_file_get_string (key_file, KEY_FILE_GROUP, "BusName", &error);
+  if (error != NULL)
+    goto out;
+  object_path = g_key_file_get_string (key_file, KEY_FILE_GROUP, "ObjectPath", &error);
+  if (error != NULL)
+    goto out;
+
+  g_print ("cloud provider found %s %s\n", bus_name, object_path);
+  cloud_provider = gtk_cloud_provider_new (bus_name, object_path);
+  g_signal_connect (cloud_provider, "changed",
+                    G_CALLBACK (on_cloud_provider_changed), self);
+  priv->providers = g_list_append (priv->providers, cloud_provider);
+
+  success = TRUE;
+out:
+  if (!success)
+    g_warning ("Error while loading cloud provider key file at %s", path);
+  g_key_file_free (key_file);
+}
+
+/**
+ * gtk_cloud_provider_manager_update
+ * @manager: A GtkCloudProviderManager
+ */
+void
+gtk_cloud_provider_manager_update (GtkCloudProviderManager *manager)
+{
+  GtkCloudProviderManagerPrivate *priv = gtk_cloud_provider_manager_get_instance_private (manager);
+  const gchar* const *data_dirs;
+  gint i;
+  gint len;
+  gchar *key_files_directory_path;
+  GFile *key_files_directory_file;
+  GError *error = NULL;
+  GFileEnumerator *file_enumerator;
+
+
+  g_list_free_full (priv->providers, g_object_unref);
+  priv->providers = NULL;
+
+  data_dirs = g_get_system_data_dirs ();
+  len = g_strv_length ((gchar **)data_dirs);
+
+  for (i = 0; i < len; i++)
+    {
+      GFileInfo *info;
+
+      key_files_directory_path = g_build_filename (data_dirs[i], "gtk+", "cloud-providers", NULL);
+      key_files_directory_file = g_file_new_for_path (key_files_directory_path);
+      file_enumerator = g_file_enumerate_children (key_files_directory_file,
+                                                   "standard::name,standard::type",
+                                                   G_FILE_QUERY_INFO_NONE,
+                                                   NULL,
+                                                   &error);
+      if (error)
+        {
+          error = NULL;
+          continue;
+        }
+
+      info = g_file_enumerator_next_file (file_enumerator, NULL, &error);
+      if (error)
+        {
+          g_warning ("Error while enumerating file %s error: %s\n", key_files_directory_path, 
error->message);
+          error = NULL;
+          continue;
+        }
+      while (info != NULL && error == NULL)
+        {
+           load_cloud_provider (manager, g_file_enumerator_get_child (file_enumerator, info));
+           info = g_file_enumerator_next_file (file_enumerator, NULL, &error);
+        }
+    }
+}
diff --git a/gtk/gtkcloudprovidermanager.h b/gtk/gtkcloudprovidermanager.h
new file mode 100644
index 0000000..560d5ff
--- /dev/null
+++ b/gtk/gtkcloudprovidermanager.h
@@ -0,0 +1,61 @@
+/* gtkcloudprovidermanager.h
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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/>.
+ */
+#ifndef GTK_CLOUD_PROVIDER_MANAGER_H
+#define GTK_CLOUD_PROVIDER_MANAGER_H
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gio/gio.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CLOUD_PROVIDER_MANAGER             (gtk_cloud_provider_manager_get_type())
+#define GTK_CLOUD_PROVIDER_MANAGER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GTK_TYPE_CLOUD_PROVIDER_MANAGER, GtkCloudProviderManager))
+#define GTK_CLOUD_PROVIDER_MANAGER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), 
GTK_TYPE_CLOUD_PROVIDER_MANAGER, GtkCloudProviderManagerClass))
+#define GTK_IS_CLOUD_PROVIDER_MANAGER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GTK_TYPE_CLOUD_PROVIDER_MANAGER))
+#define GTK_IS_CLOUD_PROVIDER_MANAGER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GTK_TYPE_CLOUD_PROVIDER_MANAGER))
+#define GTK_CLOUD_PROVIDER_MANAGER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GTK_TYPE_CLOUD_PROVIDER_MANAGER, GtkCloudProviderManagerClass))
+
+typedef struct _GtkCloudProviderManager GtkCloudProviderManager;
+typedef struct _GtkCloudProviderManagerClass GtkCloudProviderManagerClass;
+
+struct _GtkCloudProviderManagerClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GtkCloudProviderManager
+{
+  GObject parent_instance;
+};
+
+GDK_AVAILABLE_IN_3_18
+GType          gtk_cloud_provider_manager_get_type          (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_3_18
+GtkCloudProviderManager *gtk_cloud_provider_manager_dup_singleton (void);
+GDK_AVAILABLE_IN_3_18
+void gtk_cloud_provider_manager_update (GtkCloudProviderManager *self);
+GDK_AVAILABLE_IN_3_18
+GList *gtk_cloud_provider_manager_get_providers (GtkCloudProviderManager *self);
+
+G_END_DECLS
+
+#endif /* GTK_CLOUD_PROVIDER_MANAGER_H */
diff --git a/gtk/gtkfilechooser.c b/gtk/gtkfilechooser.c
index 969eae2..853330a 100644
--- a/gtk/gtkfilechooser.c
+++ b/gtk/gtkfilechooser.c
@@ -23,6 +23,7 @@
 #include "gtktypebuiltins.h"
 #include "gtkprivate.h"
 #include "gtkmarshalers.h"
+#include "gtkcloudprovidermanager.h"
 
 
 /**
@@ -181,7 +182,7 @@ static void
 gtk_file_chooser_default_init (GtkFileChooserInterface *iface)
 {
   GType iface_type = G_TYPE_FROM_INTERFACE (iface);
-
+  gtk_cloud_provider_manager_dup_singleton ();
   /**
    * GtkFileChooser::current-folder-changed:
    * @chooser: the object which received the signal.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 233daa4..fd87f68 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -30,6 +30,18 @@ if OS_LINUX
 fontconfig_programs = testfontchooserdialog
 endif
 
+cloudproviderdir = $(datadir)/gtk+/cloud-providers
+dist_cloudprovider_DATA = org.gtk.CloudProviderServerExample.ini
+
+servicedir = $(datadir)/dbus-1/services
+service_in_files = org.gtk.CloudProviderServerExample.service.in
+service_DATA = $(service_in_files:.service.in=.service)
+
+org.gtk.CloudProviderServerExample.service: org.gtk.CloudProviderServerExample.service.in
+       $(AM_V_GEN)     \
+               [ -d $(@D) ] || $(mkdir_p) $(@D) ; \
+               sed -e "s|\ bindir\@|$(bindir)|" $< > $  tmp && mv $  tmp $@
+
 noinst_PROGRAMS =  $(TEST_PROGS)       \
        overlayscroll                   \
        syncscroll                      \
@@ -166,6 +178,8 @@ noinst_PROGRAMS =  $(TEST_PROGS)    \
        testactionbar                   \
        testwindowsize                  \
        testpopover                     \
+       testcloudproviderserver         \
+       testcloudproviderclient         \
        gdkgears                        \
        $(NULL)
 
@@ -306,6 +320,8 @@ teststack_DEPENDENCIES = $(TEST_DEPS)
 testrevealer_DEPENDENCIES = $(TEST_DEPS)
 testtitlebar_DEPENDENCIES = $(TEST_DEPS)
 testwindowsize_DEPENDENCIES = $(TEST_DEPS)
+testcloudproviderserver_DEPENDENCIES = $(TEST_DEPS)
+testcloudproviderclient_DEPENDENCIES = $(TEST_DEPS)
 
 animated_resizing_SOURCES =    \
        animated-resizing.c     \
@@ -528,6 +544,10 @@ testtitlebar_SOURCES = testtitlebar.c
 
 testwindowsize_SOURCES = testwindowsize.c
 
+testcloudproviderserver_SOURCES = testcloudproviderserver.c
+
+testcloudproviderclient_SOURCES = testcloudproviderclient.c
+
 gdkgears_SOURCES =     \
        gdkgears.c      \
        gtkgears.c      \
diff --git a/tests/org.gtk.CloudProviderServerExample.ini b/tests/org.gtk.CloudProviderServerExample.ini
new file mode 100644
index 0000000..424b2eb
--- /dev/null
+++ b/tests/org.gtk.CloudProviderServerExample.ini
@@ -0,0 +1,4 @@
+[Gtk Cloud Provider]
+BusName=org.gtk.CloudProviderServerExample
+ObjectPath=/org/gtk/CloudProviderServerExample
+Version=1
diff --git a/tests/org.gtk.CloudProviderServerExample.service 
b/tests/org.gtk.CloudProviderServerExample.service
new file mode 100644
index 0000000..52e93bf
--- /dev/null
+++ b/tests/org.gtk.CloudProviderServerExample.service
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gtk.CloudProviderServerExample
+Exec=/home/csoriano/jhbuild/install/bin/testcloudprovider 
diff --git a/tests/org.gtk.CloudProviderServerExample.service.in 
b/tests/org.gtk.CloudProviderServerExample.service.in
new file mode 100644
index 0000000..2a8b5e6
--- /dev/null
+++ b/tests/org.gtk.CloudProviderServerExample.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gtk.CloudProviderServerExample
+Exec= bindir@/testcloudprovider 
diff --git a/tests/testcloudproviderclient.c b/tests/testcloudproviderclient.c
new file mode 100644
index 0000000..7880527
--- /dev/null
+++ b/tests/testcloudproviderclient.c
@@ -0,0 +1,131 @@
+#include "config.h"
+#include <glib.h>
+#include <gtk/gtk.h>
+
+static void
+print_menu (GtkWidget *menu)
+{
+  GList *children;
+  GList *l;
+  GtkWidget *submenu;
+
+  children = gtk_container_get_children (GTK_CONTAINER (menu));
+  for (l = children; l != NULL; l = l->next)
+    {
+      g_print ("Menu item - %s \n", gtk_menu_item_get_label (l->data));
+      submenu = gtk_menu_item_get_submenu (l->data);
+      if (submenu != NULL)
+        {
+          g_print ("---------\n");
+          print_menu (submenu);
+          g_print ("---------\n");
+        }
+    }
+}
+
+static void
+print_gmenu_model (GMenuModel  *model)
+{
+  gint i, n_items;
+  GMenuModel *submodel = NULL;
+  gchar *label;
+
+  n_items = g_menu_model_get_n_items (model);
+
+  for (i = 0; i < n_items; i++)
+    {
+      label = NULL;
+      if (g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label))
+        {
+          g_print ("Menu item - %s\n", label);
+          if (label != NULL)
+            g_free (label);
+        }
+
+      submodel = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION);
+      if (!submodel)
+       submodel = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU);
+
+      if (!submodel)
+          continue;
+      g_print ("---------\n");
+      print_gmenu_model (submodel);
+      g_print ("---------\n");
+      g_clear_object (&submodel);
+  }
+}
+
+static void
+on_manager_changed (GtkCloudProviderManager *manager)
+{
+  GList *providers;
+  GList *l;
+  gint provider_status;
+  gchar *status_string;
+  GIcon *icon;
+  gchar *icon_representation;
+  GtkWidget *menu;
+  gchar *path;
+
+  providers = gtk_cloud_provider_manager_get_providers (manager);
+  g_print ("Providers data\n");
+  g_print ("##############\n");
+  for (l = providers; l != NULL; l = l->next)
+    {
+      provider_status = gtk_cloud_provider_get_status (GTK_CLOUD_PROVIDER (l->data));
+      switch (provider_status)
+        {
+        case GTK_CLOUD_PROVIDER_STATUS_INVALID:
+          status_string = "invalid";
+          break;
+
+        case GTK_CLOUD_PROVIDER_STATUS_IDLE:
+          status_string = "idle";
+          break;
+
+        case GTK_CLOUD_PROVIDER_STATUS_SYNCING:
+          status_string = "syncing";
+          break;
+
+        case GTK_CLOUD_PROVIDER_STATUS_ERROR:
+          status_string = "error";
+          break;
+
+        default:
+          g_assert_not_reached ();
+        }
+
+      icon = gtk_cloud_provider_get_icon (l->data);
+      icon_representation = g_icon_to_string (icon);
+
+      g_print ("Name - %s, Status - %s, Path - %s, Icon - %s\n",
+               gtk_cloud_provider_get_name (GTK_CLOUD_PROVIDER (l->data)),
+               status_string,
+               gtk_cloud_provider_get_path (GTK_CLOUD_PROVIDER (l->data)),
+               icon_representation);
+
+      g_free (icon_representation);
+
+      menu = gtk_cloud_provider_get_menu (l->data);
+      g_print ("\nMenu\n");
+      print_menu (menu);
+    }
+  g_print ("\n");
+}
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+  GtkCloudProviderManager *manager;
+
+  gtk_init (&argc, &argv);
+
+  manager = gtk_cloud_provider_manager_dup_singleton ();
+  g_signal_connect (manager, "changed", G_CALLBACK (on_manager_changed), NULL);
+  //gtk_cloud_provider_manager_update (manager);
+
+  gtk_main ();
+
+  return 0;
+}
diff --git a/tests/testcloudproviderserver.c b/tests/testcloudproviderserver.c
new file mode 100644
index 0000000..8f88e23
--- /dev/null
+++ b/tests/testcloudproviderserver.c
@@ -0,0 +1,437 @@
+#include <gio/gio.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+
+#define TIMEOUT 2000
+
+typedef struct _CloudProviderClass CloudProviderClass;
+typedef struct _CloudProvider CloudProvider;
+
+struct _CloudProviderClass
+{
+  GObjectClass parent_class;
+};
+
+struct _CloudProvider
+{
+  GObject parent_instance;
+
+  gchar *name;
+  gint status;
+  GIcon *icon;
+  gchar *path;
+  GDBusProxy *manager_proxy;
+  guint timeout_handler;
+};
+
+
+static GType cloud_provider_get_type (void);
+G_DEFINE_TYPE (CloudProvider, cloud_provider, G_TYPE_OBJECT);
+
+static void
+cloud_provider_finalize (GObject *object)
+{
+  CloudProvider *self = (CloudProvider*)object;
+
+  g_free (self->name);
+  g_free (self->path);
+  g_clear_object (&self->icon);
+  g_clear_object (&self->manager_proxy);
+
+  G_OBJECT_CLASS (cloud_provider_parent_class)->finalize (object);
+}
+
+static void
+cloud_provider_init (CloudProvider *self)
+{
+  GFile *icon_file;
+  gchar *current_dir;
+  gchar *uri;
+
+  current_dir = g_get_current_dir ();
+
+  self->name = "MyCloud";
+  self->path = g_strdup (current_dir);
+  self->status = GTK_CLOUD_PROVIDER_STATUS_INVALID;
+  uri = g_build_filename (current_dir, "apple-red.png", NULL);
+  icon_file = g_file_new_for_uri (uri);
+  self->icon = g_file_icon_new (icon_file);
+
+  g_object_unref (icon_file);
+  g_free (uri);
+  g_free (current_dir);
+}
+
+static void
+cloud_provider_class_init (CloudProviderClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+  gobject_class->finalize = cloud_provider_finalize;
+}
+
+static void
+cloud_provider_set_status (CloudProvider *self,
+                           gint           status)
+{
+  /* Inform the manager that the provider changed */
+  self->status = status;
+  g_dbus_proxy_call (self->manager_proxy,
+                     "CloudProviderChanged",
+                     g_variant_new ("()"),
+                     G_DBUS_CALL_FLAGS_NONE,
+                     -1,
+                     NULL,
+                     NULL,
+                     NULL);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GDBusNodeInfo *introspection_data = NULL;
+
+/* Introspection data for the service we are exporting */
+static const gchar provider_xml[] =
+  "<node>"
+  "  <interface name='org.gtk.CloudProvider'>"
+  "    <method name='GetName'>"
+  "      <arg type='s' name='name' direction='out'/>"
+  "    </method>"
+  "    <method name='GetStatus'>"
+  "      <arg type='i' name='status' direction='out'/>"
+  "    </method>"
+  "    <method name='GetIcon'>"
+  "      <arg type='v' name='icon' direction='out'/>"
+  "    </method>"
+  "    <method name='GetPath'>"
+  "      <arg type='s' name='path' direction='out'/>"
+  "    </method>"
+  "  </interface>"
+  "</node>";
+
+static const gchar manager_xml[] =
+  "<node>"
+  "  <interface name='org.gtk.CloudProviderManager'>"
+  "    <method name='CloudProviderChanged'>"
+  "    </method>"
+  "  </interface>"
+  "</node>";
+
+static const gchar menu_markup[] =
+  "<interface>\n"
+  "<menu id='menu'>\n"
+  "  <section>\n"
+  "    <item>\n"
+  "      <attribute name='label' translatable='yes'>MyCloud website</attribute>\n"
+  "      <attribute name='action'>actions.website</attribute>\n"
+  "    </item>\n"
+  "    <item>\n"
+  "      <attribute name='label' translatable='yes'>MyCloud Photos</attribute>\n"
+  "      <attribute name='action'>actions.photos</attribute>\n"
+  "    </item>\n"
+  "    <item>\n"
+  "      <attribute name='label' translatable='yes'>MyCloud Notes</attribute>\n"
+  "      <attribute name='action'>actions.notes</attribute>\n"
+  "    </item>\n"
+  "  </section>\n"
+  "  <section>\n"
+  "    <item>\n"
+  "      <attribute name='label' translatable='yes'>Allow Synchronization</attribute>\n"
+  "      <attribute name='action'>actions.allow-sync</attribute>\n"
+  "    </item>\n"
+  "    <submenu>\n"
+  "      <attribute name='label' translatable='yes'>Buy Storage</attribute>\n"
+  "      <item>\n"
+  "        <attribute name='label' translatable='yes'>5GB for 200CZK</attribute>\n"
+  "        <attribute name='action'>actions.buy</attribute>\n"
+  "        <attribute name='target'>5</attribute>\n"
+  "      </item>\n"
+  "      <item>\n"
+  "        <attribute name='label' translatable='yes'>10GB for 500CZK</attribute>\n"
+  "        <attribute name='action'>actions.buy</attribute>\n"
+  "        <attribute name='target'>10</attribute>\n"
+  "      </item>\n"
+  "      <item>\n"
+  "        <attribute name='label' translatable='yes'>30GB for 600CZK</attribute>\n"
+  "        <attribute name='action'>actions.buy</attribute>\n"
+  "        <attribute name='target'>30</attribute>\n"
+  "      </item>\n"
+  "    </submenu>\n"
+  "  </section>\n"
+  "</menu>\n"
+  "</interface>\n";
+
+static void
+activate_action (GSimpleAction *action,
+                 GVariant      *parameter,
+                 gpointer       user_data)
+{
+  g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
+}
+
+static void
+activate_toggle (GSimpleAction *action,
+                 GVariant      *parameter,
+                 gpointer       user_data)
+{
+  GVariant *old_state, *new_state;
+
+  old_state = g_action_get_state (G_ACTION (action));
+  new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
+
+  g_print ("Toggle action %s activated, state changes from %d to %d\n",
+           g_action_get_name (G_ACTION (action)),
+           g_variant_get_boolean (old_state),
+           g_variant_get_boolean (new_state));
+
+  g_simple_action_set_state (action, new_state);
+  g_variant_unref (old_state);
+}
+
+static void
+activate_radio (GSimpleAction *action,
+                GVariant      *parameter,
+                gpointer       user_data)
+{
+  GVariant *old_state, *new_state;
+
+  old_state = g_action_get_state (G_ACTION (action));
+  new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
+
+  g_print ("Radio action %s activated, state changes from %s to %s\n",
+           g_action_get_name (G_ACTION (action)),
+           g_variant_get_string (old_state, NULL),
+           g_variant_get_string (new_state, NULL));
+
+  g_simple_action_set_state (action, new_state);
+  g_variant_unref (old_state);
+}
+
+static GActionEntry actions[] = {
+  { "website",  activate_action, NULL, NULL, NULL },
+  { "photos",  activate_action, NULL, NULL, NULL },
+  { "notes",   activate_action, NULL, NULL, NULL },
+  { "allow-sync",  activate_toggle, NULL, "true", NULL },
+  { "buy",  activate_radio,  "s",  NULL, NULL },
+};
+
+static GMenuModel *
+get_model (void)
+{
+  GError *error = NULL;
+  GtkBuilder *builder;
+  GMenuModel *menu;
+
+  builder = gtk_builder_new ();
+  gtk_builder_add_from_string (builder, menu_markup, -1, &error);
+  g_assert_no_error (error);
+
+  menu = g_object_ref (gtk_builder_get_object (builder, "menu"));
+  g_object_unref (builder);
+
+  return menu;
+}
+
+static GActionGroup *
+get_action_group (void)
+{
+  GSimpleActionGroup *group;
+
+  group = g_simple_action_group_new ();
+
+  g_action_map_add_action_entries (G_ACTION_MAP (group),
+                                   actions,
+                                   G_N_ELEMENTS (actions), NULL);
+
+  return G_ACTION_GROUP (group);
+}
+
+static void
+export_menu (GDBusConnection *bus,
+             gchar *object_path)
+{
+  GMenuModel *model;
+  GActionGroup *action_group;
+  GError *error;
+
+  model = get_model ();
+  action_group = get_action_group ();
+
+  g_print ("Exporting menus on the bus...\n");
+  if (!g_dbus_connection_export_menu_model (bus, object_path, model, &error))
+    {
+      g_warning ("Menu export failed: %s", error->message);
+      exit (1);
+    }
+  g_print ("Exporting actions on the bus...\n");
+  if (!g_dbus_connection_export_action_group (bus, object_path, action_group, &error))
+    {
+      g_warning ("Action export failed: %s", error->message);
+      exit (1);
+    }
+}
+
+static void
+handle_method_call (GDBusConnection       *connection,
+                    const gchar           *sender,
+                    const gchar           *object_path,
+                    const gchar           *interface_name,
+                    const gchar           *method_name,
+                    GVariant              *parameters,
+                    GDBusMethodInvocation *invocation,
+                    gpointer               user_data)
+{
+  CloudProvider *cloud_provider = user_data;
+
+  g_debug ("Handling dbus call in server\n");
+  if (g_strcmp0 (method_name, "GetName") == 0)
+    {
+      g_dbus_method_invocation_return_value (invocation,
+                                             g_variant_new ("(s)", cloud_provider->name));
+    }
+  else if (g_strcmp0 (method_name, "GetStatus") == 0)
+    {
+      g_dbus_method_invocation_return_value (invocation,
+                                             g_variant_new ("(i)", cloud_provider->status));
+    }
+  else if (g_strcmp0 (method_name, "GetIcon") == 0)
+    {
+      g_dbus_method_invocation_return_value (invocation,
+                                             g_variant_new ("(v)", g_icon_serialize (cloud_provider->icon)));
+    }
+  else if (g_strcmp0 (method_name, "GetPath") == 0)
+    {
+      g_dbus_method_invocation_return_value (invocation,
+                                             g_variant_new ("(s)", cloud_provider->path));
+    }
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+  handle_method_call,
+};
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const gchar     *name,
+                 gpointer         user_data)
+{
+  CloudProvider *cloud_provider = user_data;
+  guint registration_id;
+
+  g_debug ("Registering cloud provider server 'MyCloud'\n");
+  registration_id = g_dbus_connection_register_object (connection,
+                                                       "/org/gtk/CloudProviderServerExample",
+                                                       introspection_data->interfaces[0],
+                                                       &interface_vtable,
+                                                       cloud_provider,
+                                                       NULL,  /* user_data_free_func */
+                                                       NULL); /* GError** */
+  g_assert (registration_id > 0);
+  /* Export a menu for our own application */
+  export_menu (connection, "/org/gtk/CloudProviderServerExample");
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar     *name,
+                  gpointer         user_data)
+{
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar     *name,
+              gpointer         user_data)
+{
+  exit (1);
+}
+
+static gboolean
+change_provider (gpointer user_data)
+{
+  CloudProvider *cloud_provider = (CloudProvider *)user_data;
+  GRand *rand;
+  gint new_status;
+
+  rand = g_rand_new ();
+  new_status = g_rand_int_range (rand,
+                                 GTK_CLOUD_PROVIDER_STATUS_IDLE,
+                                 GTK_CLOUD_PROVIDER_STATUS_ERROR + 1);
+
+  cloud_provider_set_status (cloud_provider, new_status);
+
+  return TRUE;
+}
+
+static void
+on_manager_proxy_created (GObject      *source_object,
+                          GAsyncResult *res,
+                          gpointer      user_data)
+{
+  CloudProvider *cloud_provider = user_data;
+  GError *error = NULL;
+
+  cloud_provider->manager_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+  if (error != NULL)
+    g_warning ("Error creating proxy for cloud provider manager %s", error->message);
+  else
+    g_debug ("Manager proxy created for 'MyCloud'\n");
+
+  cloud_provider->timeout_handler = g_timeout_add (TIMEOUT,
+                                                   (GSourceFunc) change_provider,
+                                                   cloud_provider);
+}
+
+int
+main (int argc, char *argv[])
+{
+  GMainLoop *loop;
+  CloudProvider *cloud_provider;
+  guint owner_id;
+  GDBusNodeInfo *proxy_info;
+  GDBusInterfaceInfo *interface_info;
+  GError *error = NULL;
+
+  /* Export the interface we listen to, so clients can request properties of
+   * the cloud provider such as name, status or icon */
+  introspection_data = g_dbus_node_info_new_for_xml (provider_xml, NULL);
+  g_assert (introspection_data != NULL);
+
+  cloud_provider = g_object_new (cloud_provider_get_type (), NULL);
+
+  owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                             "org.gtk.CloudProviderServerExample",
+                             G_BUS_NAME_OWNER_FLAGS_NONE,
+                             on_bus_acquired,
+                             on_name_acquired,
+                             on_name_lost,
+                             cloud_provider,
+                             NULL);
+
+  /* Create CloudProviderManager proxy for exporting cloud provider changes */
+  proxy_info = g_dbus_node_info_new_for_xml (manager_xml, &error);
+  interface_info = g_dbus_node_info_lookup_interface (proxy_info, "org.gtk.CloudProviderManager");
+  g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+                            G_DBUS_PROXY_FLAGS_NONE,
+                            interface_info,
+                            "org.gtk.CloudProviderManager",
+                            "/org/gtk/CloudProviderManager",
+                            "org.gtk.CloudProviderManager",
+                            NULL,
+                            on_manager_proxy_created,
+                            cloud_provider);
+
+
+  loop = g_main_loop_new (NULL, FALSE);
+  g_main_loop_run (loop);
+
+  g_bus_unown_name (owner_id);
+
+  g_dbus_node_info_unref (introspection_data);
+
+  g_object_unref (cloud_provider);
+
+  return 0;
+}
+
diff --git a/tests/testpopover.c b/tests/testpopover.c
index 306e7c9..e132521 100644
--- a/tests/testpopover.c
+++ b/tests/testpopover.c
@@ -145,6 +145,7 @@ main (int argc, char *argv[])
   gtk_grid_attach (GTK_GRID (grid), label , 1, 5, 1, 1);
   gtk_grid_attach (GTK_GRID (grid), combo, 2, 5, 1, 1);
 
+  gtk_cloud_provider_manager_dup_singleton ();
 
   gtk_widget_show_all (win);
 


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