[evolution-data-server] Add a new WebDAV discover widget and dialog into libedataserverui



commit 9c74f22a3760083935065eea27298d8908b7495d
Author: Milan Crha <mcrha redhat com>
Date:   Mon Mar 23 17:39:57 2015 +0100

    Add a new WebDAV discover widget and dialog into libedataserverui
    
    This widget, and dialog, can be used to discover WebDAV sources
    and give a user a unified widget to select one, or multiple, sources
    from those found.

 libedataserverui/Makefile.am                |    4 +
 libedataserverui/e-cell-renderer-color.c    |  225 ++++++
 libedataserverui/e-cell-renderer-color.h    |   77 ++
 libedataserverui/e-webdav-discover-widget.c | 1134 +++++++++++++++++++++++++++
 libedataserverui/e-webdav-discover-widget.h |   81 ++
 libedataserverui/libedataserverui.h         |    2 +
 po/POTFILES.in                              |    1 +
 7 files changed, 1524 insertions(+), 0 deletions(-)
---
diff --git a/libedataserverui/Makefile.am b/libedataserverui/Makefile.am
index cf625b3..51578fd 100644
--- a/libedataserverui/Makefile.am
+++ b/libedataserverui/Makefile.am
@@ -41,18 +41,22 @@ libedataserveruiincludedir = $(privincludedir)/libedataserverui
 
 libedataserveruiinclude_HEADERS = \
        libedataserverui.h \
+       e-cell-renderer-color.h \
        e-credentials-prompter.h \
        e-credentials-prompter-impl.h \
        e-credentials-prompter-impl-password.h \
        e-trust-prompt.h \
+       e-webdav-discover-widget.h \
        $(NULL)
 
 libedataserverui_1_2_la_SOURCES = \
        $(BUILT_SOURCES) \
+       e-cell-renderer-color.c \
        e-credentials-prompter.c \
        e-credentials-prompter-impl.c \
        e-credentials-prompter-impl-password.c \
        e-trust-prompt.c \
+       e-webdav-discover-widget.c \
        $(NULL)
 
 %-$(API_VERSION).pc: %.pc
