summaryrefslogtreecommitdiff
path: root/community/almanah
diff options
context:
space:
mode:
authorroot <root@rshg054.dnsready.net>2013-08-13 01:33:19 -0700
committerroot <root@rshg054.dnsready.net>2013-08-13 01:33:19 -0700
commit7a65a910b77ad191d69881098c47f9b0c852d92e (patch)
tree9564e611af1442f8952a8cbddb3b0ad25ed71aab /community/almanah
parent60da6abff6c9577a783d72865f11de7a585e912e (diff)
Tue Aug 13 01:31:08 PDT 2013
Diffstat (limited to 'community/almanah')
-rw-r--r--community/almanah/PKGBUILD40
-rw-r--r--community/almanah/almanah.install12
-rw-r--r--community/almanah/eds-3.8.patch2609
-rw-r--r--community/almanah/update-gtkspell3-support.patch90
4 files changed, 2751 insertions, 0 deletions
diff --git a/community/almanah/PKGBUILD b/community/almanah/PKGBUILD
new file mode 100644
index 000000000..709f26825
--- /dev/null
+++ b/community/almanah/PKGBUILD
@@ -0,0 +1,40 @@
+# $Id: PKGBUILD 90849 2013-05-14 21:40:34Z andrea $
+# Maintainer: Balló György <ballogyor+arch at gmail dot com>
+
+pkgname=almanah
+pkgver=0.10.1
+pkgrel=3
+pkgdesc="Small GTK+ application to allow you to keep a diary of your life"
+arch=('i686' 'x86_64')
+url="http://live.gnome.org/Almanah_Diary"
+license=('GPL')
+depends=('evolution-data-server' 'gtkspell3' 'libcryptui' 'xdg-utils')
+makedepends=('intltool')
+install=$pkgname.install
+source=(http://ftp.gnome.org/pub/GNOME/sources/$pkgname/${pkgver%.*}/$pkgname-$pkgver.tar.xz
+ eds-3.8.patch
+ update-gtkspell3-support.patch)
+sha256sums=('122789c1baea1713102b200c0401d25395ec177a66ae22635d3ae2fecc1f08bd'
+ '2407cb2cc11e61b7863bf20fc13d4614a3384e8cb7c5b534f1e3ce7a3afb2d2d'
+ '60fedd6be05ef2619c3b50539b9fdc717480e4f03a2920d8b68b09e63dc7e7db')
+
+build() {
+ cd "$srcdir/$pkgname-$pkgver"
+
+ # Port to evolution-data-server 3.8
+ patch -Np1 -i "$srcdir/eds-3.8.patch"
+
+ # Port to the new gtkspell3
+ patch -Np1 -i "$srcdir/update-gtkspell3-support.patch"
+
+ autoreconf -fi
+ ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
+ --disable-static --disable-schemas-compile
+ make
+}
+
+package() {
+ cd "$srcdir/$pkgname-$pkgver"
+
+ make DESTDIR="$pkgdir/" install
+}
diff --git a/community/almanah/almanah.install b/community/almanah/almanah.install
new file mode 100644
index 000000000..6ce7836d2
--- /dev/null
+++ b/community/almanah/almanah.install
@@ -0,0 +1,12 @@
+post_install() {
+ glib-compile-schemas usr/share/glib-2.0/schemas
+ xdg-icon-resource forceupdate
+}
+
+post_upgrade() {
+ post_install $1
+}
+
+post_remove() {
+ post_install $1
+}
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
diff --git a/community/almanah/update-gtkspell3-support.patch b/community/almanah/update-gtkspell3-support.patch
new file mode 100644
index 000000000..aa2a81159
--- /dev/null
+++ b/community/almanah/update-gtkspell3-support.patch
@@ -0,0 +1,90 @@
+From 114c4f78ac9882be51c4154751760bd4fea48403 Mon Sep 17 00:00:00 2001
+From: Álvaro Peña <alvaropg@gmail.com>
+Date: Wed, 06 Feb 2013 10:12:54 +0000
+Subject: Update GtkSpell 3 support
+
+---
+diff --git a/configure.ac b/configure.ac
+index b89ba74..fbe6889 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -95,7 +95,7 @@ AC_SUBST(EVO_LIBS)
+
+ dnl Spell checking
+ if test $spell_checking = "true"; then
+- PKG_CHECK_MODULES(SPELL_CHECKING, gtkspell-3.0)
++ PKG_CHECK_MODULES(SPELL_CHECKING, gtkspell3-3.0)
+ AC_SUBST(SPELL_CHECKING_CFLAGS)
+ AC_SUBST(SPELL_CHECKING_LIBS)
+ fi
+diff --git a/src/main-window.c b/src/main-window.c
+index fb931e1..2fe01ef 100644
+--- a/src/main-window.c
++++ b/src/main-window.c
+@@ -1109,7 +1109,7 @@ mw_calendar_day_selected_cb (AlmanahCalendarButton *calendar_button, AlmanahMain
+ AlmanahEventManager *event_manager;
+ GDate calendar_date;
+ #ifdef ENABLE_SPELL_CHECKING
+- GtkSpell *gtkspell;
++ GtkSpellChecker *gtkspell;
+ #endif /* ENABLE_SPELL_CHECKING */
+ AlmanahMainWindowPrivate *priv = main_window->priv;
+ AlmanahEntry *entry;
+@@ -1166,9 +1166,9 @@ mw_calendar_day_selected_cb (AlmanahCalendarButton *calendar_button, AlmanahMain
+
+ #ifdef ENABLE_SPELL_CHECKING
+ /* Ensure the spell-checking is updated */
+- gtkspell = gtkspell_get_from_text_view (priv->entry_view);
++ gtkspell = gtk_spell_checker_get_from_text_view (priv->entry_view);
+ if (gtkspell != NULL) {
+- gtkspell_recheck_all (gtkspell);
++ gtk_spell_checker_recheck_all (gtkspell);
+ gtk_widget_queue_draw (GTK_WIDGET (priv->entry_view));
+ }
+ #endif /* ENABLE_SPELL_CHECKING */
+@@ -1326,13 +1326,13 @@ enable_spell_checking (AlmanahMainWindow *self, GError **error)
+ {
+ AlmanahApplication *application;
+ GSettings *settings;
+- GtkSpell *gtkspell;
++ GtkSpellChecker *gtkspell;
+ gchar *spelling_language;
+ GtkTextTagTable *table;
+ GtkTextTag *tag;
+
+ /* Bail out if spell checking's already enabled */
+- if (gtkspell_get_from_text_view (self->priv->entry_view) != NULL)
++ if (gtk_spell_checker_get_from_text_view (self->priv->entry_view) != NULL)
+ return TRUE;
+
+ /* If spell checking wasn't already enabled, we have a dummy gtkspell-misspelled text tag to destroy */
+@@ -1353,7 +1353,9 @@ enable_spell_checking (AlmanahMainWindow *self, GError **error)
+ spelling_language = NULL;
+ }
+
+- gtkspell = gtkspell_new_attach (self->priv->entry_view, spelling_language, error);
++ gtkspell = gtk_spell_checker_new ();
++ gtk_spell_checker_set_language (gtkspell, spelling_language, error);
++ gtk_spell_checker_attach (gtkspell, self->priv->entry_view);
+ g_free (spelling_language);
+
+ if (gtkspell == NULL)
+@@ -1364,13 +1366,13 @@ enable_spell_checking (AlmanahMainWindow *self, GError **error)
+ static void
+ disable_spell_checking (AlmanahMainWindow *self)
+ {
+- GtkSpell *gtkspell;
++ GtkSpellChecker *gtkspell;
+ GtkTextTagTable *table;
+ GtkTextTag *tag;
+
+- gtkspell = gtkspell_get_from_text_view (self->priv->entry_view);
++ gtkspell = gtk_spell_checker_get_from_text_view (self->priv->entry_view);
+ if (gtkspell != NULL)
+- gtkspell_detach (gtkspell);
++ gtk_spell_checker_detach (gtkspell);
+
+ /* Remove the old gtkspell-misspelling text tag */
+ table = gtk_text_buffer_get_tag_table (self->priv->entry_buffer);
+--
+cgit v0.9.1