From d09d8de870b6697c8a8b262e7e077b871a69b315 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Mon, 10 Dec 2012 08:09:59 -0500 Subject: Consolidate base utility libraries into libeutil. Evolution consists of entirely too many small utility libraries, which increases linking and loading time, places a burden on higher layers of the application (e.g. modules) which has to remember to link to all the small in-tree utility libraries, and makes it difficult to generate API documentation for these utility libraries in one Gtk-Doc module. Merge the following utility libraries under the umbrella of libeutil, and enforce a single-include policy on libeutil so we can reorganize the files as desired without disrupting its pseudo-public API. libemail-utils/libemail-utils.la libevolution-utils/libevolution-utils.la filter/libfilter.la widgets/e-timezone-dialog/libetimezonedialog.la widgets/menus/libmenus.la widgets/misc/libemiscwidgets.la widgets/table/libetable.la widgets/text/libetext.la This also merges libedataserverui from the Evolution-Data-Server module, since Evolution is its only consumer nowadays, and I'd like to make some improvements to those APIs without concern for backward-compatibility. And finally, start a Gtk-Doc module for libeutil. It's going to be a project just getting all the symbols _listed_ much less _documented_. But the skeletal structure is in place and I'm off to a good start. --- e-util/evolution-source-viewer.c | 1176 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1176 insertions(+) create mode 100644 e-util/evolution-source-viewer.c (limited to 'e-util/evolution-source-viewer.c') 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 + * + */ + +#include +#include +#include + +#include + +/* 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; +} -- cgit v1.2.3