aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/evolution-source-viewer.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/evolution-source-viewer.c')
-rw-r--r--e-util/evolution-source-viewer.c1176
1 files changed, 1176 insertions, 0 deletions
diff --git a/e-util/evolution-source-viewer.c b/e-util/evolution-source-viewer.c
new file mode 100644
index 0000000000..9f5fb117a5
--- /dev/null
+++ b/e-util/evolution-source-viewer.c
@@ -0,0 +1,1176 @@
+/*
+ * evolution-source-viewer.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include <libedataserver/libedataserver.h>
+
+/* XXX Even though this is all one file, I'm still being pedantic about data
+ * encapsulation (except for a private struct, even I'm not that anal!).
+ * I expect this program will eventually be too complex for one file
+ * and we'll want to split off an e-source-viewer.[ch]. */
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_VIEWER \
+ (e_source_viewer_get_type ())
+#define E_SOURCE_VIEWER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SOURCE_VIEWER, ESourceViewer))
+#define E_SOURCE_VIEWER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SOURCE_VIEWER, ESourceViewerClass))
+#define E_IS_SOURCE_VIEWER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SOURCE_VIEWER))
+#define E_IS_SOURCE_VIEWER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SOURCE_VIEWER))
+#define E_SOURCE_VIEWER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SOURCE_VIEWER, ESourceViewerClass))
+
+typedef struct _ESourceViewer ESourceViewer;
+typedef struct _ESourceViewerClass ESourceViewerClass;
+
+struct _ESourceViewer {
+ GtkWindow parent;
+ ESourceRegistry *registry;
+
+ GtkTreeStore *tree_store;
+ GHashTable *source_index;
+
+ GCancellable *delete_operation;
+
+ GtkWidget *tree_view; /* not referenced */
+ GtkWidget *text_view; /* not referenced */
+ GtkWidget *top_panel; /* not referenced */
+
+ /* Viewing Page */
+ GtkWidget *viewing_label; /* not referenced */
+ GtkWidget *delete_button; /* not referenced */
+
+ /* Deleting Page */
+ GtkWidget *deleting_label; /* not referenced */
+ GtkWidget *deleting_cancel; /* not referenced */
+};
+
+struct _ESourceViewerClass {
+ GtkWindowClass parent_class;
+};
+
+enum {
+ PAGE_VIEWING,
+ PAGE_DELETING
+};
+
+enum {
+ PROP_0,
+ PROP_REGISTRY
+};
+
+enum {
+ COLUMN_DISPLAY_NAME,
+ COLUMN_SOURCE_UID,
+ COLUMN_REMOVABLE,
+ COLUMN_WRITABLE,
+ COLUMN_REMOTE_CREATABLE,
+ COLUMN_REMOTE_DELETABLE,
+ COLUMN_SOURCE,
+ NUM_COLUMNS
+};
+
+/* Forward Declarations */
+GType e_source_viewer_get_type (void) G_GNUC_CONST;
+GtkWidget * e_source_viewer_new (GCancellable *cancellable,
+ GError **error);
+ESourceRegistry *
+ e_source_viewer_get_registry (ESourceViewer *viewer);
+GtkTreePath * e_source_viewer_dup_selected_path
+ (ESourceViewer *viewer);
+gboolean e_source_viewer_set_selected_path
+ (ESourceViewer *viewer,
+ GtkTreePath *path);
+ESource * e_source_viewer_ref_selected_source
+ (ESourceViewer *viewer);
+gboolean e_source_viewer_set_selected_source
+ (ESourceViewer *viewer,
+ ESource *source);
+GNode * e_source_viewer_build_display_tree
+ (ESourceViewer *viewer);
+
+static void e_source_viewer_initable_init (GInitableIface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ ESourceViewer,
+ e_source_viewer,
+ GTK_TYPE_WINDOW,
+ G_IMPLEMENT_INTERFACE (
+ G_TYPE_INITABLE,
+ e_source_viewer_initable_init));
+
+static GIcon *
+source_view_new_remote_creatable_icon (void)
+{
+ GEmblem *emblem;
+ GIcon *emblem_icon;
+ GIcon *folder_icon;
+ GIcon *icon;
+
+ emblem_icon = g_themed_icon_new ("emblem-new");
+ folder_icon = g_themed_icon_new ("folder-remote");
+
+ emblem = g_emblem_new (emblem_icon);
+ icon = g_emblemed_icon_new (folder_icon, emblem);
+ g_object_unref (emblem);
+
+ g_object_unref (folder_icon);
+ g_object_unref (emblem_icon);
+
+ return icon;
+}
+
+static GIcon *
+source_view_new_remote_deletable_icon (void)
+{
+ GEmblem *emblem;
+ GIcon *emblem_icon;
+ GIcon *folder_icon;
+ GIcon *icon;
+
+ emblem_icon = g_themed_icon_new ("edit-delete");
+ folder_icon = g_themed_icon_new ("folder-remote");
+
+ emblem = g_emblem_new (emblem_icon);
+ icon = g_emblemed_icon_new (folder_icon, emblem);
+ g_object_unref (emblem);
+
+ g_object_unref (folder_icon);
+ g_object_unref (emblem_icon);
+
+ return icon;
+}
+
+static gchar *
+source_viewer_get_monospace_font_name (void)
+{
+ GSettings *settings;
+ gchar *font_name;
+
+ settings = g_settings_new ("org.gnome.desktop.interface");
+ font_name = g_settings_get_string (settings, "monospace-font-name");
+ g_object_unref (settings);
+
+ /* Fallback to a reasonable default. */
+ if (font_name == NULL)
+ font_name = g_strdup ("Monospace 10");
+
+ return font_name;
+}
+
+static void
+source_viewer_set_text (ESourceViewer *viewer,
+ ESource *source)
+{
+ GtkTextView *text_view;
+ GtkTextBuffer *buffer;
+ GtkTextIter start;
+ GtkTextIter end;
+
+ text_view = GTK_TEXT_VIEW (viewer->text_view);
+ buffer = gtk_text_view_get_buffer (text_view);
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+ gtk_text_buffer_delete (buffer, &start, &end);
+
+ if (source != NULL) {
+ gchar *string;
+ gsize length;
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+
+ string = e_source_to_string (source, &length);
+ gtk_text_buffer_insert (buffer, &start, string, length);
+ g_free (string);
+ }
+}
+
+static void
+source_viewer_update_row (ESourceViewer *viewer,
+ ESource *source)
+{
+ GHashTable *source_index;
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ const gchar *display_name;
+ const gchar *source_uid;
+ gboolean removable;
+ gboolean writable;
+ gboolean remote_creatable;
+ gboolean remote_deletable;
+
+ source_index = viewer->source_index;
+ reference = g_hash_table_lookup (source_index, source);
+
+ /* We show all sources, so the reference should be valid. */
+ g_return_if_fail (gtk_tree_row_reference_valid (reference));
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+
+ source_uid = e_source_get_uid (source);
+ display_name = e_source_get_display_name (source);
+ removable = e_source_get_removable (source);
+ writable = e_source_get_writable (source);
+ remote_creatable = e_source_get_remote_creatable (source);
+ remote_deletable = e_source_get_remote_deletable (source);
+
+ gtk_tree_store_set (
+ GTK_TREE_STORE (model), &iter,
+ COLUMN_DISPLAY_NAME, display_name,
+ COLUMN_SOURCE_UID, source_uid,
+ COLUMN_REMOVABLE, removable,
+ COLUMN_WRITABLE, writable,
+ COLUMN_REMOTE_CREATABLE, remote_creatable,
+ COLUMN_REMOTE_DELETABLE, remote_deletable,
+ COLUMN_SOURCE, source,
+ -1);
+}
+
+static gboolean
+source_viewer_traverse (GNode *node,
+ gpointer user_data)
+{
+ ESourceViewer *viewer;
+ ESource *source;
+ GHashTable *source_index;
+ GtkTreeRowReference *reference = NULL;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ /* Skip the root node. */
+ if (G_NODE_IS_ROOT (node))
+ return FALSE;
+
+ viewer = E_SOURCE_VIEWER (user_data);
+
+ source_index = viewer->source_index;
+
+ tree_view = GTK_TREE_VIEW (viewer->tree_view);
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (node->parent != NULL && node->parent->data != NULL)
+ reference = g_hash_table_lookup (
+ source_index, node->parent->data);
+
+ if (gtk_tree_row_reference_valid (reference)) {
+ GtkTreeIter parent;
+
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &parent, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent);
+ } else
+ gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
+
+ /* Source index takes ownership. */
+ source = g_object_ref (node->data);
+
+ path = gtk_tree_model_get_path (model, &iter);
+ reference = gtk_tree_row_reference_new (model, path);
+ g_hash_table_insert (source_index, source, reference);
+ gtk_tree_path_free (path);
+
+ source_viewer_update_row (viewer, source);
+
+ return FALSE;
+}
+
+static void
+source_viewer_save_expanded (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GQueue *queue)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ ESource *source;
+
+ model = gtk_tree_view_get_model (tree_view);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
+ g_queue_push_tail (queue, source);
+}
+
+static void
+source_viewer_build_model (ESourceViewer *viewer)
+{
+ GQueue queue = G_QUEUE_INIT;
+ GHashTable *source_index;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreePath *sel_path;
+ ESource *sel_source;
+ GNode *root;
+
+ tree_view = GTK_TREE_VIEW (viewer->tree_view);
+
+ source_index = viewer->source_index;
+ sel_path = e_source_viewer_dup_selected_path (viewer);
+ sel_source = e_source_viewer_ref_selected_source (viewer);
+
+ /* Save expanded sources to restore later. */
+ gtk_tree_view_map_expanded_rows (
+ tree_view, (GtkTreeViewMappingFunc)
+ source_viewer_save_expanded, &queue);
+
+ model = gtk_tree_view_get_model (tree_view);
+ gtk_tree_store_clear (GTK_TREE_STORE (model));
+
+ g_hash_table_remove_all (source_index);
+
+ root = e_source_viewer_build_display_tree (viewer);
+
+ g_node_traverse (
+ root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ (GNodeTraverseFunc) source_viewer_traverse, viewer);
+
+ e_source_registry_free_display_tree (root);
+
+ /* Restore previously expanded sources. */
+ while (!g_queue_is_empty (&queue)) {
+ GtkTreeRowReference *reference;
+ ESource *source;
+
+ source = g_queue_pop_head (&queue);
+ reference = g_hash_table_lookup (source_index, source);
+
+ if (gtk_tree_row_reference_valid (reference)) {
+ GtkTreePath *path;
+
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_view_expand_to_path (tree_view, path);
+ gtk_tree_path_free (path);
+ }
+
+ g_object_unref (source);
+ }
+
+ /* Restore the selection. */
+ if (sel_source != NULL && sel_path != NULL) {
+ if (!e_source_viewer_set_selected_source (viewer, sel_source))
+ e_source_viewer_set_selected_path (viewer, sel_path);
+ }
+
+ if (sel_path != NULL)
+ gtk_tree_path_free (sel_path);
+
+ if (sel_source != NULL)
+ g_object_unref (sel_source);
+}
+
+static void
+source_viewer_expand_to_source (ESourceViewer *viewer,
+ ESource *source)
+{
+ GHashTable *source_index;
+ GtkTreeRowReference *reference;
+ GtkTreeView *tree_view;
+ GtkTreePath *path;
+
+ source_index = viewer->source_index;
+ reference = g_hash_table_lookup (source_index, source);
+
+ /* We show all sources, so the reference should be valid. */
+ g_return_if_fail (gtk_tree_row_reference_valid (reference));
+
+ /* Expand the tree view to the path containing the ESource. */
+ tree_view = GTK_TREE_VIEW (viewer->tree_view);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_view_expand_to_path (tree_view, path);
+ gtk_tree_path_free (path);
+}
+
+static void
+source_viewer_source_added_cb (ESourceRegistry *registry,
+ ESource *source,
+ ESourceViewer *viewer)
+{
+ source_viewer_build_model (viewer);
+
+ source_viewer_expand_to_source (viewer, source);
+}
+
+static void
+source_viewer_source_changed_cb (ESourceRegistry *registry,
+ ESource *source,
+ ESourceViewer *viewer)
+{
+ ESource *selected;
+
+ source_viewer_update_row (viewer, source);
+
+ selected = e_source_viewer_ref_selected_source (viewer);
+ if (selected != NULL) {
+ if (e_source_equal (source, selected))
+ source_viewer_set_text (viewer, source);
+ g_object_unref (selected);
+ }
+}
+
+static void
+source_viewer_source_removed_cb (ESourceRegistry *registry,
+ ESource *source,
+ ESourceViewer *viewer)
+{
+ source_viewer_build_model (viewer);
+}
+
+static void
+source_viewer_selection_changed_cb (GtkTreeSelection *selection,
+ ESourceViewer *viewer)
+{
+ ESource *source;
+ const gchar *uid = NULL;
+ gboolean removable = FALSE;
+
+ source = e_source_viewer_ref_selected_source (viewer);
+
+ source_viewer_set_text (viewer, source);
+
+ if (source != NULL) {
+ uid = e_source_get_uid (source);
+ removable = e_source_get_removable (source);
+ }
+
+ gtk_label_set_text (GTK_LABEL (viewer->viewing_label), uid);
+ gtk_widget_set_visible (viewer->delete_button, removable);
+
+ if (source != NULL)
+ g_object_unref (source);
+}
+
+static void
+source_viewer_delete_done_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ESource *source;
+ ESourceViewer *viewer;
+ GError *error = NULL;
+
+ source = E_SOURCE (source_object);
+ viewer = E_SOURCE_VIEWER (user_data);
+
+ e_source_remove_finish (source, result, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_clear_error (&error);
+
+ /* FIXME Show an info bar with the error message. */
+ } else if (error != NULL) {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_notebook_set_current_page (
+ GTK_NOTEBOOK (viewer->top_panel), PAGE_VIEWING);
+ gtk_widget_set_sensitive (viewer->tree_view, TRUE);
+
+ g_object_unref (viewer->delete_operation);
+ viewer->delete_operation = NULL;
+
+ g_object_unref (viewer);
+}
+
+static void
+source_viewer_delete_button_clicked_cb (GtkButton *delete_button,
+ ESourceViewer *viewer)
+{
+ ESource *source;
+ const gchar *uid;
+
+ g_return_if_fail (viewer->delete_operation == NULL);
+
+ source = e_source_viewer_ref_selected_source (viewer);
+ g_return_if_fail (source != NULL);
+
+ uid = e_source_get_uid (source);
+ gtk_label_set_text (GTK_LABEL (viewer->deleting_label), uid);
+
+ gtk_notebook_set_current_page (
+ GTK_NOTEBOOK (viewer->top_panel), PAGE_DELETING);
+ gtk_widget_set_sensitive (viewer->tree_view, FALSE);
+
+ viewer->delete_operation = g_cancellable_new ();
+
+ e_source_remove (
+ source,
+ viewer->delete_operation,
+ source_viewer_delete_done_cb,
+ g_object_ref (viewer));
+
+ g_object_unref (source);
+}
+
+static void
+source_viewer_deleting_cancel_clicked_cb (GtkButton *deleting_cancel,
+ ESourceViewer *viewer)
+{
+ g_return_if_fail (viewer->delete_operation != NULL);
+
+ g_cancellable_cancel (viewer->delete_operation);
+}
+
+static void
+source_viewer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_REGISTRY:
+ g_value_set_object (
+ value,
+ e_source_viewer_get_registry (
+ E_SOURCE_VIEWER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_viewer_dispose (GObject *object)
+{
+ ESourceViewer *viewer = E_SOURCE_VIEWER (object);
+
+ if (viewer->registry != NULL) {
+ g_signal_handlers_disconnect_matched (
+ viewer->registry,
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
+ g_object_unref (viewer->registry);
+ viewer->registry = NULL;
+ }
+
+ if (viewer->tree_store != NULL) {
+ g_object_unref (viewer->tree_store);
+ viewer->tree_store = NULL;
+ }
+
+ g_hash_table_remove_all (viewer->source_index);
+
+ if (viewer->delete_operation != NULL) {
+ g_object_unref (viewer->delete_operation);
+ viewer->delete_operation = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_source_viewer_parent_class)->dispose (object);
+}
+
+static void
+source_viewer_finalize (GObject *object)
+{
+ ESourceViewer *viewer = E_SOURCE_VIEWER (object);
+
+ g_hash_table_destroy (viewer->source_index);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_source_viewer_parent_class)->finalize (object);
+}
+
+static void
+source_viewer_constructed (GObject *object)
+{
+ ESourceViewer *viewer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkWidget *container;
+ GtkWidget *paned;
+ GtkWidget *widget;
+ PangoAttribute *attr;
+ PangoAttrList *bold;
+ PangoFontDescription *desc;
+ GIcon *icon;
+ const gchar *title;
+ gchar *font_name;
+ gint page_num;
+
+ viewer = E_SOURCE_VIEWER (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_source_viewer_parent_class)->constructed (object);
+
+ bold = pango_attr_list_new ();
+ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+ pango_attr_list_insert (bold, attr);
+
+ title = _("Evolution Source Viewer");
+ gtk_window_set_title (GTK_WINDOW (viewer), title);
+ gtk_window_set_default_size (GTK_WINDOW (viewer), 800, 600);
+
+ paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_paned_set_position (GTK_PANED (paned), 400);
+ gtk_container_add (GTK_CONTAINER (viewer), paned);
+ gtk_widget_show (paned);
+
+ /* Left panel */
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_paned_add1 (GTK_PANED (paned), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_tree_view_new_with_model (
+ GTK_TREE_MODEL (viewer->tree_store));
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ viewer->tree_view = widget; /* do not reference */
+ gtk_widget_show (widget);
+
+ column = gtk_tree_view_column_new ();
+ /* Translators: The name that is displayed in the user interface */
+ gtk_tree_view_column_set_title (column, _("Display Name"));
+ gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "text", COLUMN_DISPLAY_NAME);
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("Flags"));
+ gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (
+ renderer,
+ "stock-id", GTK_STOCK_EDIT,
+ "stock-size", GTK_ICON_SIZE_MENU,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible", COLUMN_WRITABLE);
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (
+ renderer,
+ "stock-id", GTK_STOCK_DELETE,
+ "stock-size", GTK_ICON_SIZE_MENU,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible", COLUMN_REMOVABLE);
+
+ icon = source_view_new_remote_creatable_icon ();
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (
+ renderer,
+ "gicon", icon,
+ "stock-size", GTK_ICON_SIZE_MENU,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible", COLUMN_REMOTE_CREATABLE);
+ g_object_unref (icon);
+
+ icon = source_view_new_remote_deletable_icon ();
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (
+ renderer,
+ "gicon", icon,
+ "stock-size", GTK_ICON_SIZE_MENU,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible", COLUMN_REMOTE_DELETABLE);
+ g_object_unref (icon);
+
+ /* Append an empty pixbuf renderer to fill leftover space. */
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("Identity"));
+ gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "text", COLUMN_SOURCE_UID);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+
+ /* Right panel */
+
+ widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_paned_add2 (GTK_PANED (paned), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_notebook_new ();
+ gtk_widget_set_margin_top (widget, 3);
+ gtk_widget_set_margin_right (widget, 3);
+ gtk_widget_set_margin_bottom (widget, 3);
+ /* leave left margin at zero */
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ viewer->top_panel = widget; /* do not reference */
+ gtk_widget_show (widget);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_text_view_new ();
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (widget), FALSE);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ viewer->text_view = widget; /* do not reference */
+ gtk_widget_show (widget);
+
+ font_name = source_viewer_get_monospace_font_name ();
+ desc = pango_font_description_from_string (font_name);
+ gtk_widget_override_font (widget, desc);
+ pango_font_description_free (desc);
+ g_free (font_name);
+
+ /* Top panel: Viewing */
+
+ container = viewer->top_panel;
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ page_num = gtk_notebook_append_page (
+ GTK_NOTEBOOK (container), widget, NULL);
+ g_warn_if_fail (page_num == PAGE_VIEWING);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_label_new ("Identity:");
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_attributes (GTK_LABEL (widget), bold);
+ gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ viewer->viewing_label = widget; /* do not reference */
+ gtk_widget_show (widget);
+
+ widget = gtk_button_new_from_stock (GTK_STOCK_DELETE);
+ gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ viewer->delete_button = widget; /* do not reference */
+ gtk_widget_hide (widget);
+
+ /* Top panel: Deleting */
+
+ container = viewer->top_panel;
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ page_num = gtk_notebook_append_page (
+ GTK_NOTEBOOK (container), widget, NULL);
+ g_warn_if_fail (page_num == PAGE_DELETING);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_label_new ("Deleting");
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_attributes (GTK_LABEL (widget), bold);
+ gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ viewer->deleting_label = widget; /* do not reference */
+ gtk_widget_show (widget);
+
+ widget = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
+ gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ viewer->deleting_cancel = widget; /* do not reference */
+ gtk_widget_show (widget);
+
+ pango_attr_list_unref (bold);
+
+ g_signal_connect (
+ selection, "changed",
+ G_CALLBACK (source_viewer_selection_changed_cb), viewer);
+
+ g_signal_connect (
+ viewer->delete_button, "clicked",
+ G_CALLBACK (source_viewer_delete_button_clicked_cb), viewer);
+
+ g_signal_connect (
+ viewer->deleting_cancel, "clicked",
+ G_CALLBACK (source_viewer_deleting_cancel_clicked_cb), viewer);
+}
+
+static gboolean
+source_viewer_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESourceViewer *viewer;
+ ESourceRegistry *registry;
+
+ viewer = E_SOURCE_VIEWER (initable);
+
+ registry = e_source_registry_new_sync (cancellable, error);
+
+ if (registry == NULL)
+ return FALSE;
+
+ viewer->registry = registry; /* takes ownership */
+
+ g_signal_connect (
+ registry, "source-added",
+ G_CALLBACK (source_viewer_source_added_cb), viewer);
+
+ g_signal_connect (
+ registry, "source-changed",
+ G_CALLBACK (source_viewer_source_changed_cb), viewer);
+
+ g_signal_connect (
+ registry, "source-removed",
+ G_CALLBACK (source_viewer_source_removed_cb), viewer);
+
+ source_viewer_build_model (viewer);
+
+ gtk_tree_view_expand_all (GTK_TREE_VIEW (viewer->tree_view));
+
+ return TRUE;
+}
+
+static void
+e_source_viewer_class_init (ESourceViewerClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = source_viewer_get_property;
+ object_class->dispose = source_viewer_dispose;
+ object_class->finalize = source_viewer_finalize;
+ object_class->constructed = source_viewer_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_REGISTRY,
+ g_param_spec_object (
+ "registry",
+ "Registry",
+ "Data source registry",
+ E_TYPE_SOURCE_REGISTRY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_source_viewer_initable_init (GInitableIface *interface)
+{
+ interface->init = source_viewer_initable_init;
+}
+
+static void
+e_source_viewer_init (ESourceViewer *viewer)
+{
+ viewer->tree_store = gtk_tree_store_new (
+ NUM_COLUMNS,
+ G_TYPE_STRING, /* COLUMN_DISPLAY_NAME */
+ G_TYPE_STRING, /* COLUMN_SOURCE_UID */
+ G_TYPE_BOOLEAN, /* COLUMN_REMOVABLE */
+ G_TYPE_BOOLEAN, /* COLUMN_WRITABLE */
+ G_TYPE_BOOLEAN, /* COLUMN_REMOTE_CREATABLE */
+ G_TYPE_BOOLEAN, /* COLUMN_REMOTE_DELETABLE */
+ E_TYPE_SOURCE); /* COLUMN_SOURCE */
+
+ viewer->source_index = g_hash_table_new_full (
+ (GHashFunc) e_source_hash,
+ (GEqualFunc) e_source_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) gtk_tree_row_reference_free);
+}
+
+GtkWidget *
+e_source_viewer_new (GCancellable *cancellable,
+ GError **error)
+{
+ return g_initable_new (
+ E_TYPE_SOURCE_VIEWER,
+ cancellable, error, NULL);
+}
+
+ESourceRegistry *
+e_source_viewer_get_registry (ESourceViewer *viewer)
+{
+ g_return_val_if_fail (E_IS_SOURCE_VIEWER (viewer), NULL);
+
+ return viewer->registry;
+}
+
+GtkTreePath *
+e_source_viewer_dup_selected_path (ESourceViewer *viewer)
+{
+ GtkTreeSelection *selection;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (E_IS_SOURCE_VIEWER (viewer), NULL);
+
+ tree_view = GTK_TREE_VIEW (viewer->tree_view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return NULL;
+
+ return gtk_tree_model_get_path (model, &iter);
+}
+
+gboolean
+e_source_viewer_set_selected_path (ESourceViewer *viewer,
+ GtkTreePath *path)
+{
+ GtkTreeSelection *selection;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (E_IS_SOURCE_VIEWER (viewer), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ tree_view = GTK_TREE_VIEW (viewer->tree_view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ /* Check that the path is valid. */
+ model = gtk_tree_view_get_model (tree_view);
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ return FALSE;
+
+ gtk_tree_selection_unselect_all (selection);
+
+ gtk_tree_view_expand_to_path (tree_view, path);
+ gtk_tree_selection_select_path (selection, path);
+
+ return TRUE;
+}
+
+ESource *
+e_source_viewer_ref_selected_source (ESourceViewer *viewer)
+{
+ ESource *source;
+ GtkTreeSelection *selection;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (E_IS_SOURCE_VIEWER (viewer), NULL);
+
+ tree_view = GTK_TREE_VIEW (viewer->tree_view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return NULL;
+
+ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
+
+ return source;
+}
+
+gboolean
+e_source_viewer_set_selected_source (ESourceViewer *viewer,
+ ESource *source)
+{
+ GHashTable *source_index;
+ GtkTreeRowReference *reference;
+ GtkTreePath *path;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_SOURCE_VIEWER (viewer), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ source_index = viewer->source_index;
+ reference = g_hash_table_lookup (source_index, source);
+
+ if (!gtk_tree_row_reference_valid (reference))
+ return FALSE;
+
+ path = gtk_tree_row_reference_get_path (reference);
+ success = e_source_viewer_set_selected_path (viewer, path);
+ gtk_tree_path_free (path);
+
+ return success;
+}
+
+/* Helper for e_source_viewer_build_display_tree() */
+static gint
+source_viewer_compare_nodes (GNode *node_a,
+ GNode *node_b)
+{
+ ESource *source_a = E_SOURCE (node_a->data);
+ ESource *source_b = E_SOURCE (node_b->data);
+
+ return e_source_compare_by_display_name (source_a, source_b);
+}
+
+/* Helper for e_source_viewer_build_display_tree() */
+static gboolean
+source_viewer_sort_nodes (GNode *node,
+ gpointer unused)
+{
+ GQueue queue = G_QUEUE_INIT;
+ GNode *child_node;
+
+ /* Unlink all the child nodes and place them in a queue. */
+ while ((child_node = g_node_first_child (node)) != NULL) {
+ g_node_unlink (child_node);
+ g_queue_push_tail (&queue, child_node);
+ }
+
+ /* Sort the queue by source name. */
+ g_queue_sort (
+ &queue, (GCompareDataFunc)
+ source_viewer_compare_nodes, NULL);
+
+ /* Pop nodes off the head of the queue and put them back
+ * under the parent node (preserving the sorted order). */
+ while ((child_node = g_queue_pop_head (&queue)) != NULL)
+ g_node_append (node, child_node);
+
+ return FALSE;
+}
+
+GNode *
+e_source_viewer_build_display_tree (ESourceViewer *viewer)
+{
+ GNode *root;
+ GHashTable *index;
+ GList *list, *link;
+ GHashTableIter iter;
+ gpointer value;
+
+ /* This is just like e_source_registry_build_display_tree()
+ * except it includes all data sources, even disabled ones.
+ * Free the tree with e_source_registry_free_display_tree(). */
+
+ g_return_val_if_fail (E_IS_SOURCE_VIEWER (viewer), NULL);
+
+ root = g_node_new (NULL);
+ index = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* Add a GNode for each ESource to the index.
+ * The GNodes take ownership of the ESource references. */
+ list = e_source_registry_list_sources (viewer->registry, NULL);
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESource *source = E_SOURCE (link->data);
+ gpointer key = (gpointer) e_source_get_uid (source);
+ g_hash_table_insert (index, key, g_node_new (source));
+ }
+ g_list_free (list);
+
+ /* Traverse the index and link the nodes together. */
+ g_hash_table_iter_init (&iter, index);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ ESource *source;
+ GNode *source_node;
+ GNode *parent_node;
+ const gchar *parent_uid;
+
+ source_node = (GNode *) value;
+ source = E_SOURCE (source_node->data);
+ parent_uid = e_source_get_parent (source);
+
+ if (parent_uid == NULL || *parent_uid == '\0') {
+ parent_node = root;
+ } else {
+ parent_node = g_hash_table_lookup (index, parent_uid);
+ }
+
+ /* This could be NULL if the registry service was
+ * shutdown or reloaded. All sources will vanish. */
+ if (parent_node != NULL)
+ g_node_append (parent_node, source_node);
+ }
+
+ /* Sort nodes by display name in post order. */
+ g_node_traverse (
+ root, G_POST_ORDER, G_TRAVERSE_ALL,
+ -1, source_viewer_sort_nodes, NULL);
+
+ g_hash_table_destroy (index);
+
+ return root;
+}
+
+gint
+main (gint argc,
+ gchar **argv)
+{
+ GtkWidget *viewer;
+ GError *error = NULL;
+
+ bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ gtk_init (&argc, &argv);
+
+ viewer = e_source_viewer_new (NULL, &error);
+
+ if (error != NULL) {
+ g_warn_if_fail (viewer == NULL);
+ g_error ("%s", error->message);
+ g_assert_not_reached ();
+ }
+
+ g_signal_connect (
+ viewer, "delete-event",
+ G_CALLBACK (gtk_main_quit), NULL);
+
+ gtk_widget_show (viewer);
+
+ gtk_main ();
+
+ return 0;
+}