diff options
author | root <root@rshg054.dnsready.net> | 2013-07-19 01:10:32 -0700 |
---|---|---|
committer | root <root@rshg054.dnsready.net> | 2013-07-19 01:10:32 -0700 |
commit | 8fbc0076a4827ddc6af92e0b9daa4c4c31450808 (patch) | |
tree | 03fd0e2921ebd53228d9a93e32ed3976b636cbea /community/almanah/eds-3.8.patch | |
parent | e445a313723389ba9ee1fded025c567dae5b21ea (diff) |
Fri Jul 19 01:09:18 PDT 2013
Diffstat (limited to 'community/almanah/eds-3.8.patch')
-rw-r--r-- | community/almanah/eds-3.8.patch | 2609 |
1 files changed, 2609 insertions, 0 deletions
diff --git a/community/almanah/eds-3.8.patch b/community/almanah/eds-3.8.patch new file mode 100644 index 000000000..dea51eb74 --- /dev/null +++ b/community/almanah/eds-3.8.patch @@ -0,0 +1,2609 @@ +From e481f5abdb46d41ac00ee3393332e92f605b55e7 Mon Sep 17 00:00:00 2001 +From: Matthew Barnes <mbarnes@redhat.com> +Date: Thu, 13 Dec 2012 14:16:58 +0000 +Subject: Bug 687229 - Embed ESourceSelector widget from libedataserverui + +Evolution developers are merging libedataserverui back into Evolution. +Drop the libedataserverui dependency and embed the ECellRendererColor +and ESourceSelector widgets from libedataserverui in event-factories. + +See also: +https://mail.gnome.org/archives/distributor-list/2012-December/msg00000.html +--- +diff --git a/configure.ac b/configure.ac +index a529568..b89ba74 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -83,7 +83,7 @@ AC_SUBST(STANDARD_CFLAGS) + AC_SUBST(STANDARD_LIBS) + + dnl Evolution +-PKG_CHECK_MODULES(EVO, libecal-1.2 libedataserver-1.2 libedataserverui-3.0, have_evo=yes, have_evo=no) ++PKG_CHECK_MODULES(EVO, libecal-1.2 >= 3.5.91 libedataserver-1.2, have_evo=yes, have_evo=no) + if test "x$have_evo" = "xyes"; then + AC_DEFINE(HAVE_EVO, 1, [Defined if libecal-1.2 is installed]) + fi +diff --git a/src/Makefile.am b/src/Makefile.am +index 0de37e3..740c9f2 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -52,6 +52,10 @@ almanah_SOURCES += \ + event-factories/calendar-debug.h \ + event-factories/calendar-sources.c \ + event-factories/calendar-sources.h \ ++ event-factories/e-cell-renderer-color.c \ ++ event-factories/e-cell-renderer-color.h \ ++ event-factories/e-source-selector.c \ ++ event-factories/e-source-selector.h \ + events/calendar-appointment.c \ + events/calendar-appointment.h \ + events/calendar-task.c \ +diff --git a/src/event-factories/calendar-sources.c b/src/event-factories/calendar-sources.c +index 217327d..9451a0d 100644 +--- a/src/event-factories/calendar-sources.c ++++ b/src/event-factories/calendar-sources.c +@@ -32,7 +32,8 @@ + #define HANDLE_LIBICAL_MEMORY + #include <libecal/libecal.h> + #include <libedataserver/libedataserver.h> +-#include <libedataserverui/libedataserverui.h> ++ ++#include "e-source-selector.h" + + #undef CALENDAR_ENABLE_DEBUG + #include "calendar-debug.h" +diff --git a/src/event-factories/e-cell-renderer-color.c b/src/event-factories/e-cell-renderer-color.c +new file mode 100644 +index 0000000..748bea5 +--- /dev/null ++++ b/src/event-factories/e-cell-renderer-color.c +@@ -0,0 +1,237 @@ ++/* -*- 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 version 2 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * Boston, MA 02111-1307, USA. ++ */ ++ ++#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_COLOR ++}; ++ ++struct _ECellRendererColorPrivate { ++ GdkColor *color; ++}; ++ ++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); ++ ++ if (priv->color == NULL) ++ return; ++ ++ 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_color (cr, priv->color); ++ 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; ++ ++ priv = E_CELL_RENDERER_COLOR_GET_PRIVATE (object); ++ ++ switch (property_id) { ++ case PROP_COLOR: ++ if (priv->color != NULL) ++ gdk_color_free (priv->color); ++ priv->color = g_value_dup_boxed (value); ++ 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_COLOR: ++ g_value_set_boxed (value, priv->color); ++ return; ++ } ++ ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); ++} ++ ++static void ++cell_renderer_color_finalize (GObject *object) ++{ ++ ECellRendererColorPrivate *priv; ++ ++ priv = E_CELL_RENDERER_COLOR_GET_PRIVATE (object); ++ ++ if (priv->color != NULL) ++ gdk_color_free (priv->color); ++ ++ /* Chain up to parent's finalize() method. */ ++ G_OBJECT_CLASS (e_cell_renderer_color_parent_class)->finalize (object); ++} ++ ++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; ++ object_class->finalize = cell_renderer_color_finalize; ++ ++ 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_COLOR, ++ g_param_spec_boxed ( ++ "color", ++ "Color Info", ++ "The color to render", ++ GDK_TYPE_COLOR, ++ 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/src/event-factories/e-cell-renderer-color.h b/src/event-factories/e-cell-renderer-color.h +new file mode 100644 +index 0000000..0e0da70 +--- /dev/null ++++ b/src/event-factories/e-cell-renderer-color.h +@@ -0,0 +1,75 @@ ++/* -*- 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 version 2 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * Boston, MA 02111-1307, USA. ++ */ ++ ++#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); ++GtkCellRenderer *e_cell_renderer_color_new (void); ++ ++G_END_DECLS ++ ++#endif /* _E_CELL_RENDERER_COLOR_H_ */ +diff --git a/src/event-factories/e-source-selector.c b/src/event-factories/e-source-selector.c +new file mode 100644 +index 0000000..925d9cd +--- /dev/null ++++ b/src/event-factories/e-source-selector.c +@@ -0,0 +1,2080 @@ ++/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ ++/* e-source-selector.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; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this program; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Ettore Perazzoli <ettore@ximian.com> ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include <config.h> ++#endif ++ ++#include <string.h> ++ ++#include "e-cell-renderer-color.h" ++#include "e-source-selector.h" ++ ++#define E_SOURCE_SELECTOR_GET_PRIVATE(obj) \ ++ (G_TYPE_INSTANCE_GET_PRIVATE \ ++ ((obj), E_TYPE_SOURCE_SELECTOR, ESourceSelectorPrivate)) ++ ++typedef struct _AsyncContext AsyncContext; ++ ++struct _ESourceSelectorPrivate { ++ ESourceRegistry *registry; ++ GHashTable *source_index; ++ gchar *extension_name; ++ ++ GtkTreeRowReference *saved_primary_selection; ++ ++ /* ESource -> GSource */ ++ GHashTable *pending_writes; ++ GMainContext *main_context; ++ ++ gboolean toggled_last; ++ gboolean select_new; ++ gboolean show_colors; ++ gboolean show_toggles; ++}; ++ ++struct _AsyncContext { ++ ESourceSelector *selector; ++ ESource *source; ++}; ++ ++enum { ++ PROP_0, ++ PROP_EXTENSION_NAME, ++ PROP_PRIMARY_SELECTION, ++ PROP_REGISTRY, ++ PROP_SHOW_COLORS, ++ PROP_SHOW_TOGGLES ++}; ++ ++enum { ++ SELECTION_CHANGED, ++ PRIMARY_SELECTION_CHANGED, ++ POPUP_EVENT, ++ DATA_DROPPED, ++ NUM_SIGNALS ++}; ++ ++enum { ++ COLUMN_NAME, ++ COLUMN_COLOR, ++ COLUMN_ACTIVE, ++ COLUMN_SHOW_COLOR, ++ COLUMN_SHOW_TOGGLE, ++ COLUMN_WEIGHT, ++ COLUMN_SOURCE, ++ NUM_COLUMNS ++}; ++ ++static guint signals[NUM_SIGNALS]; ++ ++G_DEFINE_TYPE (ESourceSelector, e_source_selector, GTK_TYPE_TREE_VIEW) ++ ++/* ESafeToggleRenderer does not emit 'toggled' signal ++ * on 'activate' when mouse is not over the toggle. */ ++ ++typedef GtkCellRendererToggle ECellRendererSafeToggle; ++typedef GtkCellRendererToggleClass ECellRendererSafeToggleClass; ++ ++/* Forward Declarations */ ++GType e_cell_renderer_safe_toggle_get_type (void); ++ ++G_DEFINE_TYPE ( ++ ECellRendererSafeToggle, ++ e_cell_renderer_safe_toggle, ++ GTK_TYPE_CELL_RENDERER_TOGGLE) ++ ++static gboolean ++safe_toggle_activate (GtkCellRenderer *cell, ++ GdkEvent *event, ++ GtkWidget *widget, ++ const gchar *path, ++ const GdkRectangle *background_area, ++ const GdkRectangle *cell_area, ++ GtkCellRendererState flags) ++{ ++ gboolean point_in_cell_area = TRUE; ++ ++ if (event->type == GDK_BUTTON_PRESS && cell_area != NULL) { ++ cairo_region_t *region; ++ ++ region = cairo_region_create_rectangle (cell_area); ++ point_in_cell_area = cairo_region_contains_point ( ++ region, event->button.x, event->button.y); ++ cairo_region_destroy (region); ++ } ++ ++ if (!point_in_cell_area) ++ return FALSE; ++ ++ return GTK_CELL_RENDERER_CLASS ( ++ e_cell_renderer_safe_toggle_parent_class)->activate ( ++ cell, event, widget, path, background_area, cell_area, flags); ++} ++ ++static void ++e_cell_renderer_safe_toggle_class_init (ECellRendererSafeToggleClass *class) ++{ ++ GtkCellRendererClass *cell_renderer_class; ++ ++ cell_renderer_class = GTK_CELL_RENDERER_CLASS (class); ++ cell_renderer_class->activate = safe_toggle_activate; ++} ++ ++static void ++e_cell_renderer_safe_toggle_init (ECellRendererSafeToggle *obj) ++{ ++} ++ ++static GtkCellRenderer * ++e_cell_renderer_safe_toggle_new (void) ++{ ++ return g_object_new (e_cell_renderer_safe_toggle_get_type (), NULL); ++} ++ ++static void ++clear_saved_primary_selection (ESourceSelector *selector) ++{ ++ gtk_tree_row_reference_free (selector->priv->saved_primary_selection); ++ selector->priv->saved_primary_selection = NULL; ++} ++ ++static void ++async_context_free (AsyncContext *async_context) ++{ ++ if (async_context->selector != NULL) ++ g_object_unref (async_context->selector); ++ ++ if (async_context->source != NULL) ++ g_object_unref (async_context->source); ++ ++ g_slice_free (AsyncContext, async_context); ++} ++ ++static void ++pending_writes_destroy_source (GSource *source) ++{ ++ g_source_destroy (source); ++ g_source_unref (source); ++} ++ ++static void ++source_selector_write_done_cb (GObject *source_object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ ESource *source; ++ ESourceSelector *selector; ++ GError *error = NULL; ++ ++ source = E_SOURCE (source_object); ++ selector = E_SOURCE_SELECTOR (user_data); ++ ++ e_source_write_finish (source, result, &error); ++ ++ /* FIXME Display the error in the selector somehow? */ ++ if (error != NULL) { ++ g_warning ("%s: %s", G_STRFUNC, error->message); ++ g_error_free (error); ++ } ++ ++ g_object_unref (selector); ++} ++ ++static gboolean ++source_selector_write_idle_cb (gpointer user_data) ++{ ++ AsyncContext *async_context = user_data; ++ GHashTable *pending_writes; ++ ++ /* XXX This operation is not cancellable. */ ++ e_source_write ( ++ async_context->source, NULL, ++ source_selector_write_done_cb, ++ g_object_ref (async_context->selector)); ++ ++ pending_writes = async_context->selector->priv->pending_writes; ++ g_hash_table_remove (pending_writes, async_context->source); ++ ++ return FALSE; ++} ++ ++static void ++source_selector_cancel_write (ESourceSelector *selector, ++ ESource *source) ++{ ++ GHashTable *pending_writes; ++ ++ /* Cancel any pending writes for this ESource so as not ++ * to overwrite whatever change we're being notified of. */ ++ pending_writes = selector->priv->pending_writes; ++ g_hash_table_remove (pending_writes, source); ++} ++ ++static void ++source_selector_update_row (ESourceSelector *selector, ++ ESource *source) ++{ ++ GHashTable *source_index; ++ ESourceExtension *extension = NULL; ++ GtkTreeRowReference *reference; ++ GtkTreeModel *model; ++ GtkTreePath *path; ++ GtkTreeIter iter; ++ const gchar *extension_name; ++ const gchar *display_name; ++ gboolean selected; ++ ++ source_index = selector->priv->source_index; ++ reference = g_hash_table_lookup (source_index, source); ++ ++ /* This function runs when ANY ESource in the registry changes. ++ * If the ESource is not in our tree model then return silently. */ ++ if (reference == NULL) ++ return; ++ ++ /* If we do have a row reference, it 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); ++ ++ display_name = e_source_get_display_name (source); ++ ++ extension_name = e_source_selector_get_extension_name (selector); ++ selected = e_source_selector_source_is_selected (selector, source); ++ ++ if (e_source_has_extension (source, extension_name)) ++ extension = e_source_get_extension (source, extension_name); ++ ++ if (extension != NULL) { ++ GdkColor color; ++ const gchar *color_spec = NULL; ++ gboolean show_color = FALSE; ++ gboolean show_toggle; ++ ++ show_color = ++ E_IS_SOURCE_SELECTABLE (extension) && ++ e_source_selector_get_show_colors (selector); ++ ++ if (show_color) ++ color_spec = e_source_selectable_get_color ( ++ E_SOURCE_SELECTABLE (extension)); ++ ++ if (color_spec != NULL && *color_spec != '\0') ++ show_color = gdk_color_parse (color_spec, &color); ++ ++ show_toggle = e_source_selector_get_show_toggles (selector); ++ ++ gtk_tree_store_set ( ++ GTK_TREE_STORE (model), &iter, ++ COLUMN_NAME, display_name, ++ COLUMN_COLOR, show_color ? &color : NULL, ++ COLUMN_ACTIVE, selected, ++ COLUMN_SHOW_COLOR, show_color, ++ COLUMN_SHOW_TOGGLE, show_toggle, ++ COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL, ++ COLUMN_SOURCE, source, ++ -1); ++ } else { ++ gtk_tree_store_set ( ++ GTK_TREE_STORE (model), &iter, ++ COLUMN_NAME, display_name, ++ COLUMN_COLOR, NULL, ++ COLUMN_ACTIVE, FALSE, ++ COLUMN_SHOW_COLOR, FALSE, ++ COLUMN_SHOW_TOGGLE, FALSE, ++ COLUMN_WEIGHT, PANGO_WEIGHT_BOLD, ++ COLUMN_SOURCE, source, ++ -1); ++ } ++} ++ ++static gboolean ++source_selector_traverse (GNode *node, ++ ESourceSelector *selector) ++{ ++ ESource *source; ++ GHashTable *source_index; ++ GtkTreeRowReference *reference = NULL; ++ GtkTreeModel *model; ++ GtkTreePath *path; ++ GtkTreeIter iter; ++ ++ /* Skip the root node. */ ++ if (G_NODE_IS_ROOT (node)) ++ return FALSE; ++ ++ source_index = selector->priv->source_index; ++ ++ model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); ++ ++ 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 = E_SOURCE (node->data); ++ ++ path = gtk_tree_model_get_path (model, &iter); ++ reference = gtk_tree_row_reference_new (model, path); ++ g_hash_table_insert (source_index, g_object_ref (source), reference); ++ gtk_tree_path_free (path); ++ ++ source_selector_update_row (selector, source); ++ ++ return FALSE; ++} ++ ++static void ++source_selector_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_selector_build_model (ESourceSelector *selector) ++{ ++ ESourceRegistry *registry; ++ GQueue queue = G_QUEUE_INIT; ++ GHashTable *source_index; ++ GtkTreeView *tree_view; ++ GtkTreeModel *model; ++ ESource *selected; ++ const gchar *extension_name; ++ GNode *root; ++ ++ tree_view = GTK_TREE_VIEW (selector); ++ ++ registry = e_source_selector_get_registry (selector); ++ extension_name = e_source_selector_get_extension_name (selector); ++ ++ /* Make sure we have what we need to build the model, since ++ * this can get called early in the initialization phase. */ ++ if (registry == NULL || extension_name == NULL) ++ return; ++ ++ source_index = selector->priv->source_index; ++ selected = e_source_selector_ref_primary_selection (selector); ++ ++ /* Save expanded sources to restore later. */ ++ gtk_tree_view_map_expanded_rows ( ++ tree_view, (GtkTreeViewMappingFunc) ++ source_selector_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_registry_build_display_tree (registry, extension_name); ++ ++ g_node_traverse ( ++ root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, ++ (GNodeTraverseFunc) source_selector_traverse, ++ selector); ++ ++ 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 primary selection. */ ++ if (selected != NULL) { ++ e_source_selector_set_primary_selection (selector, selected); ++ g_object_unref (selected); ++ } ++ ++ /* Make sure we have a primary selection. If not, pick one. */ ++ selected = e_source_selector_ref_primary_selection (selector); ++ if (selected == NULL) { ++ selected = e_source_registry_ref_default_for_extension_name ( ++ registry, extension_name); ++ e_source_selector_set_primary_selection (selector, selected); ++ } ++ g_object_unref (selected); ++} ++ ++static void ++source_selector_expand_to_source (ESourceSelector *selector, ++ ESource *source) ++{ ++ GHashTable *source_index; ++ GtkTreeRowReference *reference; ++ GtkTreePath *path; ++ ++ source_index = selector->priv->source_index; ++ reference = g_hash_table_lookup (source_index, source); ++ ++ /* If the ESource is not in our tree model then return silently. */ ++ if (reference == NULL) ++ return; ++ ++ /* If we do have a row reference, it should be valid. */ ++ g_return_if_fail (gtk_tree_row_reference_valid (reference)); ++ ++ /* Expand the tree view to the path containing the ESource */ ++ path = gtk_tree_row_reference_get_path (reference); ++ gtk_tree_view_expand_to_path (GTK_TREE_VIEW (selector), path); ++ gtk_tree_path_free (path); ++} ++ ++static void ++source_selector_source_added_cb (ESourceRegistry *registry, ++ ESource *source, ++ ESourceSelector *selector) ++{ ++ source_selector_build_model (selector); ++ ++ source_selector_expand_to_source (selector, source); ++} ++ ++static void ++source_selector_source_changed_cb (ESourceRegistry *registry, ++ ESource *source, ++ ESourceSelector *selector) ++{ ++ source_selector_cancel_write (selector, source); ++ ++ source_selector_update_row (selector, source); ++} ++ ++static void ++source_selector_source_removed_cb (ESourceRegistry *registry, ++ ESource *source, ++ ESourceSelector *selector) ++{ ++ source_selector_build_model (selector); ++} ++ ++static void ++source_selector_source_enabled_cb (ESourceRegistry *registry, ++ ESource *source, ++ ESourceSelector *selector) ++{ ++ source_selector_build_model (selector); ++ ++ source_selector_expand_to_source (selector, source); ++} ++ ++static void ++source_selector_source_disabled_cb (ESourceRegistry *registry, ++ ESource *source, ++ ESourceSelector *selector) ++{ ++ source_selector_build_model (selector); ++} ++ ++static gboolean ++same_source_name_exists (ESourceSelector *selector, ++ const gchar *display_name) ++{ ++ GHashTable *source_index; ++ GHashTableIter iter; ++ gpointer key; ++ ++ source_index = selector->priv->source_index; ++ g_hash_table_iter_init (&iter, source_index); ++ ++ while (g_hash_table_iter_next (&iter, &key, NULL)) { ++ ESource *source = E_SOURCE (key); ++ const gchar *source_name; ++ ++ source_name = e_source_get_display_name (source); ++ if (g_strcmp0 (display_name, source_name) == 0) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static gboolean ++selection_func (GtkTreeSelection *selection, ++ GtkTreeModel *model, ++ GtkTreePath *path, ++ gboolean path_currently_selected, ++ ESourceSelector *selector) ++{ ++ ESource *source; ++ GtkTreeIter iter; ++ const gchar *extension_name; ++ ++ if (selector->priv->toggled_last) { ++ selector->priv->toggled_last = FALSE; ++ return FALSE; ++ } ++ ++ if (path_currently_selected) ++ return TRUE; ++ ++ if (!gtk_tree_model_get_iter (model, &iter, path)) ++ return FALSE; ++ ++ extension_name = e_source_selector_get_extension_name (selector); ++ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); ++ ++ if (!e_source_has_extension (source, extension_name)) { ++ g_object_unref (source); ++ return FALSE; ++ } ++ ++ clear_saved_primary_selection (selector); ++ ++ g_object_unref (source); ++ ++ return TRUE; ++} ++ ++static void ++text_cell_edited_cb (ESourceSelector *selector, ++ const gchar *path_string, ++ const gchar *new_name) ++{ ++ GtkTreeView *tree_view; ++ GtkTreeModel *model; ++ GtkTreePath *path; ++ GtkTreeIter iter; ++ ESource *source; ++ ++ tree_view = GTK_TREE_VIEW (selector); ++ model = gtk_tree_view_get_model (tree_view); ++ path = gtk_tree_path_new_from_string (path_string); ++ ++ gtk_tree_model_get_iter (model, &iter, path); ++ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); ++ gtk_tree_path_free (path); ++ ++ if (new_name == NULL || *new_name == '\0') ++ return; ++ ++ if (same_source_name_exists (selector, new_name)) ++ return; ++ ++ e_source_set_display_name (source, new_name); ++ ++ e_source_selector_queue_write (selector, source); ++} ++ ++static void ++cell_toggled_callback (GtkCellRendererToggle *renderer, ++ const gchar *path_string, ++ ESourceSelector *selector) ++{ ++ ESource *source; ++ GtkTreeModel *model; ++ GtkTreePath *path; ++ GtkTreeIter iter; ++ ++ model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); ++ path = gtk_tree_path_new_from_string (path_string); ++ ++ if (!gtk_tree_model_get_iter (model, &iter, path)) { ++ gtk_tree_path_free (path); ++ return; ++ } ++ ++ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); ++ ++ if (e_source_selector_source_is_selected (selector, source)) ++ e_source_selector_unselect_source (selector, source); ++ else ++ e_source_selector_select_source (selector, source); ++ ++ selector->priv->toggled_last = TRUE; ++ ++ gtk_tree_path_free (path); ++ ++ g_object_unref (source); ++} ++ ++static void ++selection_changed_callback (GtkTreeSelection *selection, ++ ESourceSelector *selector) ++{ ++ g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0); ++ g_object_notify (G_OBJECT (selector), "primary-selection"); ++} ++ ++static void ++source_selector_set_extension_name (ESourceSelector *selector, ++ const gchar *extension_name) ++{ ++ g_return_if_fail (extension_name != NULL); ++ g_return_if_fail (selector->priv->extension_name == NULL); ++ ++ selector->priv->extension_name = g_strdup (extension_name); ++} ++ ++static void ++source_selector_set_registry (ESourceSelector *selector, ++ ESourceRegistry *registry) ++{ ++ g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); ++ g_return_if_fail (selector->priv->registry == NULL); ++ ++ selector->priv->registry = g_object_ref (registry); ++} ++ ++static void ++source_selector_set_property (GObject *object, ++ guint property_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ switch (property_id) { ++ case PROP_EXTENSION_NAME: ++ source_selector_set_extension_name ( ++ E_SOURCE_SELECTOR (object), ++ g_value_get_string (value)); ++ return; ++ ++ case PROP_PRIMARY_SELECTION: ++ e_source_selector_set_primary_selection ( ++ E_SOURCE_SELECTOR (object), ++ g_value_get_object (value)); ++ return; ++ ++ case PROP_REGISTRY: ++ source_selector_set_registry ( ++ E_SOURCE_SELECTOR (object), ++ g_value_get_object (value)); ++ return; ++ ++ case PROP_SHOW_COLORS: ++ e_source_selector_set_show_colors ( ++ E_SOURCE_SELECTOR (object), ++ g_value_get_boolean (value)); ++ return; ++ ++ case PROP_SHOW_TOGGLES: ++ e_source_selector_set_show_toggles ( ++ E_SOURCE_SELECTOR (object), ++ g_value_get_boolean (value)); ++ return; ++ } ++ ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); ++} ++ ++static void ++source_selector_get_property (GObject *object, ++ guint property_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ switch (property_id) { ++ case PROP_EXTENSION_NAME: ++ g_value_set_string ( ++ value, ++ e_source_selector_get_extension_name ( ++ E_SOURCE_SELECTOR (object))); ++ return; ++ ++ case PROP_PRIMARY_SELECTION: ++ g_value_take_object ( ++ value, ++ e_source_selector_ref_primary_selection ( ++ E_SOURCE_SELECTOR (object))); ++ return; ++ ++ case PROP_REGISTRY: ++ g_value_set_object ( ++ value, ++ e_source_selector_get_registry ( ++ E_SOURCE_SELECTOR (object))); ++ return; ++ ++ case PROP_SHOW_COLORS: ++ g_value_set_boolean ( ++ value, ++ e_source_selector_get_show_colors ( ++ E_SOURCE_SELECTOR (object))); ++ return; ++ ++ case PROP_SHOW_TOGGLES: ++ g_value_set_boolean ( ++ value, ++ e_source_selector_get_show_toggles ( ++ E_SOURCE_SELECTOR (object))); ++ return; ++ } ++ ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); ++} ++ ++static void ++source_selector_dispose (GObject *object) ++{ ++ ESourceSelectorPrivate *priv; ++ ++ priv = E_SOURCE_SELECTOR_GET_PRIVATE (object); ++ ++ if (priv->registry != NULL) { ++ g_signal_handlers_disconnect_matched ( ++ priv->registry, ++ G_SIGNAL_MATCH_DATA, ++ 0, 0, NULL, NULL, object); ++ g_object_unref (priv->registry); ++ priv->registry = NULL; ++ } ++ ++ g_hash_table_remove_all (priv->source_index); ++ g_hash_table_remove_all (priv->pending_writes); ++ ++ clear_saved_primary_selection (E_SOURCE_SELECTOR (object)); ++ ++ /* Chain up to parent's dispose() method. */ ++ G_OBJECT_CLASS (e_source_selector_parent_class)->dispose (object); ++} ++ ++static void ++source_selector_finalize (GObject *object) ++{ ++ ESourceSelectorPrivate *priv; ++ ++ priv = E_SOURCE_SELECTOR_GET_PRIVATE (object); ++ ++ g_hash_table_destroy (priv->source_index); ++ g_hash_table_destroy (priv->pending_writes); ++ ++ g_free (priv->extension_name); ++ ++ if (priv->main_context != NULL) ++ g_main_context_unref (priv->main_context); ++ ++ /* Chain up to parent's finalize() method. */ ++ G_OBJECT_CLASS (e_source_selector_parent_class)->finalize (object); ++} ++ ++static void ++source_selector_constructed (GObject *object) ++{ ++ ESourceRegistry *registry; ++ ESourceSelector *selector; ++ ++ selector = E_SOURCE_SELECTOR (object); ++ registry = e_source_selector_get_registry (selector); ++ ++ g_signal_connect ( ++ registry, "source-added", ++ G_CALLBACK (source_selector_source_added_cb), selector); ++ ++ g_signal_connect ( ++ registry, "source-changed", ++ G_CALLBACK (source_selector_source_changed_cb), selector); ++ ++ g_signal_connect ( ++ registry, "source-removed", ++ G_CALLBACK (source_selector_source_removed_cb), selector); ++ ++ g_signal_connect ( ++ registry, "source-enabled", ++ G_CALLBACK (source_selector_source_enabled_cb), selector); ++ ++ g_signal_connect ( ++ registry, "source-disabled", ++ G_CALLBACK (source_selector_source_disabled_cb), selector); ++ ++ source_selector_build_model (selector); ++ ++ gtk_tree_view_expand_all (GTK_TREE_VIEW (selector)); ++} ++ ++static gboolean ++source_selector_button_press_event (GtkWidget *widget, ++ GdkEventButton *event) ++{ ++ ESourceSelector *selector; ++ GtkWidgetClass *widget_class; ++ GtkTreePath *path; ++ ESource *source = NULL; ++ ESource *primary; ++ gboolean right_click = FALSE; ++ gboolean triple_click = FALSE; ++ gboolean row_exists; ++ gboolean res = FALSE; ++ ++ selector = E_SOURCE_SELECTOR (widget); ++ ++ selector->priv->toggled_last = FALSE; ++ ++ /* Triple-clicking a source selects it exclusively. */ ++ ++ if (event->button == 3 && event->type == GDK_BUTTON_PRESS) ++ right_click = TRUE; ++ else if (event->button == 1 && event->type == GDK_3BUTTON_PRESS) ++ triple_click = TRUE; ++ else ++ goto chainup; ++ ++ row_exists = gtk_tree_view_get_path_at_pos ( ++ GTK_TREE_VIEW (widget), event->x, event->y, ++ &path, NULL, NULL, NULL); ++ ++ /* Get the source/group */ ++ if (row_exists) { ++ GtkTreeModel *model; ++ GtkTreeIter iter; ++ ++ model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); ++ ++ gtk_tree_model_get_iter (model, &iter, path); ++ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); ++ } ++ ++ if (source == NULL) ++ goto chainup; ++ ++ primary = e_source_selector_ref_primary_selection (selector); ++ if (source != primary) ++ e_source_selector_set_primary_selection (selector, source); ++ if (primary != NULL) ++ g_object_unref (primary); ++ ++ if (right_click) ++ g_signal_emit ( ++ widget, signals[POPUP_EVENT], 0, source, event, &res); ++ ++ if (triple_click) { ++ e_source_selector_select_exclusive (selector, source); ++ res = TRUE; ++ } ++ ++ g_object_unref (source); ++ ++ return res; ++ ++chainup: ++ ++ /* Chain up to parent's button_press_event() method. */ ++ widget_class = GTK_WIDGET_CLASS (e_source_selector_parent_class); ++ return widget_class->button_press_event (widget, event); ++} ++ ++static void ++source_selector_drag_leave (GtkWidget *widget, ++ GdkDragContext *context, ++ guint time_) ++{ ++ GtkTreeView *tree_view; ++ GtkTreeViewDropPosition pos; ++ ++ tree_view = GTK_TREE_VIEW (widget); ++ pos = GTK_TREE_VIEW_DROP_BEFORE; ++ ++ gtk_tree_view_set_drag_dest_row (tree_view, NULL, pos); ++} ++ ++static gboolean ++source_selector_drag_motion (GtkWidget *widget, ++ GdkDragContext *context, ++ gint x, ++ gint y, ++ guint time_) ++{ ++ ESource *source = NULL; ++ GtkTreeView *tree_view; ++ GtkTreeModel *model; ++ GtkTreePath *path = NULL; ++ GtkTreeIter iter; ++ GtkTreeViewDropPosition pos; ++ GdkDragAction action = 0; ++ ++ tree_view = GTK_TREE_VIEW (widget); ++ model = gtk_tree_view_get_model (tree_view); ++ ++ if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, NULL)) ++ goto exit; ++ ++ if (!gtk_tree_model_get_iter (model, &iter, path)) ++ goto exit; ++ ++ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); ++ ++ if (!e_source_get_writable (source)) ++ goto exit; ++ ++ pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; ++ gtk_tree_view_set_drag_dest_row (tree_view, path, pos); ++ ++ if (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) ++ action = GDK_ACTION_MOVE; ++ else ++ action = gdk_drag_context_get_suggested_action (context); ++ ++exit: ++ if (path != NULL) ++ gtk_tree_path_free (path); ++ ++ if (source != NULL) ++ g_object_unref (source); ++ ++ gdk_drag_status (context, action, time_); ++ ++ return TRUE; ++} ++ ++static gboolean ++source_selector_drag_drop (GtkWidget *widget, ++ GdkDragContext *context, ++ gint x, ++ gint y, ++ guint time_) ++{ ++ ESource *source; ++ ESourceSelector *selector; ++ GtkTreeView *tree_view; ++ GtkTreeModel *model; ++ GtkTreePath *path; ++ GtkTreeIter iter; ++ const gchar *extension_name; ++ gboolean drop_zone; ++ gboolean valid; ++ ++ tree_view = GTK_TREE_VIEW (widget); ++ model = gtk_tree_view_get_model (tree_view); ++ ++ if (!gtk_tree_view_get_path_at_pos ( ++ tree_view, x, y, &path, NULL, NULL, NULL)) ++ return FALSE; ++ ++ valid = gtk_tree_model_get_iter (model, &iter, path); ++ gtk_tree_path_free (path); ++ g_return_val_if_fail (valid, FALSE); ++ ++ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); ++ ++ selector = E_SOURCE_SELECTOR (widget); ++ extension_name = e_source_selector_get_extension_name (selector); ++ drop_zone = e_source_has_extension (source, extension_name); ++ ++ g_object_unref (source); ++ ++ return drop_zone; ++} ++ ++static void ++source_selector_drag_data_received (GtkWidget *widget, ++ GdkDragContext *context, ++ gint x, ++ gint y, ++ GtkSelectionData *selection_data, ++ guint info, ++ guint time_) ++{ ++ ESource *source = NULL; ++ GtkTreeView *tree_view; ++ GtkTreeModel *model; ++ GtkTreePath *path = NULL; ++ GtkTreeIter iter; ++ GdkDragAction action; ++ gboolean delete; ++ gboolean success = FALSE; ++ ++ tree_view = GTK_TREE_VIEW (widget); ++ model = gtk_tree_view_get_model (tree_view); ++ ++ action = gdk_drag_context_get_selected_action (context); ++ delete = (action == GDK_ACTION_MOVE); ++ ++ if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, NULL)) ++ goto exit; ++ ++ if (!gtk_tree_model_get_iter (model, &iter, path)) ++ goto exit; ++ ++ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); ++ ++ if (!e_source_get_writable (source)) ++ goto exit; ++ ++ g_signal_emit ( ++ widget, signals[DATA_DROPPED], 0, selection_data, ++ source, gdk_drag_context_get_selected_action (context), ++ info, &success); ++ ++exit: ++ if (path != NULL) ++ gtk_tree_path_free (path); ++ ++ if (source != NULL) ++ g_object_unref (source); ++ ++ gtk_drag_finish (context, success, delete, time_); ++} ++ ++static gboolean ++source_selector_popup_menu (GtkWidget *widget) ++{ ++ ESourceSelector *selector; ++ ESource *source; ++ gboolean res = FALSE; ++ ++ selector = E_SOURCE_SELECTOR (widget); ++ source = e_source_selector_ref_primary_selection (selector); ++ g_signal_emit (selector, signals[POPUP_EVENT], 0, source, NULL, &res); ++ ++ if (source != NULL) ++ g_object_unref (source); ++ ++ return res; ++} ++ ++static gboolean ++source_selector_test_collapse_row (GtkTreeView *tree_view, ++ GtkTreeIter *iter, ++ GtkTreePath *path) ++{ ++ ESourceSelectorPrivate *priv; ++ GtkTreeSelection *selection; ++ GtkTreeModel *model; ++ GtkTreeIter child_iter; ++ ++ priv = E_SOURCE_SELECTOR_GET_PRIVATE (tree_view); ++ ++ /* Clear this because something else has been clicked on now */ ++ priv->toggled_last = FALSE; ++ ++ if (priv->saved_primary_selection) ++ return FALSE; ++ ++ selection = gtk_tree_view_get_selection (tree_view); ++ ++ if (!gtk_tree_selection_get_selected (selection, &model, &child_iter)) ++ return FALSE; ++ ++ if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), iter, &child_iter)) { ++ GtkTreeRowReference *reference; ++ GtkTreePath *child_path; ++ ++ child_path = gtk_tree_model_get_path (model, &child_iter); ++ reference = gtk_tree_row_reference_new (model, child_path); ++ priv->saved_primary_selection = reference; ++ gtk_tree_path_free (child_path); ++ } ++ ++ return FALSE; ++} ++ ++static void ++source_selector_row_expanded (GtkTreeView *tree_view, ++ GtkTreeIter *iter, ++ GtkTreePath *path) ++{ ++ ESourceSelectorPrivate *priv; ++ GtkTreeModel *model; ++ GtkTreePath *child_path; ++ GtkTreeIter child_iter; ++ ++ priv = E_SOURCE_SELECTOR_GET_PRIVATE (tree_view); ++ ++ if (!priv->saved_primary_selection) ++ return; ++ ++ model = gtk_tree_view_get_model (tree_view); ++ ++ child_path = gtk_tree_row_reference_get_path ( ++ priv->saved_primary_selection); ++ gtk_tree_model_get_iter (model, &child_iter, child_path); ++ ++ if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), iter, &child_iter)) { ++ GtkTreeSelection *selection; ++ ++ selection = gtk_tree_view_get_selection (tree_view); ++ gtk_tree_selection_select_iter (selection, &child_iter); ++ ++ clear_saved_primary_selection (E_SOURCE_SELECTOR (tree_view)); ++ } ++ ++ gtk_tree_path_free (child_path); ++} ++ ++static gboolean ++source_selector_get_source_selected (ESourceSelector *selector, ++ ESource *source) ++{ ++ ESourceSelectable *extension; ++ const gchar *extension_name; ++ gboolean selected = TRUE; ++ ++ extension_name = e_source_selector_get_extension_name (selector); ++ ++ if (!e_source_has_extension (source, extension_name)) ++ return FALSE; ++ ++ extension = e_source_get_extension (source, extension_name); ++ ++ if (E_IS_SOURCE_SELECTABLE (extension)) ++ selected = e_source_selectable_get_selected (extension); ++ ++ return selected; ++} ++ ++static void ++source_selector_set_source_selected (ESourceSelector *selector, ++ ESource *source, ++ gboolean selected) ++{ ++ ESourceSelectable *extension; ++ const gchar *extension_name; ++ ++ extension_name = e_source_selector_get_extension_name (selector); ++ ++ if (!e_source_has_extension (source, extension_name)) ++ return; ++ ++ extension = e_source_get_extension (source, extension_name); ++ ++ if (!E_IS_SOURCE_SELECTABLE (extension)) ++ return; ++ ++ if (selected != e_source_selectable_get_selected (extension)) { ++ e_source_selectable_set_selected (extension, selected); ++ e_source_selector_queue_write (selector, source); ++ } ++} ++ ++static gboolean ++ess_bool_accumulator (GSignalInvocationHint *ihint, ++ GValue *out, ++ const GValue *in, ++ gpointer data) ++{ ++ gboolean v_boolean; ++ ++ v_boolean = g_value_get_boolean (in); ++ g_value_set_boolean (out, v_boolean); ++ ++ return !v_boolean; ++} ++ ++static void ++e_source_selector_class_init (ESourceSelectorClass *class) ++{ ++ GObjectClass *object_class; ++ GtkWidgetClass *widget_class; ++ GtkTreeViewClass *tree_view_class; ++ ++ g_type_class_add_private (class, sizeof (ESourceSelectorPrivate)); ++ ++ object_class = G_OBJECT_CLASS (class); ++ object_class->set_property = source_selector_set_property; ++ object_class->get_property = source_selector_get_property; ++ object_class->dispose = source_selector_dispose; ++ object_class->finalize = source_selector_finalize; ++ object_class->constructed = source_selector_constructed; ++ ++ widget_class = GTK_WIDGET_CLASS (class); ++ widget_class->button_press_event = source_selector_button_press_event; ++ widget_class->drag_leave = source_selector_drag_leave; ++ widget_class->drag_motion = source_selector_drag_motion; ++ widget_class->drag_drop = source_selector_drag_drop; ++ widget_class->drag_data_received = source_selector_drag_data_received; ++ widget_class->popup_menu = source_selector_popup_menu; ++ ++ tree_view_class = GTK_TREE_VIEW_CLASS (class); ++ tree_view_class->test_collapse_row = source_selector_test_collapse_row; ++ tree_view_class->row_expanded = source_selector_row_expanded; ++ ++ class->get_source_selected = source_selector_get_source_selected; ++ class->set_source_selected = source_selector_set_source_selected; ++ ++ g_object_class_install_property ( ++ object_class, ++ PROP_EXTENSION_NAME, ++ g_param_spec_string ( ++ "extension-name", ++ NULL, ++ NULL, ++ NULL, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY | ++ G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property ( ++ object_class, ++ PROP_PRIMARY_SELECTION, ++ g_param_spec_object ( ++ "primary-selection", ++ NULL, ++ NULL, ++ E_TYPE_SOURCE, ++ G_PARAM_READWRITE | ++ G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property ( ++ object_class, ++ PROP_REGISTRY, ++ g_param_spec_object ( ++ "registry", ++ NULL, ++ NULL, ++ E_TYPE_SOURCE_REGISTRY, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY | ++ G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property ( ++ object_class, ++ PROP_SHOW_COLORS, ++ g_param_spec_boolean ( ++ "show-colors", ++ NULL, ++ NULL, ++ TRUE, ++ G_PARAM_READWRITE | ++ G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property ( ++ object_class, ++ PROP_SHOW_TOGGLES, ++ g_param_spec_boolean ( ++ "show-toggles", ++ NULL, ++ NULL, ++ TRUE, ++ G_PARAM_READWRITE | ++ G_PARAM_STATIC_STRINGS)); ++ ++ signals[SELECTION_CHANGED] = g_signal_new ( ++ "selection-changed", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (ESourceSelectorClass, selection_changed), ++ NULL, NULL, NULL, ++ G_TYPE_NONE, 0); ++ ++ /* XXX Consider this signal deprecated. Connect ++ * to "notify::primary-selection" instead. */ ++ signals[PRIMARY_SELECTION_CHANGED] = g_signal_new ( ++ "primary-selection-changed", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (ESourceSelectorClass, primary_selection_changed), ++ NULL, NULL, NULL, ++ G_TYPE_NONE, 0); ++ ++ signals[POPUP_EVENT] = g_signal_new ( ++ "popup-event", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (ESourceSelectorClass, popup_event), ++ ess_bool_accumulator, NULL, NULL, ++ G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT, ++ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); ++ ++ signals[DATA_DROPPED] = g_signal_new ( ++ "data-dropped", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (ESourceSelectorClass, data_dropped), ++ NULL, NULL, NULL, ++ G_TYPE_BOOLEAN, 4, ++ GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE, ++ E_TYPE_SOURCE, ++ GDK_TYPE_DRAG_ACTION, ++ G_TYPE_UINT); ++} ++ ++static void ++e_source_selector_init (ESourceSelector *selector) ++{ ++ GHashTable *pending_writes; ++ GtkTreeViewColumn *column; ++ GtkTreeSelection *selection; ++ GtkCellRenderer *renderer; ++ GtkTreeStore *tree_store; ++ GtkTreeView *tree_view; ++ ++ pending_writes = g_hash_table_new_full ( ++ (GHashFunc) g_direct_hash, ++ (GEqualFunc) g_direct_equal, ++ (GDestroyNotify) g_object_unref, ++ (GDestroyNotify) pending_writes_destroy_source); ++ ++ selector->priv = E_SOURCE_SELECTOR_GET_PRIVATE (selector); ++ ++ selector->priv->pending_writes = pending_writes; ++ ++ selector->priv->main_context = g_main_context_get_thread_default (); ++ if (selector->priv->main_context != NULL) ++ g_main_context_ref (selector->priv->main_context); ++ ++ tree_view = GTK_TREE_VIEW (selector); ++ ++ gtk_tree_view_set_search_column (tree_view, COLUMN_SOURCE); ++ gtk_tree_view_set_enable_search (tree_view, TRUE); ++ ++ selector->priv->toggled_last = FALSE; ++ selector->priv->select_new = FALSE; ++ selector->priv->show_colors = TRUE; ++ selector->priv->show_toggles = TRUE; ++ ++ selector->priv->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); ++ ++ tree_store = gtk_tree_store_new ( ++ NUM_COLUMNS, ++ G_TYPE_STRING, /* COLUMN_NAME */ ++ GDK_TYPE_COLOR, /* COLUMN_COLOR */ ++ G_TYPE_BOOLEAN, /* COLUMN_ACTIVE */ ++ G_TYPE_BOOLEAN, /* COLUMN_SHOW_COLOR */ ++ G_TYPE_BOOLEAN, /* COLUMN_SHOW_TOGGLE */ ++ G_TYPE_INT, /* COLUMN_WEIGHT */ ++ E_TYPE_SOURCE); /* COLUMN_SOURCE */ ++ ++ gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (tree_store)); ++ ++ column = gtk_tree_view_column_new (); ++ gtk_tree_view_append_column (tree_view, column); ++ ++ renderer = e_cell_renderer_color_new (); ++ g_object_set ( ++ G_OBJECT (renderer), "mode", ++ GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); ++ gtk_tree_view_column_pack_start (column, renderer, FALSE); ++ gtk_tree_view_column_add_attribute ( ++ column, renderer, "color", COLUMN_COLOR); ++ gtk_tree_view_column_add_attribute ( ++ column, renderer, "visible", COLUMN_SHOW_COLOR); ++ ++ renderer = e_cell_renderer_safe_toggle_new (); ++ gtk_tree_view_column_pack_start (column, renderer, FALSE); ++ gtk_tree_view_column_add_attribute ( ++ column, renderer, "active", COLUMN_ACTIVE); ++ gtk_tree_view_column_add_attribute ( ++ column, renderer, "visible", COLUMN_SHOW_TOGGLE); ++ g_signal_connect ( ++ renderer, "toggled", ++ G_CALLBACK (cell_toggled_callback), selector); ++ ++ renderer = gtk_cell_renderer_text_new (); ++ g_object_set ( ++ G_OBJECT (renderer), ++ "ellipsize", PANGO_ELLIPSIZE_END, NULL); ++ g_signal_connect_swapped ( ++ renderer, "edited", ++ G_CALLBACK (text_cell_edited_cb), selector); ++ gtk_tree_view_column_pack_start (column, renderer, TRUE); ++ gtk_tree_view_column_set_attributes ( ++ column, renderer, ++ "text", COLUMN_NAME, ++ "weight", COLUMN_WEIGHT, ++ NULL); ++ ++ selection = gtk_tree_view_get_selection (tree_view); ++ gtk_tree_selection_set_select_function ( ++ selection, (GtkTreeSelectionFunc) ++ selection_func, selector, NULL); ++ g_signal_connect_object ( ++ selection, "changed", ++ G_CALLBACK (selection_changed_callback), ++ G_OBJECT (selector), 0); ++ ++ gtk_tree_view_set_headers_visible (tree_view, FALSE); ++} ++ ++/** ++ * e_source_selector_new: ++ * @registry: an #ESourceRegistry ++ * @extension_name: the name of an #ESource extension ++ * ++ * Displays a list of sources from @registry having an extension named ++ * @extension_name. The sources are grouped by backend or groupware ++ * account, which are described by the parent source. ++ * ++ * Returns: a new #ESourceSelector ++ **/ ++GtkWidget * ++e_source_selector_new (ESourceRegistry *registry, ++ const gchar *extension_name) ++{ ++ g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); ++ g_return_val_if_fail (extension_name != NULL, NULL); ++ ++ return g_object_new ( ++ E_TYPE_SOURCE_SELECTOR, "registry", registry, ++ "extension-name", extension_name, NULL); ++} ++ ++/** ++ * e_source_selector_get_registry: ++ * @selector: an #ESourceSelector ++ * ++ * Returns the #ESourceRegistry that @selector is getting sources from. ++ * ++ * Returns: an #ESourceRegistry ++ * ++ * Since: 3.6 ++ **/ ++ESourceRegistry * ++e_source_selector_get_registry (ESourceSelector *selector) ++{ ++ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); ++ ++ return selector->priv->registry; ++} ++ ++/** ++ * e_source_selector_get_extension_name: ++ * @selector: an #ESourceSelector ++ * ++ * Returns the extension name used to filter which sources are displayed. ++ * ++ * Returns: the #ESource extension name ++ * ++ * Since: 3.6 ++ **/ ++const gchar * ++e_source_selector_get_extension_name (ESourceSelector *selector) ++{ ++ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); ++ ++ return selector->priv->extension_name; ++} ++ ++/** ++ * e_source_selector_get_show_colors: ++ * @selector: an #ESourceSelector ++ * ++ * Returns whether colors are shown next to data sources. ++ * ++ * Returns: %TRUE if colors are being shown ++ * ++ * Since: 3.6 ++ **/ ++gboolean ++e_source_selector_get_show_colors (ESourceSelector *selector) ++{ ++ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE); ++ ++ return selector->priv->show_colors; ++} ++ ++/** ++ * e_source_selector_set_show_colors: ++ * @selector: an #ESourceSelector ++ * @show_colors: whether to show colors ++ * ++ * Sets whether to show colors next to data sources. ++ * ++ * Since: 3.6 ++ **/ ++void ++e_source_selector_set_show_colors (ESourceSelector *selector, ++ gboolean show_colors) ++{ ++ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); ++ ++ if ((show_colors ? 1 : 0) == (selector->priv->show_colors ? 1 : 0)) ++ return; ++ ++ selector->priv->show_colors = show_colors; ++ ++ g_object_notify (G_OBJECT (selector), "show-colors"); ++ ++ source_selector_build_model (selector); ++} ++ ++/** ++ * e_source_selector_get_show_toggles: ++ * @selector: an #ESourceSelector ++ * ++ * Returns whether toggles are shown next to data sources. ++ * ++ * Returns: %TRUE if toggles are being shown ++ * ++ * Since: 3.6 ++ **/ ++gboolean ++e_source_selector_get_show_toggles (ESourceSelector *selector) ++{ ++ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE); ++ ++ return selector->priv->show_toggles; ++} ++ ++/** ++ * e_source_selector_set_show_toggles: ++ * @selector: an #ESourceSelector ++ * @show_toggles: whether to show toggles ++ * ++ * Sets whether to show toggles next to data sources. ++ * ++ * Since: 3.6 ++ **/ ++void ++e_source_selector_set_show_toggles (ESourceSelector *selector, ++ gboolean show_toggles) ++{ ++ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); ++ ++ if ((show_toggles ? 1 : 0) == (selector->priv->show_toggles ? 1 : 0)) ++ return; ++ ++ selector->priv->show_toggles = show_toggles; ++ ++ g_object_notify (G_OBJECT (selector), "show-toggles"); ++ ++ source_selector_build_model (selector); ++} ++ ++/* Helper for e_source_selector_get_selection() */ ++static gboolean ++source_selector_check_selected (GtkTreeModel *model, ++ GtkTreePath *path, ++ GtkTreeIter *iter, ++ gpointer user_data) ++{ ++ ESource *source; ++ ++ struct { ++ ESourceSelector *selector; ++ GSList *list; ++ } *closure = user_data; ++ ++ gtk_tree_model_get (model, iter, COLUMN_SOURCE, &source, -1); ++ ++ if (e_source_selector_source_is_selected (closure->selector, source)) ++ closure->list = g_slist_prepend (closure->list, source); ++ else ++ g_object_unref (source); ++ ++ return FALSE; ++} ++ ++/** ++ * e_source_selector_get_selection: ++ * @selector: an #ESourceSelector ++ * ++ * Get the list of selected sources, i.e. those that were enabled through the ++ * corresponding checkboxes in the tree. ++ * ++ * Returns: A list of the ESources currently selected. The sources will ++ * be in the same order as they appear on the screen, and the list should be ++ * freed using e_source_selector_free_selection(). ++ **/ ++GSList * ++e_source_selector_get_selection (ESourceSelector *selector) ++{ ++ struct { ++ ESourceSelector *selector; ++ GSList *list; ++ } closure; ++ ++ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); ++ ++ closure.selector = selector; ++ closure.list = NULL; ++ ++ gtk_tree_model_foreach ( ++ gtk_tree_view_get_model (GTK_TREE_VIEW (selector)), ++ (GtkTreeModelForeachFunc) source_selector_check_selected, ++ &closure); ++ ++ return g_slist_reverse (closure.list); ++} ++ ++/** ++ * e_source_list_free_selection: ++ * @list: A selection list returned by e_source_selector_get_selection(). ++ * ++ * Free the selection list. ++ **/ ++void ++e_source_selector_free_selection (GSList *list) ++{ ++ g_slist_foreach (list, (GFunc) g_object_unref, NULL); ++ g_slist_free (list); ++} ++ ++/** ++ * e_source_selector_set_select_new: ++ * @selector: An #ESourceSelector widget ++ * @state: A gboolean ++ * ++ * Set whether or not to select new sources added to @selector. ++ **/ ++void ++e_source_selector_set_select_new (ESourceSelector *selector, ++ gboolean state) ++{ ++ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); ++ ++ selector->priv->select_new = state; ++} ++ ++/** ++ * e_source_selector_select_source: ++ * @selector: An #ESourceSelector widget ++ * @source: An #ESource. ++ * ++ * Select @source in @selector. ++ **/ ++void ++e_source_selector_select_source (ESourceSelector *selector, ++ ESource *source) ++{ ++ ESourceSelectorClass *class; ++ GtkTreeRowReference *reference; ++ GHashTable *source_index; ++ ++ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); ++ g_return_if_fail (E_IS_SOURCE (source)); ++ ++ /* Make sure the ESource is in our tree model. */ ++ source_index = selector->priv->source_index; ++ reference = g_hash_table_lookup (source_index, source); ++ g_return_if_fail (gtk_tree_row_reference_valid (reference)); ++ ++ class = E_SOURCE_SELECTOR_GET_CLASS (selector); ++ g_return_if_fail (class->set_source_selected != NULL); ++ ++ class->set_source_selected (selector, source, TRUE); ++ ++ g_signal_emit (selector, signals[SELECTION_CHANGED], 0); ++} ++ ++/** ++ * e_source_selector_unselect_source: ++ * @selector: An #ESourceSelector widget ++ * @source: An #ESource. ++ * ++ * Unselect @source in @selector. ++ **/ ++void ++e_source_selector_unselect_source (ESourceSelector *selector, ++ ESource *source) ++{ ++ ESourceSelectorClass *class; ++ GtkTreeRowReference *reference; ++ GHashTable *source_index; ++ ++ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); ++ g_return_if_fail (E_IS_SOURCE (source)); ++ ++ /* Make sure the ESource is in our tree model. */ ++ source_index = selector->priv->source_index; ++ reference = g_hash_table_lookup (source_index, source); ++ g_return_if_fail (gtk_tree_row_reference_valid (reference)); ++ ++ class = E_SOURCE_SELECTOR_GET_CLASS (selector); ++ g_return_if_fail (class->set_source_selected != NULL); ++ ++ class->set_source_selected (selector, source, FALSE); ++ ++ g_signal_emit (selector, signals[SELECTION_CHANGED], 0); ++} ++ ++/** ++ * e_source_selector_select_exclusive: ++ * @selector: An #ESourceSelector widget ++ * @source: An #ESource. ++ * ++ * Select @source in @selector and unselect all others. ++ * ++ * Since: 2.30 ++ **/ ++void ++e_source_selector_select_exclusive (ESourceSelector *selector, ++ ESource *source) ++{ ++ ESourceSelectorClass *class; ++ GHashTable *source_index; ++ GHashTableIter iter; ++ gpointer key; ++ ++ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); ++ g_return_if_fail (E_IS_SOURCE (source)); ++ ++ class = E_SOURCE_SELECTOR_GET_CLASS (selector); ++ g_return_if_fail (class->set_source_selected != NULL); ++ ++ source_index = selector->priv->source_index; ++ g_hash_table_iter_init (&iter, source_index); ++ ++ while (g_hash_table_iter_next (&iter, &key, NULL)) { ++ gboolean selected = e_source_equal (key, source); ++ class->set_source_selected (selector, key, selected); ++ } ++ ++ g_signal_emit (selector, signals[SELECTION_CHANGED], 0); ++} ++ ++/** ++ * e_source_selector_source_is_selected: ++ * @selector: An #ESourceSelector widget ++ * @source: An #ESource. ++ * ++ * Check whether @source is selected in @selector. ++ * ++ * Returns: %TRUE if @source is currently selected, %FALSE otherwise. ++ **/ ++gboolean ++e_source_selector_source_is_selected (ESourceSelector *selector, ++ ESource *source) ++{ ++ ESourceSelectorClass *class; ++ GtkTreeRowReference *reference; ++ GHashTable *source_index; ++ ++ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE); ++ g_return_val_if_fail (E_IS_SOURCE (source), FALSE); ++ ++ /* Make sure the ESource is in our tree model. */ ++ source_index = selector->priv->source_index; ++ reference = g_hash_table_lookup (source_index, source); ++ g_return_val_if_fail (gtk_tree_row_reference_valid (reference), FALSE); ++ ++ class = E_SOURCE_SELECTOR_GET_CLASS (selector); ++ g_return_val_if_fail (class->get_source_selected != NULL, FALSE); ++ ++ return class->get_source_selected (selector, source); ++} ++ ++/** ++ * e_source_selector_edit_primary_selection: ++ * @selector: An #ESourceSelector widget ++ * ++ * Allows the user to rename the primary selected source by opening an ++ * entry box directly in @selector. ++ * ++ * Since: 2.26 ++ **/ ++void ++e_source_selector_edit_primary_selection (ESourceSelector *selector) ++{ ++ GtkTreeRowReference *reference; ++ GtkTreeSelection *selection; ++ GtkTreeViewColumn *column; ++ GtkCellRenderer *renderer; ++ GtkTreeView *tree_view; ++ GtkTreeModel *model; ++ GtkTreePath *path = NULL; ++ GtkTreeIter iter; ++ GList *list; ++ ++ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); ++ ++ tree_view = GTK_TREE_VIEW (selector); ++ column = gtk_tree_view_get_column (tree_view, 0); ++ reference = selector->priv->saved_primary_selection; ++ selection = gtk_tree_view_get_selection (tree_view); ++ ++ if (reference != NULL) ++ path = gtk_tree_row_reference_get_path (reference); ++ else if (gtk_tree_selection_get_selected (selection, &model, &iter)) ++ path = gtk_tree_model_get_path (model, &iter); ++ ++ if (path == NULL) ++ return; ++ ++ /* XXX Because we stuff three renderers in a single column, ++ * we have to manually hunt for the text renderer. */ ++ renderer = NULL; ++ list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); ++ while (list != NULL) { ++ renderer = list->data; ++ if (GTK_IS_CELL_RENDERER_TEXT (renderer)) ++ break; ++ list = g_list_delete_link (list, list); ++ } ++ g_list_free (list); ++ ++ /* Make the text cell renderer editable, but only temporarily. ++ * We don't want editing to be activated by simply clicking on ++ * the source name. Too easy for accidental edits to occur. */ ++ g_object_set (renderer, "editable", TRUE, NULL); ++ gtk_tree_view_expand_to_path (tree_view, path); ++ gtk_tree_view_set_cursor_on_cell ( ++ tree_view, path, column, renderer, TRUE); ++ g_object_set (renderer, "editable", FALSE, NULL); ++ ++ gtk_tree_path_free (path); ++} ++ ++/** ++ * e_source_selector_ref_primary_selection: ++ * @selector: An #ESourceSelector widget ++ * ++ * Get the primary selected source. The primary selection is the one that is ++ * highlighted through the normal #GtkTreeView selection mechanism (as opposed ++ * to the "normal" selection, which is the set of source whose checkboxes are ++ * checked). ++ * ++ * The returned #ESource is referenced for thread-safety and must be ++ * unreferenced with g_object_unref() when finished with it. ++ * ++ * Returns: The selected source. ++ * ++ * Since: 3.6 ++ **/ ++ESource * ++e_source_selector_ref_primary_selection (ESourceSelector *selector) ++{ ++ ESource *source; ++ GtkTreeRowReference *reference; ++ GtkTreeSelection *selection; ++ GtkTreeView *tree_view; ++ GtkTreeModel *model; ++ GtkTreeIter iter; ++ const gchar *extension_name; ++ gboolean have_iter = FALSE; ++ ++ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); ++ ++ tree_view = GTK_TREE_VIEW (selector); ++ model = gtk_tree_view_get_model (tree_view); ++ selection = gtk_tree_view_get_selection (tree_view); ++ ++ reference = selector->priv->saved_primary_selection; ++ ++ if (gtk_tree_row_reference_valid (reference)) { ++ GtkTreePath *path; ++ ++ path = gtk_tree_row_reference_get_path (reference); ++ have_iter = gtk_tree_model_get_iter (model, &iter, path); ++ gtk_tree_path_free (path); ++ } ++ ++ if (!have_iter) ++ have_iter = gtk_tree_selection_get_selected ( ++ selection, NULL, &iter); ++ ++ if (!have_iter) ++ return NULL; ++ ++ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); ++ ++ extension_name = e_source_selector_get_extension_name (selector); ++ ++ if (!e_source_has_extension (source, extension_name)) { ++ g_object_unref (source); ++ return NULL; ++ } ++ ++ return source; ++} ++ ++/** ++ * e_source_selector_set_primary_selection: ++ * @selector: an #ESourceSelector widget ++ * @source: an #ESource to select ++ * ++ * Highlights @source in @selector. The highlighted #ESource is called ++ * the primary selection. ++ * ++ * Do not confuse this function with e_source_selector_select_source(), ++ * which activates the check box next to an #ESource's display name in ++ * @selector. This function does not alter the check box. ++ **/ ++void ++e_source_selector_set_primary_selection (ESourceSelector *selector, ++ ESource *source) ++{ ++ GHashTable *source_index; ++ GtkTreeRowReference *reference; ++ GtkTreeSelection *selection; ++ GtkTreeView *tree_view; ++ GtkTreePath *child_path; ++ GtkTreePath *parent_path; ++ const gchar *extension_name; ++ ++ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); ++ g_return_if_fail (E_IS_SOURCE (source)); ++ ++ tree_view = GTK_TREE_VIEW (selector); ++ selection = gtk_tree_view_get_selection (tree_view); ++ ++ source_index = selector->priv->source_index; ++ reference = g_hash_table_lookup (source_index, source); ++ ++ /* XXX Maybe we should return a success/fail boolean? */ ++ if (!gtk_tree_row_reference_valid (reference)) ++ return; ++ ++ extension_name = e_source_selector_get_extension_name (selector); ++ ++ /* Return silently if attempting to select a parent node ++ * lacking the expected extension (e.g. On This Computer). */ ++ if (!e_source_has_extension (source, extension_name)) ++ return; ++ ++ /* We block the signal because this all needs to be atomic */ ++ g_signal_handlers_block_matched ( ++ selection, G_SIGNAL_MATCH_FUNC, ++ 0, 0, NULL, selection_changed_callback, NULL); ++ gtk_tree_selection_unselect_all (selection); ++ g_signal_handlers_unblock_matched ( ++ selection, G_SIGNAL_MATCH_FUNC, ++ 0, 0, NULL, selection_changed_callback, NULL); ++ ++ clear_saved_primary_selection (selector); ++ ++ child_path = gtk_tree_row_reference_get_path (reference); ++ ++ parent_path = gtk_tree_path_copy (child_path); ++ gtk_tree_path_up (parent_path); ++ ++ if (gtk_tree_view_row_expanded (tree_view, parent_path)) { ++ gtk_tree_selection_select_path (selection, child_path); ++ } else { ++ selector->priv->saved_primary_selection = ++ gtk_tree_row_reference_copy (reference); ++ g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0); ++ g_object_notify (G_OBJECT (selector), "primary-selection"); ++ } ++ ++ gtk_tree_path_free (child_path); ++ gtk_tree_path_free (parent_path); ++} ++ ++/** ++ * e_source_selector_ref_source_by_path: ++ * @selector: an #ESourceSelector ++ * @path: a #GtkTreePath ++ * ++ * Returns the #ESource object at @path, or %NULL if @path is invalid. ++ * ++ * The returned #ESource is referenced for thread-safety and must be ++ * unreferenced with g_object_unref() when finished with it. ++ * ++ * Returns: the #ESource object at @path, or %NULL ++ * ++ * Since: 3.6 ++ **/ ++ESource * ++e_source_selector_ref_source_by_path (ESourceSelector *selector, ++ GtkTreePath *path) ++{ ++ ESource *source = NULL; ++ GtkTreeModel *model; ++ GtkTreeIter iter; ++ ++ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); ++ g_return_val_if_fail (path != NULL, NULL); ++ ++ model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); ++ ++ if (gtk_tree_model_get_iter (model, &iter, path)) ++ gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); ++ ++ return source; ++} ++ ++/** ++ * e_source_selector_queue_write: ++ * @selector: an #ESourceSelecetor ++ * @source: an #ESource with changes to be written ++ * ++ * Queues a main loop idle callback to write changes to @source back to ++ * the D-Bus registry service. ++ * ++ * Since: 3.6 ++ **/ ++void ++e_source_selector_queue_write (ESourceSelector *selector, ++ ESource *source) ++{ ++ GSource *idle_source; ++ GHashTable *pending_writes; ++ GMainContext *main_context; ++ AsyncContext *async_context; ++ ++ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); ++ g_return_if_fail (E_IS_SOURCE (source)); ++ ++ main_context = selector->priv->main_context; ++ pending_writes = selector->priv->pending_writes; ++ ++ idle_source = g_hash_table_lookup (pending_writes, source); ++ if (idle_source != NULL && !g_source_is_destroyed (idle_source)) ++ return; ++ ++ async_context = g_slice_new0 (AsyncContext); ++ async_context->selector = g_object_ref (selector); ++ async_context->source = g_object_ref (source); ++ ++ /* Set a higher priority so this idle source runs before our ++ * source_selector_cancel_write() signal handler, which will ++ * cancel this idle source. Cancellation is the right thing ++ * to do when receiving changes from OTHER registry clients, ++ * but we don't want to cancel our own changes. ++ * ++ * XXX This might be an argument for using etags. ++ */ ++ idle_source = g_idle_source_new (); ++ g_hash_table_insert ( ++ pending_writes, ++ g_object_ref (source), ++ g_source_ref (idle_source)); ++ g_source_set_callback ( ++ idle_source, ++ source_selector_write_idle_cb, ++ async_context, ++ (GDestroyNotify) async_context_free); ++ g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE); ++ g_source_attach (idle_source, main_context); ++ g_source_unref (idle_source); ++} ++ +diff --git a/src/event-factories/e-source-selector.h b/src/event-factories/e-source-selector.h +new file mode 100644 +index 0000000..b6d3770 +--- /dev/null ++++ b/src/event-factories/e-source-selector.h +@@ -0,0 +1,137 @@ ++/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ ++/* e-source-selector.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; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this program; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Ettore Perazzoli <ettore@ximian.com> ++ */ ++ ++#ifndef E_SOURCE_SELECTOR_H ++#define E_SOURCE_SELECTOR_H ++ ++#include <gtk/gtk.h> ++#include <libedataserver/libedataserver.h> ++ ++/* Standard GObject macros */ ++#define E_TYPE_SOURCE_SELECTOR \ ++ (e_source_selector_get_type ()) ++#define E_SOURCE_SELECTOR(obj) \ ++ (G_TYPE_CHECK_INSTANCE_CAST \ ++ ((obj), E_TYPE_SOURCE_SELECTOR, ESourceSelector)) ++#define E_SOURCE_SELECTOR_CLASS(cls) \ ++ (G_TYPE_CHECK_CLASS_CAST \ ++ ((cls), E_TYPE_SOURCE_SELECTOR, ESourceSelectorClass)) ++#define E_IS_SOURCE_SELECTOR(obj) \ ++ (G_TYPE_CHECK_INSTANCE_TYPE \ ++ ((obj), E_TYPE_SOURCE_SELECTOR)) ++#define E_IS_SOURCE_SELECTOR_CLASS(cls) \ ++ (G_TYPE_CHECK_CLASS_TYPE \ ++ ((cls), E_TYPE_SOURCE_SELECTOR)) ++#define E_SOURCE_SELECTOR_GET_CLASS(obj) \ ++ (G_TYPE_INSTANCE_GET_CLASS \ ++ ((obj), E_TYPE_SOURCE_SELECTOR, ESourceSelectorClass)) ++ ++G_BEGIN_DECLS ++ ++typedef struct _ESourceSelector ESourceSelector; ++typedef struct _ESourceSelectorClass ESourceSelectorClass; ++typedef struct _ESourceSelectorPrivate ESourceSelectorPrivate; ++ ++struct _ESourceSelector { ++ GtkTreeView parent; ++ ESourceSelectorPrivate *priv; ++}; ++ ++struct _ESourceSelectorClass { ++ GtkTreeViewClass parent_class; ++ ++ /* Methods */ ++ gboolean (*get_source_selected) (ESourceSelector *selector, ++ ESource *source); ++ void (*set_source_selected) (ESourceSelector *selector, ++ ESource *source, ++ gboolean selected); ++ ++ /* Signals */ ++ void (*selection_changed) (ESourceSelector *selector); ++ void (*primary_selection_changed) ++ (ESourceSelector *selector); ++ gboolean (*popup_event) (ESourceSelector *selector, ++ ESource *primary, ++ GdkEventButton *event); ++ gboolean (*data_dropped) (ESourceSelector *selector, ++ GtkSelectionData *data, ++ ESource *destination, ++ GdkDragAction action, ++ guint target_info); ++ ++ gpointer padding1; ++ gpointer padding2; ++ gpointer padding3; ++}; ++ ++GType e_source_selector_get_type (void); ++GtkWidget * e_source_selector_new (ESourceRegistry *registry, ++ const gchar *extension_name); ++ESourceRegistry * ++ e_source_selector_get_registry (ESourceSelector *selector); ++const gchar * e_source_selector_get_extension_name ++ (ESourceSelector *selector); ++gboolean e_source_selector_get_show_colors ++ (ESourceSelector *selector); ++void e_source_selector_set_show_colors ++ (ESourceSelector *selector, ++ gboolean show_colors); ++gboolean e_source_selector_get_show_toggles ++ (ESourceSelector *selector); ++void e_source_selector_set_show_toggles ++ (ESourceSelector *selector, ++ gboolean show_toggles); ++void e_source_selector_select_source (ESourceSelector *selector, ++ ESource *source); ++void e_source_selector_unselect_source ++ (ESourceSelector *selector, ++ ESource *source); ++void e_source_selector_select_exclusive ++ (ESourceSelector *selector, ++ ESource *source); ++gboolean e_source_selector_source_is_selected ++ (ESourceSelector *selector, ++ ESource *source); ++GSList * e_source_selector_get_selection (ESourceSelector *selector); ++void e_source_selector_free_selection ++ (GSList *list); ++void e_source_selector_set_select_new ++ (ESourceSelector *selector, ++ gboolean state); ++void e_source_selector_edit_primary_selection ++ (ESourceSelector *selector); ++ESource * e_source_selector_ref_primary_selection ++ (ESourceSelector *selector); ++void e_source_selector_set_primary_selection ++ (ESourceSelector *selector, ++ ESource *source); ++ESource * e_source_selector_ref_source_by_path ++ (ESourceSelector *selector, ++ GtkTreePath *path); ++void e_source_selector_queue_write (ESourceSelector *selector, ++ ESource *source); ++ ++G_END_DECLS ++ ++#endif /* E_SOURCE_SELECTOR_H */ +-- +cgit v0.9.1 |