diff --git a/libedataserverui/e-cell-renderer-color.c b/libedataserverui/e-cell-renderer-color.c
new file mode 100644
index 0000000..761afa5
--- /dev/null
+++ b/libedataserverui/e-cell-renderer-color.c
@@ -0,0 +1,225 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-cell-renderer-color.c
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This program 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.
+ *
+ * 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-cell-renderer-color.h"
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#define E_CELL_RENDERER_COLOR_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_CELL_RENDERER_COLOR, ECellRendererColorPrivate))
+
+enum {
+       PROP_0,
+       PROP_RGBA
+};
+
+struct _ECellRendererColorPrivate {
+       GdkRGBA rgba;
+};
+
+G_DEFINE_TYPE (
+       ECellRendererColor,
+       e_cell_renderer_color,
+       GTK_TYPE_CELL_RENDERER)
+
+static void
+cell_renderer_color_get_size (GtkCellRenderer *cell,
+                              GtkWidget *widget,
+                              const GdkRectangle *cell_area,
+                              gint *x_offset,
+                              gint *y_offset,
+                              gint *width,
+                              gint *height)
+{
+       gint color_width = 16;
+       gint color_height = 16;
+       gint calc_width;
+       gint calc_height;
+       gfloat xalign;
+       gfloat yalign;
+       guint xpad;
+       guint ypad;
+
+       g_object_get (
+               cell, "xalign", &xalign, "yalign", &yalign,
+               "xpad", &xpad, "ypad", &ypad, NULL);
+
+       calc_width = (gint) xpad * 2 + color_width;
+       calc_height = (gint) ypad * 2 + color_height;
+
+       if (cell_area && color_width > 0 && color_height > 0) {
+               if (x_offset) {
+                       *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
+                                       (1.0 - xalign) : xalign) *
+                                       (cell_area->width - calc_width));
+                       *x_offset = MAX (*x_offset, 0);
+               }
+
+               if (y_offset) {
+                       *y_offset =(yalign *
+                               (cell_area->height - calc_height));
+                       *y_offset = MAX (*y_offset, 0);
+               }
+       } else {
+               if (x_offset) *x_offset = 0;
+               if (y_offset) *y_offset = 0;
+       }
+
+       if (width)
+               *width = calc_width;
+
+       if (height)
+               *height = calc_height;
+}
+
+static void
+cell_renderer_color_render (GtkCellRenderer *cell,
+                            cairo_t *cr,
+                            GtkWidget *widget,
+                            const GdkRectangle *background_area,
+                            const GdkRectangle *cell_area,
+                            GtkCellRendererState flags)
+{
+       ECellRendererColorPrivate *priv;
+       GdkRectangle pix_rect;
+       GdkRectangle draw_rect;
+       guint xpad;
+       guint ypad;
+
+       priv = E_CELL_RENDERER_COLOR_GET_PRIVATE (cell);
+
+       cell_renderer_color_get_size (
+               cell, widget, cell_area,
+               &pix_rect.x, &pix_rect.y,
+               &pix_rect.width, &pix_rect.height);
+
+       g_object_get (cell, "xpad", &xpad, "ypad", &ypad, NULL);
+
+       pix_rect.x += cell_area->x + xpad;
+       pix_rect.y += cell_area->y + ypad;
+       pix_rect.width  -= xpad * 2;
+       pix_rect.height -= ypad * 2;
+
+       if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect))
+               return;
+
+       gdk_cairo_set_source_rgba (cr, &priv->rgba);
+       cairo_rectangle (cr, pix_rect.x, pix_rect.y, draw_rect.width, draw_rect.height);
+
+       cairo_fill (cr);
+}
+
+static void
+cell_renderer_color_set_property (GObject *object,
+                                  guint property_id,
+                                  const GValue *value,
+                                  GParamSpec *pspec)
+{
+       ECellRendererColorPrivate *priv;
+       GdkRGBA *rgba;
+
+       priv = E_CELL_RENDERER_COLOR_GET_PRIVATE (object);
+
+       switch (property_id) {
+               case PROP_RGBA:
+                       rgba = g_value_dup_boxed (value);
+                       if (rgba) {
+                               priv->rgba = *rgba;
+                               gdk_rgba_free (rgba);
+                       } else {
+                               priv->rgba.red = 0.0;
+                               priv->rgba.green = 0.0;
+                               priv->rgba.blue = 0.0;
+                               priv->rgba.alpha = 0.0;
+                       }
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+cell_renderer_color_get_property (GObject *object,
+                                  guint property_id,
+                                  GValue *value,
+                                  GParamSpec *pspec)
+{
+       ECellRendererColorPrivate *priv;
+
+       priv = E_CELL_RENDERER_COLOR_GET_PRIVATE (object);
+
+       switch (property_id) {
+               case PROP_RGBA:
+                       g_value_set_boxed (value, &priv->rgba);
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_cell_renderer_color_class_init (ECellRendererColorClass *class)
+{
+       GObjectClass *object_class;
+       GtkCellRendererClass *cell_class;
+
+       g_type_class_add_private (class, sizeof (ECellRendererColorPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = cell_renderer_color_set_property;
+       object_class->get_property = cell_renderer_color_get_property;
+
+       cell_class = GTK_CELL_RENDERER_CLASS (class);
+       cell_class->get_size = cell_renderer_color_get_size;
+       cell_class->render = cell_renderer_color_render;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_RGBA,
+               g_param_spec_boxed (
+                       "rgba",
+                       "Color Info",
+                       "The GdkRGBA color to render",
+                       GDK_TYPE_RGBA,
+                       G_PARAM_READWRITE));
+}
+
+static void
+e_cell_renderer_color_init (ECellRendererColor *cellcolor)
+{
+       cellcolor->priv = E_CELL_RENDERER_COLOR_GET_PRIVATE (cellcolor);
+
+       g_object_set (cellcolor, "xpad", 4, NULL);
+}
+
+/**
+ * e_cell_renderer_color_new:
+ *
+ * Since: 2.22
+ **/
+GtkCellRenderer *
+e_cell_renderer_color_new (void)
+{
+       return g_object_new (E_TYPE_CELL_RENDERER_COLOR, NULL);
+}
diff --git a/libedataserverui/e-cell-renderer-color.h b/libedataserverui/e-cell-renderer-color.h
new file mode 100644
index 0000000..705bb76
--- /dev/null
+++ b/libedataserverui/e-cell-renderer-color.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-cell-renderer-color.h
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This program 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.
+ *
+ * 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__LIBEDATASERVERUI_H_INSIDE__) && !defined (LIBEDATASERVERUI_COMPILATION)
+#error "Only <libedataserverui/libedataserverui.h> should be included directly."
+#endif
+
+#ifndef E_CELL_RENDERER_COLOR_H
+#define E_CELL_RENDERER_COLOR_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CELL_RENDERER_COLOR \
+       (e_cell_renderer_color_get_type ())
+#define E_CELL_RENDERER_COLOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_CELL_RENDERER_COLOR, ECellRendererColor))
+#define E_CELL_RENDERER_COLOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_CELL_RENDERER_COLOR, ECellRendererColorClass))
+#define E_IS_CELL_RENDERER_COLOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_CELL_RENDERER_COLOR))
+#define E_IS_CELL_RENDERER_COLOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE ((cls), E_TYPE_CELL_RENDERER_COLOR))
+#define E_CELL_RENDERER_COLOR_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_CELL_RENDERER_COLOR, ECellRendererColorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECellRendererColor ECellRendererColor;
+typedef struct _ECellRendererColorClass ECellRendererColorClass;
+typedef struct _ECellRendererColorPrivate ECellRendererColorPrivate;
+
+/**
+ * ECellRendererColor:
+ *
+ * Since: 2.22
+ **/
+struct _ECellRendererColor {
+       GtkCellRenderer parent;
+       ECellRendererColorPrivate *priv;
+};
+
+struct _ECellRendererColorClass {
+       GtkCellRendererClass parent_class;
+
+       /* Padding for future expansion */
+       void (*_gtk_reserved1) (void);
+       void (*_gtk_reserved2) (void);
+       void (*_gtk_reserved3) (void);
+       void (*_gtk_reserved4) (void);
+};
+
+GType            e_cell_renderer_color_get_type        (void) G_GNUC_CONST;
+GtkCellRenderer *e_cell_renderer_color_new     (void);
+
+G_END_DECLS
+
+#endif /* E_CELL_RENDERER_COLOR_H */
diff --git a/libedataserverui/e-webdav-discover-widget.c b/libedataserverui/e-webdav-discover-widget.c
new file mode 100644
index 0000000..69801b2
--- /dev/null
+++ b/libedataserverui/e-webdav-discover-widget.c
@@ -0,0 +1,1134 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-cell-renderer-color.h"
+#include "e-trust-prompt.h"
+#include "e-webdav-discover-widget.h"
+
+#define WEBDAV_DISCOVER_CONTENT_KEY            "e-webdav-discover-content-widget-key"
+#define WEBDAV_DISCOVER_CONTENT_ENTRY_KEY      "e-webdav-discover-content-widget-entry-key"
+#define WEBDAV_DISCOVER_CONTENT_DATA_KEY       "e-webdav-discover-content-widget-data-key"
+
+enum {
+       COL_HREF_STRING = 0,
+       COL_SUPPORTS_UINT,
+       COL_DISPLAY_NAME_STRING,
+       COL_COLOR_STRING,
+       COL_DESCRIPTION_STRING,
+       COL_SUPPORTS_STRING,
+       COL_COLOR_GDKRGBA,
+       COL_SHOW_COLOR_BOOLEAN,
+       N_COLUMNS
+};
+
+typedef struct _EWebDAVDiscoverContentData {
+       ECredentialsPrompter *credentials_prompter;
+       ESource *source;
+       gchar *base_url;
+       guint supports_filter;
+
+       GtkTreeView *sources_tree_view; /* not referenced */
+       GtkComboBox *email_addresses_combo; /* not referenced */
+       GtkInfoBar *info_bar; /* not referenced */
+} EWebDAVDiscoverContentData;
+
+static void
+e_webdav_discover_content_data_free (gpointer ptr)
+{
+       EWebDAVDiscoverContentData *data = ptr;
+
+       if (data) {
+               g_clear_object (&data->credentials_prompter);
+               g_clear_object (&data->source);
+               g_free (data->base_url);
+               g_free (data);
+       }
+}
+
+/**
+ * e_webdav_discover_content_new:
+ * @credentials_prompter: an #ECredentialsPrompter to use to ask for credentials
+ * @source: (allow none): optional #ESource to use for authentication, or %NULL
+ * @base_url: (allow-none): optional base URL to use for discovery, or %NULL
+ * @supports_filter: a bit-or of #EWebDAVDiscoverSupports, a filter to limit what source
+ *    types will be shown in the dialog content; use %E_WEBDAV_DISCOVER_SUPPORTS_NONE
+ *    to show all
+ *
+ * Creates a new WebDAV discovery content, which is a #GtkGrid containing necessary
+ * widgets to provide a UI interface for a user to search and select for available
+ * WebDAV (CalDAV or CardDAV) sources provided by the given server. Do not pack
+ * anything into this content, its content can be changed dynamically.
+ *
+ * Returns: (transfer full): a new WebDAV discovery content widget.
+ *
+ * Since: 3.18
+ **/
+GtkWidget *
+e_webdav_discover_content_new (ECredentialsPrompter *credentials_prompter,
+                              ESource *source,
+                              const gchar *base_url,
+                              guint supports_filter)
+{
+       EWebDAVDiscoverContentData *data;
+       GtkWidget *content, *scrolled_window, *tree_view;
+       GtkTreeViewColumn *column;
+       GtkCellRenderer *renderer;
+       GtkListStore *list_store;
+       GtkGrid *grid;
+
+       g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (credentials_prompter), NULL);
+       g_return_val_if_fail (base_url != NULL, NULL);
+
+       data = g_new0 (EWebDAVDiscoverContentData, 1);
+       data->credentials_prompter = g_object_ref (credentials_prompter);
+       data->source = source ? g_object_ref (source) : NULL;
+       data->base_url = g_strdup (base_url);
+       data->supports_filter = supports_filter;
+
+       content = gtk_grid_new ();
+       grid = GTK_GRID (content);
+       gtk_container_set_border_width (GTK_CONTAINER (grid), 4);
+       gtk_grid_set_row_spacing (grid, 4);
+       gtk_grid_set_column_spacing (grid, 4);
+
+       g_object_set_data_full (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY, data, 
e_webdav_discover_content_data_free);
+
+       list_store = gtk_list_store_new (N_COLUMNS,
+                                        G_TYPE_STRING, /* COL_HREF_STRING */
+                                        G_TYPE_UINT, /* COL_SUPPORTS_UINT */
+                                        G_TYPE_STRING, /* COL_DISPLAY_NAME_STRING */
+                                        G_TYPE_STRING, /* COL_COLOR_STRING */
+                                        G_TYPE_STRING, /* COL_DESCRIPTION_STRING */
+                                        G_TYPE_STRING, /* COL_SUPPORTS_STRING */
+                                        GDK_TYPE_RGBA, /* COL_COLOR_GDKRGBA */
+                                        G_TYPE_BOOLEAN); /* COL_SHOW_COLOR_BOOLEAN */
+
+       tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+       g_object_unref (list_store);
+
+       g_object_set (G_OBJECT (tree_view),
+               "hexpand", TRUE,
+               "vexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+
+       scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+       gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+       gtk_grid_attach (grid, scrolled_window, 0, 0, 1, 1);
+
+       data->sources_tree_view = GTK_TREE_VIEW (tree_view);
+
+       renderer = e_cell_renderer_color_new ();
+       g_object_set (G_OBJECT (renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
+
+       column = gtk_tree_view_column_new_with_attributes (_("Name"), renderer, "rgba", COL_COLOR_GDKRGBA, 
"visible", COL_SHOW_COLOR_BOOLEAN, NULL);
+       gtk_tree_view_append_column (data->sources_tree_view, column);
+
+       renderer = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (column, renderer, FALSE);
+       gtk_tree_view_column_add_attribute (column, renderer, "markup", COL_DESCRIPTION_STRING);
+       g_object_set (G_OBJECT (renderer),
+               "max-width-chars", 60,
+               "wrap-mode", PANGO_WRAP_WORD_CHAR,
+               "wrap-width", 640,
+               NULL);
+
+       renderer = gtk_cell_renderer_text_new ();
+       column = gtk_tree_view_column_new_with_attributes (_("Supports"), renderer, "text", 
COL_SUPPORTS_STRING, NULL);
+       gtk_tree_view_append_column (data->sources_tree_view, column);
+
+       if (!supports_filter || (supports_filter & (E_WEBDAV_DISCOVER_SUPPORTS_EVENTS |
+           E_WEBDAV_DISCOVER_SUPPORTS_MEMOS | E_WEBDAV_DISCOVER_SUPPORTS_TASKS)) != 0) {
+               GtkWidget *widget, *box;
+
+               widget = gtk_combo_box_text_new ();
+               data->email_addresses_combo = GTK_COMBO_BOX (widget);
+
+               box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+               widget = gtk_label_new_with_mnemonic (_("_User mail:"));
+               gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (data->email_addresses_combo));
+
+               gtk_container_add (GTK_CONTAINER (box), widget);
+               gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (data->email_addresses_combo));
+
+               g_object_set (G_OBJECT (widget),
+                       "hexpand", FALSE,
+                       "vexpand", FALSE,
+                       "halign", GTK_ALIGN_START,
+                       "valign", GTK_ALIGN_CENTER,
+                       NULL);
+
+               g_object_set (G_OBJECT (data->email_addresses_combo),
+                       "hexpand", TRUE,
+                       "vexpand", FALSE,
+                       "halign", GTK_ALIGN_FILL,
+                       "valign", GTK_ALIGN_START,
+                       NULL);
+
+               g_object_set (G_OBJECT (box),
+                       "hexpand", TRUE,
+                       "vexpand", FALSE,
+                       "halign", GTK_ALIGN_FILL,
+                       "valign", GTK_ALIGN_START,
+                       NULL);
+
+               gtk_grid_attach (grid, box, 0, 1, 1, 1);
+       }
+
+       gtk_widget_show_all (content);
+
+       return content;
+}
+
+/**
+ * e_webdav_discover_content_get_tree_selection:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ *
+ * Returns inner #GtkTreeViewSelection. This is meant to be able to connect
+ * to its "changed" signal and update other parts of the parent widgets accordingly.
+ *
+ * Returns: (transfer none): inner #GtkTreeViewSelection
+ *
+ * Since: 3.18
+ **/
+GtkTreeSelection *
+e_webdav_discover_content_get_tree_selection (GtkWidget *content)
+{
+       EWebDAVDiscoverContentData *data;
+
+       g_return_val_if_fail (GTK_IS_GRID (content), NULL);
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_val_if_fail (data != NULL, NULL);
+
+       return gtk_tree_view_get_selection (data->sources_tree_view);
+}
+
+/**
+ * e_webdav_discover_content_set_multiselect:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ * @multiselect: whether multiselect is allowed
+ *
+ * Sets whether the WebDAV discovery content allows multiselect.
+ *
+ * Since: 3.18
+ **/
+void
+e_webdav_discover_content_set_multiselect (GtkWidget *content,
+                                          gboolean multiselect)
+{
+       EWebDAVDiscoverContentData *data;
+       GtkTreeSelection *selection;
+
+       g_return_if_fail (GTK_IS_GRID (content));
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_if_fail (data != NULL);
+
+       selection = gtk_tree_view_get_selection (data->sources_tree_view);
+       gtk_tree_selection_set_mode (selection, multiselect ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE);
+}
+
+/**
+ * e_webdav_discover_content_get_multiselect:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ *
+ * Returns: whether multiselect is allowed for the @content.
+ *
+ * Since: 3.18
+ **/
+gboolean
+e_webdav_discover_content_get_multiselect (GtkWidget *content)
+{
+       EWebDAVDiscoverContentData *data;
+       GtkTreeSelection *selection;
+
+       g_return_val_if_fail (GTK_IS_GRID (content), FALSE);
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_val_if_fail (data != NULL, FALSE);
+
+       selection = gtk_tree_view_get_selection (data->sources_tree_view);
+       return gtk_tree_selection_get_mode (selection) == GTK_SELECTION_MULTIPLE;
+}
+
+/**
+ * e_webdav_discover_content_set_base_url:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ *
+ * Sets base URL for the @content. This is used to overwrite the one set on
+ * the #ESource from the creation time. The URL can be either a full URL, a path
+ * or even a %NULL.
+ *
+ * Since: 3.18
+ **/
+void
+e_webdav_discover_content_set_base_url (GtkWidget *content,
+                                       const gchar *base_url)
+{
+       EWebDAVDiscoverContentData *data;
+
+       g_return_if_fail (GTK_IS_GRID (content));
+       g_return_if_fail (base_url != NULL);
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_if_fail (data != NULL);
+
+       if (g_strcmp0 (base_url, data->base_url) != 0) {
+               g_free (data->base_url);
+               data->base_url = g_strdup (base_url);
+       }
+}
+
+/**
+ * e_webdav_discover_content_get_base_url:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ *
+ * Returns currently set base URL for the @content. This is used to overwrite the one
+ * set on the #ESource from the creation time. The URL can be either a full URL, a path
+ * or even a %NULL.
+ *
+ * Returns: currently set base URL for the @content.
+ *
+ * Since: 3.18
+ **/
+const gchar *
+e_webdav_discover_content_get_base_url (GtkWidget *content)
+{
+       EWebDAVDiscoverContentData *data;
+
+       g_return_val_if_fail (GTK_IS_GRID (content), NULL);
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_val_if_fail (data != NULL, NULL);
+
+       return data->base_url;
+}
+
+/**
+ * e_webdav_discover_content_get_base_url:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ * @index: an index of the selected source; counts from 0
+ * @out_href: (out): an output location for the URL of the selected source
+ * @out_supports: (out): an output location of a bit-or of #EWebDAVDiscoverSupports, the set
+ *    of source types this server source location supports
+ * @out_display_name: (out): an output location of the sources display name
+ * @out_color: (out): an output location of the string representation of the color
+ *    for the source, as set on the server
+ *
+ * Returns information about selected source at index @index. The function can be called
+ * multiple times, with the index starting at zero and as long as it doesn't return %FALSE.
+ * If the @content doesn't have allowed multiselection, then the only valid @index is 0.
+ *
+ * All the @out_href, @out_display_name and @out_color are newly allocated strings, which should
+ * be freed with g_free(), when no longer needed.
+ *
+ * Returns: %TRUE, when a selected source of index @index exists, %FALSE otherwise.
+ *
+ * Since: 3.18
+ **/
+gboolean
+e_webdav_discover_content_get_selected (GtkWidget *content,
+                                       gint index,
+                                       gchar **out_href,
+                                       guint *out_supports,
+                                       gchar **out_display_name,
+                                       gchar **out_color)
+{
+       EWebDAVDiscoverContentData *data;
+       GtkTreeSelection *selection;
+       GtkTreeModel *model = NULL;
+       GList *selected_rows, *link;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (GTK_IS_GRID (content), FALSE);
+       g_return_val_if_fail (index >= 0, FALSE);
+       g_return_val_if_fail (out_href != NULL, FALSE);
+       g_return_val_if_fail (out_supports != NULL, FALSE);
+       g_return_val_if_fail (out_display_name != NULL, FALSE);
+       g_return_val_if_fail (out_color != NULL, FALSE);
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_val_if_fail (data != NULL, FALSE);
+
+       selection = gtk_tree_view_get_selection (data->sources_tree_view);
+       selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+       for (link = selected_rows; link && index > 0; link = g_list_next (link)) {
+               index--;
+       }
+
+       if (index == 0 && link) {
+               GtkTreePath *path = link->data;
+
+               if (path) {
+                       GtkTreeIter iter;
+
+                       success = gtk_tree_model_get_iter (model, &iter, path);
+                       if (success) {
+                               gtk_tree_model_get (model, &iter,
+                                       COL_HREF_STRING, out_href,
+                                       COL_SUPPORTS_UINT, out_supports,
+                                       COL_DISPLAY_NAME_STRING, out_display_name,
+                                       COL_COLOR_STRING, out_color,
+                                       -1);
+                       }
+               }
+       }
+
+       g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
+
+       return success;
+}
+
+/**
+ * e_webdav_discover_content_get_user_address:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ *
+ * Get currently selected user address in the @content, if the server returned any.
+ * This value has meaning only with calendar sources.
+ *
+ * Returns: (transfer full): currently selected user address. The returned string
+ *   is newly allocated and should be freed with g_free() when no longer needed.
+ *   If there are none addresses provided by the server, or no calendar sources
+ *   were found, then %NULL is returned instead.
+ *
+ * Since: 3.18
+ **/
+gchar *
+e_webdav_discover_content_get_user_address (GtkWidget *content)
+{
+       EWebDAVDiscoverContentData *data;
+       gchar *active_text;
+
+       g_return_val_if_fail (GTK_IS_GRID (content), NULL);
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_val_if_fail (data != NULL, NULL);
+
+       if (!data->email_addresses_combo)
+               return NULL;
+
+       active_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (data->email_addresses_combo));
+       if (active_text && !*active_text) {
+               g_free (active_text);
+               active_text = NULL;
+       }
+
+       return active_text;
+}
+
+static void
+e_webdav_discover_content_fill_discovered_sources (GtkTreeView *tree_view,
+                                                  guint supports_filter,
+                                                  GSList *discovered_sources)
+{
+       GtkListStore *list_store;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       GSList *link;
+
+       /* It's okay to pass NULL here */
+       if (!tree_view)
+               return;
+
+       g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+       model = gtk_tree_view_get_model (tree_view);
+       list_store = GTK_LIST_STORE (model);
+       gtk_list_store_clear (list_store);
+
+       for (link = discovered_sources; link; link = g_slist_next (link)) {
+               const EWebDAVDiscoveredSource *source = link->data;
+               guint supports_bits;
+               GString *supports;
+               gchar *description_markup, *colorstr = NULL;
+               gboolean show_color = FALSE;
+               GdkRGBA rgba;
+
+               if (!source || (supports_filter && (source->supports & supports_filter) == 0) || 
!source->display_name)
+                       continue;
+
+               if (source->color && *source->color) {
+                       gint rr, gg, bb;
+
+                       if (gdk_rgba_parse (&rgba, source->color)) {
+                               show_color = TRUE;
+                       } else if (sscanf (source->color, "#%02x%02x%02x", &rr, &gg, &bb) == 3) {
+                               rgba.red = ((gdouble) rr) / 255.0;
+                               rgba.green = ((gdouble) gg) / 255.0;
+                               rgba.blue = ((gdouble) bb) / 255.0;
+                               rgba.alpha = 1.0;
+
+                               show_color = TRUE;
+                       }
+
+                       if (show_color) {
+                               rr = 0xFF * rgba.red;
+                               gg = 0xFF * rgba.green;
+                               bb = 0xFF * rgba.blue;
+
+                               colorstr = g_strdup_printf ("#%02x%02x%02x", rr & 0xFF, gg & 0xFF, bb & 0xFF);
+                       }
+               }
+
+               if (source->description && *source->description) {
+                       description_markup = g_markup_printf_escaped ("<b>%s</b>\n<small>%s</small>",
+                               source->display_name, source->description);
+               } else {
+                       description_markup = g_markup_printf_escaped ("<b>%s</b>",
+                               source->display_name);
+               }
+
+               supports_bits = supports_filter ? (source->supports & supports_filter) : source->supports;
+               supports = g_string_new ("");
+
+               #define addbit(flg, cpt) { \
+                       if (((flg) & supports_bits) != 0) { \
+                               if (supports->len) \
+                                       g_string_append (supports, ", "); \
+                               g_string_append (supports, cpt); \
+                       } \
+               }
+
+               addbit (E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS, C_("WebDAVDiscover", "Contacts"));
+               addbit (E_WEBDAV_DISCOVER_SUPPORTS_EVENTS, C_("WebDAVDiscover", "Events"));
+               addbit (E_WEBDAV_DISCOVER_SUPPORTS_MEMOS, C_("WebDAVDiscover", "Memos"));
+               addbit (E_WEBDAV_DISCOVER_SUPPORTS_TASKS, C_("WebDAVDiscover", "Tasks"));
+
+               #undef addbit
+
+               gtk_list_store_append (list_store, &iter);
+               gtk_list_store_set (list_store, &iter,
+                       COL_HREF_STRING, source->href,
+                       COL_SUPPORTS_UINT, source->supports,
+                       COL_DISPLAY_NAME_STRING, source->display_name,
+                       COL_COLOR_STRING, colorstr,
+                       COL_DESCRIPTION_STRING, description_markup,
+                       COL_SUPPORTS_STRING, supports->str,
+                       COL_COLOR_GDKRGBA, show_color ? &rgba : NULL,
+                       COL_SHOW_COLOR_BOOLEAN, show_color,
+                       -1);
+
+               g_free (description_markup);
+               g_free (colorstr);
+               g_string_free (supports, TRUE);
+       }
+}
+
+static void
+e_webdav_discover_content_fill_calendar_emails (GtkComboBox *combo_box,
+                                               GSList *calendar_user_addresses)
+{
+       GtkComboBoxText *text_combo;
+       gboolean any_added = FALSE;
+       GSList *link;
+
+       /* It's okay to pass NULL here */
+       if (!combo_box)
+               return;
+
+       g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box));
+
+       text_combo = GTK_COMBO_BOX_TEXT (combo_box);
+
+       gtk_combo_box_text_remove_all (text_combo);
+
+       for (link = calendar_user_addresses; link; link = g_slist_next (link)) {
+               const gchar *address = link->data;
+
+               if (address && *address) {
+                       gtk_combo_box_text_append_text (text_combo, address);
+                       any_added = TRUE;
+               }
+       }
+
+       if (any_added)
+               gtk_combo_box_set_active (combo_box, 0);
+}
+
+typedef struct _RefreshData {
+       GtkWidget *content;
+       GCancellable *cancellable;
+       GSimpleAsyncResult *simple;
+       gchar *base_url;
+       ENamedParameters *credentials;
+} RefreshData;
+
+static void
+refresh_data_free (gpointer ptr)
+{
+       RefreshData *rd = ptr;
+
+       if (rd) {
+               if (rd->content) {
+                       EWebDAVDiscoverContentData *data;
+
+                       data = g_object_get_data (G_OBJECT (rd->content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+
+                       if (data) {
+                               if (data->info_bar && gtk_info_bar_get_message_type (data->info_bar) == 
GTK_MESSAGE_INFO) {
+                                       gtk_widget_destroy (GTK_WIDGET (data->info_bar));
+                                       data->info_bar = NULL;
+                               }
+
+                               gtk_widget_set_sensitive (GTK_WIDGET (data->sources_tree_view), TRUE);
+                               if (data->email_addresses_combo)
+                                       gtk_widget_set_sensitive (GTK_WIDGET (data->email_addresses_combo), 
TRUE);
+                       }
+               }
+
+               g_clear_object (&rd->content);
+               g_clear_object (&rd->cancellable);
+               g_clear_object (&rd->simple);
+               g_free (rd->base_url);
+               e_named_parameters_free (rd->credentials);
+               g_free (rd);
+       }
+}
+
+static void
+e_webdav_discover_content_refresh_done_cb (GObject *source_object,
+                                          GAsyncResult *result,
+                                          gpointer user_data);
+
+static void
+e_webdav_discover_content_trust_prompt_done_cb (GObject *source_object,
+                                               GAsyncResult *result,
+                                               gpointer user_data)
+{
+       ETrustPromptResponse response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+       ESource *source;
+       RefreshData *rd = user_data;
+       GError *local_error = NULL;
+
+       g_return_if_fail (E_IS_SOURCE (source_object));
+       g_return_if_fail (rd != NULL);
+
+       source = E_SOURCE (source_object);
+       if (!e_trust_prompt_run_for_source_finish (source, result, &response, &local_error)) {
+               g_simple_async_result_take_error (rd->simple, local_error);
+               local_error = NULL;
+               g_simple_async_result_complete (rd->simple);
+               refresh_data_free (rd);
+       } else if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT || response == 
E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY) {
+               /* Use NULL credentials to reuse those from the last time. */
+               e_webdav_discover_sources (source, rd->base_url, E_WEBDAV_DISCOVER_SUPPORTS_NONE, 
rd->credentials, rd->cancellable,
+                       e_webdav_discover_content_refresh_done_cb, rd);
+       } else {
+               g_cancellable_cancel (rd->cancellable);
+               g_cancellable_set_error_if_cancelled (rd->cancellable, &local_error);
+               g_simple_async_result_take_error (rd->simple, local_error);
+               local_error = NULL;
+               g_simple_async_result_complete (rd->simple);
+               refresh_data_free (rd);
+       }
+
+       g_clear_error (&local_error);
+}
+
+static void
+e_webdav_discover_content_credentials_prompt_done_cb (GObject *source_object,
+                                                     GAsyncResult *result,
+                                                     gpointer user_data)
+{
+       RefreshData *rd = user_data;
+       ENamedParameters *credentials = NULL;
+       ESource *source = NULL;
+       GError *local_error = NULL;
+
+       g_return_if_fail (rd != NULL);
+       g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (source_object));
+
+       if (!e_credentials_prompter_prompt_finish (E_CREDENTIALS_PROMPTER (source_object), result,
+               &source, &credentials, &local_error)) {
+               g_simple_async_result_take_error (rd->simple, local_error);
+               local_error = NULL;
+               g_simple_async_result_complete (rd->simple);
+               refresh_data_free (rd);
+       } else {
+               e_named_parameters_free (rd->credentials);
+               rd->credentials = credentials;
+               credentials = NULL;
+
+               e_webdav_discover_sources (source, rd->base_url, E_WEBDAV_DISCOVER_SUPPORTS_NONE, 
rd->credentials, rd->cancellable,
+                       e_webdav_discover_content_refresh_done_cb, rd);
+       }
+
+       e_named_parameters_free (credentials);
+       g_clear_object (&source);
+       g_clear_error (&local_error);
+}
+
+static void
+e_webdav_discover_content_refresh_done_cb (GObject *source_object,
+                                          GAsyncResult *result,
+                                          gpointer user_data)
+{
+       RefreshData *rd = user_data;
+       ESource *source;
+       gchar *certificate_pem = NULL;
+       GTlsCertificateFlags certificate_errors = 0;
+       GSList *discovered_sources = NULL;
+       GSList *calendar_user_addresses = NULL;
+       GError *local_error = NULL;
+
+       g_return_if_fail (E_IS_SOURCE (source_object));
+       g_return_if_fail (rd != NULL);
+
+       source = E_SOURCE (source_object);
+
+       if (!e_webdav_discover_sources_finish (source, result,
+               &certificate_pem, &certificate_errors, &discovered_sources,
+               &calendar_user_addresses, &local_error)) {
+               if (!g_cancellable_is_cancelled (rd->cancellable) && certificate_pem &&
+                   g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+                       GtkWindow *parent;
+                       GtkWidget *widget;
+
+                       widget = gtk_widget_get_toplevel (rd->content);
+                       parent = widget ? GTK_WINDOW (widget) : NULL;
+
+                       e_trust_prompt_run_for_source (parent, source, certificate_pem, certificate_errors,
+                               NULL, FALSE, rd->cancellable, e_webdav_discover_content_trust_prompt_done_cb, 
rd);
+                       rd = NULL;
+               } else if (g_cancellable_is_cancelled (rd->cancellable) ||
+                   !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) {
+                       g_simple_async_result_take_error (rd->simple, local_error);
+                       local_error = NULL;
+                       g_simple_async_result_complete (rd->simple);
+               } else {
+                       EWebDAVDiscoverContentData *data;
+
+                       data = g_object_get_data (G_OBJECT (rd->content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+                       g_return_if_fail (data != NULL);
+
+                       e_credentials_prompter_prompt (data->credentials_prompter, source,
+                               local_error ? local_error->message : NULL,
+                               rd->credentials ? E_CREDENTIALS_PROMPTER_PROMPT_FLAG_NONE:
+                               E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_STORED_CREDENTIALS,
+                               e_webdav_discover_content_credentials_prompt_done_cb, rd);
+                       rd = NULL;
+               }
+       } else {
+               EWebDAVDiscoverContentData *data;
+
+               data = g_object_get_data (G_OBJECT (rd->content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+               g_warn_if_fail (data != NULL);
+
+               if (data) {
+                       e_webdav_discover_content_fill_discovered_sources (data->sources_tree_view,
+                               data->supports_filter, discovered_sources);
+                       e_webdav_discover_content_fill_calendar_emails (data->email_addresses_combo,
+                               calendar_user_addresses);
+               }
+
+               g_simple_async_result_set_op_res_gboolean (rd->simple, TRUE);
+               g_simple_async_result_complete (rd->simple);
+       }
+
+       g_free (certificate_pem);
+       e_webdav_discover_free_discovered_sources (discovered_sources);
+       g_slist_free_full (calendar_user_addresses, g_free);
+       refresh_data_free (rd);
+       g_clear_error (&local_error);
+}
+
+static void
+e_webdav_discover_info_bar_response_cb (GtkInfoBar *info_bar,
+                                       gint response_id,
+                                       RefreshData *rd)
+{
+       if (response_id == GTK_RESPONSE_CANCEL) {
+               g_return_if_fail (rd != NULL);
+               g_return_if_fail (rd->cancellable != NULL);
+
+               g_cancellable_cancel (rd->cancellable);
+       }
+}
+
+/**
+ * e_webdav_discover_content_refresh:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ * @display_name: (allow none): optional display name to use for scratch sources
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request
+ *            is satisfied
+ * @user_data: (closure): data to pass to the callback function
+ *
+ * Asynchronously starts refresh of the @content. This means to access the server
+ * and search it for available sources. The @content shows a feedback and a Cancel
+ * button during the operation.
+ *
+ * The @display_name is used only if the @content wasn't created with an #ESource and
+ * it's shown in the password prompts, if there are required any.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_webdav_discover_content_refresh_finish() to get the result of the operation.
+ *
+ * Since: 3.18
+ **/
+void
+e_webdav_discover_content_refresh (GtkWidget *content,
+                                  const gchar *display_name,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+       EWebDAVDiscoverContentData *data;
+       RefreshData *rd;
+       ESource *source;
+       SoupURI *soup_uri;
+       GtkWidget *label;
+
+       g_return_if_fail (GTK_IS_GRID (content));
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_if_fail (data != NULL);
+       g_return_if_fail (data->base_url != NULL);
+
+       soup_uri = soup_uri_new (data->base_url);
+       if (!soup_uri) {
+               GSimpleAsyncResult *simple;
+
+               simple = g_simple_async_result_new (G_OBJECT (content), callback, user_data, 
e_webdav_discover_content_refresh);
+               g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                       _("Invalid URL"));
+               g_simple_async_result_complete_in_idle (simple);
+               g_object_unref (simple);
+
+               return;
+       }
+
+       if (!soup_uri_get_user (soup_uri)) {
+               GSimpleAsyncResult *simple;
+
+               soup_uri_free (soup_uri);
+
+               simple = g_simple_async_result_new (G_OBJECT (content), callback, user_data, 
e_webdav_discover_content_refresh);
+               g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                       _("User name not filled"));
+               g_simple_async_result_complete_in_idle (simple);
+               g_object_unref (simple);
+
+               return;
+       }
+
+       rd = g_new0 (RefreshData, 1);
+       rd->content = g_object_ref (content);
+       rd->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+       rd->simple = g_simple_async_result_new (G_OBJECT (content), callback, user_data, 
e_webdav_discover_content_refresh);
+       rd->base_url = g_strdup (data->base_url);
+       rd->credentials = NULL;
+
+       if (data->source) {
+               source = g_object_ref (data->source);
+       } else {
+               ESourceWebdav *webdav_extension;
+               ESourceAuthentication *auth_extension;
+
+               source = e_source_new_with_uid (data->base_url, NULL, NULL);
+               g_return_if_fail (source != NULL);
+
+               webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+               auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+
+               if (display_name && *display_name)
+                       e_source_set_display_name (source, display_name);
+               e_source_webdav_set_soup_uri (webdav_extension, soup_uri);
+               e_source_authentication_set_host (auth_extension, soup_uri_get_host (soup_uri));
+               e_source_authentication_set_port (auth_extension, soup_uri_get_port (soup_uri));
+               e_source_authentication_set_user (auth_extension, soup_uri_get_user (soup_uri));
+       }
+
+       gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (data->sources_tree_view)));
+       if (data->email_addresses_combo)
+               gtk_combo_box_text_remove_all (GTK_COMBO_BOX_TEXT (data->email_addresses_combo));
+
+       if (data->info_bar)
+               gtk_widget_destroy (GTK_WIDGET (data->info_bar));
+
+       data->info_bar = GTK_INFO_BAR (gtk_info_bar_new_with_buttons (_("Cancel"), GTK_RESPONSE_CANCEL, 
NULL));
+       gtk_info_bar_set_message_type (data->info_bar, GTK_MESSAGE_INFO);
+       gtk_info_bar_set_show_close_button (data->info_bar, FALSE);
+       label = gtk_label_new (_("Searching server sources..."));
+       gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (data->info_bar)), label);
+       gtk_widget_show (label);
+       gtk_widget_show (GTK_WIDGET (data->info_bar));
+
+       g_signal_connect (data->info_bar, "response", G_CALLBACK (e_webdav_discover_info_bar_response_cb), 
rd);
+
+       gtk_widget_set_sensitive (GTK_WIDGET (data->sources_tree_view), FALSE);
+       if (data->email_addresses_combo)
+               gtk_widget_set_sensitive (GTK_WIDGET (data->email_addresses_combo), FALSE);
+
+       gtk_grid_attach (GTK_GRID (content), GTK_WIDGET (data->info_bar), 0, 2, 1, 1);
+
+       e_webdav_discover_sources (source, rd->base_url, E_WEBDAV_DISCOVER_SUPPORTS_NONE, rd->credentials, 
rd->cancellable,
+               e_webdav_discover_content_refresh_done_cb, rd);
+
+       g_object_unref (source);
+       soup_uri_free (soup_uri);
+}
+
+/**
+ * e_webdav_discover_content_refresh_finish:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ * @result: a #GAsyncResult
+ * @error: (allow-none): return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_webdav_discover_content_refresh(). If an
+ * error occurred, the function will set @error and return %FALSE. There is
+ * available e_webdav_discover_content_show_error() for convenience, which
+ * shows the error within @content and takes care of it when refreshing
+ * the content.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.18
+ **/
+gboolean
+e_webdav_discover_content_refresh_finish (GtkWidget *content,
+                                         GAsyncResult *result,
+                                         GError **error)
+{
+       EWebDAVDiscoverContentData *data;
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (GTK_IS_GRID (content), FALSE);
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_val_if_fail (data != NULL, FALSE);
+       g_return_val_if_fail (g_simple_async_result_is_valid (
+               result, G_OBJECT (content), e_webdav_discover_content_refresh), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+static void
+e_webdav_discover_info_bar_error_response_cb (GtkInfoBar *info_bar,
+                                             gint response_id,
+                                             GtkWidget *content)
+{
+       EWebDAVDiscoverContentData *data;
+
+       g_return_if_fail (GTK_IS_GRID (content));
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_if_fail (data != NULL);
+
+       if (data->info_bar == info_bar) {
+               gtk_widget_destroy (GTK_WIDGET (data->info_bar));
+               data->info_bar = NULL;
+       }
+}
+
+/**
+ * e_webdav_discover_content_show_error:
+ * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
+ * @error: (allow none): a #GError to show in the UI, or %NULL
+ *
+ * Shows the @error within @content, unless it's a #G_IO_ERROR_CANCELLED, or %NULL,
+ * which are safely ignored. The advantage of this function is that the error
+ * message is removed when the refresh operation is started.
+ *
+ * Since: 3.18
+ **/
+void
+e_webdav_discover_content_show_error (GtkWidget *content,
+                                     const GError *error)
+{
+       EWebDAVDiscoverContentData *data;
+       GtkWidget *label;
+
+       g_return_if_fail (GTK_IS_GRID (content));
+
+       data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
+       g_return_if_fail (data != NULL);
+
+       if (data->info_bar) {
+               gtk_widget_destroy (GTK_WIDGET (data->info_bar));
+               data->info_bar = NULL;
+       }
+
+       if (!error || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+               return;
+
+       data->info_bar = GTK_INFO_BAR (gtk_info_bar_new ());
+       gtk_info_bar_set_message_type (data->info_bar, GTK_MESSAGE_ERROR);
+       gtk_info_bar_set_show_close_button (data->info_bar, TRUE);
+
+       label = gtk_label_new (error->message);
+       gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (data->info_bar)), label);
+       gtk_widget_show (label);
+       gtk_widget_show (GTK_WIDGET (data->info_bar));
+
+       g_signal_connect (data->info_bar, "response", G_CALLBACK 
(e_webdav_discover_info_bar_error_response_cb), content);
+
+       gtk_grid_attach (GTK_GRID (content), GTK_WIDGET (data->info_bar), 0, 2, 1, 1);
+}
+
+static void
+e_webdav_discover_content_dialog_refresh_done_cb (GObject *source_object,
+                                                 GAsyncResult *result,
+                                                 gpointer user_data)
+{
+       GError *local_error = NULL;
+
+       if (!e_webdav_discover_content_refresh_finish (GTK_WIDGET (source_object), result, &local_error)) {
+               e_webdav_discover_content_show_error (GTK_WIDGET (source_object), local_error);
+       }
+
+       g_clear_error (&local_error);
+}
+
+static void
+e_webdav_discover_content_selection_changed_cb (GtkTreeSelection *selection,
+                                               GtkDialog *dialog)
+{
+       g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+       g_return_if_fail (GTK_IS_DIALOG (dialog));
+
+       gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_ACCEPT,
+               gtk_tree_selection_count_selected_rows (selection) > 0);
+}
+
+/**
+ * e_webdav_discover_dialog_new:
+ * @parent: a #GtkWindow parent for the dialog
+ * @title: title of the window
+ * @credentials_prompter: an #ECredentialsPrompter to use to ask for credentials
+ * @source: an #ESource to use for authentication
+ * @base_url: (allow-none): optional base URL to use for discovery, or %NULL
+ * @supports_filter: a bit-or of #EWebDAVDiscoverSupports, a filter to limit what source
+ *    types will be shown in the dialog content; use %E_WEBDAV_DISCOVER_SUPPORTS_NONE
+ *    to show all
+ *
+ * Creates a new #GtkDialog which has as its content a WebDAV discovery widget,
+ * created with e_webdav_discover_content_new(). This dialog can be shown to a user
+ * and when its final response is %GTK_RESPONSE_ACCEPT, then the inner content
+ * can be asked for currently selected source(s).
+ *
+ * Returns: (transfer full): a newly created #GtkDialog, which should be freed
+ * with gtk_widget_destroy(), when no longer needed.
+ *
+ * Since: 3.18
+ **/
+GtkDialog *
+e_webdav_discover_dialog_new (GtkWindow *parent,
+                             const gchar *title,
+                             ECredentialsPrompter *credentials_prompter,
+                             ESource *source,
+                             const gchar *base_url,
+                             guint supports_filter)
+{
+       GtkWidget *dialog, *container, *widget;
+       GtkTreeSelection *selection;
+
+       dialog = gtk_dialog_new_with_buttons (title, parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+               _("_Cancel"), GTK_RESPONSE_REJECT,
+               _("_OK"), GTK_RESPONSE_ACCEPT,
+               NULL);
+
+       widget = e_webdav_discover_content_new (credentials_prompter, source, base_url, supports_filter);
+
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "vexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+
+       container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+       gtk_container_add (GTK_CONTAINER (container), widget);
+
+       g_object_set_data (G_OBJECT (dialog), WEBDAV_DISCOVER_CONTENT_KEY, widget);
+
+       gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 400);
+
+       selection = e_webdav_discover_content_get_tree_selection (widget);
+       g_signal_connect (selection, "changed", G_CALLBACK (e_webdav_discover_content_selection_changed_cb), 
dialog);
+       e_webdav_discover_content_selection_changed_cb (selection, GTK_DIALOG (dialog));
+
+       return GTK_DIALOG (dialog);
+}
+
+/**
+ * e_webdav_discover_dialog_get_content:
+ * @dialog: a #GtkDialog returned by e_webdav_discover_dialog_new()
+ *
+ * Returns inner WebDAV discovery content, which can be further manipulated.
+ *
+ * Returns: (transfer none): inner WebDAV discovery content
+ *
+ * Since: 3.18
+ **/
+GtkWidget *
+e_webdav_discover_dialog_get_content (GtkDialog *dialog)
+{
+       GtkWidget *content;
+
+       g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
+
+       content = g_object_get_data (G_OBJECT (dialog), WEBDAV_DISCOVER_CONTENT_KEY);
+       g_return_val_if_fail (content != NULL, NULL);
+
+       return content;
+}
+
+/**
+ * e_webdav_discover_dialog_refresh:
+ * @dialog: a #GtkDialog returned by e_webdav_discover_dialog_new()
+ *
+ * Invokes refresh of the inner content of the WebDAV discovery dialog.
+ *
+ * Since: 3.18
+ **/
+void
+e_webdav_discover_dialog_refresh (GtkDialog *dialog)
+{
+       GtkWidget *content;
+
+       g_return_if_fail (GTK_IS_DIALOG (dialog));
+
+       content = g_object_get_data (G_OBJECT (dialog), WEBDAV_DISCOVER_CONTENT_KEY);
+       g_return_if_fail (content != NULL);
+
+       e_webdav_discover_content_refresh (content, gtk_window_get_title (GTK_WINDOW (dialog)),
+               NULL, e_webdav_discover_content_dialog_refresh_done_cb, NULL);
+}
diff --git a/libedataserverui/e-webdav-discover-widget.h b/libedataserverui/e-webdav-discover-widget.h
new file mode 100644
index 0000000..fdb7ee2
--- /dev/null
+++ b/libedataserverui/e-webdav-discover-widget.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVERUI_H_INSIDE__) && !defined (LIBEDATASERVERUI_COMPILATION)
+#error "Only <libedataserverui/libedataserverui.h> should be included directly."
+#endif
+
+#ifndef E_WEBDAV_DISCOVER_WIDGET_H
+#define E_WEBDAV_DISCOVER_WIDGET_H
+
+#include <gio/gio.h>
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+#include <libedataserverui/libedataserverui.h>
+
+G_BEGIN_DECLS
+
+GtkWidget *    e_webdav_discover_content_new           (ECredentialsPrompter *credentials_prompter,
+                                                        ESource *source,
+                                                        const gchar *base_url,
+                                                        guint supports_filter);
+GtkTreeSelection *
+               e_webdav_discover_content_get_tree_selection
+                                                       (GtkWidget *content);
+gboolean       e_webdav_discover_content_get_multiselect
+                                                       (GtkWidget *content);
+void           e_webdav_discover_content_set_multiselect
+                                                       (GtkWidget *content,
+                                                        gboolean multiselect);
+const gchar *  e_webdav_discover_content_get_base_url  (GtkWidget *content);
+void           e_webdav_discover_content_set_base_url  (GtkWidget *content,
+                                                        const gchar *base_url);
+gboolean       e_webdav_discover_content_get_selected  (GtkWidget *content,
+                                                        gint index,
+                                                        gchar **out_href,
+                                                        guint *out_supports,
+                                                        gchar **out_display_name,
+                                                        gchar **out_color);
+gchar *                e_webdav_discover_content_get_user_address
+                                                       (GtkWidget *content);
+void           e_webdav_discover_content_refresh       (GtkWidget *content,
+                                                        const gchar *display_name,
+                                                        GCancellable *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer user_data);
+gboolean       e_webdav_discover_content_refresh_finish
+                                                       (GtkWidget *content,
+                                                        GAsyncResult *result,
+                                                        GError **error);
+void           e_webdav_discover_content_show_error    (GtkWidget *content,
+                                                        const GError *error);
+
+GtkDialog *    e_webdav_discover_dialog_new            (GtkWindow *parent,
+                                                        const gchar *title,
+                                                        ECredentialsPrompter *credentials_prompter,
+                                                        ESource *source,
+                                                        const gchar *base_url,
+                                                        guint supports_filter);
+
+GtkWidget *    e_webdav_discover_dialog_get_content    (GtkDialog *dialog);
+void           e_webdav_discover_dialog_refresh        (GtkDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_WEBDAV_DISCOVER_WIDGET_H */
diff --git a/libedataserverui/libedataserverui.h b/libedataserverui/libedataserverui.h
index 19c60cb..3223492 100644
--- a/libedataserverui/libedataserverui.h
+++ b/libedataserverui/libedataserverui.h
@@ -20,10 +20,12 @@
 
 #define __LIBEDATASERVERUI_H_INSIDE__
 
+#include <libedataserverui/e-cell-renderer-color.h>
 #include <libedataserverui/e-credentials-prompter.h>
 #include <libedataserverui/e-credentials-prompter-impl.h>
 #include <libedataserverui/e-credentials-prompter-impl-password.h>
 #include <libedataserverui/e-trust-prompt.h>
+#include <libedataserverui/e-webdav-discover-widget.h>
 
 #undef __LIBEDATASERVERUI_H_INSIDE__
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 94cd9d5..04d2a17 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -195,6 +195,7 @@ libedataserver/e-webdav-discover.c
 libedataserverui/e-credentials-prompter.c
 libedataserverui/e-credentials-prompter-impl-password.c
 libedataserverui/e-trust-prompt.c
+libedataserverui/e-webdav-discover-widget.c
 modules/gnome-online-accounts/e-goa-password-based.c
 modules/gnome-online-accounts/goaewsclient.c
 modules/gnome-online-accounts/module-gnome-online-accounts.c


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