aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2009-06-25 00:59:33 +0800
committerMatthew Barnes <mbarnes@redhat.com>2009-06-25 06:29:22 +0800
commitf0d3f3afdfa314e1e8cd7d8da790878008a46aad (patch)
tree7116e2a18c7bd50633b2f8de42b5377af1e8172a /modules
parent94302ea73cde0b470faad653f752406f19f202d7 (diff)
downloadgsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.tar
gsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.tar.gz
gsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.tar.bz2
gsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.tar.lz
gsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.tar.xz
gsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.tar.zst
gsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.zip
Radically reorganize source code.
- Collect all shell modules into a new top-level 'modules' directory: $(top_srcdir)/modules/addressbook $(top_srcdir)/modules/calendar $(top_srcdir)/modules/mail Nothing is allowed to link to these, not plugins nor other modules. THIS SOLVES BUG #571275 AND OPENS THE DOOR TO PORTING TO MAC OS X. - Mimic the libevolution-mail-shared library from master (except drop the "shared" suffix) and have libevolution-mail-importers and all mail-related plugins link to it. - Discard the a11y subdirectories and have the files live alongside their counterpart widgets.
Diffstat (limited to 'modules')
-rw-r--r--modules/Makefile.am3
-rw-r--r--modules/addressbook/Makefile.am129
-rw-r--r--modules/addressbook/addressbook-config.c1250
-rw-r--r--modules/addressbook/addressbook-config.h52
-rw-r--r--modules/addressbook/apps_evolution_addressbook.schemas.in82
-rw-r--r--modules/addressbook/autocompletion-config.c137
-rw-r--r--modules/addressbook/autocompletion-config.h37
-rw-r--r--modules/addressbook/e-book-shell-backend.c576
-rw-r--r--modules/addressbook/e-book-shell-backend.h70
-rw-r--r--modules/addressbook/e-book-shell-content.c492
-rw-r--r--modules/addressbook/e-book-shell-content.h110
-rw-r--r--modules/addressbook/e-book-shell-migrate.c1230
-rw-r--r--modules/addressbook/e-book-shell-migrate.h41
-rw-r--r--modules/addressbook/e-book-shell-sidebar.c232
-rw-r--r--modules/addressbook/e-book-shell-sidebar.h79
-rw-r--r--modules/addressbook/e-book-shell-view-actions.c1011
-rw-r--r--modules/addressbook/e-book-shell-view-actions.h91
-rw-r--r--modules/addressbook/e-book-shell-view-private.c621
-rw-r--r--modules/addressbook/e-book-shell-view-private.h129
-rw-r--r--modules/addressbook/e-book-shell-view.c318
-rw-r--r--modules/addressbook/e-book-shell-view.h66
-rw-r--r--modules/addressbook/eab-composer-util.c197
-rw-r--r--modules/addressbook/eab-composer-util.h31
-rw-r--r--modules/addressbook/evolution-module-addressbook.c45
-rw-r--r--modules/addressbook/ldap-config.glade1454
-rw-r--r--modules/addressbook/openldap-extract.h1427
-rw-r--r--modules/calendar/Makefile.am81
-rw-r--r--modules/calendar/e-cal-shell-backend.c666
-rw-r--r--modules/calendar/e-cal-shell-backend.h70
-rw-r--r--modules/calendar/e-cal-shell-content.c827
-rw-r--r--modules/calendar/e-cal-shell-content.h108
-rw-r--r--modules/calendar/e-cal-shell-migrate.c796
-rw-r--r--modules/calendar/e-cal-shell-migrate.h38
-rw-r--r--modules/calendar/e-cal-shell-settings.c59
-rw-r--r--modules/calendar/e-cal-shell-settings.h33
-rw-r--r--modules/calendar/e-cal-shell-sidebar.c759
-rw-r--r--modules/calendar/e-cal-shell-sidebar.h101
-rw-r--r--modules/calendar/e-cal-shell-view-actions.c1202
-rw-r--r--modules/calendar/e-cal-shell-view-actions.h153
-rw-r--r--modules/calendar/e-cal-shell-view-memopad.c526
-rw-r--r--modules/calendar/e-cal-shell-view-private.c650
-rw-r--r--modules/calendar/e-cal-shell-view-private.h170
-rw-r--r--modules/calendar/e-cal-shell-view-taskpad.c654
-rw-r--r--modules/calendar/e-cal-shell-view.c226
-rw-r--r--modules/calendar/e-cal-shell-view.h69
-rw-r--r--modules/calendar/e-memo-shell-backend.c613
-rw-r--r--modules/calendar/e-memo-shell-backend.h70
-rw-r--r--modules/calendar/e-memo-shell-content.c676
-rw-r--r--modules/calendar/e-memo-shell-content.h96
-rw-r--r--modules/calendar/e-memo-shell-migrate.c257
-rw-r--r--modules/calendar/e-memo-shell-migrate.h38
-rw-r--r--modules/calendar/e-memo-shell-sidebar.c718
-rw-r--r--modules/calendar/e-memo-shell-sidebar.h95
-rw-r--r--modules/calendar/e-memo-shell-view-actions.c949
-rw-r--r--modules/calendar/e-memo-shell-view-actions.h87
-rw-r--r--modules/calendar/e-memo-shell-view-private.c524
-rw-r--r--modules/calendar/e-memo-shell-view-private.h126
-rw-r--r--modules/calendar/e-memo-shell-view.c222
-rw-r--r--modules/calendar/e-memo-shell-view.h67
-rw-r--r--modules/calendar/e-task-shell-backend.c621
-rw-r--r--modules/calendar/e-task-shell-backend.h70
-rw-r--r--modules/calendar/e-task-shell-content.c700
-rw-r--r--modules/calendar/e-task-shell-content.h100
-rw-r--r--modules/calendar/e-task-shell-migrate.c664
-rw-r--r--modules/calendar/e-task-shell-migrate.h38
-rw-r--r--modules/calendar/e-task-shell-sidebar.c696
-rw-r--r--modules/calendar/e-task-shell-sidebar.h97
-rw-r--r--modules/calendar/e-task-shell-view-actions.c1154
-rw-r--r--modules/calendar/e-task-shell-view-actions.h105
-rw-r--r--modules/calendar/e-task-shell-view-private.c744
-rw-r--r--modules/calendar/e-task-shell-view-private.h141
-rw-r--r--modules/calendar/e-task-shell-view.c255
-rw-r--r--modules/calendar/e-task-shell-view.h67
-rw-r--r--modules/calendar/evolution-module-calendar.c65
-rw-r--r--modules/mail/Makefile.am56
-rw-r--r--modules/mail/e-attachment-handler-mail.c524
-rw-r--r--modules/mail/e-attachment-handler-mail.h65
-rw-r--r--modules/mail/e-mail-shell-backend.c787
-rw-r--r--modules/mail/e-mail-shell-backend.h82
-rw-r--r--modules/mail/e-mail-shell-content.c1053
-rw-r--r--modules/mail/e-mail-shell-content.h94
-rw-r--r--modules/mail/e-mail-shell-migrate.c3104
-rw-r--r--modules/mail/e-mail-shell-migrate.h38
-rw-r--r--modules/mail/e-mail-shell-settings.c521
-rw-r--r--modules/mail/e-mail-shell-settings.h33
-rw-r--r--modules/mail/e-mail-shell-sidebar.c644
-rw-r--r--modules/mail/e-mail-shell-sidebar.h81
-rw-r--r--modules/mail/e-mail-shell-view-actions.c1806
-rw-r--r--modules/mail/e-mail-shell-view-actions.h257
-rw-r--r--modules/mail/e-mail-shell-view-private.c905
-rw-r--r--modules/mail/e-mail-shell-view-private.h173
-rw-r--r--modules/mail/e-mail-shell-view.c260
-rw-r--r--modules/mail/e-mail-shell-view.h72
-rw-r--r--modules/mail/em-account-editor.c3113
-rw-r--r--modules/mail/em-account-editor.h86
-rw-r--r--modules/mail/em-account-prefs.c323
-rw-r--r--modules/mail/em-account-prefs.h69
-rw-r--r--modules/mail/em-composer-prefs.c621
-rw-r--r--modules/mail/em-composer-prefs.h93
-rw-r--r--modules/mail/em-mailer-prefs.c1332
-rw-r--r--modules/mail/em-mailer-prefs.h141
-rw-r--r--modules/mail/em-network-prefs.c499
-rw-r--r--modules/mail/em-network-prefs.h102
-rw-r--r--modules/mail/evolution-module-mail.c59
104 files changed, 44146 insertions, 0 deletions
diff --git a/modules/Makefile.am b/modules/Makefile.am
new file mode 100644
index 0000000000..5030d2aeb6
--- /dev/null
+++ b/modules/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = addressbook calendar mail
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/addressbook/Makefile.am b/modules/addressbook/Makefile.am
new file mode 100644
index 0000000000..960df59e09
--- /dev/null
+++ b/modules/addressbook/Makefile.am
@@ -0,0 +1,129 @@
+if OS_WIN32
+WIN32_BOOTSTRAP_LIBS = $(top_builddir)/win32/libevolution-mail.la
+endif
+
+INCLUDES = \
+ -DG_LOG_DOMAIN=\"evolution-addressbook\" \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/widgets \
+ -I$(top_srcdir)/shell \
+ -I$(top_builddir)/shell \
+ -I$(top_srcdir)/widgets/menus \
+ -I$(top_srcdir)/widgets/misc \
+ -I$(top_srcdir)/addressbook/util \
+ -I$(top_srcdir)/addressbook/gui/contact-editor \
+ -I$(top_srcdir)/addressbook/gui/contact-list-editor \
+ -I$(top_srcdir)/addressbook/gui/widgets \
+ -I$(top_srcdir)/a11y/addressbook \
+ -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \
+ -DEVOLUTION_GALVIEWSDIR=\""$(viewsdir)"\" \
+ -DEVOLUTION_GLADEDIR=\""$(gladedir)"\" \
+ -DEVOLUTION_LOCALEDIR=\""$(localedir)"\" \
+ -DEVOLUTION_UIDIR=\""$(evolutionuidir)"\" \
+ -DPREFIX=\""$(prefix)"\" \
+ $(LDAP_CFLAGS) \
+ $(EVOLUTION_ADDRESSBOOK_CFLAGS)
+
+module_LTLIBRARIES = libevolution-module-addressbook.la
+
+libevolution_module_addressbook_la_SOURCES = \
+ evolution-module-addressbook.c \
+ addressbook-config.c \
+ addressbook-config.h \
+ autocompletion-config.c \
+ autocompletion-config.h \
+ eab-composer-util.c \
+ eab-composer-util.h \
+ e-book-shell-backend.c \
+ e-book-shell-backend.h \
+ e-book-shell-content.c \
+ e-book-shell-content.h \
+ e-book-shell-migrate.c \
+ e-book-shell-migrate.h \
+ e-book-shell-sidebar.c \
+ e-book-shell-sidebar.h \
+ e-book-shell-view.c \
+ e-book-shell-view.h \
+ e-book-shell-view-actions.c \
+ e-book-shell-view-actions.h \
+ e-book-shell-view-private.c \
+ e-book-shell-view-private.h
+
+if ENABLE_SMIME
+SMIME_LIB=$(top_builddir)/smime/gui/libevolution-smime.la
+endif
+
+libevolution_module_addressbook_la_LIBADD = \
+ $(SMIME_LIB) \
+ $(top_builddir)/e-util/libeutil.la \
+ $(top_builddir)/composer/libcomposer.la \
+ $(top_builddir)/addressbook/printing/libecontactprint.la \
+ $(top_builddir)/shell/libeshell.la \
+ $(top_builddir)/addressbook/gui/merging/libeabbookmerging.la \
+ $(top_builddir)/addressbook/gui/widgets/libeabwidgets.la \
+ $(top_builddir)/filter/libfilter.la \
+ $(top_builddir)/addressbook/util/libeabutil.la \
+ $(top_builddir)/addressbook/gui/contact-editor/libecontacteditor.la \
+ $(top_builddir)/addressbook/gui/contact-list-editor/libecontactlisteditor.la \
+ $(top_builddir)/widgets/table/libetable.la \
+ $(top_builddir)/widgets/text/libetext.la \
+ $(top_builddir)/widgets/misc/libemiscwidgets.la \
+ $(top_builddir)/widgets/menus/libmenus.la \
+ $(top_builddir)/addressbook/importers/libevolution-addressbook-importers.la \
+ $(EVOLUTION_ADDRESSBOOK_LIBS) $(LDAP_LIBS)
+
+
+libevolution_module_addressbook_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED)
+
+# GConf schemas
+
+schemadir = $(GCONF_SCHEMA_FILE_DIR)
+schema_in_files = apps_evolution_addressbook.schemas.in
+schema_DATA = $(schema_in_files:.schemas.in=.schemas)
+
+@INTLTOOL_SCHEMAS_RULE@
+
+if OS_WIN32
+install-data-local:
+ if test -z "$(DESTDIR)" ; then \
+ for p in $(schema_DATA) ; do \
+ (echo set GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE); \
+ echo $(GCONFTOOL) --makefile-install-rule $$p) >_temp.bat; \
+ cmd /c _temp.bat; \
+ done \
+ fi
+else
+install-data-local:
+ if test -z "$(DESTDIR)" ; then \
+ for p in $(schema_DATA) ; do \
+ GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $$p; \
+ done \
+ fi
+endif
+
+glade_DATA = \
+ ldap-config.glade
+
+DISTCLEANFILES = $(schema_DATA)
+
+EXTRA_DIST = \
+ $(glade_DATA) \
+ $(schema_in_files) \
+ openldap-extract.h
+
+dist-hook:
+ cd $(distdir); rm -f $(BUILT_SOURCES)
+
+if ENABLE_PURIFY
+PLINK = $(LIBTOOL) --mode=link $(PURIFY) $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+
+all-local: evolution-addressbook.pure
+
+evolution-addressbook.pure: evolution-addressbook
+ @rm -f evolution-addressbook.pure
+ $(PLINK) $(evolution_addressbook_LDFLAGS) $(evolution_addressbook_OBJECTS) $(evolution_addressbook_LDADD) $(LIBS)
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/addressbook/addressbook-config.c b/modules/addressbook/addressbook-config.c
new file mode 100644
index 0000000000..e663de03da
--- /dev/null
+++ b/modules/addressbook/addressbook-config.c
@@ -0,0 +1,1250 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Toshok <toshok@ximian.com>
+ * Chris Lahey <clahey@ximian.com>
+ * Michael Zucchi <notzed@ximian.com>
+ * And no doubt others ...
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/*#define STANDALONE*/
+
+#include <config.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#ifdef G_OS_WIN32
+/* Include <windows.h> early and work around DATADIR lossage */
+#define DATADIR crap_DATADIR
+#include <windows.h>
+#undef DATADIR
+#endif
+
+#include <glade/glade.h>
+
+#include "addressbook.h"
+#include "addressbook-config.h"
+
+#include "e-util/e-error.h"
+#include "e-util/e-util-private.h"
+
+#include "addressbook/gui/widgets/eab-config.h"
+
+#define d(x)
+
+#ifdef HAVE_LDAP
+#ifndef G_OS_WIN32
+#include <ldap.h>
+#ifndef SUNLDAP
+#include <ldap_schema.h>
+#endif
+#else
+#include <winldap.h>
+#include "openldap-extract.h"
+#endif
+#endif
+
+#define LDAP_PORT_STRING "389"
+#define LDAPS_PORT_STRING "636"
+
+#define GLADE_FILE_NAME "ldap-config.glade"
+
+GtkWidget* supported_bases_create_table (gchar *name, gchar *string1, gchar *string2,
+ gint num1, gint num2);
+
+/* default objectclasses */
+#define TOP "top"
+#define PERSON "person"
+#define ORGANIZATIONALPERSON "organizationalPerson"
+#define INETORGPERSON "inetOrgPerson"
+#define EVOLUTIONPERSON "evolutionPerson"
+#define CALENTRY "calEntry"
+
+
+typedef struct _AddressbookSourceDialog AddressbookSourceDialog;
+
+struct _AddressbookSourceDialog {
+ GladeXML *gui;
+
+ EABConfig *config; /* the config manager */
+
+ GtkWidget *window;
+
+ /* Source selection (druid only) */
+ ESourceList *source_list;
+ GSList *menu_source_groups;
+
+ /* ESource we're currently editing */
+ ESource *source;
+ /* The original source in edit mode. Also used to flag when we are in edit mode. */
+ ESource *original_source;
+
+ /* Source group we're creating/editing a source in */
+ ESourceGroup *source_group;
+
+ /* info page fields */
+ GtkWidget *host;
+ GtkWidget *auth_combobox;
+ AddressbookLDAPAuthType auth;
+ GtkWidget *auth_principal;
+
+ /* connecting page fields */
+ GtkWidget *port_comboentry;
+ GtkWidget *ssl_combobox;
+ AddressbookLDAPSSLType ssl;
+
+ /* searching page fields */
+ GtkWidget *rootdn;
+ AddressbookLDAPScopeType scope;
+ GtkWidget *scope_combobox;
+ GtkWidget *search_filter;
+ GtkWidget *timeout_scale;
+ GtkWidget *limit_spinbutton;
+ GtkWidget *canbrowsecheck;
+
+ /* display name page fields */
+ GtkWidget *display_name;
+};
+
+
+
+#ifdef HAVE_LDAP
+
+static const gchar *
+ldap_unparse_auth (AddressbookLDAPAuthType auth_type)
+{
+ switch (auth_type) {
+ case ADDRESSBOOK_LDAP_AUTH_NONE:
+ return "none";
+ case ADDRESSBOOK_LDAP_AUTH_SIMPLE_EMAIL:
+ return "ldap/simple-email";
+ case ADDRESSBOOK_LDAP_AUTH_SIMPLE_BINDDN:
+ return "ldap/simple-binddn";
+ default:
+ g_return_val_if_reached ("none");
+ }
+}
+
+static AddressbookLDAPAuthType
+ldap_parse_auth (const gchar *auth)
+{
+ if (!auth)
+ return ADDRESSBOOK_LDAP_AUTH_NONE;
+
+ if (!strcmp (auth, "ldap/simple-email") || !strcmp (auth, "simple"))
+ return ADDRESSBOOK_LDAP_AUTH_SIMPLE_EMAIL;
+ else if (!strcmp (auth, "ldap/simple-binddn"))
+ return ADDRESSBOOK_LDAP_AUTH_SIMPLE_BINDDN;
+ else
+ return ADDRESSBOOK_LDAP_AUTH_NONE;
+}
+
+static const gchar *
+ldap_unparse_scope (AddressbookLDAPScopeType scope_type)
+{
+ switch (scope_type) {
+ case ADDRESSBOOK_LDAP_SCOPE_BASE:
+ return "base";
+ case ADDRESSBOOK_LDAP_SCOPE_ONELEVEL:
+ return "one";
+ case ADDRESSBOOK_LDAP_SCOPE_SUBTREE:
+ return "sub";
+ default:
+ g_return_val_if_reached ("");
+ }
+}
+
+static const gchar *
+ldap_unparse_ssl (AddressbookLDAPSSLType ssl_type)
+{
+ switch (ssl_type) {
+ case ADDRESSBOOK_LDAP_SSL_NEVER:
+ return "never";
+ case ADDRESSBOOK_LDAP_SSL_WHENEVER_POSSIBLE:
+ return "whenever_possible";
+ case ADDRESSBOOK_LDAP_SSL_ALWAYS:
+ return "always";
+ default:
+ g_return_val_if_reached ("");
+ }
+}
+
+static AddressbookLDAPSSLType
+ldap_parse_ssl (const gchar *ssl)
+{
+ if (!ssl)
+ return ADDRESSBOOK_LDAP_SSL_WHENEVER_POSSIBLE; /* XXX good default? */
+
+ if (!strcmp (ssl, "always"))
+ return ADDRESSBOOK_LDAP_SSL_ALWAYS;
+ else if (!strcmp (ssl, "never"))
+ return ADDRESSBOOK_LDAP_SSL_NEVER;
+ else
+ return ADDRESSBOOK_LDAP_SSL_WHENEVER_POSSIBLE;
+}
+
+static const gchar *
+ldap_get_ssl_tooltip (AddressbookLDAPSSLType ssl_type)
+{
+ switch (ssl_type) {
+ case ADDRESSBOOK_LDAP_SSL_ALWAYS:
+ return _("Selecting this option means that Evolution will only connect to your LDAP server if your LDAP server supports SSL.");
+ case ADDRESSBOOK_LDAP_SSL_WHENEVER_POSSIBLE:
+ return _("Selecting this option means that Evolution will only connect to your LDAP server if your LDAP server supports TLS.");
+ case ADDRESSBOOK_LDAP_SSL_NEVER:
+ return _("Selecting this option means that your server does not support either SSL or TLS. This means that your connection will be insecure, and that you will be vulnerable to security exploits.");
+ }
+
+ return NULL;
+}
+
+static gboolean
+source_to_uri_parts (ESource *source, gchar **host, gchar **rootdn, AddressbookLDAPScopeType *scope, gchar **search_filter, gint *port)
+{
+ gchar *uri;
+ LDAPURLDesc *lud;
+ gint ldap_error;
+
+ g_return_val_if_fail (source, FALSE);
+
+ uri = e_source_get_uri (source);
+ ldap_error = ldap_url_parse ((gchar *) uri, &lud);
+ g_free (uri);
+
+ if (ldap_error != LDAP_SUCCESS)
+ return FALSE;
+
+ if (host)
+ *host = g_strdup (lud->lud_host ? lud->lud_host : "");
+ if (rootdn)
+ *rootdn = g_strdup (lud->lud_dn ? lud->lud_dn : "");
+ if (port)
+ *port = lud->lud_port ? lud->lud_port : LDAP_PORT;
+ if (scope)
+ *scope = lud->lud_scope == LDAP_SCOPE_BASE ? ADDRESSBOOK_LDAP_SCOPE_BASE :
+ lud->lud_scope == LDAP_SCOPE_ONELEVEL ? ADDRESSBOOK_LDAP_SCOPE_ONELEVEL :
+ lud->lud_scope == LDAP_SCOPE_SUBTREE ? ADDRESSBOOK_LDAP_SCOPE_SUBTREE :
+ ADDRESSBOOK_LDAP_SCOPE_ONELEVEL;
+ if (search_filter && lud->lud_filter)
+ *search_filter = g_strdup (lud->lud_filter);
+
+ ldap_free_urldesc (lud);
+ return TRUE;
+}
+
+static gboolean
+source_group_is_remote (ESourceGroup *group)
+{
+ return strncmp ("ldap:", e_source_group_peek_base_uri (group), 5) == 0;
+}
+
+/* ldap api foo */
+static LDAP *
+addressbook_ldap_init (GtkWidget *window, ESource *source)
+{
+ LDAP *ldap;
+ gchar *host;
+ gint port;
+ gint ldap_error;
+ gint protocol_version = LDAP_VERSION3;
+
+ if (!source_to_uri_parts (source, &host, NULL, NULL, NULL, &port))
+ return NULL;
+
+ if (!(ldap = ldap_init (host, port))) {
+ e_error_run ((GtkWindow *) window, "addressbook:ldap-init", NULL);
+ goto done;
+ }
+
+ ldap_error = ldap_set_option (ldap, LDAP_OPT_PROTOCOL_VERSION, &protocol_version);
+ if (LDAP_SUCCESS != ldap_error)
+ g_warning ("failed to set protocol version to LDAPv3");
+
+ /* XXX do TLS if it's configured in */
+
+ done:
+ g_free (host);
+ return ldap;
+}
+
+static gint
+addressbook_ldap_auth (GtkWidget *window, LDAP *ldap)
+{
+ gint ldap_error;
+
+ /* XXX use auth info from source */
+ ldap_error = ldap_simple_bind_s (ldap, NULL, NULL);
+ if (LDAP_SUCCESS != ldap_error)
+ e_error_run ((GtkWindow *) window, "addressbook:ldap-auth", NULL);
+
+ return ldap_error;
+}
+
+static gint
+addressbook_root_dse_query (AddressbookSourceDialog *dialog, LDAP *ldap,
+ const gchar **attrs, LDAPMessage **resp)
+{
+ gint ldap_error;
+ struct timeval timeout;
+
+ timeout.tv_sec = (gint) gtk_adjustment_get_value (GTK_RANGE(dialog->timeout_scale)->adjustment);
+ timeout.tv_usec = 0;
+
+ ldap_error = ldap_search_ext_s (ldap,
+ LDAP_ROOT_DSE, LDAP_SCOPE_BASE,
+ "(objectclass=*)",
+ (gchar **) attrs, 0, NULL, NULL, &timeout, LDAP_NO_LIMIT, resp);
+ if (LDAP_SUCCESS != ldap_error)
+ e_error_run (GTK_WINDOW (dialog->window), "addressbook:ldap-search-base", NULL);
+
+ return ldap_error;
+}
+
+/* searching page */
+GtkWidget*
+supported_bases_create_table (gchar *name, gchar *string1, gchar *string2, gint num1, gint num2)
+{
+ GtkWidget *table, *scrolled;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkListStore *model;
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
+
+ model = gtk_list_store_new (1, G_TYPE_STRING);
+ table = gtk_tree_view_new_with_model ((GtkTreeModel *) model);
+ g_object_unref (model);
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes ((GtkTreeView *) table, -1, _("Base"), renderer, "text", 0, NULL);
+ gtk_tree_view_set_headers_visible ((GtkTreeView *) table, FALSE);
+ selection = gtk_tree_view_get_selection ((GtkTreeView *) table);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+ gtk_container_add (GTK_CONTAINER (scrolled), table);
+ g_object_set_data((GObject *)scrolled, "table", table);
+
+ return scrolled;
+}
+
+static gboolean
+do_ldap_root_dse_query (AddressbookSourceDialog *sdialog, GtkListStore *model, ESource *source)
+{
+ LDAP *ldap;
+ const gchar *attrs[2];
+ gint ldap_error;
+ gchar **values;
+ LDAPMessage *resp;
+ gint i;
+
+ ldap = addressbook_ldap_init (sdialog->window, source);
+ if (!ldap)
+ return FALSE;
+
+ if (LDAP_SUCCESS != addressbook_ldap_auth (sdialog->window, ldap))
+ goto fail;
+
+ attrs[0] = "namingContexts";
+ attrs[1] = NULL;
+
+ ldap_error = addressbook_root_dse_query (sdialog, ldap, attrs, &resp);
+
+ if (ldap_error != LDAP_SUCCESS)
+ goto fail;
+
+ values = ldap_get_values (ldap, resp, "namingContexts");
+ if (!values || values[0] == NULL || strlen (values[0]) == 0) {
+ e_error_run (GTK_WINDOW (sdialog->window), "addressbook:ldap-search-base", NULL);
+ goto fail;
+ }
+
+ for (i = 0; values[i]; i++) {
+ GtkTreeIter iter;
+
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter, 0, values[i], -1);
+ }
+
+ ldap_value_free (values);
+ ldap_unbind_s (ldap);
+ return TRUE;
+
+ fail:
+ ldap_unbind_s (ldap);
+ return FALSE;
+}
+
+static void
+search_base_selection_model_changed (GtkTreeSelection *selection, GtkWidget *dialog)
+{
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ gtk_tree_selection_get_selected(selection, NULL, NULL));
+}
+
+static void
+query_for_supported_bases (GtkWidget *button, AddressbookSourceDialog *sdialog)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeView *table;
+ GtkWidget *dialog;
+ GtkWidget *supported_bases_table;
+ GladeXML *gui;
+ GtkTreeIter iter;
+ gchar *gladefile;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ GLADE_FILE_NAME,
+ NULL);
+ gui = glade_xml_new (gladefile, "supported-bases-dialog", NULL);
+ g_free (gladefile);
+
+ dialog = glade_xml_get_widget (gui, "supported-bases-dialog");
+
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (sdialog->window));
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+ gtk_widget_ensure_style (dialog);
+ gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 0);
+ gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 12);
+
+ supported_bases_table = glade_xml_get_widget (gui, "supported-bases-table");
+ gtk_widget_show_all (supported_bases_table);
+
+ table = g_object_get_data (G_OBJECT (supported_bases_table), "table");
+ model = gtk_tree_view_get_model (table);
+ selection = gtk_tree_view_get_selection (table);
+ g_signal_connect (selection, "changed", G_CALLBACK (search_base_selection_model_changed), dialog);
+ search_base_selection_model_changed (selection, dialog);
+
+ if (do_ldap_root_dse_query (sdialog, GTK_LIST_STORE (model), sdialog->source)) {
+ gtk_widget_show (dialog);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK
+ && gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gchar *dn;
+
+ gtk_tree_model_get (model, &iter, 0, &dn, -1);
+ gtk_entry_set_text((GtkEntry *)sdialog->rootdn, dn);
+ g_free(dn);
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+#endif /* HAVE_LDAP */
+
+GtkWidget*
+addressbook_config_create_new_source (GtkWidget *parent)
+{
+ return addressbook_config_edit_source(parent, NULL);
+}
+
+/* ********************************************************************** */
+
+static void
+eabc_type_changed(GtkComboBox *dropdown, AddressbookSourceDialog *sdialog)
+{
+ gint id = gtk_combo_box_get_active(dropdown);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_combo_box_get_model(dropdown);
+ if (id == -1 || !gtk_tree_model_iter_nth_child(model, &iter, NULL, id))
+ return;
+
+ /* TODO: when we change the group type, we lose all of the pre-filled dialog info */
+
+ gtk_tree_model_get(model, &iter, 1, &sdialog->source_group, -1);
+ /* HACK: doesn't work if you don't do this */
+ e_source_set_absolute_uri(sdialog->source, NULL);
+ e_source_set_group(sdialog->source, sdialog->source_group);
+
+ /* BIG HACK: We load the defaults for each type here.
+ I guess plugins will have to use the do it in their factory callbacks */
+ if (!strncmp(e_source_group_peek_base_uri(sdialog->source_group), "groupwise:", 10)) {
+ GSList *l;
+ ESource *source;
+ gchar *tmp;
+
+ l = e_source_group_peek_sources(sdialog->source_group);
+ if (l && l->data ) {
+ source = l->data;
+ e_source_set_property(sdialog->source, "auth", e_source_get_property(source, "auth"));
+ e_source_set_property(sdialog->source, "user", e_source_get_property(source, "user"));
+ e_source_set_property(sdialog->source, "user_ssl", e_source_get_property(source, "use_ssl"));
+ }
+
+ e_source_set_property(sdialog->source, "auth-domain", "Groupwise");
+ tmp = g_strconcat (";", e_source_peek_name(sdialog->source), NULL);
+ e_source_set_relative_uri (sdialog->source, tmp);
+ g_free (tmp);
+#ifdef HAVE_LDAP
+ } else if (!strncmp(e_source_group_peek_base_uri(sdialog->source_group), "ldap:", 5)) {
+ gchar *tmp;
+
+ tmp = g_strdup_printf ("%s:%s/%s?" /* trigraph prevention */ "?%s",
+ "", LDAP_PORT_STRING,
+ "",
+ "one");
+ e_source_set_relative_uri (sdialog->source, tmp);
+ g_free (tmp);
+ e_source_set_property(sdialog->source, "timeout", "3");
+ e_source_set_property(sdialog->source, "limit", "100");
+#endif
+ } else {
+ e_source_set_relative_uri (sdialog->source, e_source_peek_uid (sdialog->source));
+ }
+
+ e_config_target_changed((EConfig *)sdialog->config, E_CONFIG_TARGET_CHANGED_REBUILD);
+}
+
+static GtkWidget *
+eabc_general_type(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+ GtkComboBox *dropdown;
+ GtkCellRenderer *cell;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GSList *l;
+ GtkWidget *w, *label;
+ gint i, row = 0;
+
+ if (old)
+ return old;
+
+ w = gtk_hbox_new(FALSE, 6);
+ label = gtk_label_new_with_mnemonic(_("_Type:"));
+ gtk_box_pack_start((GtkBox *)w, label, FALSE, FALSE, 0);
+
+ dropdown = (GtkComboBox *)gtk_combo_box_new();
+ cell = gtk_cell_renderer_text_new();
+ store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+ i = 0;
+ for (l=sdialog->menu_source_groups;l;l=g_slist_next(l)) {
+ ESourceGroup *group = l->data;
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, e_source_group_peek_name(group), 1, group, -1);
+ if (e_source_peek_group(sdialog->source) == group)
+ row = i;
+ i++;
+ }
+
+ gtk_cell_layout_pack_start((GtkCellLayout *)dropdown, cell, TRUE);
+ gtk_cell_layout_set_attributes((GtkCellLayout *)dropdown, cell, "text", 0, NULL);
+ gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store);
+ gtk_combo_box_set_active(dropdown, -1);
+ gtk_combo_box_set_active(dropdown, row);
+ g_signal_connect(dropdown, "changed", G_CALLBACK(eabc_type_changed), sdialog);
+ gtk_widget_show((GtkWidget *)dropdown);
+ gtk_box_pack_start((GtkBox *)w, (GtkWidget *)dropdown, TRUE, TRUE, 0);
+ gtk_label_set_mnemonic_widget((GtkLabel *)label, (GtkWidget *)dropdown);
+
+ gtk_box_pack_start((GtkBox *)parent, (GtkWidget *)w, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(w);
+
+ return (GtkWidget *)w;
+}
+
+static void
+name_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ e_source_set_name (sdialog->source, gtk_entry_get_text (GTK_ENTRY (sdialog->display_name)));
+}
+
+static void
+offline_status_changed_cb (GtkWidget *widget, AddressbookSourceDialog *sdialog)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ e_source_set_property (sdialog->source, "offline_sync", "1");
+ else
+ e_source_set_property (sdialog->source, "offline_sync", "0");
+
+}
+
+static GtkWidget *
+eabc_general_name(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+ const gchar *uri;
+ GtkWidget *w;
+ GladeXML *gui;
+ gchar *gladefile;
+
+ if (old)
+ return old;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ GLADE_FILE_NAME,
+ NULL);
+ gui = glade_xml_new (gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ w = glade_xml_get_widget(gui, item->label);
+ gtk_box_pack_start((GtkBox *)parent, w, FALSE, FALSE, 0);
+
+ sdialog->display_name = glade_xml_get_widget (gui, "account-editor-display-name-entry");
+ g_signal_connect(sdialog->display_name, "changed", G_CALLBACK(name_changed_cb), sdialog);
+ gtk_entry_set_text((GtkEntry *)sdialog->display_name, e_source_peek_name(sdialog->source));
+
+ /* Hardcoded: groupwise can't edit the name (or anything else) */
+ if (sdialog->original_source) {
+ uri = e_source_group_peek_base_uri (sdialog->source_group);
+ if (uri && strncmp(uri, "groupwise:", 10) == 0) {
+ gtk_widget_set_sensitive (GTK_WIDGET(sdialog->display_name), FALSE);
+ }
+ }
+
+ g_object_unref(gui);
+
+ return w;
+}
+
+
+static GtkWidget *
+eabc_general_offline(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+ GtkWidget *offline_setting;
+ const gchar *offline_sync;
+ gboolean is_local_book;
+
+ is_local_book = g_str_has_prefix (e_source_group_peek_base_uri (sdialog->source_group), "file:");
+ offline_sync = e_source_get_property (sdialog->source, "offline_sync");
+ if (old)
+ return old;
+ else {
+ offline_setting = gtk_check_button_new_with_mnemonic (_("Copy _book content locally for offline operation"));
+ gtk_widget_show (offline_setting);
+ gtk_container_add (GTK_CONTAINER (parent), offline_setting);
+ g_signal_connect (offline_setting, "toggled", G_CALLBACK (offline_status_changed_cb), sdialog);
+
+ }
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (offline_setting), (offline_sync && g_str_equal (offline_sync, "1")) ? TRUE : FALSE);
+ if (is_local_book)
+ gtk_widget_hide (offline_setting);
+ return offline_setting;
+
+}
+
+#ifdef HAVE_LDAP
+static gchar *
+form_ldap_search_filter (GtkWidget *w)
+{
+ gchar *filter;
+ const gchar *search_filter = gtk_entry_get_text ((GtkEntry *) w);
+
+ /* this function can be used to format the search filter entered */
+ if ((strlen (search_filter) !=0) && *search_filter != '(' && *(search_filter + (strlen (search_filter-1))) != ')')
+ filter = g_strdup_printf ("(%s)", search_filter);
+ else
+ filter = g_strdup_printf ("%s", search_filter);
+
+ return filter;
+}
+
+static void
+url_changed(AddressbookSourceDialog *sdialog)
+{
+ gchar *str, *search_filter;
+
+ search_filter = form_ldap_search_filter (sdialog->search_filter);
+ str = g_strdup_printf ("%s:%s/%s?" /* trigraph prevention */ "?%s?%s",
+ gtk_entry_get_text (GTK_ENTRY (sdialog->host)),
+ gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (sdialog->port_comboentry)))),
+ gtk_entry_get_text (GTK_ENTRY (sdialog->rootdn)),
+ ldap_unparse_scope (sdialog->scope),
+ search_filter);
+ e_source_set_relative_uri (sdialog->source, str);
+ g_free (search_filter);
+ g_free (str);
+}
+
+static void
+host_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ url_changed(sdialog);
+}
+
+static void
+port_entry_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ const gchar *port = gtk_entry_get_text((GtkEntry *)w);
+
+ if (!strcmp (port, LDAPS_PORT_STRING)) {
+ sdialog->ssl = ADDRESSBOOK_LDAP_SSL_ALWAYS;
+ gtk_combo_box_set_active (GTK_COMBO_BOX (sdialog->ssl_combobox), sdialog->ssl);
+ gtk_widget_set_sensitive (sdialog->ssl_combobox, FALSE);
+ } else {
+ gtk_widget_set_sensitive (sdialog->ssl_combobox, TRUE);
+ }
+
+ url_changed(sdialog);
+}
+
+static void
+ssl_combobox_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ sdialog->ssl = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
+ e_source_set_property (sdialog->source, "ssl", ldap_unparse_ssl (sdialog->ssl));
+
+ gtk_widget_set_tooltip_text (sdialog->ssl_combobox, ldap_get_ssl_tooltip (sdialog->ssl));
+}
+
+
+static GtkWidget *
+eabc_general_host(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+ const gchar *tmp;
+ GtkWidget *w;
+ gchar *uri, port[16];
+ LDAPURLDesc *lud;
+ GladeXML *gui;
+ gchar *gladefile;
+
+ if (!source_group_is_remote(sdialog->source_group))
+ return NULL;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ GLADE_FILE_NAME,
+ NULL);
+ gui = glade_xml_new (gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ w = glade_xml_get_widget(gui, item->label);
+ gtk_box_pack_start((GtkBox *)parent, w, FALSE, FALSE, 0);
+
+ uri = e_source_get_uri(sdialog->source);
+ if (ldap_url_parse(uri, &lud) != LDAP_SUCCESS)
+ lud = NULL;
+ g_free(uri);
+
+ sdialog->host = glade_xml_get_widget (gui, "server-name-entry");
+ gtk_entry_set_text((GtkEntry *)sdialog->host, lud && lud->lud_host ? lud->lud_host : "");
+ g_signal_connect (sdialog->host, "changed", G_CALLBACK (host_changed_cb), sdialog);
+
+ sdialog->port_comboentry = glade_xml_get_widget (gui, "port-comboentry");
+ gtk_widget_set_has_tooltip (sdialog->port_comboentry, TRUE);
+ gtk_widget_set_tooltip_text (sdialog->port_comboentry, _("This is the port on the LDAP server that Evolution will try to connect to. A list of standard ports has been provided. Ask your system administrator what port you should specify."));
+ sprintf(port, "%u", lud && lud->lud_port? lud->lud_port : LDAP_PORT);
+ gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (sdialog->port_comboentry))), port);
+ g_signal_connect (gtk_bin_get_child (GTK_BIN (sdialog->port_comboentry)), "changed", G_CALLBACK (port_entry_changed_cb), sdialog);
+
+ if (lud)
+ ldap_free_urldesc (lud);
+
+ sdialog->ssl_combobox = glade_xml_get_widget (gui, "ssl-combobox");
+ gtk_widget_set_has_tooltip (sdialog->ssl_combobox, TRUE);
+ tmp = e_source_get_property (sdialog->source, "ssl");
+ sdialog->ssl = tmp ? ldap_parse_ssl (tmp) : ADDRESSBOOK_LDAP_SSL_WHENEVER_POSSIBLE;
+ gtk_combo_box_set_active (GTK_COMBO_BOX (sdialog->ssl_combobox), sdialog->ssl);
+ gtk_widget_set_tooltip_text (sdialog->ssl_combobox, ldap_get_ssl_tooltip (sdialog->ssl));
+ gtk_widget_set_sensitive (sdialog->ssl_combobox, strcmp (port, LDAPS_PORT_STRING) != 0);
+ g_signal_connect (sdialog->ssl_combobox, "changed", G_CALLBACK (ssl_combobox_changed_cb), sdialog);
+
+ g_object_unref(gui);
+
+ return w;
+}
+
+static void
+auth_entry_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ const gchar *principal = gtk_entry_get_text((GtkEntry *)w);
+
+ /* seems messy ... but the api is */
+ switch (sdialog->auth) {
+ case ADDRESSBOOK_LDAP_AUTH_SIMPLE_BINDDN:
+ e_source_set_property(sdialog->source, "email_addr", NULL);
+ e_source_set_property(sdialog->source, "binddn", principal);
+ break;
+ case ADDRESSBOOK_LDAP_AUTH_SIMPLE_EMAIL:
+ e_source_set_property(sdialog->source, "binddn", NULL);
+ e_source_set_property(sdialog->source, "email_addr", principal);
+ break;
+ case ADDRESSBOOK_LDAP_AUTH_NONE:
+ default:
+ e_source_set_property(sdialog->source, "email_addr", NULL);
+ e_source_set_property(sdialog->source, "binddn", NULL);
+ break;
+ }
+}
+
+static void
+auth_combobox_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ sdialog->auth = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
+ e_source_set_property (sdialog->source, "auth", ldap_unparse_auth (sdialog->auth));
+
+ /* make sure the right property is set for the auth - ugh, funny api */
+ auth_entry_changed_cb(sdialog->auth_principal, sdialog);
+}
+
+static GtkWidget *
+eabc_general_auth(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+ GtkWidget *w;
+ const gchar *tmp;
+ GladeXML *gui;
+ gchar *gladefile;
+
+ if (!source_group_is_remote(sdialog->source_group))
+ return NULL;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ GLADE_FILE_NAME,
+ NULL);
+ gui = glade_xml_new (gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ w = glade_xml_get_widget(gui, item->label);
+ gtk_box_pack_start((GtkBox *)parent, w, FALSE, FALSE, 0);
+
+ sdialog->auth_combobox = glade_xml_get_widget (gui, "auth-combobox");
+ gtk_widget_set_has_tooltip (sdialog->auth_combobox, TRUE);
+ gtk_widget_set_tooltip_text (sdialog->auth_combobox, _("This is the method Evolution will use to authenticate you. Note that setting this to \"Email Address\" requires anonymous access to your LDAP server."));
+ tmp = e_source_get_property(sdialog->source, "auth");
+ sdialog->auth = tmp ? ldap_parse_auth(tmp) : ADDRESSBOOK_LDAP_AUTH_NONE;
+ gtk_combo_box_set_active (GTK_COMBO_BOX (sdialog->auth_combobox), sdialog->auth);
+ g_signal_connect (sdialog->auth_combobox, "changed", G_CALLBACK(auth_combobox_changed_cb), sdialog);
+
+ sdialog->auth_principal = glade_xml_get_widget (gui, "auth-entry");
+ switch (sdialog->auth) {
+ case ADDRESSBOOK_LDAP_AUTH_SIMPLE_EMAIL:
+ tmp = e_source_get_property(sdialog->source, "email_addr");
+ break;
+ case ADDRESSBOOK_LDAP_AUTH_SIMPLE_BINDDN:
+ tmp = e_source_get_property(sdialog->source, "binddn");
+ break;
+ case ADDRESSBOOK_LDAP_AUTH_NONE:
+ default:
+ tmp = "";
+ break;
+ }
+ gtk_entry_set_text((GtkEntry *)sdialog->auth_principal, tmp?tmp:"");
+ g_signal_connect (sdialog->auth_principal, "changed", G_CALLBACK (auth_entry_changed_cb), sdialog);
+
+ g_object_unref(gui);
+
+ return w;
+}
+
+static void
+rootdn_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ url_changed(sdialog);
+}
+
+static void
+search_filter_changed_cb (GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ url_changed (sdialog);
+}
+
+static void
+scope_combobox_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ sdialog->scope = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
+ url_changed(sdialog);
+}
+
+static GtkWidget *
+eabc_details_search(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+ GtkWidget *w;
+ LDAPURLDesc *lud;
+ gchar *uri;
+ GladeXML *gui;
+ gchar *gladefile;
+
+ if (!source_group_is_remote(sdialog->source_group))
+ return NULL;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ GLADE_FILE_NAME,
+ NULL);
+ gui = glade_xml_new (gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ w = glade_xml_get_widget(gui, item->label);
+ gtk_box_pack_start((GtkBox *)parent, w, FALSE, FALSE, 0);
+
+ uri = e_source_get_uri(sdialog->source);
+ if (ldap_url_parse(uri, &lud) != LDAP_SUCCESS)
+ lud = NULL;
+ g_free(uri);
+
+ sdialog->rootdn = glade_xml_get_widget (gui, "rootdn-entry");
+ gtk_entry_set_text((GtkEntry *)sdialog->rootdn, lud && lud->lud_dn ? lud->lud_dn : "");
+ g_signal_connect (sdialog->rootdn, "changed", G_CALLBACK (rootdn_changed_cb), sdialog);
+
+ sdialog->scope_combobox = glade_xml_get_widget (gui, "scope-combobox");
+ gtk_widget_set_has_tooltip (sdialog->scope_combobox, TRUE);
+ gtk_widget_set_tooltip_text (sdialog->scope_combobox, _("The search scope defines how deep you would like the search to extend down the directory tree. A search scope of \"sub\" will include all entries below your search base. A search scope of \"one\" will only include the entries one level beneath your base."));
+ if (lud) {
+ switch (lud->lud_scope) {
+ case LDAP_SCOPE_BASE:
+ sdialog->scope = ADDRESSBOOK_LDAP_SCOPE_BASE;
+ break;
+ default:
+ case LDAP_SCOPE_ONELEVEL:
+ sdialog->scope = ADDRESSBOOK_LDAP_SCOPE_ONELEVEL;
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ sdialog->scope = ADDRESSBOOK_LDAP_SCOPE_SUBTREE;
+ break;
+ }
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (sdialog->scope_combobox), sdialog->scope);
+ g_signal_connect (sdialog->scope_combobox, "changed", G_CALLBACK(scope_combobox_changed_cb), sdialog);
+
+ sdialog->search_filter = glade_xml_get_widget (gui, "search-filter-entry");
+ gtk_entry_set_text((GtkEntry *)sdialog->search_filter, lud && lud->lud_filter ? lud->lud_filter : "");
+ g_signal_connect (sdialog->search_filter, "changed", G_CALLBACK (search_filter_changed_cb), sdialog);
+
+ g_signal_connect (glade_xml_get_widget(gui, "rootdn-button"), "clicked",
+ G_CALLBACK(query_for_supported_bases), sdialog);
+
+ if (lud)
+ ldap_free_urldesc (lud);
+
+ g_object_unref(gui);
+
+ return w;
+}
+
+static void
+timeout_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ gchar *timeout;
+
+ timeout = g_strdup_printf("%f", gtk_adjustment_get_value(((GtkRange *)sdialog->timeout_scale)->adjustment));
+ e_source_set_property(sdialog->source, "timeout", timeout);
+ g_free(timeout);
+}
+
+static void
+limit_changed_cb(GtkWidget *w, AddressbookSourceDialog *sdialog)
+{
+ gchar limit[16];
+
+ sprintf(limit, "%d", gtk_spin_button_get_value_as_int((GtkSpinButton *)sdialog->limit_spinbutton));
+ e_source_set_property(sdialog->source, "limit", limit);
+}
+
+static void
+canbrowse_toggled_cb (GtkWidget *toggle_button, ESource *source)
+{
+ if (!source || !toggle_button)
+ return;
+
+ e_source_set_property (source, "can-browse", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle_button)) ? "1" : NULL);
+}
+
+static GtkWidget *
+eabc_details_limit(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+ GtkWidget *w;
+ const gchar *tmp;
+ GladeXML *gui;
+ gchar *gladefile;
+
+ if (!source_group_is_remote(sdialog->source_group))
+ return NULL;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ GLADE_FILE_NAME,
+ NULL);
+ gui = glade_xml_new (gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ w = glade_xml_get_widget(gui, item->label);
+ gtk_box_pack_start((GtkBox *)parent, w, FALSE, FALSE, 0);
+
+ sdialog->timeout_scale = glade_xml_get_widget (gui, "timeout-scale");
+ tmp = e_source_get_property(sdialog->source, "timeout");
+ gtk_adjustment_set_value(((GtkRange *)sdialog->timeout_scale)->adjustment, tmp?g_strtod(tmp, NULL):3.0);
+ g_signal_connect (GTK_RANGE(sdialog->timeout_scale)->adjustment, "value_changed", G_CALLBACK (timeout_changed_cb), sdialog);
+
+ sdialog->limit_spinbutton = glade_xml_get_widget (gui, "download-limit-spinbutton");
+ tmp = e_source_get_property(sdialog->source, "limit");
+ gtk_spin_button_set_value((GtkSpinButton *)sdialog->limit_spinbutton, tmp?g_strtod(tmp, NULL):100.0);
+ g_signal_connect (sdialog->limit_spinbutton, "value_changed", G_CALLBACK (limit_changed_cb), sdialog);
+
+ sdialog->canbrowsecheck = glade_xml_get_widget (gui, "canbrowsecheck");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sdialog->canbrowsecheck), e_source_get_property (sdialog->source, "can-browse") && strcmp (e_source_get_property (sdialog->source, "can-browse"), "1") == 0);
+ g_signal_connect (sdialog->canbrowsecheck, "toggled", G_CALLBACK (canbrowse_toggled_cb), sdialog->source);
+
+ g_object_unref(gui);
+
+ return w;
+}
+#endif
+
+static EConfigItem eabc_items[] = {
+ { E_CONFIG_BOOK, (gchar *) (gchar *) "", },
+ { E_CONFIG_PAGE, (gchar *) "00.general", (gchar *) N_("General") },
+ { E_CONFIG_SECTION, (gchar *) "00.general/10.display", (gchar *) N_("Address Book") },
+ { E_CONFIG_ITEM, (gchar *) "00.general/10.display/10.name", (gchar *) "hbox122", eabc_general_name },
+ { E_CONFIG_ITEM, (gchar *) "00.general/10.display/20.offline", NULL, eabc_general_offline },
+#ifdef HAVE_LDAP
+ { E_CONFIG_SECTION, (gchar *) "00.general/20.server", (gchar *) N_("Server Information") },
+ { E_CONFIG_ITEM, (gchar *) "00.general/20.server/00.host", (gchar *) "table31", eabc_general_host },
+ { E_CONFIG_SECTION, (gchar *) "00.general/30.auth", (gchar *) N_("Authentication") },
+ { E_CONFIG_ITEM, (gchar *) "00.general/30.auth/00.auth", (gchar *) "table32", eabc_general_auth },
+
+ { E_CONFIG_PAGE, (gchar *) "10.details", (gchar *) N_("Details") },
+ { E_CONFIG_SECTION, (gchar *) "10.details/00.search", (gchar *) N_("Searching") },
+ { E_CONFIG_ITEM, (gchar *) "10.details/00.search/00.search", (gchar *) "table33", eabc_details_search },
+ { E_CONFIG_SECTION, (gchar *) "10.details/10.limit", (gchar *) N_("Downloading") },
+ { E_CONFIG_ITEM, (gchar *) "10.details/10.limit/00.limit", (gchar *) "table34", eabc_details_limit },
+#endif
+ { 0 },
+};
+
+/* items needed for the 'new addressbook' window */
+static EConfigItem eabc_new_items[] = {
+ { E_CONFIG_ITEM, (gchar *) "00.general/10.display/00.type", NULL, eabc_general_type },
+ { 0 },
+};
+
+static void
+eabc_commit(EConfig *ec, GSList *items, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+ xmlNodePtr xml;
+#if d(!)0
+ gchar *txt;
+#endif
+ if (sdialog->original_source) {
+ d(printf("committing addressbook changes\n"));
+
+ /* these api's kinda suck */
+ xml = xmlNewNode(NULL, (const guchar *)"dummy");
+ e_source_dump_to_xml_node(sdialog->source, xml);
+ e_source_update_from_xml_node(sdialog->original_source, xml->children, NULL);
+ xmlFreeNode(xml);
+#if d(!)0
+ txt = e_source_to_standalone_xml(sdialog->original_source);
+ printf("source is now:\n%s\n", txt);
+ g_free(txt);
+#endif
+ } else {
+ d(printf("committing new source\n"));
+ e_source_group_add_source(sdialog->source_group, sdialog->source, -1);
+ e_source_list_sync(sdialog->source_list, NULL);
+ }
+
+#if d(!)0
+ txt = e_source_to_standalone_xml(sdialog->source);
+ printf("running source is now:\n%s\n", txt);
+ g_free(txt);
+#endif
+}
+
+static void
+eabc_free(EConfig *ec, GSList *items, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+
+ g_slist_free(items);
+
+ g_object_unref(sdialog->source);
+ if (sdialog->original_source)
+ g_object_unref(sdialog->original_source);
+ if (sdialog->source_list)
+ g_object_unref(sdialog->source_list);
+ g_slist_free(sdialog->menu_source_groups);
+
+ g_object_unref(sdialog->gui);
+
+ g_free(sdialog);
+}
+
+static gboolean
+eabc_check_complete(EConfig *ec, const gchar *pageid, gpointer data)
+{
+ AddressbookSourceDialog *sdialog = data;
+ gint valid = TRUE;
+ const gchar *tmp;
+ ESource *source;
+
+ d(printf("check complete, pageid = '%s'\n", pageid?pageid:"<all>"));
+ /* have name, and unique */
+ tmp = e_source_peek_name(sdialog->source);
+ valid = tmp && tmp[0] != 0
+ && ((source = e_source_group_peek_source_by_name(sdialog->source_group, tmp)) == NULL
+ || source == sdialog->original_source);
+
+#ifdef HAVE_LDAP
+ if (valid && source_group_is_remote(sdialog->source_group)) {
+ gchar *uri = e_source_get_uri(sdialog->source);
+ LDAPURLDesc *lud;
+
+ /* check host and port set */
+ if (ldap_url_parse(uri, &lud) == LDAP_SUCCESS) {
+ valid = lud->lud_host != NULL
+ && lud->lud_host[0] != 0
+ && lud->lud_port != 0;
+ ldap_free_urldesc (lud);
+ } else
+ valid = FALSE;
+ g_free(uri);
+
+ /* check auth name provided if auth set */
+ if (valid && (tmp = e_source_get_property(sdialog->source, "auth"))) {
+ switch (ldap_parse_auth(tmp)) {
+ case ADDRESSBOOK_LDAP_AUTH_SIMPLE_EMAIL:
+ tmp = e_source_get_property(sdialog->source, "email_addr");
+ break;
+ case ADDRESSBOOK_LDAP_AUTH_SIMPLE_BINDDN:
+ tmp = e_source_get_property(sdialog->source, "binddn");
+ break;
+ default:
+ tmp = "dummy";
+ break;
+ }
+ valid = tmp && tmp[0];
+ }
+
+ /* check timeout isn't too short (why don't we just force it?) */
+ if (valid) {
+ tmp = e_source_get_property(sdialog->source, "timeout");
+ valid = tmp && g_strtod(tmp, NULL) > 0.0;
+ }
+ }
+#endif
+ return valid;
+}
+
+/* debug only: */
+#if d(!)0
+static void
+source_changed(ESource *source, AddressbookSourceDialog *sdialog)
+{
+ gchar *xml;
+
+ xml = e_source_to_standalone_xml(source);
+ printf("source changed:\n%s\n", xml);
+ g_free(xml);
+}
+#endif
+
+GtkWidget*
+addressbook_config_edit_source (GtkWidget *parent, ESource *source)
+{
+ AddressbookSourceDialog *sdialog = g_new0 (AddressbookSourceDialog, 1);
+ EABConfig *ec;
+ gint i;
+ GSList *items = NULL;
+ EABConfigTargetSource *target;
+ gchar *xml;
+ gchar *gladefile;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ GLADE_FILE_NAME,
+ NULL);
+ sdialog->gui = glade_xml_new (gladefile, "account-editor-notebook", NULL);
+ g_free (gladefile);
+
+ if (source) {
+ sdialog->original_source = source;
+ g_object_ref(source);
+ sdialog->source_group = e_source_peek_group (source);
+ xml = e_source_to_standalone_xml(source);
+ sdialog->source = e_source_new_from_standalone_xml(xml);
+ g_free(xml);
+ } else {
+ GConfClient *gconf;
+ GSList *l;
+
+ sdialog->source = e_source_new("", "");
+ gconf = gconf_client_get_default();
+ sdialog->source_list = e_source_list_new_for_gconf(gconf, "/apps/evolution/addressbook/sources");
+ l = e_source_list_peek_groups(sdialog->source_list);
+ if (!l) {
+ g_warning ("Address Book source groups are missing! Check your GConf setup.");
+ g_object_unref (gconf);
+ g_free (sdialog);
+ return NULL;
+ }
+
+ sdialog->menu_source_groups = g_slist_copy(l);
+#ifndef HAVE_LDAP
+ for (;l;l = g_slist_next(l))
+ if (!strncmp("ldap:", e_source_group_peek_base_uri(l->data), 5))
+ sdialog->menu_source_groups = g_slist_remove (sdialog->menu_source_groups, l->data);
+#endif
+ sdialog->source_group = (ESourceGroup *)sdialog->menu_source_groups->data;
+ for (i=0;eabc_new_items[i].path;i++)
+ items = g_slist_prepend(items, &eabc_new_items[i]);
+ g_object_unref(gconf);
+ }
+
+ /* HACK: doesn't work if you don't do this */
+ e_source_set_group(sdialog->source, sdialog->source_group);
+
+#if d(!)0
+ xml = e_source_to_standalone_xml(sdialog->source);
+ printf("but working standalone xml: %s\n", xml);
+ g_free(xml);
+ g_signal_connect(sdialog->source, "changed", source_changed, sdialog);
+#endif
+
+ sdialog->config = ec = eab_config_new(E_CONFIG_BOOK, "com.novell.evolution.addressbook.config.accountEditor");
+
+ for (i=0;eabc_items[i].path;i++) {
+ if (eabc_items[i].label)
+ eabc_items[i].label = gettext(eabc_items[i].label);
+ items = g_slist_prepend(items, &eabc_items[i]);
+ }
+
+ e_config_add_items((EConfig *)ec, items, eabc_commit, NULL, eabc_free, sdialog);
+ e_config_add_page_check((EConfig *)ec, NULL, eabc_check_complete, sdialog);
+
+ target = eab_config_target_new_source(ec, sdialog->source);
+ e_config_set_target((EConfig *)ec, (EConfigTarget *)target);
+
+ if(source)
+ sdialog->window = e_config_create_window((EConfig *)ec, NULL, _("Address Book Properties"));
+ else
+ sdialog->window = e_config_create_window((EConfig *)ec, NULL, _("New Address Book"));
+
+
+ /* forces initial validation */
+ if (!sdialog->original_source)
+ e_config_target_changed((EConfig *)ec, E_CONFIG_TARGET_CHANGED_STATE);
+
+ return sdialog->window;
+}
diff --git a/modules/addressbook/addressbook-config.h b/modules/addressbook/addressbook-config.h
new file mode 100644
index 0000000000..26c1788e51
--- /dev/null
+++ b/modules/addressbook/addressbook-config.h
@@ -0,0 +1,52 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Toshok <toshok@ximian.com>
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef __ADDRESSBOOK_CONFIG_H__
+#define __ADDRESSBOOK_CONFIG_H__
+
+#include <gtk/gtk.h>
+#include <libedataserver/e-source.h>
+
+typedef enum {
+ ADDRESSBOOK_LDAP_AUTH_NONE,
+ ADDRESSBOOK_LDAP_AUTH_SIMPLE_EMAIL,
+ ADDRESSBOOK_LDAP_AUTH_SIMPLE_BINDDN
+} AddressbookLDAPAuthType;
+
+typedef enum {
+ ADDRESSBOOK_LDAP_SCOPE_ONELEVEL,
+ ADDRESSBOOK_LDAP_SCOPE_SUBTREE,
+ ADDRESSBOOK_LDAP_SCOPE_BASE,
+ ADDRESSBOOK_LDAP_SCOPE_LAST
+} AddressbookLDAPScopeType;
+
+typedef enum {
+ ADDRESSBOOK_LDAP_SSL_ALWAYS,
+ ADDRESSBOOK_LDAP_SSL_WHENEVER_POSSIBLE,
+ ADDRESSBOOK_LDAP_SSL_NEVER
+} AddressbookLDAPSSLType;
+
+GtkWidget* addressbook_config_edit_source (GtkWidget *parent, ESource *source);
+GtkWidget* addressbook_config_create_new_source (GtkWidget *parent);
+
+#endif /* __ADDRESSBOOK_CONFIG_H__ */
diff --git a/modules/addressbook/apps_evolution_addressbook.schemas.in b/modules/addressbook/apps_evolution_addressbook.schemas.in
new file mode 100644
index 0000000000..4a336409df
--- /dev/null
+++ b/modules/addressbook/apps_evolution_addressbook.schemas.in
@@ -0,0 +1,82 @@
+<gconfschemafile>
+ <schemalist>
+
+ <!-- Completion uris -->
+
+ <schema>
+ <key>/schemas/apps/evolution/addressbook/completion/uris</key>
+ <applyto>/apps/evolution/addressbook/completion/uris</applyto>
+ <owner>evolution-addressbook</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>EFolderList XML for the list of completion URIs</short>
+ <long>EFolderList XML for the list of completion URIs.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/evolution/addressbook/completion/minimum_query_length</key>
+ <applyto>/apps/evolution/addressbook/completion/minimum_query_length</applyto>
+ <owner>evolution-addressbook</owner>
+ <type>int</type>
+ <default>3</default>
+ <locale name="C">
+ <short>Autocomplete length</short>
+ <long>The number of characters that must be typed before Evolution will attempt to autocomplete.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/evolution/addressbook/completion/show_address</key>
+ <applyto>/apps/evolution/addressbook/completion/show_address</applyto>
+ <owner>evolution-addressbook</owner>
+ <type>bool</type>
+ <default>false</default>
+ <locale name="C">
+ <short>Show autocompleted name with an address</short>
+ <long>Whether force showing the mail address with the name of the autocompleted contact in the entry.</long>
+ </locale>
+ </schema>
+
+ <!-- Select names last used uri -->
+
+ <schema>
+ <key>/schemas/apps/evolution/addressbook/select_names/last_used_uri</key>
+ <applyto>/apps/evolution/addressbook/select_names/last_used_uri</applyto>
+ <owner>evolution-addressbook</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>URI for the folder last used in the select names dialog</short>
+ <long>URI for the folder last used in the select names dialog.</long>
+ </locale>
+ </schema>
+
+ <!-- View state -->
+
+ <schema>
+ <key>/schemas/apps/evolution/addressbook/display/vpane_position</key>
+ <applyto>/apps/evolution/addressbook/display/vpane_position</applyto>
+ <owner>evolution-addressbook</owner>
+ <type>int</type>
+ <locale name="C">
+ <short>Vertical pane position</short>
+ <long>Position of the vertical pane, between the card and list views and the preview pane, in pixels.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/evolution/addressbook/display/show_preview</key>
+ <applyto>/apps/evolution/addressbook/display/show_preview</applyto>
+ <owner>evolution-addressbook</owner>
+ <type>bool</type>
+ <default>true</default>
+ <locale name="C">
+ <short>Show preview pane</short>
+ <long>Whether to show the preview pane.</long>
+ </locale>
+ </schema>
+
+ </schemalist>
+</gconfschemafile>
diff --git a/modules/addressbook/autocompletion-config.c b/modules/addressbook/autocompletion-config.c
new file mode 100644
index 0000000000..370c1a1e7e
--- /dev/null
+++ b/modules/addressbook/autocompletion-config.c
@@ -0,0 +1,137 @@
+/*
+ * e-shell-config-autocompletion.h - Configuration page for addressbook autocompletion.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Toshok <toshok@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "autocompletion-config.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <libedataserver/e-source-list.h>
+#include <libedataserverui/e-source-selector.h>
+#include <libedataserverui/e-name-selector-entry.h>
+
+#include "widgets/misc/e-preferences-window.h"
+
+static void
+source_selection_changed_cb (ESourceSelector *source_selector)
+{
+ ESourceList *source_list;
+ GSList *selection;
+ GSList *l;
+ GSList *groups;
+
+ source_list = e_source_selector_get_source_list (source_selector);
+
+ /* first we clear all the completion flags from all sources */
+ for (groups = e_source_list_peek_groups (source_list); groups; groups = groups->next) {
+ ESourceGroup *group = E_SOURCE_GROUP (groups->data);
+ GSList *sources;
+
+ for (sources = e_source_group_peek_sources (group); sources; sources = sources->next) {
+ ESource *source = E_SOURCE (sources->data);
+
+ e_source_set_property (source, "completion", NULL);
+ }
+ }
+
+ /* then we loop over the selector's selection, setting the
+ property on those sources */
+ selection = e_source_selector_get_selection (source_selector);
+ for (l = selection; l; l = l->next) {
+ ESource *source = E_SOURCE (l->data);
+
+ e_source_set_property (source, "completion", "true");
+ }
+ e_source_selector_free_selection (selection);
+
+ /* XXX we should pop up a dialog if this fails */
+ e_source_list_sync (source_list, NULL);
+}
+
+static void
+initialize_selection (ESourceSelector *source_selector)
+{
+ ESourceList *source_list;
+ GSList *groups;
+
+ source_list = e_source_selector_get_source_list (source_selector);
+
+ for (groups = e_source_list_peek_groups (source_list); groups; groups = groups->next) {
+ ESourceGroup *group = E_SOURCE_GROUP (groups->data);
+ GSList *sources;
+
+ for (sources = e_source_group_peek_sources (group); sources; sources = sources->next) {
+ ESource *source = E_SOURCE (sources->data);
+ const gchar *completion;
+
+ completion = e_source_get_property (source, "completion");
+ if (completion && !g_ascii_strcasecmp (completion, "true"))
+ e_source_selector_select_source (source_selector, source);
+ }
+ }
+}
+
+void
+autocompletion_config_init (EShell *shell)
+{
+ ESourceList *source_list;
+ GtkWidget *scrolled_window;
+ GtkWidget *source_selector;
+ GtkWidget *preferences_window;
+
+ g_return_if_fail (E_IS_SHELL (shell));
+
+ source_list = e_source_list_new_for_gconf_default (
+ "/apps/evolution/addressbook/sources");
+
+ /* XXX should we watch for the source list to change and
+ update it in the control? what about our local changes? */
+ /* g_signal_connect (ac->source_list, "changed", G_CALLBACK (source_list_changed), ac); */
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
+ gtk_widget_show (scrolled_window);
+
+ source_selector = e_source_selector_new (source_list);
+ g_signal_connect (
+ source_selector, "selection_changed",
+ G_CALLBACK (source_selection_changed_cb), NULL);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), source_selector);
+ gtk_widget_show (source_selector);
+
+ initialize_selection (E_SOURCE_SELECTOR (source_selector));
+
+ preferences_window = e_shell_get_preferences_window (shell);
+
+ e_preferences_window_add_page (
+ E_PREFERENCES_WINDOW (preferences_window),
+ "autocompletion",
+ "preferences-autocompletion",
+ _("Autocompletion"),
+ scrolled_window,
+ 200);
+}
diff --git a/modules/addressbook/autocompletion-config.h b/modules/addressbook/autocompletion-config.h
new file mode 100644
index 0000000000..5769bdce9d
--- /dev/null
+++ b/modules/addressbook/autocompletion-config.h
@@ -0,0 +1,37 @@
+/*
+ * e-shell-config-autocompletion.h - Configuration page for addressbook autocompletion.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Toshok <toshok@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef _AUTOCOMPLETION_CONFIG_H
+#define _AUTOCOMPLETION_CONFIG_H
+
+#include <glib.h>
+#include <shell/e-shell.h>
+
+G_BEGIN_DECLS
+
+void autocompletion_config_init (EShell *shell);
+
+G_END_DECLS
+
+#endif /* _AUTOCOMPLETION_CONFIG_H */
diff --git a/modules/addressbook/e-book-shell-backend.c b/modules/addressbook/e-book-shell-backend.c
new file mode 100644
index 0000000000..68af7ed03b
--- /dev/null
+++ b/modules/addressbook/e-book-shell-backend.c
@@ -0,0 +1,576 @@
+/*
+ * e-book-shell-backend.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-book-shell-backend.h"
+
+#include <config.h>
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libebook/e-book.h>
+#include <libedataserver/e-url.h>
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-group.h>
+
+#include "shell/e-shell.h"
+#include "shell/e-shell-window.h"
+
+#include "e-util/e-import.h"
+#include "addressbook/gui/widgets/eab-gui-util.h"
+#include "addressbook/gui/contact-editor/e-contact-editor.h"
+#include "addressbook/gui/contact-list-editor/e-contact-list-editor.h"
+#include "addressbook/importers/evolution-addressbook-importers.h"
+
+#include "eab-config.h"
+#include "addressbook-config.h"
+#include "autocompletion-config.h"
+
+#include "e-book-shell-migrate.h"
+#include "e-book-shell-view.h"
+
+#ifdef ENABLE_SMIME
+#include "smime/gui/component.h"
+#include "smime/gui/certificate-manager.h"
+#endif
+
+#define E_BOOK_SHELL_BACKEND_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_BOOK_SHELL_BACKEND, EBookShellBackendPrivate))
+
+#define LDAP_BASE_URI "ldap://"
+#define PERSONAL_RELATIVE_URI "system"
+
+struct _EBookShellBackendPrivate {
+ ESourceList *source_list;
+};
+
+enum {
+ PROP_0,
+ PROP_SOURCE_LIST
+};
+
+static gpointer parent_class;
+static GType book_shell_backend_type;
+
+static void
+book_shell_backend_ensure_sources (EShellBackend *shell_backend)
+{
+ /* XXX This is basically the same algorithm across all backends.
+ * Maybe we could somehow integrate this into EShellBackend? */
+
+ EBookShellBackendPrivate *priv;
+ ESourceGroup *on_this_computer;
+ ESourceGroup *on_ldap_servers;
+ ESource *personal;
+ GSList *groups, *iter;
+ const gchar *data_dir;
+ const gchar *name;
+ gchar *base_uri;
+ gchar *filename;
+
+ on_this_computer = NULL;
+ on_ldap_servers = NULL;
+ personal = NULL;
+
+ priv = E_BOOK_SHELL_BACKEND_GET_PRIVATE (shell_backend);
+
+ if (!e_book_get_addressbooks (&priv->source_list, NULL)) {
+ g_warning ("Could not get addressbook sources from GConf!");
+ return;
+ }
+
+ data_dir = e_shell_backend_get_data_dir (shell_backend);
+ filename = g_build_filename (data_dir, "local", NULL);
+ base_uri = g_filename_to_uri (filename, NULL, NULL);
+ g_free (filename);
+
+ groups = e_source_list_peek_groups (priv->source_list);
+ for (iter = groups; iter != NULL; iter = iter->next) {
+ ESourceGroup *source_group = iter->data;
+ const gchar *group_base_uri;
+
+ group_base_uri = e_source_group_peek_base_uri (source_group);
+
+ /* Compare only "file://" part. If the user's home
+ * changes, we do not want to create another group. */
+ if (on_this_computer == NULL &&
+ strncmp (base_uri, group_base_uri, 7) == 0)
+ on_this_computer = source_group;
+
+ else if (on_ldap_servers == NULL &&
+ strcmp (LDAP_BASE_URI, group_base_uri) == 0)
+ on_ldap_servers = source_group;
+ }
+
+ name = _("On This Computer");
+
+ if (on_this_computer != NULL) {
+ GSList *sources;
+ const gchar *group_base_uri;
+
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (on_this_computer, name);
+
+ sources = e_source_group_peek_sources (on_this_computer);
+ group_base_uri = e_source_group_peek_base_uri (on_this_computer);
+
+ /* Make sure this group includes a "Personal" source. */
+ for (iter = sources; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+ const gchar *relative_uri;
+
+ relative_uri = e_source_peek_relative_uri (source);
+ if (relative_uri == NULL)
+ continue;
+
+ if (strcmp (PERSONAL_RELATIVE_URI, relative_uri) != 0)
+ continue;
+
+ personal = source;
+ break;
+ }
+
+ /* Make sure we have the correct base URI. This can
+ * change when the user's home directory changes. */
+ if (strcmp (base_uri, group_base_uri) != 0) {
+ e_source_group_set_base_uri (
+ on_this_computer, base_uri);
+
+ /* XXX We shouldn't need this sync call here as
+ * set_base_uri() results in synching to GConf,
+ * but that happens in an idle loop and too late
+ * to prevent the user from seeing a "Cannot
+ * Open ... because of invalid URI" error. */
+ e_source_list_sync (priv->source_list, NULL);
+ }
+
+ } else {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, base_uri);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+ }
+
+ name = _("Personal");
+
+ if (personal == NULL) {
+ ESource *source;
+
+ /* Create the default Personal address book. */
+ source = e_source_new (name, PERSONAL_RELATIVE_URI);
+ e_source_group_add_source (on_this_computer, source, -1);
+ e_source_set_property (source, "completion", "true");
+ g_object_unref (source);
+ } else {
+ /* Force the source name to the current locale. */
+ e_source_set_name (personal, name);
+ }
+
+ name = _("On LDAP Servers");
+
+ if (on_ldap_servers == NULL) {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, LDAP_BASE_URI);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+ } else {
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (on_ldap_servers, name);
+ }
+
+ g_free (base_uri);
+}
+
+static void
+book_shell_backend_init_importers (void)
+{
+ EImportClass *import_class;
+ EImportImporter *importer;
+
+ import_class = g_type_class_ref (e_import_get_type ());
+
+ importer = evolution_ldif_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+
+ importer = evolution_vcard_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+
+ importer = evolution_csv_outlook_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+
+ importer = evolution_csv_mozilla_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+
+ importer = evolution_csv_evolution_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+}
+
+static void
+book_shell_backend_book_loaded_cb (EBook *book,
+ EBookStatus status,
+ gpointer user_data)
+{
+ EContact *contact;
+ GtkAction *action;
+ GtkWidget *editor;
+ const gchar *action_name;
+
+ /* XXX Handle errors better. */
+ if (status != E_BOOK_ERROR_OK)
+ return;
+
+ contact = e_contact_new ();
+ action = GTK_ACTION (user_data);
+ action_name = gtk_action_get_name (action);
+
+ if (strcmp (action_name, "contact-new") == 0)
+ editor = e_contact_editor_new (book, contact, TRUE, TRUE);
+
+ if (strcmp (action_name, "contact-new-list") == 0)
+ editor = e_contact_list_editor_new (book, contact, TRUE, TRUE);
+
+ eab_editor_show (EAB_EDITOR (editor));
+
+ g_object_unref (contact);
+ g_object_unref (book);
+}
+
+static void
+action_contact_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ EShell *shell;
+ EBook *book = NULL;
+ GConfClient *client;
+ ESourceList *source_list;
+ const gchar *key;
+ gchar *uid;
+
+ /* This callback is used for both contacts and contact lists. */
+
+ if (!e_book_get_addressbooks (&source_list, NULL)) {
+ g_warning ("Could not get addressbook sources from GConf!");
+ return;
+ }
+
+ shell = e_shell_window_get_shell (shell_window);
+ client = e_shell_get_gconf_client (shell);
+
+ key = "/apps/evolution/addressbook/display/primary_addressbook";
+ uid = gconf_client_get_string (client, key, NULL);
+
+ if (uid != NULL) {
+ ESource *source;
+
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ if (source != NULL)
+ book = e_book_new (source, NULL);
+ g_free (uid);
+ }
+
+ if (book == NULL)
+ book = e_book_new_default_addressbook (NULL);
+
+ e_book_async_open (
+ book, FALSE, book_shell_backend_book_loaded_cb, action);
+}
+
+static void
+action_address_book_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ addressbook_config_create_new_source (NULL);
+}
+
+static GtkActionEntry item_entries[] = {
+
+ { "contact-new",
+ "contact-new",
+ NC_("New", "_Contact"),
+ "<Shift><Control>c",
+ N_("Create a new contact"),
+ G_CALLBACK (action_contact_new_cb) },
+
+ { "contact-new-list",
+ "stock_contact-list",
+ N_("Contact _List"),
+ "<Shift><Control>l",
+ N_("Create a new contact list"),
+ G_CALLBACK (action_contact_new_cb) }
+};
+
+static GtkActionEntry source_entries[] = {
+
+ { "address-book-new",
+ "address-book-new",
+ NC_("New", "Address _Book"),
+ NULL,
+ N_("Create a new address book"),
+ G_CALLBACK (action_address_book_new_cb) }
+};
+
+static gboolean
+book_shell_backend_handle_uri_cb (EShellBackend *shell_backend,
+ const gchar *uri)
+{
+ EUri *euri;
+ const gchar *cp;
+ gchar *source_uid = NULL;
+ gchar *contact_uid = NULL;
+
+ if (!g_str_has_prefix (uri, "contacts:"))
+ return FALSE;
+
+ euri = e_uri_new (uri);
+ cp = euri->query;
+
+ if (cp == NULL) {
+ e_uri_free (euri);
+ return FALSE;
+ }
+
+ while (*cp != '\0') {
+ gchar *header;
+ gchar *content;
+ gsize length;
+ gsize content_length;
+
+ length = strcspn (cp, "=&");
+
+ /* If it's malformed, give up. */
+ if (cp[length] != '=')
+ break;
+
+ header = (gchar *) cp;
+ header[length] = '\0';
+ cp += length + 1;
+
+ content_length = strcspn (cp, "&");
+ content = g_strndup (cp, content_length);
+
+ if (g_ascii_strcasecmp (header, "source-uid") == 0)
+ source_uid = g_strdup (content);
+
+ if (g_ascii_strcasecmp (header, "contact-uid") == 0)
+ contact_uid = g_strdup (content);
+
+ g_free (content);
+
+ cp += content_length;
+ if (*cp == '&') {
+ cp++;
+ if (strcmp (cp, "amp;"))
+ cp += 4;
+ }
+ }
+
+ /* FIXME */
+ /*addressbook_view_edit_contact (view, source_uid, contact_uid);*/
+
+ g_free (source_uid);
+ g_free (contact_uid);
+
+ e_uri_free (euri);
+
+ return TRUE;
+}
+
+static void
+book_shell_backend_window_created_cb (EShellBackend *shell_backend,
+ GtkWindow *window)
+{
+ const gchar *backend_name;
+
+ if (!E_IS_SHELL_WINDOW (window))
+ return;
+
+ backend_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name;
+
+ e_shell_window_register_new_item_actions (
+ E_SHELL_WINDOW (window), backend_name,
+ item_entries, G_N_ELEMENTS (item_entries));
+
+ e_shell_window_register_new_source_actions (
+ E_SHELL_WINDOW (window), backend_name,
+ source_entries, G_N_ELEMENTS (source_entries));
+}
+
+static void
+book_shell_backend_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SOURCE_LIST:
+ g_value_set_object (
+ value,
+ e_book_shell_backend_get_source_list (
+ E_BOOK_SHELL_BACKEND (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+book_shell_backend_dispose (GObject *object)
+{
+ EBookShellBackendPrivate *priv;
+
+ priv = E_BOOK_SHELL_BACKEND_GET_PRIVATE (object);
+
+ if (priv->source_list != NULL) {
+ g_object_unref (priv->source_list);
+ priv->source_list = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+book_shell_backend_constructed (GObject *object)
+{
+ EShell *shell;
+ EShellBackend *shell_backend;
+
+ shell_backend = E_SHELL_BACKEND (object);
+ shell = e_shell_backend_get_shell (shell_backend);
+
+ /* XXX Why is this here? Address books aren't the only
+ * things that use S/MIME. Maybe put it in EShell? */
+#ifdef ENABLE_SMIME
+ smime_component_init ();
+ certificate_manager_config_init (shell);
+#endif
+
+ book_shell_backend_init_importers ();
+ book_shell_backend_ensure_sources (shell_backend);
+
+ e_plugin_hook_register_type (eab_config_get_type ());
+
+ g_signal_connect_swapped (
+ shell, "handle-uri",
+ G_CALLBACK (book_shell_backend_handle_uri_cb),
+ shell_backend);
+
+ g_signal_connect_swapped (
+ shell, "window-created",
+ G_CALLBACK (book_shell_backend_window_created_cb),
+ shell_backend);
+
+ autocompletion_config_init (shell);
+}
+
+static gboolean
+book_shell_backend_is_busy (EShellBackend *shell_backend)
+{
+ return !eab_editor_request_close_all ();
+}
+
+static gboolean
+book_shell_backend_shutdown (EShellBackend *shell_backend)
+{
+ /* FIXME */
+ return TRUE;
+}
+
+static void
+book_shell_backend_class_init (EBookShellBackendClass *class)
+{
+ GObjectClass *object_class;
+ EShellBackendClass *shell_backend_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EBookShellBackendPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = book_shell_backend_get_property;
+ object_class->dispose = book_shell_backend_dispose;
+ object_class->constructed = book_shell_backend_constructed;
+
+ shell_backend_class = E_SHELL_BACKEND_CLASS (class);
+ shell_backend_class->shell_view_type = E_TYPE_BOOK_SHELL_VIEW;
+ shell_backend_class->name = "addressbook";
+ shell_backend_class->aliases = "contacts";
+ shell_backend_class->schemes = "";
+ shell_backend_class->sort_order = 300;
+ shell_backend_class->start = NULL;
+ shell_backend_class->is_busy = book_shell_backend_is_busy;
+ shell_backend_class->shutdown = book_shell_backend_shutdown;
+ shell_backend_class->migrate = e_book_shell_backend_migrate;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SOURCE_LIST,
+ g_param_spec_object (
+ "source-list",
+ _("Source List"),
+ _("The registry of address books"),
+ E_TYPE_SOURCE_LIST,
+ G_PARAM_READABLE));
+}
+
+static void
+book_shell_backend_init (EBookShellBackend *book_shell_backend)
+{
+ book_shell_backend->priv =
+ E_BOOK_SHELL_BACKEND_GET_PRIVATE (book_shell_backend);
+}
+
+GType
+e_book_shell_backend_get_type (void)
+{
+ return book_shell_backend_type;
+}
+
+void
+e_book_shell_backend_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (EBookShellBackendClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) book_shell_backend_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EBookShellBackend),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) book_shell_backend_init,
+ NULL /* value_table */
+ };
+
+ book_shell_backend_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_BACKEND,
+ "EBookShellBackend", &type_info, 0);
+}
+
+ESourceList *
+e_book_shell_backend_get_source_list (EBookShellBackend *book_shell_backend)
+{
+ g_return_val_if_fail (
+ E_IS_BOOK_SHELL_BACKEND (book_shell_backend), NULL);
+
+ return book_shell_backend->priv->source_list;
+}
diff --git a/modules/addressbook/e-book-shell-backend.h b/modules/addressbook/e-book-shell-backend.h
new file mode 100644
index 0000000000..c61e43b814
--- /dev/null
+++ b/modules/addressbook/e-book-shell-backend.h
@@ -0,0 +1,70 @@
+/*
+ * e-book-shell-backend.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_BOOK_SHELL_BACKEND_H
+#define E_BOOK_SHELL_BACKEND_H
+
+#include <shell/e-shell-backend.h>
+#include <libedataserver/e-source-list.h>
+
+/* Standard GObject macros */
+#define E_TYPE_BOOK_SHELL_BACKEND \
+ (e_book_shell_backend_get_type ())
+#define E_BOOK_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_BOOK_SHELL_BACKEND, EBookShellBackend))
+#define E_BOOK_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_BOOK_SHELL_BACKEND, EBookShellBackendClass))
+#define E_IS_BOOK_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_BOOK_SHELL_BACKEND))
+#define E_IS_BOOK_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_BOOK_SHELL_BACKEND))
+#define E_BOOK_SHELL_BACKEND_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_BOOK_SHELL_BACKEND, EBookShellBackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EBookShellBackend EBookShellBackend;
+typedef struct _EBookShellBackendClass EBookShellBackendClass;
+typedef struct _EBookShellBackendPrivate EBookShellBackendPrivate;
+
+struct _EBookShellBackend {
+ EShellBackend parent;
+ EBookShellBackendPrivate *priv;
+};
+
+struct _EBookShellBackendClass {
+ EShellBackendClass parent_class;
+};
+
+GType e_book_shell_backend_get_type (void);
+void e_book_shell_backend_register_type
+ (GTypeModule *type_module);
+ESourceList * e_book_shell_backend_get_source_list
+ (EBookShellBackend *book_shell_backend);
+
+G_END_DECLS
+
+#endif /* E_BOOK_SHELL_BACKEND_H */
diff --git a/modules/addressbook/e-book-shell-content.c b/modules/addressbook/e-book-shell-content.c
new file mode 100644
index 0000000000..cce03b1575
--- /dev/null
+++ b/modules/addressbook/e-book-shell-content.c
@@ -0,0 +1,492 @@
+/*
+ * e-book-shell-content.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-book-shell-content.h"
+
+#include <glib/gi18n.h>
+
+#include "e-util/gconf-bridge.h"
+
+#define E_BOOK_SHELL_CONTENT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_BOOK_SHELL_CONTENT, EBookShellContentPrivate))
+
+struct _EBookShellContentPrivate {
+ GtkWidget *paned;
+ GtkWidget *notebook;
+ GtkWidget *preview;
+};
+
+enum {
+ PROP_0,
+ PROP_CURRENT_VIEW,
+ PROP_PREVIEW_CONTACT,
+ PROP_PREVIEW_VISIBLE
+};
+
+static gpointer parent_class;
+static GType book_shell_view_type;
+
+static void
+book_shell_content_send_message_cb (EBookShellContent *book_shell_content,
+ EDestination *destination,
+ EABContactDisplay *display)
+{
+ GList node = { destination, NULL, NULL };
+
+ eab_send_as_to (&node);
+}
+
+static void
+book_shell_content_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_VIEW:
+ e_book_shell_content_set_current_view (
+ E_BOOK_SHELL_CONTENT (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_PREVIEW_CONTACT:
+ e_book_shell_content_set_preview_contact (
+ E_BOOK_SHELL_CONTENT (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_PREVIEW_VISIBLE:
+ e_book_shell_content_set_preview_visible (
+ E_BOOK_SHELL_CONTENT (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+book_shell_content_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_VIEW:
+ g_value_set_object (
+ value, e_book_shell_content_get_current_view (
+ E_BOOK_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_PREVIEW_CONTACT:
+ g_value_set_object (
+ value, e_book_shell_content_get_preview_contact (
+ E_BOOK_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_PREVIEW_VISIBLE:
+ g_value_set_boolean (
+ value, e_book_shell_content_get_preview_visible (
+ E_BOOK_SHELL_CONTENT (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+book_shell_content_dispose (GObject *object)
+{
+ EBookShellContentPrivate *priv;
+
+ priv = E_BOOK_SHELL_CONTENT_GET_PRIVATE (object);
+
+ if (priv->paned != NULL) {
+ g_object_unref (priv->paned);
+ priv->paned = NULL;
+ }
+
+ if (priv->notebook != NULL) {
+ g_object_unref (priv->notebook);
+ priv->notebook = NULL;
+ }
+
+ if (priv->preview != NULL) {
+ g_object_unref (priv->preview);
+ priv->preview = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+book_shell_content_constructed (GObject *object)
+{
+ EBookShellContentPrivate *priv;
+ GConfBridge *bridge;
+ GtkWidget *container;
+ GtkWidget *widget;
+ const gchar *key;
+
+ priv = E_BOOK_SHELL_CONTENT_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ container = GTK_WIDGET (object);
+
+ widget = gtk_vpaned_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->paned = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_notebook_new ();
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
+ gtk_paned_add1 (GTK_PANED (container), widget);
+ priv->notebook = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_paned_add2 (GTK_PANED (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = eab_contact_display_new ();
+ eab_contact_display_set_mode (
+ EAB_CONTACT_DISPLAY (widget),
+ EAB_CONTACT_DISPLAY_RENDER_NORMAL);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->preview = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ g_signal_connect_swapped (
+ priv->preview, "send-message",
+ G_CALLBACK (book_shell_content_send_message_cb), object);
+
+ /* Bind GObject properties to GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (priv->paned);
+ key = "/apps/evolution/addressbook/display/vpane_position";
+ gconf_bridge_bind_property_delayed (bridge, key, object, "position");
+}
+
+static guint32
+book_shell_content_check_state (EShellContent *shell_content)
+{
+ EBookShellContent *book_shell_content;
+ ESelectionModel *selection_model;
+ EAddressbookModel *model;
+ EAddressbookView *view;
+ guint32 state = 0;
+ gint n_contacts;
+ gint n_selected;
+
+ book_shell_content = E_BOOK_SHELL_CONTENT (shell_content);
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ model = e_addressbook_view_get_model (view);
+
+ selection_model = e_addressbook_view_get_selection_model (view);
+ n_contacts = (selection_model != NULL) ?
+ e_selection_model_row_count (selection_model) : 0;
+ n_selected = (selection_model != NULL) ?
+ e_selection_model_selected_count (selection_model) : 0;
+
+ /* FIXME Finish the rest of the flags. */
+ if (n_selected == 1)
+ state |= E_BOOK_SHELL_CONTENT_SELECTION_SINGLE;
+ if (n_selected > 1)
+ state |= E_BOOK_SHELL_CONTENT_SELECTION_MULTIPLE;
+ if (e_addressbook_model_can_stop (model))
+ state |= E_BOOK_SHELL_CONTENT_SOURCE_IS_BUSY;
+ if (e_addressbook_model_get_editable (model))
+ state |= E_BOOK_SHELL_CONTENT_SOURCE_IS_EDITABLE;
+ if (n_contacts == 0)
+ state |= E_BOOK_SHELL_CONTENT_SOURCE_IS_EMPTY;
+
+ return state;
+}
+
+static void
+book_shell_content_class_init (EBookShellContentClass *class)
+{
+ GObjectClass *object_class;
+ EShellContentClass *shell_content_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EBookShellContentPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = book_shell_content_set_property;
+ object_class->get_property = book_shell_content_get_property;
+ object_class->dispose = book_shell_content_dispose;
+ object_class->constructed = book_shell_content_constructed;
+
+ shell_content_class = E_SHELL_CONTENT_CLASS (class);
+ shell_content_class->check_state = book_shell_content_check_state;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CURRENT_VIEW,
+ g_param_spec_object (
+ "current-view",
+ _("Current View"),
+ _("The currently selected address book view"),
+ E_TYPE_ADDRESSBOOK_VIEW,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PREVIEW_CONTACT,
+ g_param_spec_object (
+ "preview-contact",
+ _("Previewed Contact"),
+ _("The contact being shown in the preview pane"),
+ E_TYPE_CONTACT,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PREVIEW_VISIBLE,
+ g_param_spec_boolean (
+ "preview-visible",
+ _("Preview is Visible"),
+ _("Whether the preview pane is visible"),
+ TRUE,
+ G_PARAM_READWRITE));
+}
+
+static void
+book_shell_content_init (EBookShellContent *book_shell_content)
+{
+ book_shell_content->priv =
+ E_BOOK_SHELL_CONTENT_GET_PRIVATE (book_shell_content);
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_book_shell_content_get_type (void)
+{
+ return book_shell_view_type;
+}
+
+void
+e_book_shell_content_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (EBookShellContentClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) book_shell_content_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EBookShellContent),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) book_shell_content_init,
+ NULL /* value_table */
+ };
+
+ book_shell_view_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_CONTENT,
+ "EBookShellContent", &type_info, 0);
+}
+
+GtkWidget *
+e_book_shell_content_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_BOOK_SHELL_CONTENT,
+ "shell-view", shell_view, NULL);
+}
+
+void
+e_book_shell_content_insert_view (EBookShellContent *book_shell_content,
+ EAddressbookView *addressbook_view)
+{
+ GtkNotebook *notebook;
+ GtkWidget *child;
+
+ g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content));
+ g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (addressbook_view));
+
+ notebook = GTK_NOTEBOOK (book_shell_content->priv->notebook);
+ child = GTK_WIDGET (addressbook_view);
+ gtk_notebook_append_page (notebook, child, NULL);
+}
+
+void
+e_book_shell_content_remove_view (EBookShellContent *book_shell_content,
+ EAddressbookView *addressbook_view)
+{
+ GtkNotebook *notebook;
+ GtkWidget *child;
+ gint page_num;
+
+ g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content));
+ g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (addressbook_view));
+
+ notebook = GTK_NOTEBOOK (book_shell_content->priv->notebook);
+ child = GTK_WIDGET (addressbook_view);
+ page_num = gtk_notebook_page_num (notebook, child);
+ g_return_if_fail (page_num >= 0);
+
+ gtk_notebook_remove_page (notebook, page_num);
+}
+
+EAddressbookView *
+e_book_shell_content_get_current_view (EBookShellContent *book_shell_content)
+{
+ GtkNotebook *notebook;
+ GtkWidget *widget;
+ gint page_num;
+
+ g_return_val_if_fail (
+ E_IS_BOOK_SHELL_CONTENT (book_shell_content), NULL);
+
+ notebook = GTK_NOTEBOOK (book_shell_content->priv->notebook);
+ page_num = gtk_notebook_get_current_page (notebook);
+ widget = gtk_notebook_get_nth_page (notebook, page_num);
+ g_return_val_if_fail (widget != NULL, NULL);
+
+ return E_ADDRESSBOOK_VIEW (widget);
+}
+
+void
+e_book_shell_content_set_current_view (EBookShellContent *book_shell_content,
+ EAddressbookView *addressbook_view)
+{
+ GtkNotebook *notebook;
+ GtkWidget *child;
+ gint page_num;
+
+ g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content));
+ g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (addressbook_view));
+
+ notebook = GTK_NOTEBOOK (book_shell_content->priv->notebook);
+ child = GTK_WIDGET (addressbook_view);
+ page_num = gtk_notebook_page_num (notebook, child);
+ g_return_if_fail (page_num >= 0);
+
+ gtk_notebook_set_current_page (notebook, page_num);
+ g_object_notify (G_OBJECT (book_shell_content), "current-view");
+}
+
+EContact *
+e_book_shell_content_get_preview_contact (EBookShellContent *book_shell_content)
+{
+ EABContactDisplay *display;
+
+ g_return_val_if_fail (
+ E_IS_BOOK_SHELL_CONTENT (book_shell_content), NULL);
+
+ display = EAB_CONTACT_DISPLAY (book_shell_content->priv->preview);
+
+ return eab_contact_display_get_contact (display);
+}
+
+void
+e_book_shell_content_set_preview_contact (EBookShellContent *book_shell_content,
+ EContact *preview_contact)
+{
+ EABContactDisplay *display;
+
+ g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content));
+
+ display = EAB_CONTACT_DISPLAY (book_shell_content->priv->preview);
+
+ eab_contact_display_set_contact (display, preview_contact);
+ g_object_notify (G_OBJECT (book_shell_content), "preview-contact");
+}
+
+gboolean
+e_book_shell_content_get_preview_visible (EBookShellContent *book_shell_content)
+{
+ GtkPaned *paned;
+ GtkWidget *child;
+
+ g_return_val_if_fail (
+ E_IS_BOOK_SHELL_CONTENT (book_shell_content), FALSE);
+
+ paned = GTK_PANED (book_shell_content->priv->paned);
+ child = gtk_paned_get_child2 (paned);
+
+ return GTK_WIDGET_VISIBLE (child);
+}
+
+void
+e_book_shell_content_set_preview_visible (EBookShellContent *book_shell_content,
+ gboolean preview_visible)
+{
+ GtkPaned *paned;
+ GtkWidget *child;
+
+ g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content));
+
+ paned = GTK_PANED (book_shell_content->priv->paned);
+ child = gtk_paned_get_child2 (paned);
+
+ if (preview_visible)
+ gtk_widget_show (child);
+ else
+ gtk_widget_hide (child);
+
+ g_object_notify (G_OBJECT (book_shell_content), "preview-visible");
+}
+
+void
+e_book_shell_content_clipboard_copy (EBookShellContent *book_shell_content)
+{
+ EAddressbookView *addressbook_view;
+ GtkHTML *html;
+ gchar *selection;
+
+ g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content));
+
+ html = GTK_HTML (book_shell_content->priv->preview);
+ addressbook_view =
+ e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (addressbook_view != NULL);
+
+ if (!GTK_WIDGET_HAS_FOCUS (html)) {
+ e_addressbook_view_copy (addressbook_view);
+ return;
+ }
+
+ selection = gtk_html_get_selection_html (html, NULL);
+ if (selection != NULL)
+ gtk_html_copy (html);
+ g_free (selection);
+}
diff --git a/modules/addressbook/e-book-shell-content.h b/modules/addressbook/e-book-shell-content.h
new file mode 100644
index 0000000000..da78c01274
--- /dev/null
+++ b/modules/addressbook/e-book-shell-content.h
@@ -0,0 +1,110 @@
+/*
+ * e-book-shell-content.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_BOOK_SHELL_CONTENT_H
+#define E_BOOK_SHELL_CONTENT_H
+
+#include <libebook/e-contact.h>
+
+#include "shell/e-shell-content.h"
+#include "shell/e-shell-view.h"
+
+#include "addressbook/gui/widgets/e-addressbook-view.h"
+#include "eab-composer-util.h"
+
+/* Standard GObject macros */
+#define E_TYPE_BOOK_SHELL_CONTENT \
+ (e_book_shell_content_get_type ())
+#define E_BOOK_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_BOOK_SHELL_CONTENT, EBookShellContent))
+#define E_BOOK_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_BOOK_SHELL_CONTENT, EBookShellContentClass))
+#define E_IS_BOOK_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_BOOK_SHELL_CONTENT))
+#define E_IS_BOOK_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_BOOK_SHELL_CONTENT))
+#define E_BOOK_SHELL_CONTENT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_BOOK_SHELL_CONTENT, EBookShellContentClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EBookShellContent EBookShellContent;
+typedef struct _EBookShellContentClass EBookShellContentClass;
+typedef struct _EBookShellContentPrivate EBookShellContentPrivate;
+
+enum {
+ E_BOOK_SHELL_CONTENT_SELECTION_SINGLE = 1 << 0,
+ E_BOOK_SHELL_CONTENT_SELECTION_MULTIPLE = 1 << 1,
+ E_BOOK_SHELL_CONTENT_SELECTION_HAS_EMAIL = 1 << 2,
+ E_BOOK_SHELL_CONTENT_SELECTION_IS_CONTACT_LIST = 1 << 3,
+ E_BOOK_SHELL_CONTENT_SELECTION_HAS_HTTP_URI = 1 << 4,
+ E_BOOK_SHELL_CONTENT_SELECTION_HAS_MAILTO_URI = 1 << 5,
+ E_BOOK_SHELL_CONTENT_SOURCE_IS_BUSY = 1 << 6,
+ E_BOOK_SHELL_CONTENT_SOURCE_IS_EDITABLE = 1 << 7,
+ E_BOOK_SHELL_CONTENT_SOURCE_IS_EMPTY = 1 << 8
+};
+
+struct _EBookShellContent {
+ EShellContent parent;
+ EBookShellContentPrivate *priv;
+};
+
+struct _EBookShellContentClass {
+ EShellContentClass parent_class;
+};
+
+GType e_book_shell_content_get_type (void);
+void e_book_shell_content_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_book_shell_content_new(EShellView *shell_view);
+void e_book_shell_content_insert_view
+ (EBookShellContent *book_shell_content,
+ EAddressbookView *addressbook_view);
+void e_book_shell_content_remove_view
+ (EBookShellContent *book_shell_content,
+ EAddressbookView *addressbook_view);
+EAddressbookView *
+ e_book_shell_content_get_current_view
+ (EBookShellContent *book_shell_content);
+void e_book_shell_content_set_current_view
+ (EBookShellContent *book_shell_content,
+ EAddressbookView *addressbook_view);
+EContact * e_book_shell_content_get_preview_contact
+ (EBookShellContent *book_shell_content);
+void e_book_shell_content_set_preview_contact
+ (EBookShellContent *book_shell_content,
+ EContact *preview_contact);
+gboolean e_book_shell_content_get_preview_visible
+ (EBookShellContent *book_shell_content);
+void e_book_shell_content_set_preview_visible
+ (EBookShellContent *book_shell_content,
+ gboolean preview_visible);
+void e_book_shell_content_clipboard_copy
+ (EBookShellContent *book_shell_content);
+
+G_END_DECLS
+
+#endif /* E_BOOK_SHELL_CONTENT_H */
diff --git a/modules/addressbook/e-book-shell-migrate.c b/modules/addressbook/e-book-shell-migrate.c
new file mode 100644
index 0000000000..fce6e0a634
--- /dev/null
+++ b/modules/addressbook/e-book-shell-migrate.c
@@ -0,0 +1,1230 @@
+/*
+ * e-book-shell-backend-migrate.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Toshok <toshok@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include <libebook/e-destination.h>
+#include <libebook/e-book.h>
+#include <glib/gi18n.h>
+
+#include <libedataserver/e-xml-utils.h>
+
+#include "e-util/e-util.h"
+#include "e-util/e-util-private.h"
+#include "e-util/e-xml-utils.h"
+#include "e-util/e-folder-map.h"
+
+#include "e-book-shell-migrate.h"
+
+/*#define SLOW_MIGRATION*/
+
+typedef struct {
+ /* this hash table maps old folder uris to new uids. It's
+ build in migrate_contact_folder and it's used in
+ migrate_completion_folders. */
+ GHashTable *folder_uid_map;
+
+ ESourceList *source_list;
+
+ const gchar *data_dir;
+
+ GtkWidget *window;
+ GtkWidget *label;
+ GtkWidget *folder_label;
+ GtkWidget *progress;
+} MigrationContext;
+
+static void
+setup_progress_dialog (MigrationContext *context)
+{
+ GtkWidget *vbox, *hbox;
+
+ context->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (context->window), _("Migrating..."));
+ gtk_window_set_modal (GTK_WINDOW (context->window), TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (context->window), 6);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (vbox);
+ gtk_container_add (GTK_CONTAINER (context->window), vbox);
+
+ context->label = gtk_label_new ("");
+ gtk_label_set_line_wrap (GTK_LABEL (context->label), TRUE);
+ gtk_widget_show (context->label);
+ gtk_box_pack_start (GTK_BOX (vbox), context->label, TRUE, TRUE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_widget_show (hbox);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+ context->folder_label = gtk_label_new ("");
+ gtk_widget_show (context->folder_label);
+ gtk_box_pack_start (GTK_BOX (hbox), context->folder_label, TRUE, TRUE, 0);
+
+ context->progress = gtk_progress_bar_new ();
+ gtk_widget_show (context->progress);
+ gtk_box_pack_start (GTK_BOX (hbox), context->progress, TRUE, TRUE, 0);
+
+ gtk_widget_show (context->window);
+}
+
+static void
+dialog_close (MigrationContext *context)
+{
+ gtk_widget_destroy (context->window);
+}
+
+static void
+dialog_set_label (MigrationContext *context, const gchar *str)
+{
+ gtk_label_set_text (GTK_LABEL (context->label), str);
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+
+#ifdef SLOW_MIGRATION
+ sleep (1);
+#endif
+}
+
+static void
+dialog_set_folder_name (MigrationContext *context, const gchar *folder_name)
+{
+ gchar *text;
+
+ text = g_strdup_printf (_("Migrating '%s':"), folder_name);
+ gtk_label_set_text (GTK_LABEL (context->folder_label), text);
+ g_free (text);
+
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (context->progress), 0.0);
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+
+#ifdef SLOW_MIGRATION
+ sleep (1);
+#endif
+}
+
+static void
+dialog_set_progress (MigrationContext *context, double percent)
+{
+ gchar text[5];
+
+ snprintf (text, sizeof (text), "%d%%", (gint) (percent * 100.0f));
+
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (context->progress), percent);
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (context->progress), text);
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+
+#ifdef SLOW_MIGRATION
+ sleep (1);
+#endif
+}
+
+static gboolean
+check_for_conflict (ESourceGroup *group, gchar *name)
+{
+ GSList *sources;
+ GSList *s;
+
+ sources = e_source_group_peek_sources (group);
+
+ for (s = sources; s; s = s->next) {
+ ESource *source = E_SOURCE (s->data);
+
+ if (!strcmp (e_source_peek_name (source), name))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+get_source_name (ESourceGroup *group, const gchar *path)
+{
+#ifndef G_OS_WIN32
+ gchar **p = g_strsplit (path, "/", 0);
+#else
+ gchar **p = g_strsplit_set (path, "\\/", 0);
+#endif
+ gint i, j, starting_index;
+ gint num_elements;
+ gboolean conflict;
+ GString *s = g_string_new ("");
+
+ for (i = 0; p[i]; i ++);
+
+ num_elements = i;
+ i--;
+
+ /* p[i] is now the last path element */
+
+ /* check if it conflicts */
+ starting_index = i;
+ do {
+ g_string_assign (s, "");
+ for (j = starting_index; j < num_elements; j += 2) {
+ if (j != starting_index)
+ g_string_append_c (s, '_');
+ g_string_append (s, p[j]);
+ }
+
+ conflict = check_for_conflict (group, s->str);
+
+
+ /* if there was a conflict back up 2 levels (skipping the /subfolder/ element) */
+ if (conflict)
+ starting_index -= 2;
+
+ /* we always break out if we can't go any further,
+ regardless of whether or not we conflict. */
+ if (starting_index < 0)
+ break;
+
+ } while (conflict);
+
+ g_strfreev (p);
+
+ return g_string_free (s, FALSE);
+}
+
+static void
+migrate_contacts (MigrationContext *context, EBook *old_book, EBook *new_book)
+{
+ EBookQuery *query = e_book_query_any_field_contains ("");
+ GList *l, *contacts;
+ gint num_added = 0;
+ gint num_contacts;
+
+ /* both books are loaded, start the actual migration */
+ e_book_get_contacts (old_book, query, &contacts, NULL);
+ e_book_query_unref (query);
+
+ num_contacts = g_list_length (contacts);
+ for (l = contacts; l; l = l->next) {
+ EContact *contact = l->data;
+ GError *e = NULL;
+ GList *attrs, *attr;
+
+ /* do some last minute massaging of the contact's attributes */
+
+ attrs = e_vcard_get_attributes (E_VCARD (contact));
+ for (attr = attrs; attr;) {
+ EVCardAttribute *a = attr->data;
+
+ /* evo 1.4 used the non-standard X-EVOLUTION-OFFICE attribute,
+ evo 1.5 uses the third element in the ORG list attribute. */
+ if (!strcmp ("X-EVOLUTION-OFFICE", e_vcard_attribute_get_name (a))) {
+ GList *v = e_vcard_attribute_get_values (a);
+ GList *next_attr;
+
+ if (v && v->data)
+ e_contact_set (contact, E_CONTACT_OFFICE, v->data);
+
+ next_attr = attr->next;
+ e_vcard_remove_attribute (E_VCARD (contact), a);
+ attr = next_attr;
+ }
+ /* evo 1.4 didn't put TYPE=VOICE in for phone numbers.
+ evo 1.5 does.
+
+ so we search through the attribute params for
+ either TYPE=VOICE or TYPE=FAX. If we find
+ either we do nothing. If we find neither, we
+ add TYPE=VOICE.
+ */
+ else if (!strcmp ("TEL", e_vcard_attribute_get_name (a))) {
+ GList *params, *param;
+ gboolean found = FALSE;
+
+ params = e_vcard_attribute_get_params (a);
+ for (param = params; param; param = param->next) {
+ EVCardAttributeParam *p = param->data;
+ if (!strcmp (EVC_TYPE, e_vcard_attribute_param_get_name (p))) {
+ GList *v = e_vcard_attribute_param_get_values (p);
+ while (v && v->data) {
+ if (!strcmp ("VOICE", v->data)
+ || !strcmp ("FAX", v->data)) {
+ found = TRUE;
+ break;
+ }
+ v = v->next;
+ }
+ }
+ }
+
+ if (!found)
+ e_vcard_attribute_add_param_with_value (a,
+ e_vcard_attribute_param_new (EVC_TYPE),
+ "VOICE");
+ attr = attr->next;
+ }
+ /* Replace "POSTAL" (1.4) addresses with "OTHER" (1.5) */
+ else if (!strcmp ("ADR", e_vcard_attribute_get_name (a))) {
+ GList *params, *param;
+ gboolean found = FALSE;
+ EVCardAttributeParam *p;
+
+ params = e_vcard_attribute_get_params (a);
+ for (param = params; param; param = param->next) {
+ p = param->data;
+ if (!strcmp (EVC_TYPE, e_vcard_attribute_param_get_name (p))) {
+ GList *v = e_vcard_attribute_param_get_values (p);
+ while (v && v->data ) {
+ if (!strcmp ("POSTAL", v->data)) {
+ found = TRUE;
+ break;
+ }
+ v = v->next;
+ }
+ if (found)
+ break;
+ }
+ }
+
+ if (found) {
+ e_vcard_attribute_param_remove_values (p);
+ e_vcard_attribute_param_add_value (p, "OTHER");
+ }
+
+ attr = attr->next;
+ }
+ /* this is kinda gross. The new vcard parser
+ needs ';'s to be escaped by \'s. but the
+ 1.4 vcard generator would put unescaped xml
+ (including entities like &gt;) in the value
+ of attributes, so we need to go through and
+ escape those ';'s. */
+ else if (!strcmp ("EMAIL", e_vcard_attribute_get_name (a))) {
+ GList *params;
+ GList *v = e_vcard_attribute_get_values (a);
+
+ /* Add TYPE=OTHER if there is no type set */
+ params = e_vcard_attribute_get_params (a);
+ if (!params)
+ e_vcard_attribute_add_param_with_value (a,
+ e_vcard_attribute_param_new (EVC_TYPE),
+ "OTHER");
+
+ if (v && v->data) {
+ if (!strncmp ((gchar *)v->data, "<?xml", 5)) {
+ /* k, this is the nasty part. we glomb all the
+ value strings back together again (if there is
+ more than one), then work our magic */
+ GString *str = g_string_new ("");
+ while (v) {
+ g_string_append (str, v->data);
+ if (v->next)
+ g_string_append_c (str, ';');
+ v = v->next;
+ }
+
+ e_vcard_attribute_remove_values (a);
+ e_vcard_attribute_add_value (a, str->str);
+ g_string_free (str, TRUE);
+ }
+ }
+
+ attr = attr->next;
+ }
+ else {
+ attr = attr->next;
+ }
+ }
+
+ if (!e_book_add_contact (new_book,
+ contact,
+ &e))
+ g_warning ("contact add failed: `%s'", e->message);
+
+ num_added ++;
+
+ dialog_set_progress (context, (double)num_added / num_contacts);
+ }
+
+ g_list_foreach (contacts, (GFunc)g_object_unref, NULL);
+ g_list_free (contacts);
+}
+
+static void
+migrate_contact_folder_to_source (MigrationContext *context, gchar *old_path, ESource *new_source)
+{
+ gchar *old_uri = g_filename_to_uri (old_path, NULL, NULL);
+ GError *e = NULL;
+
+ EBook *old_book = NULL, *new_book = NULL;
+ ESource *old_source;
+ ESourceGroup *group;
+
+ group = e_source_group_new ("", old_uri);
+ old_source = e_source_new ("", "");
+ e_source_group_add_source (group, old_source, -1);
+
+ dialog_set_folder_name (context, e_source_peek_name (new_source));
+
+ old_book = e_book_new (old_source, &e);
+ if (!old_book
+ || !e_book_open (old_book, TRUE, &e)) {
+ g_warning ("failed to load source book for migration: `%s'", e->message);
+ goto finish;
+ }
+
+ new_book = e_book_new (new_source, &e);
+ if (!new_book
+ || !e_book_open (new_book, FALSE, &e)) {
+ g_warning ("failed to load destination book for migration: `%s'", e->message);
+ goto finish;
+ }
+
+ migrate_contacts (context, old_book, new_book);
+
+ finish:
+ g_object_unref (old_source);
+ g_object_unref (group);
+ if (old_book)
+ g_object_unref (old_book);
+ if (new_book)
+ g_object_unref (new_book);
+ g_free (old_uri);
+}
+
+static void
+migrate_contact_folder (MigrationContext *context, gchar *old_path, ESourceGroup *dest_group, gchar *source_name)
+{
+ ESource *new_source;
+
+ new_source = e_source_new (source_name, source_name);
+ e_source_set_relative_uri (new_source, e_source_peek_uid (new_source));
+ e_source_group_add_source (dest_group, new_source, -1);
+
+ g_hash_table_insert (context->folder_uid_map, g_strdup (old_path), g_strdup (e_source_peek_uid (new_source)));
+
+ migrate_contact_folder_to_source (context, old_path, new_source);
+
+ g_object_unref (new_source);
+}
+
+#define LDAP_BASE_URI "ldap://"
+#define PERSONAL_RELATIVE_URI "system"
+
+static void
+create_groups (MigrationContext *context,
+ ESourceGroup **on_this_computer,
+ ESourceGroup **on_ldap_servers,
+ ESource **personal_source)
+{
+ GSList *groups;
+ ESourceGroup *group;
+ gchar *base_uri, *base_uri_proto;
+
+ *on_this_computer = NULL;
+ *on_ldap_servers = NULL;
+ *personal_source = NULL;
+
+ base_uri = g_build_filename (context->data_dir, "local", NULL);
+
+ base_uri_proto = g_filename_to_uri (base_uri, NULL, NULL);
+
+ groups = e_source_list_peek_groups (context->source_list);
+ if (groups) {
+ /* groups are already there, we need to search for things... */
+ GSList *g;
+
+ for (g = groups; g; g = g->next) {
+
+ group = E_SOURCE_GROUP (g->data);
+
+ if (!*on_this_computer && !strcmp (base_uri_proto, e_source_group_peek_base_uri (group)))
+ *on_this_computer = g_object_ref (group);
+ else if (!*on_ldap_servers && !strcmp (LDAP_BASE_URI, e_source_group_peek_base_uri (group)))
+ *on_ldap_servers = g_object_ref (group);
+ }
+ }
+
+ if (*on_this_computer) {
+ /* make sure "Personal" shows up as a source under
+ this group */
+ GSList *sources = e_source_group_peek_sources (*on_this_computer);
+ GSList *s;
+ for (s = sources; s; s = s->next) {
+ ESource *source = E_SOURCE (s->data);
+ const gchar *relative_uri;
+
+ relative_uri = e_source_peek_relative_uri (source);
+ if (relative_uri == NULL)
+ continue;
+ if (!strcmp (PERSONAL_RELATIVE_URI, relative_uri)) {
+ *personal_source = g_object_ref (source);
+ break;
+ }
+ }
+ }
+ else {
+ /* create the local source group */
+ group = e_source_group_new (_("On This Computer"), base_uri_proto);
+ e_source_list_add_group (context->source_list, group, -1);
+
+ *on_this_computer = group;
+ }
+
+ if (!*personal_source) {
+ /* Create the default Person addressbook */
+ ESource *source = e_source_new (_("Personal"), PERSONAL_RELATIVE_URI);
+ e_source_group_add_source (*on_this_computer, source, -1);
+
+ e_source_set_property (source, "completion", "true");
+
+ *personal_source = source;
+ }
+
+ if (!*on_ldap_servers) {
+ /* Create the LDAP source group */
+ group = e_source_group_new (_("On LDAP Servers"), LDAP_BASE_URI);
+ e_source_list_add_group (context->source_list, group, -1);
+
+ *on_ldap_servers = group;
+ }
+
+ g_free (base_uri_proto);
+ g_free (base_uri);
+}
+
+static gboolean
+migrate_local_folders (MigrationContext *context, ESourceGroup *on_this_computer, ESource *personal_source)
+{
+ gchar *old_path = NULL;
+ GSList *dirs, *l;
+ gchar *local_contact_folder = NULL;
+
+ old_path = g_strdup_printf ("%s/evolution/local", g_get_home_dir ());
+
+ dirs = e_folder_map_local_folders (old_path, "contacts");
+
+ /* migrate the local addressbook first, to local/system */
+ local_contact_folder = g_build_filename (g_get_home_dir (),
+ "evolution", "local", "Contacts",
+ NULL);
+
+ for (l = dirs; l; l = l->next) {
+ gchar *source_name;
+ /* we handle the system folder differently */
+ if (personal_source && !strcmp ((gchar *)l->data, local_contact_folder)) {
+ g_hash_table_insert (context->folder_uid_map, g_strdup (l->data), g_strdup (e_source_peek_uid (personal_source)));
+ migrate_contact_folder_to_source (context, local_contact_folder, personal_source);
+ continue;
+ }
+
+ source_name = get_source_name (on_this_computer, (gchar *)l->data);
+ migrate_contact_folder (context, l->data, on_this_computer, source_name);
+ g_free (source_name);
+ }
+
+ g_slist_foreach (dirs, (GFunc)g_free, NULL);
+ g_slist_free (dirs);
+ g_free (local_contact_folder);
+ g_free (old_path);
+
+ return TRUE;
+}
+
+static gchar *
+get_string_child (xmlNode *node,
+ const gchar *name)
+{
+ xmlNode *p;
+ xmlChar *xml_string;
+ gchar *retval;
+
+ p = e_xml_get_child_by_name (node, (xmlChar *) name);
+ if (p == NULL)
+ return NULL;
+
+ p = e_xml_get_child_by_name (p, (xmlChar *) "text");
+ if (p == NULL) /* there's no text between the tags, return the empty string */
+ return g_strdup("");
+
+ xml_string = xmlNodeListGetString (node->doc, p, 1);
+ retval = g_strdup ((gchar *) xml_string);
+ xmlFree (xml_string);
+
+ return retval;
+}
+
+static gint
+get_integer_child (xmlNode *node,
+ const gchar *name,
+ gint defval)
+{
+ xmlNode *p;
+ xmlChar *xml_string;
+ gint retval;
+
+ p = e_xml_get_child_by_name (node, (xmlChar *) name);
+ if (p == NULL)
+ return defval;
+
+ p = e_xml_get_child_by_name (p, (xmlChar *) "text");
+ if (p == NULL) /* there's no text between the tags, return the default */
+ return defval;
+
+ xml_string = xmlNodeListGetString (node->doc, p, 1);
+ retval = atoi ((gchar *)xml_string);
+ xmlFree (xml_string);
+
+ return retval;
+}
+
+static gboolean
+migrate_ldap_servers (MigrationContext *context, ESourceGroup *on_ldap_servers)
+{
+ gchar *sources_xml = g_strdup_printf ("%s/evolution/addressbook-sources.xml",
+ g_get_home_dir ());
+
+ printf ("trying to migrate from %s\n", sources_xml);
+
+ if (g_file_test (sources_xml, G_FILE_TEST_EXISTS)) {
+ xmlDoc *doc = xmlParseFile (sources_xml);
+ xmlNode *root;
+ xmlNode *child;
+ gint num_contactservers;
+ gint servernum;
+
+ if (!doc)
+ return FALSE;
+
+ root = xmlDocGetRootElement (doc);
+ if (root == NULL || strcmp ((const gchar *)root->name, "addressbooks") != 0) {
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ /* count the number of servers, so we can give progress */
+ num_contactservers = 0;
+ for (child = root->children; child; child = child->next) {
+ if (!strcmp ((const gchar *)child->name, "contactserver")) {
+ num_contactservers++;
+ }
+ }
+ printf ("found %d contact servers to migrate\n", num_contactservers);
+
+ dialog_set_folder_name (context, _("LDAP Servers"));
+
+ servernum = 0;
+ for (child = root->children; child; child = child->next) {
+ if (!strcmp ((const gchar *)child->name, "contactserver")) {
+ gchar *port, *host, *rootdn, *scope, *authmethod, *ssl;
+ gchar *emailaddr, *binddn, *limitstr;
+ gint limit;
+ gchar *name, *description;
+ GString *uri = g_string_new ("");
+ ESource *source;
+
+ name = get_string_child (child, "name");
+ description = get_string_child (child, "description");
+ port = get_string_child (child, "port");
+ host = get_string_child (child, "host");
+ rootdn = get_string_child (child, "rootdn");
+ scope = get_string_child (child, "scope");
+ authmethod = get_string_child (child, "authmethod");
+ ssl = get_string_child (child, "ssl");
+ emailaddr = get_string_child (child, "emailaddr");
+ binddn = get_string_child (child, "binddn");
+ limit = get_integer_child (child, "limit", 100);
+ limitstr = g_strdup_printf ("%d", limit);
+
+ g_string_append_printf (uri,
+ "%s:%s/%s?"/*trigraph prevention*/"?%s",
+ host, port, rootdn, scope);
+
+ source = e_source_new (name, uri->str);
+ e_source_set_property (source, "description", description);
+ e_source_set_property (source, "limit", limitstr);
+ e_source_set_property (source, "ssl", ssl);
+ e_source_set_property (source, "auth", authmethod);
+ if (emailaddr)
+ e_source_set_property (source, "email_addr", emailaddr);
+ if (binddn)
+ e_source_set_property (source, "binddn", binddn);
+
+ e_source_group_add_source (on_ldap_servers, source, -1);
+
+ g_string_free (uri, TRUE);
+ g_free (port);
+ g_free (host);
+ g_free (rootdn);
+ g_free (scope);
+ g_free (authmethod);
+ g_free (ssl);
+ g_free (emailaddr);
+ g_free (binddn);
+ g_free (limitstr);
+ g_free (name);
+ g_free (description);
+
+ servernum++;
+ dialog_set_progress (context, (double)servernum/num_contactservers);
+ }
+ }
+
+ xmlFreeDoc (doc);
+ }
+
+ g_free (sources_xml);
+
+ return TRUE;
+}
+
+static ESource*
+get_source_by_name (ESourceList *source_list, const gchar *name)
+{
+ GSList *groups;
+ GSList *g;
+
+ groups = e_source_list_peek_groups (source_list);
+ if (!groups)
+ return NULL;
+
+ for (g = groups; g; g = g->next) {
+ GSList *sources;
+ GSList *s;
+ ESourceGroup *group = E_SOURCE_GROUP (g->data);
+
+ sources = e_source_group_peek_sources (group);
+ if (!sources)
+ continue;
+
+ for (s = sources; s; s = s->next) {
+ ESource *source = E_SOURCE (s->data);
+ const gchar *source_name = e_source_peek_name (source);
+
+ if (!strcmp (name, source_name))
+ return source;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+migrate_completion_folders (MigrationContext *context)
+{
+ GConfClient *client;
+ const gchar *key;
+ gchar *uris_xml;
+
+ printf ("trying to migrate completion folders\n");
+
+ client = gconf_client_get_default ();
+ key = "/apps/evolution/addressbook/completion/uris";
+ uris_xml = gconf_client_get_string (client, key, NULL);
+ g_object_unref (client);
+
+ if (uris_xml) {
+ xmlDoc *doc = xmlParseMemory (uris_xml, strlen (uris_xml));
+ xmlNode *root;
+ xmlNode *child;
+
+ if (!doc)
+ return FALSE;
+
+ dialog_set_folder_name (context, _("Autocompletion Settings"));
+
+ root = xmlDocGetRootElement (doc);
+ if (root == NULL || strcmp ((const gchar *)root->name, "EvolutionFolderList") != 0) {
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ for (child = root->children; child; child = child->next) {
+ if (!strcmp ((const gchar *)child->name, "folder")) {
+ gchar *physical_uri = e_xml_get_string_prop_by_name (child, (const guchar *)"physical-uri");
+ ESource *source = NULL;
+
+ /* if the physical uri is file://...
+ we look it up in our folder_uid_map
+ hashtable. If it's a folder we
+ converted over, we should get back
+ a uid we can search for.
+
+ if the physical_uri is anything
+ else, we strip off the args
+ (anything after;) before searching
+ for the uri. */
+
+ if (!strncmp (physical_uri, "file://", 7)) {
+ gchar *filename = g_filename_from_uri (physical_uri, NULL, NULL);
+ gchar *uid = NULL;
+
+ if (filename)
+ uid = g_hash_table_lookup (context->folder_uid_map,
+ filename);
+ g_free (filename);
+ if (uid)
+ source = e_source_list_peek_source_by_uid (context->source_list, uid);
+ }
+ else {
+ gchar *name = e_xml_get_string_prop_by_name (child, (const guchar *)"display-name");
+
+ source = get_source_by_name (context->source_list, name);
+
+ g_free (name);
+ }
+
+ if (source) {
+ e_source_set_property (source, "completion", "true");
+ }
+ else {
+ g_warning ("found completion folder with uri `%s' that "
+ "doesn't correspond to anything we migrated.", physical_uri);
+ }
+
+ g_free (physical_uri);
+ }
+ }
+
+ g_free (uris_xml);
+ }
+ else {
+ g_message ("no completion folder settings to migrate");
+ }
+
+ return TRUE;
+}
+
+static void
+migrate_contact_lists_for_local_folders (MigrationContext *context, ESourceGroup *on_this_computer)
+{
+ GSList *sources, *s;
+
+ sources = e_source_group_peek_sources (on_this_computer);
+ for (s = sources; s; s = s->next) {
+ ESource *source = s->data;
+ EBook *book;
+ EBookQuery *query;
+ GList *l, *contacts;
+ gint num_contacts, num_converted;
+
+ dialog_set_folder_name (context, e_source_peek_name (source));
+
+ book = e_book_new (source, NULL);
+ if (!book
+ || !e_book_open (book, TRUE, NULL)) {
+ gchar *uri = e_source_get_uri (source);
+ g_warning ("failed to migrate contact lists for source %s", uri);
+ g_free (uri);
+ continue;
+ }
+
+ query = e_book_query_any_field_contains ("");
+ e_book_get_contacts (book, query, &contacts, NULL);
+ e_book_query_unref (query);
+
+ num_converted = 0;
+ num_contacts = g_list_length (contacts);
+ for (l = contacts; l; l = l->next) {
+ EContact *contact = l->data;
+ GError *e = NULL;
+ GList *attrs, *attr;
+ gboolean converted = FALSE;
+
+ attrs = e_contact_get_attributes (contact, E_CONTACT_EMAIL);
+ for (attr = attrs; attr; attr = attr->next) {
+ EVCardAttribute *a = attr->data;
+ GList *v = e_vcard_attribute_get_values (a);
+
+ if (v && v->data) {
+ if (!strncmp ((gchar *)v->data, "<?xml", 5)) {
+ EDestination *dest = e_destination_import ((gchar *)v->data);
+
+ e_destination_export_to_vcard_attribute (dest, a);
+
+ g_object_unref (dest);
+
+ converted = TRUE;
+ }
+ }
+ }
+
+ if (converted) {
+ e_contact_set_attributes (contact, E_CONTACT_EMAIL, attrs);
+
+ if (!e_book_commit_contact (book,
+ contact,
+ &e))
+ g_warning ("contact commit failed: `%s'", e->message);
+ }
+
+ num_converted ++;
+
+ dialog_set_progress (context, (double)num_converted / num_contacts);
+ }
+
+ g_list_foreach (contacts, (GFunc)g_object_unref, NULL);
+ g_list_free (contacts);
+
+ g_object_unref (book);
+ }
+}
+
+static void
+migrate_company_phone_for_local_folders (MigrationContext *context, ESourceGroup *on_this_computer)
+{
+ GSList *sources, *s;
+
+ sources = e_source_group_peek_sources (on_this_computer);
+ for (s = sources; s; s = s->next) {
+ ESource *source = s->data;
+ EBook *book;
+ EBookQuery *query;
+ GList *l, *contacts;
+ gint num_contacts, num_converted;
+
+ dialog_set_folder_name (context, e_source_peek_name (source));
+
+ book = e_book_new (source, NULL);
+ if (!book
+ || !e_book_open (book, TRUE, NULL)) {
+ gchar *uri = e_source_get_uri (source);
+ g_warning ("failed to migrate company phone numbers for source %s", uri);
+ g_free (uri);
+ continue;
+ }
+
+ query = e_book_query_any_field_contains ("");
+ e_book_get_contacts (book, query, &contacts, NULL);
+ e_book_query_unref (query);
+
+ num_converted = 0;
+ num_contacts = g_list_length (contacts);
+ for (l = contacts; l; l = l->next) {
+ EContact *contact = l->data;
+ GError *e = NULL;
+ GList *attrs, *attr;
+ gboolean converted = FALSE;
+ gint num_work_voice = 0;
+
+ attrs = e_vcard_get_attributes (E_VCARD (contact));
+ for (attr = attrs; attr;) {
+ EVCardAttribute *a = attr->data;
+ GList *next_attr = attr->next;
+
+ if (!strcmp ("TEL", e_vcard_attribute_get_name (a))) {
+ GList *params, *param;
+ gboolean found_voice = FALSE;
+ gboolean found_work = FALSE;
+
+ params = e_vcard_attribute_get_params (a);
+ for (param = params; param; param = param->next) {
+ EVCardAttributeParam *p = param->data;
+ if (!strcmp (EVC_TYPE, e_vcard_attribute_param_get_name (p))) {
+ GList *v = e_vcard_attribute_param_get_values (p);
+ while (v && v->data) {
+ if (!strcmp ("VOICE", v->data))
+ found_voice = TRUE;
+ else if (!strcmp ("WORK", v->data))
+ found_work = TRUE;
+ v = v->next;
+ }
+ }
+
+ if (found_work && found_voice)
+ num_work_voice++;
+
+ if (num_work_voice == 3) {
+ GList *v = e_vcard_attribute_get_values (a);
+
+ if (v && v->data)
+ e_contact_set (contact, E_CONTACT_PHONE_COMPANY, v->data);
+
+ e_vcard_remove_attribute (E_VCARD (contact), a);
+
+ converted = TRUE;
+ break;
+ }
+ }
+ }
+
+ attr = next_attr;
+
+ if (converted)
+ break;
+ }
+
+ if (converted) {
+ if (!e_book_commit_contact (book,
+ contact,
+ &e))
+ g_warning ("contact commit failed: `%s'", e->message);
+ }
+
+ num_converted ++;
+
+ dialog_set_progress (context, (double)num_converted / num_contacts);
+ }
+
+ g_list_foreach (contacts, (GFunc)g_object_unref, NULL);
+ g_list_free (contacts);
+
+ g_object_unref (book);
+ }
+}
+
+static void
+migrate_pilot_data (const gchar *old_path, const gchar *new_path)
+{
+ const gchar *dent;
+ const gchar *ext;
+ gchar *filename;
+ GDir *dir;
+
+ if (!(dir = g_dir_open (old_path, 0, NULL)))
+ return;
+
+ while ((dent = g_dir_read_name (dir))) {
+ if ((!strncmp (dent, "pilot-map-", 10) &&
+ ((ext = strrchr (dent, '.')) && !strcmp (ext, ".xml"))) ||
+ (!strncmp (dent, "pilot-sync-evolution-addressbook-", 33) &&
+ ((ext = strrchr (dent, '.')) && !strcmp (ext, ".db")))) {
+ /* src and dest file formats are identical for both map and changelog files */
+ guchar inbuf[4096];
+ gsize nread, nwritten;
+ gint fd0, fd1;
+ gssize n;
+
+ filename = g_build_filename (old_path, dent, NULL);
+ if ((fd0 = g_open (filename, O_RDONLY | O_BINARY, 0)) == -1) {
+ g_free (filename);
+ continue;
+ }
+
+ g_free (filename);
+ filename = g_build_filename (new_path, dent, NULL);
+ if ((fd1 = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) == -1) {
+ g_free (filename);
+ close (fd0);
+ continue;
+ }
+
+ do {
+ do {
+ n = read (fd0, inbuf, sizeof (inbuf));
+ } while (n == -1 && errno == EINTR);
+
+ if (n < 1)
+ break;
+
+ nread = n;
+ nwritten = 0;
+ do {
+ do {
+ n = write (fd1, inbuf + nwritten, nread - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } while (nwritten < nread && n != -1);
+
+ if (n == -1)
+ break;
+ } while (1);
+
+ if (n != -1)
+ n = fsync (fd1);
+
+ if (n == -1) {
+ g_warning ("Failed to migrate %s: %s", dent, g_strerror (errno));
+ g_unlink (filename);
+ }
+
+ close (fd0);
+ close (fd1);
+ g_free (filename);
+ }
+ }
+
+ g_dir_close (dir);
+}
+
+static MigrationContext *
+migration_context_new (const gchar *data_dir)
+{
+ MigrationContext *context = g_new (MigrationContext, 1);
+
+ /* set up the mapping from old uris to new uids */
+ context->folder_uid_map = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ e_book_get_addressbooks (&context->source_list, NULL);
+
+ context->data_dir = data_dir;
+
+ return context;
+}
+
+static void
+migration_context_free (MigrationContext *context)
+{
+ e_source_list_sync (context->source_list, NULL);
+
+ g_hash_table_destroy (context->folder_uid_map);
+
+ g_object_unref (context->source_list);
+
+ g_free (context);
+}
+
+gboolean
+e_book_shell_backend_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error)
+{
+ ESourceGroup *on_this_computer;
+ ESourceGroup *on_ldap_servers;
+ ESource *personal_source;
+ MigrationContext *context;
+ gboolean need_dialog = FALSE;
+ const gchar *data_dir;
+
+ g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), FALSE);
+
+ data_dir = e_shell_backend_get_data_dir (shell_backend);
+ context = migration_context_new (data_dir);
+
+ /* we call this unconditionally now - create_groups either
+ creates the groups/sources or it finds the necessary
+ groups/sources. */
+ create_groups (context, &on_this_computer, &on_ldap_servers, &personal_source);
+
+ /* figure out if we need the dialog displayed */
+ if (major == 1
+ /* we only need the most recent upgrade point here.
+ further decomposition will happen below. */
+ && (minor < 5 || (minor == 5 && micro <= 10)))
+ need_dialog = TRUE;
+
+ if (need_dialog)
+ setup_progress_dialog (context);
+
+ if (major == 1) {
+
+ if (minor < 5 || (minor == 5 && micro <= 2)) {
+ /* initialize our dialog */
+ dialog_set_label (context,
+ _("The location and hierarchy of the Evolution contact "
+ "folders has changed since Evolution 1.x.\n\nPlease be "
+ "patient while Evolution migrates your folders..."));
+
+ if (on_this_computer)
+ migrate_local_folders (context, on_this_computer, personal_source);
+ if (on_ldap_servers)
+ migrate_ldap_servers (context, on_ldap_servers);
+
+ migrate_completion_folders (context);
+ }
+
+ if (minor < 5 || (minor == 5 && micro <= 7)) {
+ dialog_set_label (context,
+ _("The format of mailing list contacts has changed.\n\n"
+ "Please be patient while Evolution migrates your "
+ "folders..."));
+
+ migrate_contact_lists_for_local_folders (context, on_this_computer);
+ }
+
+ if (minor < 5 || (minor == 5 && micro <= 8)) {
+ dialog_set_label (context,
+ _("The way Evolution stores some phone numbers has changed.\n\n"
+ "Please be patient while Evolution migrates your "
+ "folders..."));
+
+ migrate_company_phone_for_local_folders (context, on_this_computer);
+ }
+
+ if (minor < 5 || (minor == 5 && micro <= 10)) {
+ gchar *old_path, *new_path;
+
+ dialog_set_label (context, _("Evolution's Palm Sync changelog and map files have changed.\n\n"
+ "Please be patient while Evolution migrates your Pilot Sync data..."));
+
+ old_path = g_build_filename (g_get_home_dir (), "evolution", "local", "Contacts", NULL);
+ new_path = g_build_filename (data_dir, "local", "system", NULL);
+ migrate_pilot_data (old_path, new_path);
+ g_free (new_path);
+ g_free (old_path);
+ }
+
+ /* we only need to do this next step if people ran
+ older versions of 1.5. We need to clear out the
+ absolute URI's that were assigned to ESources
+ during one phase of development, as they take
+ precedent over relative uris (but aren't updated
+ when editing an ESource). */
+ if (minor == 5 && micro <= 11) {
+ GSList *g;
+ for (g = e_source_list_peek_groups (context->source_list); g; g = g->next) {
+ ESourceGroup *group = g->data;
+ GSList *s;
+
+ for (s = e_source_group_peek_sources (group); s; s = s->next) {
+ ESource *source = s->data;
+ e_source_set_absolute_uri (source, NULL);
+ }
+ }
+ }
+ }
+
+ if (need_dialog)
+ dialog_close (context);
+
+ if (on_this_computer)
+ g_object_unref (on_this_computer);
+ if (on_ldap_servers)
+ g_object_unref (on_ldap_servers);
+ if (personal_source)
+ g_object_unref (personal_source);
+
+
+ migration_context_free (context);
+
+ return TRUE;
+}
diff --git a/modules/addressbook/e-book-shell-migrate.h b/modules/addressbook/e-book-shell-migrate.h
new file mode 100644
index 0000000000..cb6128910a
--- /dev/null
+++ b/modules/addressbook/e-book-shell-migrate.h
@@ -0,0 +1,41 @@
+/*
+ * e-book-shell-migrate.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Toshok (toshok@ximian.com)
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_BOOK_SHELL_MIGRATE_H
+#define E_BOOK_SHELL_MIGRATE_H
+
+#include <glib.h>
+#include <shell/e-shell-backend.h>
+
+G_BEGIN_DECLS
+
+gboolean e_book_shell_backend_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_BOOK_SHELL_MIGRATE_H */
diff --git a/modules/addressbook/e-book-shell-sidebar.c b/modules/addressbook/e-book-shell-sidebar.c
new file mode 100644
index 0000000000..fc283e28d7
--- /dev/null
+++ b/modules/addressbook/e-book-shell-sidebar.c
@@ -0,0 +1,232 @@
+/*
+ * e-book-shell-sidebar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-book-shell-sidebar.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "e-book-shell-view.h"
+#include "e-book-shell-backend.h"
+#include "e-addressbook-selector.h"
+
+#define E_BOOK_SHELL_SIDEBAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_BOOK_SHELL_SIDEBAR, EBookShellSidebarPrivate))
+
+struct _EBookShellSidebarPrivate {
+ GtkWidget *selector;
+};
+
+enum {
+ PROP_0,
+ PROP_SELECTOR
+};
+
+static gpointer parent_class;
+static GType book_shell_sidebar_type;
+
+static void
+book_shell_sidebar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SELECTOR:
+ g_value_set_object (
+ value, e_book_shell_sidebar_get_selector (
+ E_BOOK_SHELL_SIDEBAR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+book_shell_sidebar_dispose (GObject *object)
+{
+ EBookShellSidebarPrivate *priv;
+
+ priv = E_BOOK_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ if (priv->selector != NULL) {
+ g_object_unref (priv->selector);
+ priv->selector = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+book_shell_sidebar_constructed (GObject *object)
+{
+ EBookShellSidebarPrivate *priv;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+ EShellSidebar *shell_sidebar;
+ ESourceList *source_list;
+ GtkContainer *container;
+ GtkWidget *widget;
+
+ priv = E_BOOK_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ shell_sidebar = E_SHELL_SIDEBAR (object);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+
+ source_list = e_book_shell_backend_get_source_list (
+ E_BOOK_SHELL_BACKEND (shell_backend));
+
+ container = GTK_CONTAINER (shell_sidebar);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_container_add (container, widget);
+ gtk_widget_show (widget);
+
+ container = GTK_CONTAINER (widget);
+
+ widget = e_addressbook_selector_new (source_list);
+ e_source_selector_show_selection (E_SOURCE_SELECTOR (widget), FALSE);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->selector = g_object_ref (widget);
+ gtk_widget_show (widget);
+}
+
+static guint32
+book_shell_sidebar_check_state (EShellSidebar *shell_sidebar)
+{
+ EBookShellSidebar *book_shell_sidebar;
+ ESourceSelector *selector;
+ ESource *source;
+ gboolean is_system = FALSE;
+ guint32 state = 0;
+
+ book_shell_sidebar = E_BOOK_SHELL_SIDEBAR (shell_sidebar);
+ selector = e_book_shell_sidebar_get_selector (book_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+
+ if (source != NULL) {
+ const gchar *uri;
+
+ uri = e_source_peek_relative_uri (source);
+ is_system = (uri == NULL || strcmp (uri, "system") == 0);
+ }
+
+ if (source != NULL)
+ state |= E_BOOK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE;
+ if (is_system)
+ state |= E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM;
+
+ return state;
+}
+
+static void
+book_shell_sidebar_class_init (EBookShellSidebarClass *class)
+{
+ GObjectClass *object_class;
+ EShellSidebarClass *shell_sidebar_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EBookShellSidebarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = book_shell_sidebar_get_property;
+ object_class->dispose = book_shell_sidebar_dispose;
+ object_class->constructed = book_shell_sidebar_constructed;
+
+ shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class);
+ shell_sidebar_class->check_state = book_shell_sidebar_check_state;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SELECTOR,
+ g_param_spec_object (
+ "selector",
+ _("Source Selector Widget"),
+ _("This widget displays groups of address books"),
+ E_TYPE_SOURCE_SELECTOR,
+ G_PARAM_READABLE));
+}
+
+static void
+book_shell_sidebar_init (EBookShellSidebar *book_shell_sidebar)
+{
+ book_shell_sidebar->priv =
+ E_BOOK_SHELL_SIDEBAR_GET_PRIVATE (book_shell_sidebar);
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_book_shell_sidebar_get_type (void)
+{
+ return book_shell_sidebar_type;
+}
+
+void
+e_book_shell_sidebar_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (EBookShellSidebarClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) book_shell_sidebar_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EBookShellSidebar),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) book_shell_sidebar_init,
+ NULL /* value_table */
+ };
+
+ book_shell_sidebar_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_SIDEBAR,
+ "EBookShellSidebar", &type_info, 0);
+}
+
+GtkWidget *
+e_book_shell_sidebar_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_BOOK_SHELL_SIDEBAR,
+ "shell-view", shell_view, NULL);
+}
+
+ESourceSelector *
+e_book_shell_sidebar_get_selector (EBookShellSidebar *book_shell_sidebar)
+{
+ g_return_val_if_fail (
+ E_IS_BOOK_SHELL_SIDEBAR (book_shell_sidebar), NULL);
+
+ return E_SOURCE_SELECTOR (book_shell_sidebar->priv->selector);
+}
diff --git a/modules/addressbook/e-book-shell-sidebar.h b/modules/addressbook/e-book-shell-sidebar.h
new file mode 100644
index 0000000000..716523f971
--- /dev/null
+++ b/modules/addressbook/e-book-shell-sidebar.h
@@ -0,0 +1,79 @@
+/*
+ * e-book-shell-sidebar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_BOOK_SHELL_SIDEBAR_H
+#define E_BOOK_SHELL_SIDEBAR_H
+
+#include <libedataserverui/e-source-selector.h>
+
+#include <shell/e-shell-sidebar.h>
+#include <shell/e-shell-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_BOOK_SHELL_SIDEBAR \
+ (e_book_shell_sidebar_get_type ())
+#define E_BOOK_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_BOOK_SHELL_SIDEBAR, EBookShellSidebar))
+#define E_BOOK_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_BOOK_SHELL_SIDEBAR, EBookShellSidebarClass))
+#define E_IS_BOOK_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_BOOK_SHELL_SIDEBAR))
+#define E_IS_BOOK_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_BOOK_SHELL_SIDEBAR))
+#define E_BOOK_SHELL_SIDEBAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_BOOK_SHELL_SIDEBAR, EBookShellSidebarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EBookShellSidebar EBookShellSidebar;
+typedef struct _EBookShellSidebarClass EBookShellSidebarClass;
+typedef struct _EBookShellSidebarPrivate EBookShellSidebarPrivate;
+
+enum {
+ E_BOOK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE = 1 << 0,
+ E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM = 1 << 1
+};
+
+struct _EBookShellSidebar {
+ EShellSidebar parent;
+ EBookShellSidebarPrivate *priv;
+};
+
+struct _EBookShellSidebarClass {
+ EShellSidebarClass parent_class;
+};
+
+GType e_book_shell_sidebar_get_type (void);
+void e_book_shell_sidebar_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_book_shell_sidebar_new(EShellView *shell_view);
+ESourceSelector *
+ e_book_shell_sidebar_get_selector
+ (EBookShellSidebar *book_shell_sidebar);
+
+G_END_DECLS
+
+#endif /* E_BOOK_SHELL_SIDEBAR_H */
diff --git a/modules/addressbook/e-book-shell-view-actions.c b/modules/addressbook/e-book-shell-view-actions.c
new file mode 100644
index 0000000000..773eb3fa7a
--- /dev/null
+++ b/modules/addressbook/e-book-shell-view-actions.c
@@ -0,0 +1,1011 @@
+/*
+ * e-book-shell-view-actions.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-book-shell-view-private.h"
+
+#include <e-util/e-error.h>
+#include <e-util/e-util.h>
+#include <filter/filter-rule.h>
+
+#include <addressbook-config.h>
+
+static void
+action_address_book_copy_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_copy_to_folder (view, TRUE);
+}
+
+static void
+action_address_book_delete_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EBookShellBackend *book_shell_backend;
+ EBookShellSidebar *book_shell_sidebar;
+ ESource *source;
+ ESourceSelector *selector;
+ ESourceGroup *source_group;
+ ESourceList *source_list;
+ EBook *book;
+ gint response;
+ GError *error = NULL;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ book_shell_backend = book_shell_view->priv->book_shell_backend;
+ source_list = e_book_shell_backend_get_source_list (book_shell_backend);
+
+ book_shell_sidebar = book_shell_view->priv->book_shell_sidebar;
+ selector = e_book_shell_sidebar_get_selector (book_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (source != NULL);
+
+ response = e_error_run (
+ GTK_WINDOW (shell_window),
+ "addressbook:ask-delete-addressbook",
+ e_source_peek_name (source));
+
+ if (response != GTK_RESPONSE_YES)
+ return;
+
+ book = e_book_new (source, &error);
+ if (error != NULL) {
+ g_warning ("Error removing addressbook: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (!e_book_remove (book, NULL)) {
+ e_error_run (
+ GTK_WINDOW (shell_window),
+ "addressbook:remove-addressbook", NULL);
+ g_object_unref (book);
+ return;
+ }
+
+ if (e_source_selector_source_is_selected (selector, source))
+ e_source_selector_unselect_source (selector, source);
+
+ source_group = e_source_peek_group (source);
+ e_source_group_remove_source (source_group, source);
+
+ e_source_list_sync (source_list, NULL);
+
+ g_object_unref (book);
+}
+
+static void
+action_address_book_move_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_move_to_folder (view, TRUE);
+}
+
+static void
+action_address_book_new_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ addressbook_config_create_new_source (GTK_WIDGET (shell_window));
+}
+
+static void
+action_address_book_properties_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EBookShellSidebar *book_shell_sidebar;
+ ESource *source;
+ ESourceSelector *selector;
+ EditorUidClosure *closure;
+ GHashTable *uid_to_editor;
+ const gchar *uid;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ book_shell_sidebar = book_shell_view->priv->book_shell_sidebar;
+ selector = e_book_shell_sidebar_get_selector (book_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (source != NULL);
+
+ uid = e_source_peek_uid (source);
+ uid_to_editor = book_shell_view->priv->uid_to_editor;
+
+ closure = g_hash_table_lookup (uid_to_editor, uid);
+ if (closure == NULL) {
+ GtkWidget *editor;
+
+ editor = addressbook_config_edit_source (
+ GTK_WIDGET (shell_window), source);
+
+ closure = g_new (EditorUidClosure, 1);
+ closure->editor = editor;
+ closure->uid = g_strdup (uid);
+ closure->view = book_shell_view;
+
+ g_hash_table_insert (uid_to_editor, closure->uid, closure);
+
+ g_object_weak_ref (
+ G_OBJECT (closure->editor), (GWeakNotify)
+ e_book_shell_view_editor_weak_notify, closure);
+ }
+
+ gtk_window_present (GTK_WINDOW (closure->editor));
+}
+
+static void
+action_address_book_rename_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellSidebar *book_shell_sidebar;
+ ESourceSelector *selector;
+
+ book_shell_sidebar = book_shell_view->priv->book_shell_sidebar;
+ selector = e_book_shell_sidebar_get_selector (book_shell_sidebar);
+
+ e_source_selector_edit_primary_selection (selector);
+}
+
+static void
+action_address_book_save_as_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_save_as (view, TRUE);
+}
+
+static void
+action_address_book_stop_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_stop (view);
+}
+
+static void
+action_contact_clipboard_copy_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ e_book_shell_content_clipboard_copy (book_shell_content);
+}
+
+static void
+action_contact_clipboard_cut_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_cut (view);
+}
+
+static void
+action_contact_clipboard_paste_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_paste (view);
+}
+
+static void
+action_contact_copy_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_copy_to_folder (view, FALSE);
+}
+
+static void
+action_contact_delete_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_delete_selection (view, TRUE);
+}
+
+static void
+action_contact_forward_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+ GList *list, *iter;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ list = e_addressbook_view_get_selected (view);
+ g_return_if_fail (list != NULL);
+
+ /* Convert the list of contacts to a list of destinations. */
+ for (iter = list; iter != NULL; iter = iter->next) {
+ EContact *contact = iter->data;
+ EDestination *destination;
+
+ destination = e_destination_new ();
+ e_destination_set_contact (destination, contact, 0);
+ g_object_unref (contact);
+
+ iter->data = destination;
+ }
+
+ eab_send_as_attachment (list);
+ g_list_foreach (list, (GFunc) g_object_unref, NULL);
+ g_list_free (list);
+}
+
+static void
+action_contact_move_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_move_to_folder (view, FALSE);
+}
+
+static void
+action_contact_new_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+ EAddressbookModel *model;
+ EContact *contact;
+ GtkWidget *editor;
+ EBook *book;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ model = e_addressbook_view_get_model (view);
+ book = e_addressbook_model_get_book (model);
+ g_return_if_fail (book != NULL);
+
+ contact = e_contact_new ();
+ editor = e_contact_editor_new (book, contact, TRUE, TRUE);
+ eab_editor_show (EAB_EDITOR (editor));
+ g_object_unref (contact);
+}
+
+static void
+action_contact_new_list_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+ EAddressbookModel *model;
+ EContact *contact;
+ GtkWidget *editor;
+ EBook *book;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ model = e_addressbook_view_get_model (view);
+ book = e_addressbook_model_get_book (model);
+ g_return_if_fail (book != NULL);
+
+ contact = e_contact_new ();
+ editor = e_contact_list_editor_new (book, contact, TRUE, TRUE);
+ eab_editor_show (EAB_EDITOR (editor));
+ g_object_unref (contact);
+}
+
+static void
+action_contact_open_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_view (view);
+}
+
+static void
+action_contact_preview_cb (GtkToggleAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ gboolean visible;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ visible = gtk_toggle_action_get_active (action);
+ e_book_shell_content_set_preview_visible (book_shell_content, visible);
+}
+
+static void
+action_contact_print_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+ GtkPrintOperationAction print_action;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
+ e_addressbook_view_print (view, print_action);
+}
+
+static void
+action_contact_print_preview_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+ GtkPrintOperationAction print_action;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW;
+ e_addressbook_view_print (view, print_action);
+}
+
+static void
+action_contact_save_as_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_save_as (view, FALSE);
+}
+
+static void
+action_contact_search_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EBookShellView *book_shell_view)
+{
+ EShellView *shell_view;
+ EShellContent *shell_content;
+ const gchar *search_hint;
+
+ /* XXX Figure out a way to handle this in EShellContent
+ * instead of every shell view having to handle it.
+ * The problem is EShellContent does not know what
+ * the search option actions are for this view. It
+ * would have to dig up the popup menu and retrieve
+ * the action for each menu item. Seems messy. */
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+
+ search_hint = gtk_action_get_label (GTK_ACTION (current));
+ e_shell_content_set_search_hint (shell_content, search_hint);
+}
+
+static void
+action_contact_select_all_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ e_addressbook_view_select_all (view);
+}
+
+static void
+action_contact_send_message_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+ GList *list, *iter;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ g_return_if_fail (view != NULL);
+
+ list = e_addressbook_view_get_selected (view);
+ g_return_if_fail (list != NULL);
+
+ /* Convert the list of contacts to a list of destinations. */
+ for (iter = list; iter != NULL; iter = iter->next) {
+ EContact *contact = iter->data;
+ EDestination *destination;
+
+ destination = e_destination_new ();
+ e_destination_set_contact (destination, contact, 0);
+ g_object_unref (contact);
+
+ iter->data = destination;
+ }
+
+ eab_send_as_to (list);
+ g_list_foreach (list, (GFunc) g_object_unref, NULL);
+ g_list_free (list);
+}
+
+static void
+action_gal_save_custom_view_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EShellView *shell_view;
+ EAddressbookView *address_view;
+ GalViewInstance *view_instance;
+
+ /* All shell views respond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with saving the custom view. */
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ address_view = e_book_shell_content_get_current_view (book_shell_content);
+ view_instance = e_addressbook_view_get_view_instance (address_view);
+ gal_view_instance_save_as (view_instance);
+}
+
+static void
+action_search_execute_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EShellView *shell_view;
+
+ /* All shell views respond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with executing the search. */
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ e_book_shell_view_execute_search (book_shell_view);
+}
+
+static void
+action_search_filter_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EBookShellView *book_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ gtk_action_activate (ACTION (SEARCH_EXECUTE));
+}
+
+static GtkActionEntry contact_entries[] = {
+
+ { "address-book-copy",
+ GTK_STOCK_COPY,
+ N_("Co_py All Contacts To..."),
+ NULL,
+ N_("Copy the contacts of the selected address book to another"),
+ G_CALLBACK (action_address_book_copy_cb) },
+
+ { "address-book-delete",
+ GTK_STOCK_DELETE,
+ N_("Del_ete Address Book"),
+ NULL,
+ N_("Delete the selected address book"),
+ G_CALLBACK (action_address_book_delete_cb) },
+
+ { "address-book-move",
+ "folder-move",
+ N_("Mo_ve All Contacts To..."),
+ NULL,
+ N_("Move the contacts of the selected address book to another"),
+ G_CALLBACK (action_address_book_move_cb) },
+
+ { "address-book-new",
+ "address-book-new",
+ N_("_New Address Book"),
+ NULL,
+ N_("Create a new address book"),
+ G_CALLBACK (action_address_book_new_cb) },
+
+ { "address-book-properties",
+ GTK_STOCK_PROPERTIES,
+ N_("Address _Book Properties"),
+ NULL,
+ N_("Show properties of the selected address book"),
+ G_CALLBACK (action_address_book_properties_cb) },
+
+ { "address-book-rename",
+ NULL,
+ N_("_Rename..."),
+ "F2",
+ N_("Rename the selected address book"),
+ G_CALLBACK (action_address_book_rename_cb) },
+
+ { "address-book-save-as",
+ GTK_STOCK_SAVE_AS,
+ N_("S_ave Address Book as vCard"),
+ NULL,
+ N_("Save the contacts of the selected address book as a vCard"),
+ G_CALLBACK (action_address_book_save_as_cb) },
+
+ { "address-book-stop",
+ GTK_STOCK_STOP,
+ NULL,
+ NULL,
+ N_("Stop loading"),
+ G_CALLBACK (action_address_book_stop_cb) },
+
+ { "contact-clipboard-copy",
+ GTK_STOCK_COPY,
+ NULL,
+ NULL,
+ N_("Copy the selection"),
+ G_CALLBACK (action_contact_clipboard_copy_cb) },
+
+ { "contact-clipboard-cut",
+ GTK_STOCK_CUT,
+ NULL,
+ NULL,
+ N_("Cut the selection"),
+ G_CALLBACK (action_contact_clipboard_cut_cb) },
+
+ { "contact-clipboard-paste",
+ GTK_STOCK_PASTE,
+ NULL,
+ NULL,
+ N_("Paste the clipboard"),
+ G_CALLBACK (action_contact_clipboard_paste_cb) },
+
+ { "contact-copy",
+ NULL,
+ N_("_Copy Contact To..."),
+ "<Control><Shift>y",
+ N_("Copy selected contacts to another address book"),
+ G_CALLBACK (action_contact_copy_cb) },
+
+ { "contact-delete",
+ GTK_STOCK_DELETE,
+ N_("_Delete Contact"),
+ "<Control>d",
+ N_("Delete selected contacts"),
+ G_CALLBACK (action_contact_delete_cb) },
+
+ { "contact-forward",
+ "mail-forward",
+ N_("_Forward Contact..."),
+ NULL,
+ N_("Send selected contacts to another person"),
+ G_CALLBACK (action_contact_forward_cb) },
+
+ { "contact-move",
+ NULL,
+ N_("_Move Contact To..."),
+ "<Control><Shift>v",
+ N_("Move selected contacts to another address book"),
+ G_CALLBACK (action_contact_move_cb) },
+
+ { "contact-new",
+ "contact-new",
+ N_("_New Contact..."),
+ NULL,
+ N_("Create a new contact"),
+ G_CALLBACK (action_contact_new_cb) },
+
+ { "contact-new-list",
+ "stock_contact-list",
+ N_("New Contact _List..."),
+ NULL,
+ N_("Create a new contact list"),
+ G_CALLBACK (action_contact_new_list_cb) },
+
+ { "contact-open",
+ NULL,
+ N_("_Open"),
+ "<Control>o",
+ N_("View the current contact"),
+ G_CALLBACK (action_contact_open_cb) },
+
+ { "contact-save-as",
+ GTK_STOCK_SAVE_AS,
+ N_("Save as vCard..."),
+ NULL,
+ N_("Save selected contacts as a vCard"),
+ G_CALLBACK (action_contact_save_as_cb) },
+
+ { "contact-select-all",
+ GTK_STOCK_SELECT_ALL,
+ NULL,
+ NULL,
+ N_("Select all contacts"),
+ G_CALLBACK (action_contact_select_all_cb) },
+
+ { "contact-send-message",
+ "mail-message-new",
+ N_("_Send Message to Contact..."),
+ NULL,
+ N_("Send a message to the selected contacts"),
+ G_CALLBACK (action_contact_send_message_cb) },
+
+ /*** Menus ***/
+
+ { "actions-menu",
+ NULL,
+ N_("_Actions"),
+ NULL,
+ NULL,
+ NULL }
+};
+
+static EPopupActionEntry contact_popup_entries[] = {
+
+ { "address-book-popup-delete",
+ N_("_Delete"),
+ "address-book-delete" },
+
+ { "address-book-popup-properties",
+ N_("_Properties"),
+ "address-book-properties" },
+
+ { "address-book-popup-rename",
+ NULL,
+ "address-book-rename" },
+
+ { "address-book-popup-save-as",
+ N_("_Save as vCard..."),
+ "address-book-save-as" },
+
+ { "contact-popup-clipboard-copy",
+ NULL,
+ "contact-clipboard-copy" },
+
+ { "contact-popup-clipboard-cut",
+ NULL,
+ "contact-clipboard-cut" },
+
+ { "contact-popup-clipboard-paste",
+ NULL,
+ "contact-clipboard-paste" },
+
+ { "contact-popup-copy",
+ NULL,
+ "contact-copy" },
+
+ { "contact-popup-delete",
+ NULL,
+ "contact-delete" },
+
+ { "contact-popup-forward",
+ NULL,
+ "contact-forward" },
+
+ { "contact-popup-move",
+ NULL,
+ "contact-move" },
+
+ { "contact-popup-open",
+ NULL,
+ "contact-open" },
+
+ { "contact-popup-save-as",
+ NULL,
+ "contact-save-as" },
+
+ { "contact-popup-send-message",
+ NULL,
+ "contact-send-message" },
+};
+
+static GtkToggleActionEntry contact_toggle_entries[] = {
+
+ { "contact-preview",
+ NULL,
+ N_("Contact _Preview"),
+ "<Control>m",
+ N_("Show contact preview window"),
+ G_CALLBACK (action_contact_preview_cb),
+ TRUE }
+};
+
+static GtkRadioActionEntry contact_filter_entries[] = {
+
+ { "contact-filter-any-category",
+ NULL,
+ N_("Any Category"),
+ NULL,
+ NULL,
+ CONTACT_FILTER_ANY_CATEGORY },
+
+ { "contact-filter-unmatched",
+ NULL,
+ N_("Unmatched"),
+ NULL,
+ NULL,
+ CONTACT_FILTER_UNMATCHED }
+};
+
+static GtkRadioActionEntry contact_search_entries[] = {
+
+ { "contact-search-any-field-contains",
+ NULL,
+ N_("Any field contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CONTACT_SEARCH_ANY_FIELD_CONTAINS },
+
+ { "contact-search-email-begins-with",
+ NULL,
+ N_("Email begins with"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CONTACT_SEARCH_EMAIL_BEGINS_WITH },
+
+ { "contact-search-name-contains",
+ NULL,
+ N_("Name contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CONTACT_SEARCH_NAME_CONTAINS }
+};
+
+static GtkActionEntry lockdown_printing_entries[] = {
+
+ { "contact-print",
+ GTK_STOCK_PRINT,
+ NULL,
+ "<Control>p",
+ N_("Print selected contacts"),
+ G_CALLBACK (action_contact_print_cb) },
+
+ { "contact-print-preview",
+ GTK_STOCK_PRINT_PREVIEW,
+ NULL,
+ NULL,
+ N_("Preview the contacts to be printed"),
+ G_CALLBACK (action_contact_print_preview_cb) }
+};
+
+static EPopupActionEntry lockdown_printing_popup_entries[] = {
+
+ { "contact-popup-print",
+ NULL,
+ "contact-print" }
+};
+
+void
+e_book_shell_view_actions_init (EBookShellView *book_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ GtkActionGroup *action_group;
+ GConfBridge *bridge;
+ GtkAction *action;
+ GObject *object;
+ const gchar *key;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ /* Contact Actions */
+ action_group = ACTION_GROUP (CONTACTS);
+ gtk_action_group_add_actions (
+ action_group, contact_entries,
+ G_N_ELEMENTS (contact_entries), book_shell_view);
+ e_action_group_add_popup_actions (
+ action_group, contact_popup_entries,
+ G_N_ELEMENTS (contact_popup_entries));
+ gtk_action_group_add_toggle_actions (
+ action_group, contact_toggle_entries,
+ G_N_ELEMENTS (contact_toggle_entries), book_shell_view);
+ gtk_action_group_add_radio_actions (
+ action_group, contact_search_entries,
+ G_N_ELEMENTS (contact_search_entries),
+ CONTACT_SEARCH_NAME_CONTAINS,
+ G_CALLBACK (action_contact_search_cb), book_shell_view);
+
+ /* Lockdown Printing Actions */
+ action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
+ gtk_action_group_add_actions (
+ action_group, lockdown_printing_entries,
+ G_N_ELEMENTS (lockdown_printing_entries), book_shell_view);
+ e_action_group_add_popup_actions (
+ action_group, lockdown_printing_popup_entries,
+ G_N_ELEMENTS (lockdown_printing_popup_entries));
+
+ /* Bind GObject properties to GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (ACTION (CONTACT_PREVIEW));
+ key = "/apps/evolution/addressbook/display/show_preview";
+ gconf_bridge_bind_property (bridge, key, object, "active");
+
+ /* Fine tuning. */
+
+ action = ACTION (CONTACT_DELETE);
+ g_object_set (action, "short-label", _("Delete"), NULL);
+
+ g_signal_connect (
+ ACTION (GAL_SAVE_CUSTOM_VIEW), "activate",
+ G_CALLBACK (action_gal_save_custom_view_cb), book_shell_view);
+
+ g_signal_connect (
+ ACTION (SEARCH_EXECUTE), "activate",
+ G_CALLBACK (action_search_execute_cb), book_shell_view);
+}
+
+void
+e_book_shell_view_update_search_filter (EBookShellView *book_shell_view)
+{
+ EShellView *shell_view;
+ EShellContent *shell_content;
+ EShellWindow *shell_window;
+ GtkActionGroup *action_group;
+ GtkRadioAction *radio_action;
+ GList *list, *iter;
+ GSList *group;
+ gint ii;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ action_group = ACTION_GROUP (CONTACTS_FILTER);
+ e_action_group_remove_all_actions (action_group);
+
+ /* Add the standard filter actions. */
+ gtk_action_group_add_radio_actions (
+ action_group, contact_filter_entries,
+ G_N_ELEMENTS (contact_filter_entries),
+ CONTACT_FILTER_ANY_CATEGORY,
+ G_CALLBACK (action_search_filter_cb),
+ book_shell_view);
+
+ /* Retrieve the radio group from an action we just added. */
+ list = gtk_action_group_list_actions (action_group);
+ radio_action = GTK_RADIO_ACTION (list->data);
+ group = gtk_radio_action_get_group (radio_action);
+ g_list_free (list);
+
+ /* Build the category actions. */
+
+ list = e_categories_get_list ();
+ for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) {
+ const gchar *category_name = iter->data;
+ const gchar *filename;
+ GtkAction *action;
+ gchar *action_name;
+
+ action_name = g_strdup_printf (
+ "contact-filter-category-%d", ii);
+ radio_action = gtk_radio_action_new (
+ action_name, category_name, NULL, NULL, ii);
+ g_free (action_name);
+
+ /* Convert the category icon file to a themed icon name. */
+ filename = e_categories_get_icon_file_for (category_name);
+ if (filename != NULL && *filename != '\0') {
+ gchar *basename;
+ gchar *cp;
+
+ basename = g_path_get_basename (filename);
+
+ /* Lose the file extension. */
+ if ((cp = strrchr (basename, '.')) != NULL)
+ *cp = '\0';
+
+ g_object_set (
+ radio_action, "icon-name", basename, NULL);
+
+ g_free (basename);
+ }
+
+ gtk_radio_action_set_group (radio_action, group);
+ group = gtk_radio_action_get_group (radio_action);
+
+ /* The action group takes ownership of the action. */
+ action = GTK_ACTION (radio_action);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (radio_action);
+ }
+ g_list_free (list);
+
+ /* Use any action in the group; doesn't matter which. */
+ e_shell_content_set_filter_action (shell_content, radio_action);
+
+ ii = CONTACT_FILTER_UNMATCHED;
+ e_shell_content_add_filter_separator_after (shell_content, ii);
+}
diff --git a/modules/addressbook/e-book-shell-view-actions.h b/modules/addressbook/e-book-shell-view-actions.h
new file mode 100644
index 0000000000..8e3d31f7bf
--- /dev/null
+++ b/modules/addressbook/e-book-shell-view-actions.h
@@ -0,0 +1,91 @@
+/*
+ * e-book-shell-view-actions.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_BOOK_SHELL_VIEW_ACTIONS_H
+#define E_BOOK_SHELL_VIEW_ACTIONS_H
+
+#include <shell/e-shell-window-actions.h>
+
+/* Address Book Actions */
+#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "address-book-copy")
+#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "address-book-delete")
+#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_MOVE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "address-book-move")
+#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_PROPERTIES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "address-book-properties")
+#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_RENAME(window) \
+ E_SHELL_WINDOW_ACTION ((window), "address-book-rename")
+#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_SAVE_AS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "address-book-save-as")
+#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_STOP(window) \
+ E_SHELL_WINDOW_ACTION ((window), "address-book-stop")
+
+/* Contact Actions */
+#define E_SHELL_WINDOW_ACTION_CONTACT_CLIPBOARD_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-clipboard-copy")
+#define E_SHELL_WINDOW_ACTION_CONTACT_CLIPBOARD_CUT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-clipboard-cut")
+#define E_SHELL_WINDOW_ACTION_CONTACT_CLIPBOARD_PASTE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-clipboard-paste")
+#define E_SHELL_WINDOW_ACTION_CONTACT_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-copy")
+#define E_SHELL_WINDOW_ACTION_CONTACT_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-delete")
+#define E_SHELL_WINDOW_ACTION_CONTACT_FORWARD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-forward")
+#define E_SHELL_WINDOW_ACTION_CONTACT_MOVE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-move")
+#define E_SHELL_WINDOW_ACTION_CONTACT_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-new")
+#define E_SHELL_WINDOW_ACTION_CONTACT_NEW_LIST(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-new-list")
+#define E_SHELL_WINDOW_ACTION_CONTACT_OPEN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-open")
+#define E_SHELL_WINDOW_ACTION_CONTACT_PREVIEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-preview")
+#define E_SHELL_WINDOW_ACTION_CONTACT_PRINT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-print")
+#define E_SHELL_WINDOW_ACTION_CONTACT_PRINT_PREVIEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-print-preview")
+#define E_SHELL_WINDOW_ACTION_CONTACT_SAVE_AS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-save-as")
+#define E_SHELL_WINDOW_ACTION_CONTACT_SELECT_ALL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-select-all")
+#define E_SHELL_WINDOW_ACTION_CONTACT_SEND_MESSAGE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-send-message")
+
+/* Search Actions */
+#define E_SHELL_WINDOW_ACTION_CONTACT_SEARCH_ANY_FIELD_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-search-any-field-contains")
+#define E_SHELL_WINDOW_ACTION_CONTACT_SEARCH_EMAIL_BEGINS_WITH(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-search-email-begins-with")
+#define E_SHELL_WINDOW_ACTION_CONTACT_SEARCH_NAME_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "contact-search-name-contains")
+
+/* Action Groups */
+#define E_SHELL_WINDOW_ACTION_GROUP_CONTACTS(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "contacts")
+#define E_SHELL_WINDOW_ACTION_GROUP_CONTACTS_FILTER(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "contacts-filter")
+
+#endif /* E_BOOK_SHELL_VIEW_ACTIONS_H */
diff --git a/modules/addressbook/e-book-shell-view-private.c b/modules/addressbook/e-book-shell-view-private.c
new file mode 100644
index 0000000000..ec3562e87a
--- /dev/null
+++ b/modules/addressbook/e-book-shell-view-private.c
@@ -0,0 +1,621 @@
+/*
+ * e-book-shell-view-private.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-book-shell-view-private.h"
+
+#include "widgets/menus/gal-view-factory-etable.h"
+#include "addressbook/gui/widgets/gal-view-factory-minicard.h"
+
+#include "addressbook.h"
+
+static void
+open_contact (EBookShellView *book_shell_view,
+ EContact *contact,
+ gboolean is_new_contact,
+ EAddressbookView *view)
+{
+ EAddressbookModel *model;
+ GtkWidget *editor;
+ EBook *book;
+ gboolean editable;
+
+ model = e_addressbook_view_get_model (view);
+ book = e_addressbook_model_get_book (model);
+ editable = e_addressbook_model_get_editable (model);
+
+ if (e_contact_get (contact, E_CONTACT_IS_LIST))
+ editor = e_contact_list_editor_new (
+ book, contact, is_new_contact, editable);
+ else
+ editor = e_contact_editor_new (
+ book, contact, is_new_contact, editable);
+
+ eab_editor_show (EAB_EDITOR (editor));
+}
+
+static void
+popup_event (EBookShellView *book_shell_view,
+ GdkEventButton *event)
+{
+ EShellView *shell_view;
+ const gchar *widget_path;
+
+ widget_path = "/contact-popup";
+ shell_view = E_SHELL_VIEW (book_shell_view);
+
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+}
+
+static void
+book_shell_view_selection_change_foreach (gint row,
+ EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+ EAddressbookModel *model;
+ EContact *contact;
+
+ /* XXX A "foreach" function is kind of a silly way to retrieve
+ * the one and only selected contact, but this is the only
+ * means that ESelectionModel provides. */
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ model = e_addressbook_view_get_model (view);
+ contact = e_addressbook_model_get_contact (model, row);
+
+ e_book_shell_content_set_preview_contact (book_shell_content, contact);
+}
+
+static void
+selection_change (EBookShellView *book_shell_view,
+ EAddressbookView *view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *current_view;
+ ESelectionModel *selection_model;
+ EShellView *shell_view;
+ gint n_selected;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ current_view = e_book_shell_content_get_current_view (book_shell_content);
+
+ if (view != current_view)
+ return;
+
+ e_shell_view_update_actions (shell_view);
+
+ selection_model = e_addressbook_view_get_selection_model (view);
+
+ n_selected = (selection_model != NULL) ?
+ e_selection_model_selected_count (selection_model) : 0;
+
+ if (n_selected == 1)
+ e_selection_model_foreach (
+ selection_model, (EForeachFunc)
+ book_shell_view_selection_change_foreach,
+ book_shell_view);
+ else
+ e_book_shell_content_set_preview_contact (
+ book_shell_content, NULL);
+}
+
+static void
+contact_changed (EBookShellView *book_shell_view,
+ EContact *contact)
+{
+ EBookShellContent *book_shell_content;
+ EContact *preview_contact;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+
+ preview_contact =
+ e_book_shell_content_get_preview_contact (book_shell_content);
+
+ if (contact != preview_contact)
+ return;
+
+ /* Re-render the same contact. */
+ e_book_shell_content_set_preview_contact (book_shell_content, contact);
+}
+
+static void
+contacts_removed (EBookShellView *book_shell_view,
+ GArray *removed_indices,
+ EAddressbookModel *model)
+{
+ EBookShellContent *book_shell_content;
+ EContact *preview_contact;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+
+ preview_contact =
+ e_book_shell_content_get_preview_contact (book_shell_content);
+
+ if (preview_contact == NULL)
+ return;
+
+ /* Is the displayed contact still in the model? */
+ if (e_addressbook_model_find (model, preview_contact) < 0)
+ return;
+
+ /* If not, clear the contact display. */
+ e_book_shell_content_set_preview_contact (book_shell_content, NULL);
+}
+
+static void
+book_open_cb (EBook *book,
+ EBookStatus status,
+ gpointer user_data)
+{
+ EAddressbookView *view = user_data;
+ EAddressbookModel *model;
+ ESource *source;
+
+ source = e_book_get_source (book);
+ model = e_addressbook_view_get_model (view);
+
+ if (status == E_BOOK_ERROR_OK) {
+ e_addressbook_model_set_book (model, book);
+ e_addressbook_model_force_folder_bar_message (model);
+ } else if (status != E_BOOK_ERROR_CANCELLED)
+ eab_load_error_dialog (NULL /* XXX */, source, status);
+}
+
+static void
+book_shell_view_activate_selected_source (EBookShellView *book_shell_view,
+ ESourceSelector *selector)
+{
+ EShellView *shell_view;
+ EBookShellContent *book_shell_content;
+ EAddressbookView *view;
+ EAddressbookModel *model;
+ ESource *source;
+ GalViewInstance *view_instance;
+ GHashTable *hash_table;
+ GtkWidget *widget;
+ const gchar *uid;
+ gchar *view_id;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ source = e_source_selector_peek_primary_selection (selector);
+
+ if (source == NULL)
+ return;
+
+ uid = e_source_peek_uid (source);
+ hash_table = book_shell_view->priv->uid_to_view;
+ widget = g_hash_table_lookup (hash_table, uid);
+
+ if (widget != NULL) {
+ EBook *book;
+
+ /* There is a view for this UID. Make sure the view
+ * actually contains an EBook. The absence of an EBook
+ * suggests a previous load failed, so try again. */
+ view = E_ADDRESSBOOK_VIEW (widget);
+ model = e_addressbook_view_get_model (view);
+ source = e_addressbook_view_get_source (view);
+
+ if (e_addressbook_model_get_book (model) == NULL) {
+ book = e_book_new (source, NULL);
+
+ if (book != NULL)
+ addressbook_load (book, book_open_cb, view);
+ }
+
+ } else {
+ EBook *book;
+
+ /* Create a view for this UID. */
+ widget = e_addressbook_view_new (shell_view, source);
+ gtk_widget_show (widget);
+
+ e_book_shell_content_insert_view (
+ book_shell_content,
+ E_ADDRESSBOOK_VIEW (widget));
+
+ g_hash_table_insert (
+ hash_table, g_strdup (uid),
+ g_object_ref (widget));
+
+ g_signal_connect_swapped (
+ widget, "open-contact",
+ G_CALLBACK (open_contact), book_shell_view);
+
+ g_signal_connect_swapped (
+ widget, "popup-event",
+ G_CALLBACK (popup_event), book_shell_view);
+
+ g_signal_connect_swapped (
+ widget, "command-state-change",
+ G_CALLBACK (e_shell_view_update_actions),
+ book_shell_view);
+
+ g_signal_connect_swapped (
+ widget, "selection-change",
+ G_CALLBACK (selection_change), book_shell_view);
+
+ book = e_book_new (source, NULL);
+ view = E_ADDRESSBOOK_VIEW (widget);
+
+ if (book != NULL)
+ addressbook_load (book, book_open_cb, view);
+
+ model = e_addressbook_view_get_model (view);
+
+ g_signal_connect_swapped (
+ model, "contact-changed",
+ G_CALLBACK (contact_changed), book_shell_view);
+
+ g_signal_connect_swapped (
+ model, "contacts-removed",
+ G_CALLBACK (contacts_removed), book_shell_view);
+ }
+
+ e_book_shell_content_set_current_view (
+ book_shell_content, E_ADDRESSBOOK_VIEW (widget));
+
+ /* XXX We have to keep the addressbook selector informed of the
+ * current view so it can move contacts via drag-and-drop. */
+ e_addressbook_selector_set_current_view (
+ E_ADDRESSBOOK_SELECTOR (selector),
+ E_ADDRESSBOOK_VIEW (widget));
+
+ view_instance = e_addressbook_view_get_view_instance (view);
+ view_id = gal_view_instance_get_current_view_id (view_instance);
+ e_shell_view_set_view_id (shell_view, view_id);
+ g_free (view_id);
+
+ e_addressbook_model_force_folder_bar_message (model);
+ selection_change (book_shell_view, view);
+}
+
+static gboolean
+book_shell_view_show_popup_menu (GdkEventButton *event,
+ EShellView *shell_view)
+{
+ const gchar *widget_path;
+
+ widget_path = "/address-book-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+
+ return TRUE;
+}
+
+static gboolean
+book_shell_view_selector_button_press_event_cb (EShellView *shell_view,
+ GdkEventButton *event)
+{
+ /* XXX Use ESourceSelector's "popup-event" signal instead. */
+
+ if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+ return book_shell_view_show_popup_menu (event, shell_view);
+
+ return FALSE;
+}
+
+static gboolean
+book_shell_view_selector_popup_menu_cb (EShellView *shell_view)
+{
+ /* XXX Use ESourceSelector's "popup-event" signal instead. */
+
+ return book_shell_view_show_popup_menu (NULL, shell_view);
+}
+
+static gboolean
+book_shell_view_selector_key_press_event_cb (EShellView *shell_view,
+ GdkEventKey *event)
+{
+ EShellWindow *shell_window;
+
+ /* Needed for the ACTION() macro. */
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ if (event->keyval == GDK_Delete) {
+ gtk_action_activate (ACTION (ADDRESS_BOOK_DELETE));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+book_shell_view_load_view_collection (EShellViewClass *shell_view_class)
+{
+ GalViewCollection *collection;
+ GalViewFactory *factory;
+ ETableSpecification *spec;
+ const gchar *base_dir;
+ gchar *filename;
+
+ collection = shell_view_class->view_collection;
+
+ base_dir = EVOLUTION_ETSPECDIR;
+ spec = e_table_specification_new ();
+ filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL);
+ if (!e_table_specification_load_from_file (spec, filename))
+ g_critical ("Unable to load ETable specification file "
+ "for address book");
+ g_free (filename);
+
+ factory = gal_view_factory_etable_new (spec);
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+ g_object_unref (spec);
+
+ factory = gal_view_factory_minicard_new ();
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+
+ gal_view_collection_load (collection);
+}
+
+static void
+book_shell_view_notify_view_id_cb (EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EAddressbookView *address_view;
+ GalViewInstance *view_instance;
+ const gchar *view_id;
+
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ address_view = e_book_shell_content_get_current_view (book_shell_content);
+ view_instance = e_addressbook_view_get_view_instance (address_view);
+ view_id = e_shell_view_get_view_id (E_SHELL_VIEW (book_shell_view));
+
+ /* A NULL view ID implies we're in a custom view. But you can
+ * only get to a custom view via the "Define Views" dialog, which
+ * would have already modified the view instance appropriately.
+ * Furthermore, there's no way to refer to a custom view by ID
+ * anyway, since custom views have no IDs. */
+ if (view_id == NULL)
+ return;
+
+ gal_view_instance_set_current_view_id (view_instance, view_id);
+}
+
+void
+e_book_shell_view_private_init (EBookShellView *book_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ EBookShellViewPrivate *priv = book_shell_view->priv;
+ GHashTable *uid_to_view;
+ GHashTable *uid_to_editor;
+
+ uid_to_view = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ uid_to_editor = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ priv->uid_to_view = uid_to_view;
+ priv->uid_to_editor = uid_to_editor;
+
+ if (!gal_view_collection_loaded (shell_view_class->view_collection))
+ book_shell_view_load_view_collection (shell_view_class);
+
+ g_signal_connect (
+ book_shell_view, "notify::view-id",
+ G_CALLBACK (book_shell_view_notify_view_id_cb), NULL);
+}
+
+void
+e_book_shell_view_private_constructed (EBookShellView *book_shell_view)
+{
+ EBookShellViewPrivate *priv = book_shell_view->priv;
+ EShellContent *shell_content;
+ EShellSidebar *shell_sidebar;
+ EShellBackend *shell_backend;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ESourceSelector *selector;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ e_shell_window_add_action_group (shell_window, "contacts");
+ e_shell_window_add_action_group (shell_window, "contacts-filter");
+
+ /* Cache these to avoid lots of awkward casting. */
+ priv->book_shell_backend = g_object_ref (shell_backend);
+ priv->book_shell_content = g_object_ref (shell_content);
+ priv->book_shell_sidebar = g_object_ref (shell_sidebar);
+
+ selector = e_book_shell_sidebar_get_selector (
+ E_BOOK_SHELL_SIDEBAR (shell_sidebar));
+
+ g_signal_connect_swapped (
+ selector, "button-press-event",
+ G_CALLBACK (book_shell_view_selector_button_press_event_cb),
+ book_shell_view);
+
+ g_signal_connect_swapped (
+ selector, "key-press-event",
+ G_CALLBACK (book_shell_view_selector_key_press_event_cb),
+ book_shell_view);
+
+ g_signal_connect_swapped (
+ selector, "popup-menu",
+ G_CALLBACK (book_shell_view_selector_popup_menu_cb),
+ book_shell_view);
+
+ g_signal_connect_swapped (
+ selector, "primary-selection-changed",
+ G_CALLBACK (book_shell_view_activate_selected_source),
+ book_shell_view);
+
+ e_categories_register_change_listener (
+ G_CALLBACK (e_book_shell_view_update_search_filter),
+ book_shell_view);
+
+ e_book_shell_view_actions_init (book_shell_view);
+ book_shell_view_activate_selected_source (book_shell_view, selector);
+ e_book_shell_view_update_search_filter (book_shell_view);
+}
+
+void
+e_book_shell_view_private_dispose (EBookShellView *book_shell_view)
+{
+ EBookShellViewPrivate *priv = book_shell_view->priv;
+
+ DISPOSE (priv->book_shell_backend);
+ DISPOSE (priv->book_shell_content);
+ DISPOSE (priv->book_shell_sidebar);
+
+ g_hash_table_remove_all (priv->uid_to_view);
+ g_hash_table_remove_all (priv->uid_to_editor);
+}
+
+void
+e_book_shell_view_private_finalize (EBookShellView *book_shell_view)
+{
+ EBookShellViewPrivate *priv = book_shell_view->priv;
+
+ g_hash_table_destroy (priv->uid_to_view);
+ g_hash_table_destroy (priv->uid_to_editor);
+}
+
+void
+e_book_shell_view_execute_search (EBookShellView *book_shell_view)
+{
+ EBookShellContent *book_shell_content;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellContent *shell_content;
+ GtkAction *action;
+ GString *string;
+ EAddressbookView *view;
+ EAddressbookModel *model;
+ FilterRule *rule;
+ const gchar *format;
+ const gchar *text;
+ gchar *query;
+ gchar *temp;
+ gint value;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ text = e_shell_content_get_search_text (shell_content);
+
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ action = ACTION (CONTACT_SEARCH_ANY_FIELD_CONTAINS);
+ value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
+
+ if (text == NULL || *text == '\0') {
+ text = "";
+ value = CONTACT_SEARCH_ANY_FIELD_CONTAINS;
+ }
+
+ switch (value) {
+ case CONTACT_SEARCH_NAME_CONTAINS:
+ format = "(contains \"full_name\" %s)";
+ break;
+
+ case CONTACT_SEARCH_EMAIL_BEGINS_WITH:
+ format = "(beginswith \"email\" %s)";
+ break;
+
+ default:
+ text = "";
+ /* fall through */
+
+ case CONTACT_SEARCH_ANY_FIELD_CONTAINS:
+ format = "(contains \"x-evolution-any-field\" %s)";
+ break;
+ }
+
+ /* Build the query. */
+ string = g_string_new ("");
+ e_sexp_encode_string (string, text);
+ query = g_strdup_printf (format, string->str);
+ g_string_free (string, TRUE);
+
+ /* Apply selected filter. */
+ value = e_shell_content_get_filter_value (shell_content);
+ switch (value) {
+ case CONTACT_FILTER_ANY_CATEGORY:
+ break;
+
+ case CONTACT_FILTER_UNMATCHED:
+ temp = g_strdup_printf (
+ "(and (not (and (exists \"CATEGORIES\") "
+ "(not (is \"CATEGORIES\" \"\")))) %s)",
+ query);
+ g_free (query);
+ query = temp;
+ break;
+
+ default:
+ {
+ GList *categories;
+ const gchar *category_name;
+
+ categories = e_categories_get_list ();
+ category_name = g_list_nth_data (categories, value);
+ g_list_free (categories);
+
+ temp = g_strdup_printf (
+ "(and (is \"category_list\" \"%s\") %s)",
+ category_name, query);
+ g_free (query);
+ query = temp;
+ }
+ }
+
+ /* XXX This is wrong. We need to programmatically construct a
+ * FilterRule, tell it to build code, and pass the resulting
+ * expression string to EAddressbookModel. */
+ rule = filter_rule_new ();
+ e_shell_content_set_search_rule (shell_content, rule);
+ g_object_unref (rule);
+
+ /* Submit the query. */
+ book_shell_content = book_shell_view->priv->book_shell_content;
+ view = e_book_shell_content_get_current_view (book_shell_content);
+ model = e_addressbook_view_get_model (view);
+ e_addressbook_model_set_query (model, query);
+ g_free (query);
+
+ e_book_shell_content_set_preview_contact (book_shell_content, NULL);
+}
+
+void
+e_book_shell_view_editor_weak_notify (EditorUidClosure *closure,
+ GObject *where_the_object_was)
+{
+ GHashTable *hash_table;
+
+ hash_table = closure->view->priv->uid_to_editor;
+ g_hash_table_remove (hash_table, closure->uid);
+}
diff --git a/modules/addressbook/e-book-shell-view-private.h b/modules/addressbook/e-book-shell-view-private.h
new file mode 100644
index 0000000000..b4701aea81
--- /dev/null
+++ b/modules/addressbook/e-book-shell-view-private.h
@@ -0,0 +1,129 @@
+/*
+ * e-book-shell-view-private.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_BOOK_SHELL_VIEW_PRIVATE_H
+#define E_BOOK_SHELL_VIEW_PRIVATE_H
+
+#include "e-book-shell-view.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <libebook/e-book.h>
+#include <libedataserver/e-categories.h>
+#include <libedataserver/e-sexp.h>
+#include <libedataserverui/e-source-selector.h>
+
+#include "e-util/gconf-bridge.h"
+#include "shell/e-shell-content.h"
+#include "shell/e-shell-sidebar.h"
+#include "misc/e-popup-action.h"
+
+#include "addressbook/gui/contact-editor/e-contact-editor.h"
+#include "addressbook/gui/contact-list-editor/e-contact-list-editor.h"
+#include "addressbook/gui/widgets/eab-gui-util.h"
+#include "addressbook/gui/widgets/e-addressbook-view.h"
+#include "addressbook/gui/widgets/e-addressbook-selector.h"
+
+#include "e-book-shell-backend.h"
+#include "e-book-shell-content.h"
+#include "e-book-shell-sidebar.h"
+#include "e-book-shell-view-actions.h"
+
+#define E_BOOK_SHELL_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_BOOK_SHELL_VIEW, EBookShellViewPrivate))
+
+/* Shorthand, requires a variable named "shell_window". */
+#define ACTION(name) \
+ (E_SHELL_WINDOW_ACTION_##name (shell_window))
+#define ACTION_GROUP(name) \
+ (E_SHELL_WINDOW_ACTION_GROUP_##name (shell_window))
+
+/* For use in dispose() methods. */
+#define DISPOSE(obj) \
+ G_STMT_START { \
+ if ((obj) != NULL) { g_object_unref (obj); (obj) = NULL; } \
+ } G_STMT_END
+
+/* ETable Specifications */
+#define ETSPEC_FILENAME "e-addressbook-view.etspec"
+
+G_BEGIN_DECLS
+
+typedef struct _EditorUidClosure EditorUidClosure;
+
+struct _EditorUidClosure {
+ GtkWidget *editor;
+ gchar *uid;
+ EBookShellView *view;
+};
+
+/* List these in the order to be displayed.
+ * Positive values are reserved for categories. */
+enum {
+ CONTACT_FILTER_ANY_CATEGORY = -2,
+ CONTACT_FILTER_UNMATCHED = -1
+};
+
+/* List these in the order to be displayed. */
+enum {
+ CONTACT_SEARCH_NAME_CONTAINS,
+ CONTACT_SEARCH_EMAIL_BEGINS_WITH,
+ CONTACT_SEARCH_ANY_FIELD_CONTAINS
+};
+
+struct _EBookShellViewPrivate {
+
+ /* These are just for convenience. */
+ EBookShellBackend *book_shell_backend;
+ EBookShellContent *book_shell_content;
+ EBookShellSidebar *book_shell_sidebar;
+
+ GHashTable *uid_to_view;
+ GHashTable *uid_to_editor;
+};
+
+void e_book_shell_view_private_init
+ (EBookShellView *book_shell_view,
+ EShellViewClass *shell_view_class);
+void e_book_shell_view_private_constructed
+ (EBookShellView *book_shell_view);
+void e_book_shell_view_private_dispose
+ (EBookShellView *book_shell_view);
+void e_book_shell_view_private_finalize
+ (EBookShellView *book_shell_view);
+
+/* Private Utilities */
+
+void e_book_shell_view_actions_init
+ (EBookShellView *book_shell_view);
+void e_book_shell_view_execute_search
+ (EBookShellView *book_shell_view);
+void e_book_shell_view_editor_weak_notify
+ (EditorUidClosure *closure,
+ GObject *where_the_object_was);
+void e_book_shell_view_update_search_filter
+ (EBookShellView *book_shell_view);
+
+G_END_DECLS
+
+#endif /* E_BOOK_SHELL_VIEW_PRIVATE_H */
diff --git a/modules/addressbook/e-book-shell-view.c b/modules/addressbook/e-book-shell-view.c
new file mode 100644
index 0000000000..ea48bb534c
--- /dev/null
+++ b/modules/addressbook/e-book-shell-view.c
@@ -0,0 +1,318 @@
+/*
+ * e-book-shell-view.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-book-shell-view-private.h"
+
+static gpointer parent_class;
+static GType book_shell_view_type;
+
+static void
+book_shell_view_source_list_changed_cb (EBookShellView *book_shell_view,
+ ESourceList *source_list)
+{
+ EBookShellViewPrivate *priv = book_shell_view->priv;
+ EBookShellContent *book_shell_content;
+ EShellView *shell_view;
+ GList *keys, *iter;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ book_shell_content = book_shell_view->priv->book_shell_content;
+
+ keys = g_hash_table_get_keys (priv->uid_to_view);
+ for (iter = keys; iter != NULL; iter = iter->next) {
+ gchar *uid = iter->data;
+ EAddressbookView *view;
+
+ /* If the source still exists, move on. */
+ if (e_source_list_peek_source_by_uid (source_list, uid))
+ continue;
+
+ /* Remove the view for the deleted source. */
+ view = g_hash_table_lookup (priv->uid_to_view, uid);
+ e_book_shell_content_remove_view (book_shell_content, view);
+ g_hash_table_remove (priv->uid_to_view, uid);
+ }
+ g_list_free (keys);
+
+ keys = g_hash_table_get_keys (priv->uid_to_editor);
+ for (iter = keys; iter != NULL; iter = iter->next) {
+ gchar *uid = iter->data;
+ EditorUidClosure *closure;
+
+ /* If the source still exists, move on. */
+ if (e_source_list_peek_source_by_uid (source_list, uid))
+ continue;
+
+ /* Remove the editor for the deleted source. */
+ closure = g_hash_table_lookup (priv->uid_to_editor, uid);
+ g_object_weak_unref (
+ G_OBJECT (closure->editor), (GWeakNotify)
+ e_book_shell_view_editor_weak_notify, closure);
+ gtk_widget_destroy (closure->editor);
+ g_hash_table_remove (priv->uid_to_editor, uid);
+ }
+ g_list_free (keys);
+
+ e_shell_view_update_actions (shell_view);
+}
+
+static void
+book_shell_view_dispose (GObject *object)
+{
+ EBookShellView *book_shell_view;
+
+ book_shell_view = E_BOOK_SHELL_VIEW (object);
+ e_book_shell_view_private_dispose (book_shell_view);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+book_shell_view_finalize (GObject *object)
+{
+ EBookShellView *book_shell_view;
+
+ book_shell_view = E_BOOK_SHELL_VIEW (object);
+ e_book_shell_view_private_finalize (book_shell_view);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+book_shell_view_constructed (GObject *object)
+{
+ EBookShellView *book_shell_view;
+ EBookShellBackend *book_shell_backend;
+ ESourceList *source_list;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ book_shell_view = E_BOOK_SHELL_VIEW (object);
+ e_book_shell_view_private_constructed (book_shell_view);
+
+ book_shell_backend = book_shell_view->priv->book_shell_backend;
+ source_list = e_book_shell_backend_get_source_list (book_shell_backend);
+
+ g_signal_connect_swapped (
+ source_list, "changed",
+ G_CALLBACK (book_shell_view_source_list_changed_cb),
+ book_shell_view);
+}
+
+static void
+book_shell_view_update_actions (EShellView *shell_view)
+{
+ EBookShellViewPrivate *priv;
+ EShellContent *shell_content;
+ EShellSidebar *shell_sidebar;
+ EShellWindow *shell_window;
+ GtkAction *action;
+ const gchar *label;
+ gboolean sensitive;
+ guint32 state;
+
+ /* Be descriptive. */
+ gboolean any_contacts_selected;
+ gboolean has_primary_source;
+ gboolean multiple_contacts_selected;
+ gboolean primary_source_is_system;
+ gboolean single_contact_selected;
+ gboolean selection_is_contact_list;
+ gboolean selection_has_email;
+ gboolean source_is_busy;
+ gboolean source_is_editable;
+ gboolean source_is_empty;
+
+ priv = E_BOOK_SHELL_VIEW_GET_PRIVATE (shell_view);
+
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ state = e_shell_content_check_state (shell_content);
+
+ single_contact_selected =
+ (state & E_BOOK_SHELL_CONTENT_SELECTION_SINGLE);
+ multiple_contacts_selected =
+ (state & E_BOOK_SHELL_CONTENT_SELECTION_MULTIPLE);
+ selection_has_email =
+ (state & E_BOOK_SHELL_CONTENT_SELECTION_HAS_EMAIL);
+ selection_is_contact_list =
+ (state & E_BOOK_SHELL_CONTENT_SELECTION_IS_CONTACT_LIST);
+ source_is_busy =
+ (state & E_BOOK_SHELL_CONTENT_SOURCE_IS_BUSY);
+ source_is_editable =
+ (state & E_BOOK_SHELL_CONTENT_SOURCE_IS_EDITABLE);
+ source_is_empty =
+ (state & E_BOOK_SHELL_CONTENT_SOURCE_IS_EMPTY);
+
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ state = e_shell_sidebar_check_state (shell_sidebar);
+
+ has_primary_source =
+ (state & E_BOOK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE);
+ primary_source_is_system =
+ (state & E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM);
+
+ any_contacts_selected =
+ (single_contact_selected || multiple_contacts_selected);
+
+ action = ACTION (ADDRESS_BOOK_DELETE);
+ sensitive = has_primary_source && !primary_source_is_system;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (ADDRESS_BOOK_RENAME);
+ sensitive = has_primary_source;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (ADDRESS_BOOK_STOP);
+ sensitive = source_is_busy;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_CLIPBOARD_COPY);
+ sensitive = any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_CLIPBOARD_CUT);
+ sensitive = source_is_editable && any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_CLIPBOARD_PASTE);
+ sensitive = source_is_editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_COPY);
+ sensitive = any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_DELETE);
+ sensitive = source_is_editable && any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_FORWARD);
+ sensitive = any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+ if (multiple_contacts_selected)
+ label = _("_Forward Contacts");
+ else
+ label = _("_Forward Contact");
+ g_object_set (action, "label", label, NULL);
+
+ action = ACTION (CONTACT_MOVE);
+ sensitive = source_is_editable && any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_OPEN);
+ sensitive = any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_PRINT);
+ sensitive = any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_PRINT_PREVIEW);
+ sensitive = any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_SAVE_AS);
+ sensitive = any_contacts_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_SELECT_ALL);
+ sensitive = !(source_is_empty);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CONTACT_SEND_MESSAGE);
+ sensitive = any_contacts_selected && selection_has_email;
+ gtk_action_set_sensitive (action, sensitive);
+ if (multiple_contacts_selected)
+ label = _("_Send Message to Contacts");
+ else if (selection_is_contact_list)
+ label = _("_Send Message to List");
+ else
+ label = _("_Send Message to Contact");
+ g_object_set (action, "label", label, NULL);
+}
+
+static void
+book_shell_view_class_init (EBookShellViewClass *class)
+{
+ GObjectClass *object_class;
+ EShellViewClass *shell_view_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EBookShellViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = book_shell_view_dispose;
+ object_class->finalize = book_shell_view_finalize;
+ object_class->constructed = book_shell_view_constructed;
+
+ shell_view_class = E_SHELL_VIEW_CLASS (class);
+ shell_view_class->label = _("Contacts");
+ shell_view_class->icon_name = "x-office-address-book";
+ shell_view_class->ui_definition = "evolution-contacts.ui";
+ shell_view_class->ui_manager_id = "org.gnome.evolution.contacts";
+ shell_view_class->search_options = "/contact-search-options";
+ shell_view_class->search_rules = "addresstypes.xml";
+ shell_view_class->new_shell_content = e_book_shell_content_new;
+ shell_view_class->new_shell_sidebar = e_book_shell_sidebar_new;
+ shell_view_class->update_actions = book_shell_view_update_actions;
+}
+
+static void
+book_shell_view_init (EBookShellView *book_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ book_shell_view->priv =
+ E_BOOK_SHELL_VIEW_GET_PRIVATE (book_shell_view);
+
+ e_book_shell_view_private_init (book_shell_view, shell_view_class);
+}
+
+GType
+e_book_shell_view_get_type (void)
+{
+ return book_shell_view_type;
+}
+
+void
+e_book_shell_view_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (EBookShellViewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) book_shell_view_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EBookShellView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) book_shell_view_init,
+ NULL /* value_table */
+ };
+
+ book_shell_view_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_VIEW,
+ "EBookShellView", &type_info, 0);
+}
diff --git a/modules/addressbook/e-book-shell-view.h b/modules/addressbook/e-book-shell-view.h
new file mode 100644
index 0000000000..33a0c8a75d
--- /dev/null
+++ b/modules/addressbook/e-book-shell-view.h
@@ -0,0 +1,66 @@
+/*
+ * e-book-shell-view.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_BOOK_SHELL_VIEW_H
+#define E_BOOK_SHELL_VIEW_H
+
+#include <shell/e-shell-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_BOOK_SHELL_VIEW \
+ (e_book_shell_view_get_type ())
+#define E_BOOK_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_BOOK_SHELL_VIEW, EBookShellView))
+#define E_BOOK_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_BOOK_SHELL_VIEW, EBookShellViewClass))
+#define E_IS_BOOK_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_BOOK_SHELL_VIEW))
+#define E_IS_BOOK_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_BOOK_SHELL_VIEW))
+#define E_BOOK_SHELL_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_BOOK_SHELL_VIEW, EBookShellViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EBookShellView EBookShellView;
+typedef struct _EBookShellViewClass EBookShellViewClass;
+typedef struct _EBookShellViewPrivate EBookShellViewPrivate;
+
+struct _EBookShellView {
+ EShellView parent;
+ EBookShellViewPrivate *priv;
+};
+
+struct _EBookShellViewClass {
+ EShellViewClass parent_class;
+};
+
+GType e_book_shell_view_get_type (void);
+void e_book_shell_view_register_type (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_BOOK_SHELL_VIEW_H */
diff --git a/modules/addressbook/eab-composer-util.c b/modules/addressbook/eab-composer-util.c
new file mode 100644
index 0000000000..6d45f13a94
--- /dev/null
+++ b/modules/addressbook/eab-composer-util.c
@@ -0,0 +1,197 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "eab-composer-util.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libebook/e-contact.h>
+#include <libebook/e-destination.h>
+
+#include "composer/e-msg-composer.h"
+#include "addressbook/util/eab-book-util.h"
+#include "addressbook/gui/widgets/eab-gui-util.h"
+
+void
+eab_send_as_to (GList *destinations)
+{
+ EMsgComposer *composer;
+ EComposerHeaderTable *table;
+ GPtrArray *to_array;
+ GPtrArray *bcc_array;
+
+ union {
+ gpointer *pdata;
+ EDestination **destinations;
+ } convert;
+
+ if (destinations == NULL)
+ return;
+
+ composer = e_msg_composer_new ();
+ table = e_msg_composer_get_header_table (composer);
+
+ to_array = g_ptr_array_new ();
+ bcc_array = g_ptr_array_new ();
+
+ /* Sort contacts into "To" and "Bcc" destinations. */
+ while (destinations != NULL) {
+ EDestination *destination = destinations->data;
+
+ if (e_destination_is_evolution_list (destination)) {
+ if (e_destination_list_show_addresses (destination))
+ g_ptr_array_add (to_array, destination);
+ else
+ g_ptr_array_add (bcc_array, destination);
+ } else
+ g_ptr_array_add (to_array, destination);
+
+ destinations = g_list_next (destinations);
+ }
+
+ /* Add sentinels to each array. */
+ g_ptr_array_add (to_array, NULL);
+ g_ptr_array_add (bcc_array, NULL);
+
+ /* XXX Acrobatics like this make me question whether NULL-terminated
+ * arrays are really the best argument type for passing a list of
+ * destinations to the header table. */
+
+ /* Add "To" destinations. */
+ convert.pdata = to_array->pdata;
+ e_composer_header_table_set_destinations_to (
+ table, convert.destinations);
+ g_ptr_array_free (to_array, FALSE);
+ e_destination_freev (convert.destinations);
+
+ /* Add "Bcc" destinations. */
+ convert.pdata = bcc_array->pdata;
+ e_composer_header_table_set_destinations_bcc (
+ table, convert.destinations);
+ g_ptr_array_free (bcc_array, FALSE);
+ e_destination_freev (convert.destinations);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+}
+
+static const gchar *
+get_email (EContact *contact, EContactField field_id, gchar **to_free)
+{
+ gchar *name = NULL, *mail = NULL;
+ const gchar *value = e_contact_get_const (contact, field_id);
+
+ *to_free = NULL;
+
+ if (eab_parse_qp_email (value, &name, &mail)) {
+ *to_free = g_strdup_printf ("%s <%s>", name, mail);
+ value = *to_free;
+ }
+
+ g_free (name);
+ g_free (mail);
+
+ return value;
+}
+
+void
+eab_send_as_attachment (GList *destinations)
+{
+ EMsgComposer *composer;
+ EComposerHeaderTable *table;
+ CamelMimePart *attachment;
+ GList *contacts, *iter;
+ gchar *data;
+
+ if (destinations == NULL)
+ return;
+
+ composer = e_msg_composer_new ();
+ table = e_msg_composer_get_header_table (composer);
+
+ attachment = camel_mime_part_new ();
+
+ contacts = g_list_copy (destinations);
+ for (iter = contacts; iter != NULL; iter = iter->next)
+ iter->data = e_destination_get_contact (iter->data);
+ data = eab_contact_list_to_string (contacts);
+ g_list_free (contacts);
+
+ camel_mime_part_set_content (
+ attachment, data, strlen (data), "text/x-vcard");
+
+ if (destinations->next != NULL)
+ camel_mime_part_set_description (
+ attachment, _("Multiple vCards"));
+ else {
+ EContact *contact;
+ const gchar *file_as;
+ gchar *description;
+
+ contact = e_destination_get_contact (destinations->data);
+ file_as = e_contact_get_const (contact, E_CONTACT_FILE_AS);
+ description = g_strdup_printf (_("vCard for %s"), file_as);
+ camel_mime_part_set_description (attachment, description);
+ g_free (description);
+ }
+
+ camel_mime_part_set_disposition (attachment, "attachment");
+
+ e_msg_composer_attach (composer, attachment);
+ camel_object_unref (attachment);
+
+ if (destinations->next != NULL)
+ e_composer_header_table_set_subject (
+ table, _("Contact information"));
+ else {
+ EContact *contact;
+ gchar *tempstr;
+ const gchar *tempstr2;
+ gchar *tempfree = NULL;
+
+ contact = e_destination_get_contact (destinations->data);
+ tempstr2 = e_contact_get_const (contact, E_CONTACT_FILE_AS);
+ if (!tempstr2 || !*tempstr2)
+ tempstr2 = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
+ if (!tempstr2 || !*tempstr2)
+ tempstr2 = e_contact_get_const (contact, E_CONTACT_ORG);
+ if (!tempstr2 || !*tempstr2) {
+ g_free (tempfree);
+ tempstr2 = get_email (contact, E_CONTACT_EMAIL_1, &tempfree);
+ }
+ if (!tempstr2 || !*tempstr2) {
+ g_free (tempfree);
+ tempstr2 = get_email (contact, E_CONTACT_EMAIL_2, &tempfree);
+ }
+ if (!tempstr2 || !*tempstr2) {
+ g_free (tempfree);
+ tempstr2 = get_email (contact, E_CONTACT_EMAIL_3, &tempfree);
+ }
+
+ if (!tempstr2 || !*tempstr2)
+ tempstr = g_strdup_printf (_("Contact information"));
+ else
+ tempstr = g_strdup_printf (_("Contact information for %s"), tempstr2);
+
+ e_composer_header_table_set_subject (table, tempstr);
+
+ g_free (tempstr);
+ g_free (tempfree);
+ }
+
+ gtk_widget_show (GTK_WIDGET (composer));
+}
diff --git a/modules/addressbook/eab-composer-util.h b/modules/addressbook/eab-composer-util.h
new file mode 100644
index 0000000000..4aec23074d
--- /dev/null
+++ b/modules/addressbook/eab-composer-util.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EAB_COMPOSER_UTIL_H
+#define EAB_COMPOSER_UTIL_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void eab_send_as_to (GList *destinations);
+void eab_send_as_attachment (GList *destinations);
+
+G_END_DECLS
+
+#endif /* EAB_COMPOSER_UTIL_H */
diff --git a/modules/addressbook/evolution-module-addressbook.c b/modules/addressbook/evolution-module-addressbook.c
new file mode 100644
index 0000000000..3089133e43
--- /dev/null
+++ b/modules/addressbook/evolution-module-addressbook.c
@@ -0,0 +1,45 @@
+/*
+ * evolution-module-addressbook.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-book-shell-backend.h"
+#include "e-book-shell-content.h"
+#include "e-book-shell-sidebar.h"
+#include "e-book-shell-view.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ /* Register dynamically loaded types. */
+
+ e_book_shell_backend_register_type (type_module);
+ e_book_shell_content_register_type (type_module);
+ e_book_shell_sidebar_register_type (type_module);
+ e_book_shell_view_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/modules/addressbook/ldap-config.glade b/modules/addressbook/ldap-config.glade
new file mode 100644
index 0000000000..f87cf84b14
--- /dev/null
+++ b/modules/addressbook/ldap-config.glade
@@ -0,0 +1,1454 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="account-editor-window">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Address Book Properties</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area7">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="account-editor-cancel-button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="account-editor-ok-button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="account-editor-notebook">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">True</property>
+ <property name="show_border">True</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="account-editor-general-vbox">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label564">
+ <property name="visible">True</property>
+ <property name="label">Display</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox122">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label563">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label431">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Name:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">account-editor-display-name-entry</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="account-editor-display-name-entry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">This is the name for this server that will appear in your Evolution folder list. It is for display purposes only. </property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="account-editor-connecting-vbox">
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label557">
+ <property name="visible">True</property>
+ <property name="label">Server Information</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox123">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label558">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table31">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label554">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Port:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label556">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Server:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">server-name-entry</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox123">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label555">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Use secure connection:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">ssl-combobox</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="ssl-combobox">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">SSL encryption
+TLS encryption
+No encryption</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">expand|shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="server-name-entry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">This is the full name of your LDAP server. For example, &quot;ldap.mycompany.com&quot;.</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="port-comboentry">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">389
+636
+3268</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label559">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Authentication&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox124">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label562">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table32">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label560">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Login method:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label561">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Lo_gin:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">auth-entry</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="auth-entry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Evolution will use this email address to authenticate you with the server.</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="auth-combobox">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Anonymously
+Using email address
+Using distinguished name (DN)</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label334">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">General</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="account-editor-searching-vbox">
+ <property name="border_width">12</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label565">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Searching&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox125">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label571">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table33">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label569">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label566">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Search _base:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">rootdn-entry</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">expand|shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label567">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Search scope:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">expand|shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="rootdn-entry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">The search base is the distinguished name (DN) of the entry where your searches will begin. If you leave this blank, the search will begin at the root of the directory tree.</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">expand|shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="rootdn-button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment61">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox126">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image10">
+ <property name="visible">True</property>
+ <property name="stock">gtk-find</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label568">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Find Possible Search Bases</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">expand|shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label581">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Search _filter:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">search-filter-entry</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">expand|shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="search-filter-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ <accessibility>
+ <atkproperty name="AtkObject::accessible_name" translatable="yes">Search Filter</atkproperty>
+ <atkproperty name="AtkObject::accessible_description" translatable="yes">Search filter is the type of object to be searched for. If this is not modified, the default search will be performed on the type &quot;person&quot;.</atkproperty>
+ </accessibility>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">expand|shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="scope-combobox">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">One
+Sub</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label572">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Downloading&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox127">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label579">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table34">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox128">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label575">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">1</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHScale" id="timeout-scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_TOP</property>
+ <property name="digits">1</property>
+ <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>
+ <property name="inverted">False</property>
+ <property name="adjustment">3 1 5 0.5 1 0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label576">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">5</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">expand|shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label574">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">minutes</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label578">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">cards</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label573">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Timeout:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">timeout-scale</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label577">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Download limit:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">download-limit-spinbutton</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="download-limit-spinbutton">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">This is the maximum number of entries to download. Setting this number to be too large will slow down your address book.</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 1000 1 10 0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">expand|shrink|fill</property>
+ <property name="y_options">shrink|fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="canbrowsecheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes" comments="To translators: If enabled, addressbook will only fetch contacts from the server until either set time limit or amount of contacts limit reached">B_rowse this book until limit reached</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label344">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Details</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="supported-bases-dialog">
+ <property name="title" translatable="yes">Supported Search Bases</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="modal">False</property>
+ <property name="default_width">320</property>
+ <property name="default_height">200</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox8">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area8">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="cancelbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox40">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="Custom" id="supported-bases-table">
+ <property name="visible">True</property>
+ <property name="creation_function">supported_bases_create_table</property>
+ <property name="int1">0</property>
+ <property name="int2">0</property>
+ <property name="last_modification_time">Fri, 12 Apr 2002 20:06:45 GMT</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/modules/addressbook/openldap-extract.h b/modules/addressbook/openldap-extract.h
new file mode 100644
index 0000000000..996bf370a8
--- /dev/null
+++ b/modules/addressbook/openldap-extract.h
@@ -0,0 +1,1427 @@
+/* This is extracted from the OpenLDAP sources.
+ *
+ * Stuff that isn't used in e-book-backend-ldap.c was dropped, like
+ * the LDAPSchemaExtensionItem stuff.
+ *
+ * This file basically has three parts:
+ *
+ * - some general macros from OpenLDAP that work as such on all
+ * implementations.
+ *
+ * - ldap_str2objectclass()
+ *
+ * - ldap_url_parse()
+ */
+
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2005 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file COPYING.OPENLDAP in
+ * the top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <string.h>
+#include <assert.h>
+
+/* from various header files */
+
+#define LDAP_CONST const
+
+#define LDAP_PORT 389 /* ldap:/// default LDAP port */
+#define LDAPS_PORT 636 /* ldaps:/// default LDAP over TLS port */
+
+#define LDAP_ROOT_DSE ""
+
+#define LDAP_SPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+#define LDAP_DIGIT(c) ((c) >= '0' && (c) <= '9')
+
+#define LDAP_EXOP_START_TLS "1.3.6.1.4.1.1466.20037" /* RFC 2830 */
+
+#define LDAP_MALLOC(n) malloc((n))
+#define LDAP_CALLOC(n,s) calloc((n),(s))
+#define LDAP_REALLOC(p,s) realloc((p),(s))
+#define LDAP_FREE(p) free((p))
+#define LDAP_VFREE(p) vfree((gpointer *)(p))
+#define LDAP_STRDUP(s) strdup((s))
+
+#define LDAP_RANGE(n,x,y) (((x) <= (n)) && ((n) <= (y)))
+#define LDAP_NAME_ERROR(n) LDAP_RANGE((n),0x20,0x24) /* 32-34,36 */
+
+#define ldap_msgtype(lm) (lm)->lm_msgtype
+#define ldap_msgid(lm) (lm)->lm_msgid
+
+#define LDAP_SCHERR_OUTOFMEM 1
+#define LDAP_SCHERR_UNEXPTOKEN 2
+#define LDAP_SCHERR_NOLEFTPAREN 3
+#define LDAP_SCHERR_NORIGHTPAREN 4
+#define LDAP_SCHERR_NODIGIT 5
+#define LDAP_SCHERR_BADNAME 6
+#define LDAP_SCHERR_BADDESC 7
+#define LDAP_SCHERR_BADSUP 8
+#define LDAP_SCHERR_DUPOPT 9
+#define LDAP_SCHERR_EMPTY 10
+#define LDAP_SCHERR_MISSING 11
+#define LDAP_SCHERR_OUT_OF_ORDER 12
+
+#define LDAP_SCHEMA_YES 1
+
+#define LDAP_SCHEMA_ABSTRACT 0
+#define LDAP_SCHEMA_STRUCTURAL 1
+#define LDAP_SCHEMA_AUXILIARY 2
+
+#define LDAP_SCHEMA_ALLOW_NONE 0x00U /* Strict parsing */
+#define LDAP_SCHEMA_ALLOW_NO_OID 0x01U /* Allow missing oid */
+#define LDAP_SCHEMA_ALLOW_QUOTED 0x02U /* Allow bogus extra quotes */
+#define LDAP_SCHEMA_ALLOW_DESCR 0x04U /* Allow descr instead of OID */
+#define LDAP_SCHEMA_ALLOW_DESCR_PREFIX 0x08U /* Allow descr as OID prefix */
+#define LDAP_SCHEMA_ALLOW_OID_MACRO 0x10U /* Allow OID macros in slapd */
+#define LDAP_SCHEMA_ALLOW_OUT_OF_ORDER_FIELDS 0x20U /* Allow fields in most any order */
+#define LDAP_SCHEMA_ALLOW_ALL 0x3fU /* Be very liberal in parsing */
+#define LDAP_SCHEMA_SKIP 0x80U /* Don't malloc any result */
+
+typedef struct ldap_objectclass {
+ gchar *oc_oid; /* REQUIRED */
+ gchar **oc_names; /* OPTIONAL */
+ gchar *oc_desc; /* OPTIONAL */
+ gint oc_obsolete; /* 0=no, 1=yes */
+ gchar **oc_sup_oids; /* OPTIONAL */
+ gint oc_kind; /* 0=ABSTRACT, 1=STRUCTURAL, 2=AUXILIARY */
+ gchar **oc_at_oids_must; /* OPTIONAL */
+ gchar **oc_at_oids_may; /* OPTIONAL */
+} LDAPObjectClass;
+
+
+static void
+vfree(gpointer *vec)
+{
+ gint i;
+
+ for (i = 0; vec[i] != NULL; i++)
+ free(vec[i]);
+}
+
+/* from schema.c */
+
+/*
+ * Now come the parsers. There is one parser for each entity type:
+ * objectclasses, attributetypes, etc.
+ *
+ * Each of them is written as a recursive-descent parser, except that
+ * none of them is really recursive. But the idea is kept: there
+ * is one routine per non-terminal that eithers gobbles lexical tokens
+ * or calls lower-level routines, etc.
+ *
+ * The scanner is implemented in the routine get_token. Actually,
+ * get_token is more than a scanner and will return tokens that are
+ * in fact non-terminals in the grammar. So you can see the whole
+ * approach as the combination of a low-level bottom-up recognizer
+ * combined with a scanner and a number of top-down parsers. Or just
+ * consider that the real grammars recognized by the parsers are not
+ * those of the standards. As a matter of fact, our parsers are more
+ * liberal than the spec when there is no ambiguity.
+ *
+ * The difference is pretty academic (modulo bugs or incorrect
+ * interpretation of the specs).
+ */
+
+#define TK_NOENDQUOTE -2
+#define TK_OUTOFMEM -1
+#define TK_EOS 0
+#define TK_UNEXPCHAR 1
+#define TK_BAREWORD 2
+#define TK_QDSTRING 3
+#define TK_LEFTPAREN 4
+#define TK_RIGHTPAREN 5
+#define TK_DOLLAR 6
+#define TK_QDESCR TK_QDSTRING
+
+struct token {
+ gint type;
+ gchar *sval;
+};
+
+static gint
+get_token( const gchar ** sp, gchar ** token_val )
+{
+ gint kind;
+ const gchar * p;
+ const gchar * q;
+ gchar * res;
+
+ *token_val = NULL;
+ switch (**sp) {
+ case '\0':
+ kind = TK_EOS;
+ (*sp)++;
+ break;
+ case '(':
+ kind = TK_LEFTPAREN;
+ (*sp)++;
+ break;
+ case ')':
+ kind = TK_RIGHTPAREN;
+ (*sp)++;
+ break;
+ case '$':
+ kind = TK_DOLLAR;
+ (*sp)++;
+ break;
+ case '\'':
+ kind = TK_QDSTRING;
+ (*sp)++;
+ p = *sp;
+ while ( **sp != '\'' && **sp != '\0' )
+ (*sp)++;
+ if ( **sp == '\'' ) {
+ q = *sp;
+ res = LDAP_MALLOC(q-p+1);
+ if ( !res ) {
+ kind = TK_OUTOFMEM;
+ } else {
+ strncpy(res,p,q-p);
+ res[q-p] = '\0';
+ *token_val = res;
+ }
+ (*sp)++;
+ } else {
+ kind = TK_NOENDQUOTE;
+ }
+ break;
+ default:
+ kind = TK_BAREWORD;
+ p = *sp;
+ while ( !LDAP_SPACE(**sp) &&
+ **sp != '(' &&
+ **sp != ')' &&
+ **sp != '$' &&
+ **sp != '\'' &&
+ **sp != '\0' )
+ (*sp)++;
+ q = *sp;
+ res = LDAP_MALLOC(q-p+1);
+ if ( !res ) {
+ kind = TK_OUTOFMEM;
+ } else {
+ strncpy(res,p,q-p);
+ res[q-p] = '\0';
+ *token_val = res;
+ }
+ break;
+/* kind = TK_UNEXPCHAR; */
+/* break; */
+ }
+
+ return kind;
+}
+
+/* Gobble optional whitespace */
+static void
+parse_whsp(const gchar **sp)
+{
+ while (LDAP_SPACE(**sp))
+ (*sp)++;
+}
+
+/* Parse a sequence of dot-separated decimal strings */
+static gchar *
+ldap_int_parse_numericoid(const gchar **sp, gint *code, const gint flags)
+{
+ gchar * res = NULL;
+ const gchar * start = *sp;
+ gint len;
+ gint quoted = 0;
+
+ /* Netscape puts the SYNTAX value in quotes (incorrectly) */
+ if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) {
+ quoted = 1;
+ (*sp)++;
+ start++;
+ }
+ /* Each iteration of this loop gets one decimal string */
+ while (**sp) {
+ if ( !LDAP_DIGIT(**sp) ) {
+ /*
+ * Initial gchar is not a digit or gchar after dot is
+ * not a digit
+ */
+ *code = LDAP_SCHERR_NODIGIT;
+ return NULL;
+ }
+ (*sp)++;
+ while ( LDAP_DIGIT(**sp) )
+ (*sp)++;
+ if ( **sp != '.' )
+ break;
+ /* Otherwise, gobble the dot and loop again */
+ (*sp)++;
+ }
+ /* Now *sp points at the gchar past the numericoid. Perfect. */
+ len = *sp - start;
+ if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) {
+ if ( **sp == '\'' ) {
+ (*sp)++;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ return NULL;
+ }
+ }
+ if (flags & LDAP_SCHEMA_SKIP) {
+ res = (gchar *)start;
+ } else {
+ res = LDAP_MALLOC(len+1);
+ if (!res) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return(NULL);
+ }
+ strncpy(res,start,len);
+ res[len] = '\0';
+ }
+ return(res);
+}
+
+/* Parse a qdescr or a list of them enclosed in () */
+static gchar **
+parse_qdescrs(const gchar **sp, gint *code)
+{
+ gchar ** res;
+ gchar ** res1;
+ gint kind;
+ gchar * sval;
+ gint size;
+ gint pos;
+
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_LEFTPAREN ) {
+ /* Let's presume there will be at least 2 entries */
+ size = 3;
+ res = LDAP_CALLOC(3,sizeof(gchar *));
+ if ( !res ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ pos = 0;
+ while (1) {
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_RIGHTPAREN )
+ break;
+ if ( kind == TK_QDESCR ) {
+ if ( pos == size-2 ) {
+ size++;
+ res1 = LDAP_REALLOC(res,size*sizeof(gchar *));
+ if ( !res1 ) {
+ LDAP_VFREE(res);
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return(NULL);
+ }
+ res = res1;
+ }
+ res[pos++] = sval;
+ res[pos] = NULL;
+ parse_whsp(sp);
+ } else {
+ LDAP_VFREE(res);
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ return(NULL);
+ }
+ }
+ parse_whsp(sp);
+ return(res);
+ } else if ( kind == TK_QDESCR ) {
+ res = LDAP_CALLOC(2,sizeof(gchar *));
+ if ( !res ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ res[0] = sval;
+ res[1] = NULL;
+ parse_whsp(sp);
+ return res;
+ } else {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_BADNAME;
+ return NULL;
+ }
+}
+
+/* Parse a woid or a $-separated list of them enclosed in () */
+static gchar **
+parse_oids(const gchar **sp, gint *code, const gint allow_quoted)
+{
+ gchar ** res;
+ gchar ** res1;
+ gint kind;
+ gchar * sval;
+ gint size;
+ gint pos;
+
+ /*
+ * Strictly speaking, doing this here accepts whsp before the
+ * ( at the begining of an oidlist, but this is harmless. Also,
+ * we are very liberal in what we accept as an OID. Maybe
+ * refine later.
+ */
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_LEFTPAREN ) {
+ /* Let's presume there will be at least 2 entries */
+ size = 3;
+ res = LDAP_CALLOC(3,sizeof(gchar *));
+ if ( !res ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ pos = 0;
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_BAREWORD ||
+ ( allow_quoted && kind == TK_QDSTRING ) ) {
+ res[pos++] = sval;
+ res[pos] = NULL;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ return NULL;
+ }
+ parse_whsp(sp);
+ while (1) {
+ kind = get_token(sp,&sval);
+ if ( kind == TK_RIGHTPAREN )
+ break;
+ if ( kind == TK_DOLLAR ) {
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_BAREWORD ||
+ ( allow_quoted &&
+ kind == TK_QDSTRING ) ) {
+ if ( pos == size-2 ) {
+ size++;
+ res1 = LDAP_REALLOC(res,size*sizeof(gchar *));
+ if ( !res1 ) {
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return(NULL);
+ }
+ res = res1;
+ }
+ res[pos++] = sval;
+ res[pos] = NULL;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ return NULL;
+ }
+ parse_whsp(sp);
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ return NULL;
+ }
+ }
+ parse_whsp(sp);
+ return(res);
+ } else if ( kind == TK_BAREWORD ||
+ ( allow_quoted && kind == TK_QDSTRING ) ) {
+ res = LDAP_CALLOC(2,sizeof(gchar *));
+ if ( !res ) {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ res[0] = sval;
+ res[1] = NULL;
+ parse_whsp(sp);
+ return res;
+ } else {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_BADNAME;
+ return NULL;
+ }
+}
+
+static void
+ldap_objectclass_free(LDAPObjectClass * oc)
+{
+ LDAP_FREE(oc->oc_oid);
+ if (oc->oc_names) LDAP_VFREE(oc->oc_names);
+ if (oc->oc_desc) LDAP_FREE(oc->oc_desc);
+ if (oc->oc_sup_oids) LDAP_VFREE(oc->oc_sup_oids);
+ if (oc->oc_at_oids_must) LDAP_VFREE(oc->oc_at_oids_must);
+ if (oc->oc_at_oids_may) LDAP_VFREE(oc->oc_at_oids_may);
+ LDAP_FREE(oc);
+}
+
+static LDAPObjectClass *
+ldap_str2objectclass( LDAP_CONST gchar * s,
+ gint * code,
+ LDAP_CONST gchar ** errp,
+ LDAP_CONST unsigned flags )
+{
+ gint kind;
+ const gchar * ss = s;
+ gchar * sval;
+ gint seen_name = 0;
+ gint seen_desc = 0;
+ gint seen_obsolete = 0;
+ gint seen_sup = 0;
+ gint seen_kind = 0;
+ gint seen_must = 0;
+ gint seen_may = 0;
+ LDAPObjectClass * oc;
+ gchar ** ext_vals;
+ const gchar * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
+
+ if ( !oc ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with an OID in the numericoid format.
+ * However, this routine is used by clients to parse the response
+ * from servers and very well known servers will provide an OID
+ * in the wrong format or even no OID at all. We do our best to
+ * extract info from those servers.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ oc->oc_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !oc->oc_oid ) {
+ if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "SUP") ||
+ !strcasecmp(sval, "ABSTRACT") ||
+ !strcasecmp(sval, "STRUCTURAL") ||
+ !strcasecmp(sval, "AUXILIARY") ||
+ !strcasecmp(sval, "MUST") ||
+ !strcasecmp(sval, "MAY") ||
+ !strncasecmp(sval, "X-", 2) ) {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else if ( flags &
+ LDAP_SCHEMA_ALLOW_OID_MACRO ) {
+ /* Non-numerical OID, ignore */
+ gint len = ss-savepos;
+ oc->oc_oid = LDAP_MALLOC(len+1);
+ strncpy(oc->oc_oid, savepos, len);
+ oc->oc_oid[len] = 0;
+ }
+ }
+ LDAP_FREE(sval);
+ } else {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal an accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ case TK_RIGHTPAREN:
+ return oc;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_name = 1;
+ oc->oc_names = parse_qdescrs(&ss,code);
+ if ( !oc->oc_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ oc->oc_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ oc->oc_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"SUP") ) {
+ LDAP_FREE(sval);
+ if ( seen_sup ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_sup = 1;
+ oc->oc_sup_oids = parse_oids(&ss,
+ code,
+ flags);
+ if ( !oc->oc_sup_oids ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"ABSTRACT") ) {
+ LDAP_FREE(sval);
+ if ( seen_kind ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_kind = 1;
+ oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"STRUCTURAL") ) {
+ LDAP_FREE(sval);
+ if ( seen_kind ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_kind = 1;
+ oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"AUXILIARY") ) {
+ LDAP_FREE(sval);
+ if ( seen_kind ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_kind = 1;
+ oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MUST") ) {
+ LDAP_FREE(sval);
+ if ( seen_must ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_must = 1;
+ oc->oc_at_oids_must = parse_oids(&ss,code,0);
+ if ( !oc->oc_at_oids_must ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MAY") ) {
+ LDAP_FREE(sval);
+ if ( seen_may ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_may = 1;
+ oc->oc_at_oids_may = parse_oids(&ss,code,0);
+ if ( !oc->oc_at_oids_may ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+#if 0
+ if ( add_extension(&oc->oc_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+#endif
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ }
+}
+
+/* from utf-8.c */
+
+#define LDAP_UTF8_NEXT(p) g_utf8_next_char((p))
+#define LDAP_UTF8_INCR(p) ((p)=LDAP_UTF8_NEXT((p)))
+#define ldap_x_utf8_to_ucs4(str) g_utf8_get_char(str)
+
+static gchar *ldap_utf8_strchr( const gchar *str, const gchar *chr )
+{
+ for(; *str != '\0'; LDAP_UTF8_INCR(str) ) {
+ if( ldap_x_utf8_to_ucs4( str ) == ldap_x_utf8_to_ucs4( chr ) ) {
+ return (gchar *) str;
+ }
+ }
+
+ return NULL;
+}
+
+static gsize ldap_utf8_strcspn( const gchar *str, const gchar *set )
+{
+ const gchar *cstr;
+ const gchar *cset;
+
+ for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) {
+ for( cset = set; *cset != '\0'; LDAP_UTF8_INCR(cset) ) {
+ if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) {
+ return cstr - str;
+ }
+ }
+ }
+
+ return cstr - str;
+}
+
+static gsize ldap_utf8_strspn( const gchar *str, const gchar *set )
+{
+ const gchar *cstr;
+ const gchar *cset;
+
+ for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) {
+ for( cset = set; ; LDAP_UTF8_INCR(cset) ) {
+ if( *cset == '\0' ) {
+ return cstr - str;
+ }
+
+ if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) {
+ break;
+ }
+ }
+ }
+
+ return cstr - str;
+}
+
+static gchar *ldap_utf8_strtok(gchar *str, const gchar *sep, gchar **last)
+{
+ gchar *begin;
+ gchar *end;
+
+ if( last == NULL ) return NULL;
+
+ begin = str ? str : *last;
+
+ begin += ldap_utf8_strspn( begin, sep );
+
+ if( *begin == '\0' ) {
+ *last = NULL;
+ return NULL;
+ }
+
+ end = &begin[ ldap_utf8_strcspn( begin, sep ) ];
+
+ if( *end != '\0' ) {
+ gchar *next = LDAP_UTF8_NEXT( end );
+ *end = '\0';
+ end = next;
+ }
+
+ *last = end;
+ return begin;
+}
+
+/* from ldap.h */
+
+#define LDAP_URL_SUCCESS 0x00 /* Success */
+#define LDAP_URL_ERR_MEM 0x01 /* can't allocate memory space */
+#define LDAP_URL_ERR_PARAM 0x02 /* parameter is bad */
+
+#define LDAP_URL_ERR_BADSCHEME 0x03 /* URL doesn't begin with "ldap[si]://" */
+#define LDAP_URL_ERR_BADENCLOSURE 0x04 /* URL is missing trailing ">" */
+#define LDAP_URL_ERR_BADURL 0x05 /* URL is bad */
+#define LDAP_URL_ERR_BADHOST 0x06 /* host port is bad */
+#define LDAP_URL_ERR_BADATTRS 0x07 /* bad (or missing) attributes */
+#define LDAP_URL_ERR_BADSCOPE 0x08 /* scope string is invalid (or missing) */
+#define LDAP_URL_ERR_BADFILTER 0x09 /* bad or missing filter */
+#define LDAP_URL_ERR_BADEXTS 0x0a /* bad or missing extensions */
+
+#define LDAP_URL_PREFIX "ldap://"
+#define LDAP_URL_PREFIX_LEN (sizeof(LDAP_URL_PREFIX)-1)
+#define LDAPS_URL_PREFIX "ldaps://"
+#define LDAPS_URL_PREFIX_LEN (sizeof(LDAPS_URL_PREFIX)-1)
+#define LDAPI_URL_PREFIX "ldapi://"
+#define LDAPI_URL_PREFIX_LEN (sizeof(LDAPI_URL_PREFIX)-1)
+
+#define LDAP_URL_URLCOLON "URL:"
+#define LDAP_URL_URLCOLON_LEN (sizeof(LDAP_URL_URLCOLON)-1)
+
+typedef struct ldap_url_desc {
+ struct ldap_url_desc *lud_next;
+ gchar *lud_scheme;
+ gchar *lud_host;
+ gint lud_port;
+ gchar *lud_dn;
+ gchar **lud_attrs;
+ gint lud_scope;
+ gchar *lud_filter;
+ gchar **lud_exts;
+ gint lud_crit_exts;
+} LDAPURLDesc;
+
+/* from url.c */
+
+static const gchar *
+skip_url_prefix(
+ const gchar *url,
+ gint *enclosedp,
+ const gchar **scheme )
+{
+ /*
+ * return non-zero if this looks like a LDAP URL; zero if not
+ * if non-zero returned, *urlp will be moved past "ldap://" part of URL
+ */
+ const gchar *p;
+
+ if ( url == NULL ) {
+ return( NULL );
+ }
+
+ p = url;
+
+ /* skip leading '<' (if any) */
+ if ( *p == '<' ) {
+ *enclosedp = 1;
+ ++p;
+ } else {
+ *enclosedp = 0;
+ }
+
+ /* skip leading "URL:" (if any) */
+ if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
+ p += LDAP_URL_URLCOLON_LEN;
+ }
+
+ /* check for "ldap://" prefix */
+ if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "ldap://" prefix and return success */
+ p += LDAP_URL_PREFIX_LEN;
+ *scheme = "ldap";
+ return( p );
+ }
+
+ /* check for "ldaps://" prefix */
+ if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "ldaps://" prefix and return success */
+ p += LDAPS_URL_PREFIX_LEN;
+ *scheme = "ldaps";
+ return( p );
+ }
+
+ /* check for "ldapi://" prefix */
+ if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "ldapi://" prefix and return success */
+ p += LDAPI_URL_PREFIX_LEN;
+ *scheme = "ldapi";
+ return( p );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ /* check for "cldap://" prefix */
+ if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "cldap://" prefix and return success */
+ p += LDAPC_URL_PREFIX_LEN;
+ *scheme = "cldap";
+ return( p );
+ }
+#endif
+
+ return( NULL );
+}
+
+static gint str2scope( const gchar *p )
+{
+ if ( strcasecmp( p, "one" ) == 0 ) {
+ return LDAP_SCOPE_ONELEVEL;
+
+ } else if ( strcasecmp( p, "onelevel" ) == 0 ) {
+ return LDAP_SCOPE_ONELEVEL;
+
+ } else if ( strcasecmp( p, "base" ) == 0 ) {
+ return LDAP_SCOPE_BASE;
+
+ } else if ( strcasecmp( p, "sub" ) == 0 ) {
+ return LDAP_SCOPE_SUBTREE;
+
+ } else if ( strcasecmp( p, "subtree" ) == 0 ) {
+ return LDAP_SCOPE_SUBTREE;
+ }
+
+ return( -1 );
+}
+
+static void
+ldap_free_urldesc( LDAPURLDesc *ludp )
+{
+ if ( ludp == NULL ) {
+ return;
+ }
+
+ if ( ludp->lud_scheme != NULL ) {
+ LDAP_FREE( ludp->lud_scheme );
+ }
+
+ if ( ludp->lud_host != NULL ) {
+ LDAP_FREE( ludp->lud_host );
+ }
+
+ if ( ludp->lud_dn != NULL ) {
+ LDAP_FREE( ludp->lud_dn );
+ }
+
+ if ( ludp->lud_filter != NULL ) {
+ LDAP_FREE( ludp->lud_filter);
+ }
+
+ if ( ludp->lud_attrs != NULL ) {
+ LDAP_VFREE( ludp->lud_attrs );
+ }
+
+ if ( ludp->lud_exts != NULL ) {
+ LDAP_VFREE( ludp->lud_exts );
+ }
+
+ LDAP_FREE( ludp );
+}
+
+static gint
+ldap_int_unhex( gint c )
+{
+ return( c >= '0' && c <= '9' ? c - '0'
+ : c >= 'A' && c <= 'F' ? c - 'A' + 10
+ : c - 'a' + 10 );
+}
+
+static void
+ldap_pvt_hex_unescape( gchar *s )
+{
+ /*
+ * Remove URL hex escapes from s... done in place. The basic concept for
+ * this routine is borrowed from the WWW library HTUnEscape() routine.
+ */
+ gchar *p;
+
+ for ( p = s; *s != '\0'; ++s ) {
+ if ( *s == '%' ) {
+ if ( *++s == '\0' ) {
+ break;
+ }
+ *p = ldap_int_unhex( *s ) << 4;
+ if ( *++s == '\0' ) {
+ break;
+ }
+ *p++ += ldap_int_unhex( *s );
+ } else {
+ *p++ = *s;
+ }
+ }
+
+ *p = '\0';
+}
+
+static gchar **
+ldap_str2charray( const gchar *str_in, const gchar *brkstr )
+{
+ gchar **res;
+ gchar *str, *s;
+ gchar *lasts;
+ gint i;
+
+ /* protect the input string from strtok */
+ str = LDAP_STRDUP( str_in );
+ if( str == NULL ) {
+ return NULL;
+ }
+
+ i = 1;
+ for ( s = str; *s; s++ ) {
+ if ( ldap_utf8_strchr( brkstr, s ) != NULL ) {
+ i++;
+ }
+ }
+
+ res = (gchar **) LDAP_MALLOC( (i + 1) * sizeof(gchar *) );
+
+ if( res == NULL ) {
+ LDAP_FREE( str );
+ return NULL;
+ }
+
+ i = 0;
+
+ for ( s = ldap_utf8_strtok( str, brkstr, &lasts );
+ s != NULL;
+ s = ldap_utf8_strtok( NULL, brkstr, &lasts ) )
+ {
+ res[i] = LDAP_STRDUP( s );
+
+ if(res[i] == NULL) {
+ for( --i; i >= 0; i-- ) {
+ LDAP_FREE( res[i] );
+ }
+ LDAP_FREE( res );
+ LDAP_FREE( str );
+ return NULL;
+ }
+
+ i++;
+ }
+
+ res[i] = NULL;
+
+ LDAP_FREE( str );
+ return( res );
+}
+
+static gint
+ldap_url_parse_ext( LDAP_CONST gchar *url_in, LDAPURLDesc **ludpp )
+{
+/*
+ * Pick apart the pieces of an LDAP URL.
+ */
+
+ LDAPURLDesc *ludp;
+ gchar *p, *q, *r;
+ gint i, enclosed;
+ const gchar *scheme = NULL;
+ const gchar *url_tmp;
+ gchar *url;
+
+ if( url_in == NULL || ludpp == NULL ) {
+ return LDAP_URL_ERR_PARAM;
+ }
+
+ *ludpp = NULL; /* pessimistic */
+
+ url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
+
+ if ( url_tmp == NULL ) {
+ return LDAP_URL_ERR_BADSCHEME;
+ }
+
+ assert( scheme );
+
+ /* make working copy of the remainder of the URL */
+ url = LDAP_STRDUP( url_tmp );
+ if ( url == NULL ) {
+ return LDAP_URL_ERR_MEM;
+ }
+
+ if ( enclosed ) {
+ p = &url[strlen(url)-1];
+
+ if( *p != '>' ) {
+ LDAP_FREE( url );
+ return LDAP_URL_ERR_BADENCLOSURE;
+ }
+
+ *p = '\0';
+ }
+
+ /* allocate return struct */
+ ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
+
+ if ( ludp == NULL ) {
+ LDAP_FREE( url );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ ludp->lud_next = NULL;
+ ludp->lud_host = NULL;
+ ludp->lud_port = 0;
+ ludp->lud_dn = NULL;
+ ludp->lud_attrs = NULL;
+ ludp->lud_filter = NULL;
+ ludp->lud_scope = LDAP_SCOPE_DEFAULT;
+ ludp->lud_filter = NULL;
+ ludp->lud_exts = NULL;
+
+ ludp->lud_scheme = LDAP_STRDUP( scheme );
+
+ if ( ludp->lud_scheme == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ /* scan forward for '/' that marks end of hostport and begin. of dn */
+ p = strchr( url, '/' );
+
+ if( p != NULL ) {
+ /* terminate hostport; point to start of dn */
+ *p++ = '\0';
+ }
+
+ /* IPv6 syntax with [ip address]:port */
+ if ( *url == '[' ) {
+ r = strchr( url, ']' );
+ if ( r == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+ *r++ = '\0';
+ q = strchr( r, ':' );
+ } else {
+ q = strchr( url, ':' );
+ }
+
+ if ( q != NULL ) {
+ gchar *next;
+
+ *q++ = '\0';
+ ldap_pvt_hex_unescape( q );
+
+ if( *q == '\0' ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+
+ ludp->lud_port = strtol( q, &next, 10 );
+ if ( next == NULL || next[0] != '\0' ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+ }
+
+ ldap_pvt_hex_unescape( url );
+
+ /* If [ip address]:port syntax, url is [ip and we skip the [ */
+ ludp->lud_host = LDAP_STRDUP( url + ( *url == '[' ) );
+
+ if( ludp->lud_host == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ /*
+ * Kludge. ldap://111.222.333.444:389??cn=abc,o=company
+ *
+ * On early Novell releases, search references/referrals were returned
+ * in this format, i.e., the dn was kind of in the scope position,
+ * but the required slash is missing. The whole thing is illegal syntax,
+ * but we need to account for it. Fortunately it can't be confused with
+ * anything real.
+ */
+ if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
+ q++;
+ /* ? immediately followed by question */
+ if( *q == '?') {
+ q++;
+ if( *q != '\0' ) {
+ /* parse dn part */
+ ldap_pvt_hex_unescape( q );
+ ludp->lud_dn = LDAP_STRDUP( q );
+ } else {
+ ludp->lud_dn = LDAP_STRDUP( "" );
+ }
+
+ if( ludp->lud_dn == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+ }
+ }
+
+ if( p == NULL ) {
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of dn */
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate dn part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse dn part */
+ ldap_pvt_hex_unescape( p );
+ ludp->lud_dn = LDAP_STRDUP( p );
+ } else {
+ ludp->lud_dn = LDAP_STRDUP( "" );
+ }
+
+ if( ludp->lud_dn == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ if( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of attributes */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate attributes part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse attributes */
+ ldap_pvt_hex_unescape( p );
+ ludp->lud_attrs = ldap_str2charray( p, "," );
+
+ if( ludp->lud_attrs == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADATTRS;
+ }
+ }
+
+ if ( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of scope */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate the scope part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse the scope */
+ ldap_pvt_hex_unescape( p );
+ ludp->lud_scope = str2scope( p );
+
+ if( ludp->lud_scope == -1 ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADSCOPE;
+ }
+ }
+
+ if ( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of filter */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate the filter part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse the filter */
+ ldap_pvt_hex_unescape( p );
+
+ if( ! *p ) {
+ /* missing filter */
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADFILTER;
+ }
+
+ LDAP_FREE( ludp->lud_filter );
+ ludp->lud_filter = LDAP_STRDUP( p );
+
+ if( ludp->lud_filter == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+ }
+
+ if ( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of extensions */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* extra '?' */
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+
+ /* parse the extensions */
+ ludp->lud_exts = ldap_str2charray( p, "," );
+
+ if( ludp->lud_exts == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADEXTS;
+ }
+
+ for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
+ ldap_pvt_hex_unescape( ludp->lud_exts[i] );
+
+ if( *ludp->lud_exts[i] == '!' ) {
+ /* count the number of critical extensions */
+ ludp->lud_crit_exts++;
+ }
+ }
+
+ if( i == 0 ) {
+ /* must have 1 or more */
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADEXTS;
+ }
+
+ /* no more */
+ *ludpp = ludp;
+ LDAP_FREE( url );
+ return LDAP_URL_SUCCESS;
+}
+
+static gint
+ldap_url_parse( LDAP_CONST gchar *url_in, LDAPURLDesc **ludpp )
+{
+ gint rc = ldap_url_parse_ext( url_in, ludpp );
+
+ if( rc != LDAP_URL_SUCCESS ) {
+ return rc;
+ }
+
+ if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) {
+ (*ludpp)->lud_scope = LDAP_SCOPE_BASE;
+ }
+
+ if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
+ LDAP_FREE( (*ludpp)->lud_host );
+ (*ludpp)->lud_host = NULL;
+ }
+
+ if ((*ludpp)->lud_port == 0) {
+ if( strcmp((*ludpp)->lud_scheme, "ldap") == 0 ) {
+ (*ludpp)->lud_port = LDAP_PORT;
+#ifdef LDAP_CONNECTIONLESS
+ } else if( strcmp((*ludpp)->lud_scheme, "cldap") == 0 ) {
+ (*ludpp)->lud_port = LDAP_PORT;
+#endif
+ } else if( strcmp((*ludpp)->lud_scheme, "ldaps") == 0 ) {
+ (*ludpp)->lud_port = LDAPS_PORT;
+ }
+ }
+
+ return rc;
+}
+
diff --git a/modules/calendar/Makefile.am b/modules/calendar/Makefile.am
new file mode 100644
index 0000000000..9c2d41dc38
--- /dev/null
+++ b/modules/calendar/Makefile.am
@@ -0,0 +1,81 @@
+INCLUDES = \
+ -DG_LOG_DOMAIN=\"calendar-modules\" \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/widgets \
+ -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \
+ $(EVOLUTION_CALENDAR_CFLAGS)
+
+module_LTLIBRARIES = \
+ libevolution-module-calendar.la
+
+libevolution_module_calendar_la_SOURCES = \
+ evolution-module-calendar.c \
+ e-cal-shell-backend.c \
+ e-cal-shell-backend.h \
+ e-cal-shell-content.c \
+ e-cal-shell-content.h \
+ e-cal-shell-migrate.c \
+ e-cal-shell-migrate.h \
+ e-cal-shell-settings.c \
+ e-cal-shell-settings.h \
+ e-cal-shell-sidebar.c \
+ e-cal-shell-sidebar.h \
+ e-cal-shell-view.c \
+ e-cal-shell-view.h \
+ e-cal-shell-view-actions.c \
+ e-cal-shell-view-actions.h \
+ e-cal-shell-view-memopad.c \
+ e-cal-shell-view-private.c \
+ e-cal-shell-view-private.h \
+ e-cal-shell-view-taskpad.c \
+ e-memo-shell-backend.c \
+ e-memo-shell-backend.h \
+ e-memo-shell-content.c \
+ e-memo-shell-content.h \
+ e-memo-shell-migrate.c \
+ e-memo-shell-migrate.h \
+ e-memo-shell-sidebar.c \
+ e-memo-shell-sidebar.h \
+ e-memo-shell-view.c \
+ e-memo-shell-view.h \
+ e-memo-shell-view-actions.c \
+ e-memo-shell-view-actions.h \
+ e-memo-shell-view-private.c \
+ e-memo-shell-view-private.h \
+ e-task-shell-backend.c \
+ e-task-shell-backend.h \
+ e-task-shell-content.c \
+ e-task-shell-content.h \
+ e-task-shell-migrate.c \
+ e-task-shell-migrate.h \
+ e-task-shell-sidebar.c \
+ e-task-shell-sidebar.h \
+ e-task-shell-view.c \
+ e-task-shell-view.h \
+ e-task-shell-view-actions.c \
+ e-task-shell-view-actions.h \
+ e-task-shell-view-private.c \
+ e-task-shell-view-private.h
+
+# Removed from all three
+# $(top_builddir)/a11y/calendar/libevolution-calendar-a11y.la
+
+libevolution_module_calendar_la_LIBADD = \
+ $(top_builddir)/shell/libeshell.la \
+ $(top_builddir)/calendar/gui/libcal-gui.la \
+ $(top_builddir)/calendar/importers/libevolution-calendar-importers.la \
+ $(top_builddir)/mail/libevolution-mail.la \
+ $(top_builddir)/addressbook/gui/contact-editor/libecontacteditor.la \
+ $(top_builddir)/addressbook/gui/contact-list-editor/libecontactlisteditor.la \
+ $(top_builddir)/e-util/libeutil.la \
+ $(top_builddir)/filter/libfilter.la \
+ $(top_builddir)/widgets/menus/libmenus.la \
+ $(top_builddir)/widgets/misc/libemiscwidgets.la \
+ $(top_builddir)/widgets/table/libetable.la \
+ $(CAMEL_LIBS) \
+ $(EVOLUTION_CALENDAR_LIBS)
+
+libevolution_module_calendar_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/calendar/e-cal-shell-backend.c b/modules/calendar/e-cal-shell-backend.c
new file mode 100644
index 0000000000..675a3d4fa6
--- /dev/null
+++ b/modules/calendar/e-cal-shell-backend.c
@@ -0,0 +1,666 @@
+/*
+ * e-cal-shell-backend.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-backend.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libecal/e-cal.h>
+#include <libedataserver/e-url.h>
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-group.h>
+
+#include "e-util/e-import.h"
+#include "shell/e-shell.h"
+#include "shell/e-shell-backend.h"
+#include "shell/e-shell-window.h"
+#include "widgets/misc/e-preferences-window.h"
+
+#include "calendar/common/authentication.h"
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/comp-util.h"
+#include "calendar/gui/e-attachment-handler-calendar.h"
+#include "calendar/gui/e-cal-config.h"
+#include "calendar/gui/e-cal-event.h"
+#include "calendar/gui/dialogs/cal-prefs-dialog.h"
+#include "calendar/gui/dialogs/calendar-setup.h"
+#include "calendar/gui/dialogs/event-editor.h"
+#include "calendar/importers/evolution-calendar-importer.h"
+
+#include "e-cal-shell-migrate.h"
+#include "e-cal-shell-settings.h"
+#include "e-cal-shell-view.h"
+
+#define E_CAL_SHELL_BACKEND_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_CAL_SHELL_BACKEND, ECalShellBackendPrivate))
+
+#define CONTACTS_BASE_URI "contacts://"
+#define WEATHER_BASE_URI "weather://"
+#define WEB_BASE_URI "webcal://"
+#define PERSONAL_RELATIVE_URI "system"
+
+struct _ECalShellBackendPrivate {
+ ESourceList *source_list;
+};
+
+enum {
+ PROP_0,
+ PROP_SOURCE_LIST
+};
+
+static gpointer parent_class;
+static GType cal_shell_backend_type;
+
+static void
+cal_shell_backend_ensure_sources (EShellBackend *shell_backend)
+{
+ /* XXX This is basically the same algorithm across all backends.
+ * Maybe we could somehow integrate this into EShellBackend? */
+
+ ECalShellBackendPrivate *priv;
+ ESourceGroup *on_this_computer;
+ ESourceGroup *on_the_web;
+ ESourceGroup *contacts;
+ ESourceGroup *weather;
+ ESource *birthdays;
+ ESource *personal;
+ EShell *shell;
+ EShellSettings *shell_settings;
+ GSList *groups, *iter;
+ const gchar *data_dir;
+ const gchar *name;
+ gchar *base_uri;
+ gchar *filename;
+ gchar *property;
+
+ on_this_computer = NULL;
+ on_the_web = NULL;
+ contacts = NULL;
+ weather = NULL;
+ birthdays = NULL;
+ personal = NULL;
+
+ priv = E_CAL_SHELL_BACKEND_GET_PRIVATE (shell_backend);
+
+ shell = e_shell_backend_get_shell (shell_backend);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ if (!e_cal_get_sources (&priv->source_list, E_CAL_SOURCE_TYPE_EVENT, NULL)) {
+ g_warning ("Could not get calendar sources from GConf!");
+ return;
+ }
+
+ data_dir = e_shell_backend_get_data_dir (shell_backend);
+ filename = g_build_filename (data_dir, "local", NULL);
+ base_uri = g_filename_to_uri (filename, NULL, NULL);
+ g_free (filename);
+
+ groups = e_source_list_peek_groups (priv->source_list);
+ for (iter = groups; iter != NULL; iter = iter->next) {
+ ESourceGroup *source_group = iter->data;
+ const gchar *group_base_uri;
+
+ group_base_uri = e_source_group_peek_base_uri (source_group);
+
+ /* Compare only "file://" part. if the user's home
+ * changes, we do not want to create another group. */
+ if (on_this_computer == NULL &&
+ strncmp (base_uri, group_base_uri, 7) == 0)
+ on_this_computer = source_group;
+
+ else if (on_the_web == NULL &&
+ strcmp (WEB_BASE_URI, group_base_uri) == 0)
+ on_the_web = source_group;
+
+ else if (contacts == NULL &&
+ strcmp (CONTACTS_BASE_URI, group_base_uri) == 0)
+ contacts = source_group;
+
+ else if (weather == NULL &&
+ strcmp (WEATHER_BASE_URI, group_base_uri) == 0)
+ weather = source_group;
+ }
+
+ name = _("On This Computer");
+
+ if (on_this_computer != NULL) {
+ GSList *sources;
+ const gchar *group_base_uri;
+
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (on_this_computer, name);
+
+ sources = e_source_group_peek_sources (on_this_computer);
+ group_base_uri = e_source_group_peek_base_uri (on_this_computer);
+
+ /* Make sure this group includes a "Personal" source. */
+ for (iter = sources; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+ const gchar *relative_uri;
+
+ relative_uri = e_source_peek_relative_uri (source);
+ if (relative_uri == NULL)
+ continue;
+
+ if (strcmp (PERSONAL_RELATIVE_URI, relative_uri) != 0)
+ continue;
+
+ personal = source;
+ break;
+ }
+
+ /* Make sure we have the correct base URI. This can
+ * change when the user's home directory changes. */
+ if (strcmp (base_uri, group_base_uri) != 0) {
+ e_source_group_set_base_uri (
+ on_this_computer, base_uri);
+
+ /* XXX We shouldn't need this sync call here as
+ * set_base_uri() results in synching to GConf,
+ * but that happens in an idle loop and too late
+ * to prevent the user from seeing a "Cannot
+ * Open ... because of invalid URI" error. */
+ e_source_list_sync (priv->source_list, NULL);
+ }
+
+ } else {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, base_uri);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+ }
+
+ name = _("Personal");
+
+ if (personal == NULL) {
+ ESource *source;
+ GSList *selected;
+ gchar *primary;
+
+ source = e_source_new (name, PERSONAL_RELATIVE_URI);
+ e_source_group_add_source (on_this_computer, source, -1);
+ g_object_unref (source);
+
+ primary = e_shell_settings_get_string (
+ shell_settings, "cal-primary-calendar");
+
+ selected = calendar_config_get_calendars_selected ();
+
+ if (primary == NULL && selected == NULL) {
+ const gchar *uid;
+
+ uid = e_source_peek_uid (source);
+ selected = g_slist_prepend (NULL, g_strdup (uid));
+
+ e_shell_settings_set_string (
+ shell_settings, "cal-primary-calendar", uid);
+ calendar_config_set_calendars_selected (selected);
+ }
+
+ g_slist_foreach (selected, (GFunc) g_free, NULL);
+ g_slist_free (selected);
+ g_free (primary);
+ } else {
+ /* Force the source name to the current locale. */
+ e_source_set_name (personal, name);
+ }
+
+ name = _("On The Web");
+
+ if (on_the_web == NULL) {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, WEB_BASE_URI);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+ } else {
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (on_the_web, name);
+ }
+
+ name = _("Contacts");
+
+ if (contacts != NULL) {
+ GSList *sources;
+
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (contacts, name);
+
+ sources = e_source_group_peek_sources (contacts);
+
+ if (sources != NULL) {
+ GSList *trash;
+
+ /* There is only one source under Contacts. */
+ birthdays = E_SOURCE (sources->data);
+ sources = g_slist_next (sources);
+
+ /* Delete any other sources in this group.
+ * Earlier versions allowed you to create
+ * additional sources under Contacts. */
+ trash = g_slist_copy (sources);
+ while (trash != NULL) {
+ ESource *source = trash->data;
+ e_source_group_remove_source (contacts, source);
+ trash = g_slist_delete_link (trash, trash);
+ }
+
+ }
+ } else {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, CONTACTS_BASE_URI);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+
+ /* This is now a borrowed reference. */
+ contacts = source_group;
+ }
+
+ /* XXX e_source_group_get_property() returns a newly-allocated
+ * string when it could just as easily return a const string.
+ * Unfortunately, fixing that would break the API. */
+ property = e_source_group_get_property (contacts, "create_source");
+ if (property == NULL)
+ e_source_group_set_property (contacts, "create_source", "no");
+ g_free (property);
+
+ name = _("Birthdays & Anniversaries");
+
+ if (birthdays == NULL) {
+ ESource *source;
+ const gchar *name;
+
+ name = _("Birthdays & Anniversaries");
+ source = e_source_new (name, "/");
+ e_source_group_add_source (contacts, source, -1);
+ g_object_unref (source);
+
+ /* This is now a borrowed reference. */
+ birthdays = source;
+ } else {
+ /* Force the source name to the current locale. */
+ e_source_set_name (birthdays, name);
+ }
+
+ if (e_source_get_property (birthdays, "delete") == NULL)
+ e_source_set_property (birthdays, "delete", "no");
+
+ if (e_source_peek_color_spec (birthdays) == NULL)
+ e_source_set_color_spec (birthdays, "#DDBECE");
+
+ name = _("Weather");
+
+ if (weather == NULL) {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, WEATHER_BASE_URI);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+ } else {
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (weather, name);
+ }
+
+ g_free (base_uri);
+}
+
+static void
+cal_shell_backend_cal_opened_cb (ECal *cal,
+ ECalendarStatus status,
+ GtkAction *action)
+{
+ EShell *shell;
+ ECalComponent *comp;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ const gchar *action_name;
+ gboolean all_day;
+
+ /* FIXME Pass this in. */
+ shell = e_shell_get_default ();
+
+ /* XXX Handle errors better. */
+ if (status != E_CALENDAR_STATUS_OK)
+ return;
+
+ action_name = gtk_action_get_name (action);
+
+ flags |= COMP_EDITOR_NEW_ITEM;
+ flags |= COMP_EDITOR_USER_ORG;
+ if (strcmp (action_name, "event-meeting-new") == 0)
+ flags |= COMP_EDITOR_MEETING;
+
+ all_day = (strcmp (action_name, "event-all-day-new") == 0);
+
+ editor = event_editor_new (cal, shell, flags);
+ comp = cal_comp_event_new_with_current_time (cal, all_day);
+ comp_editor_edit_comp (editor, comp);
+
+ gtk_window_present (GTK_WINDOW (editor));
+
+ g_object_unref (comp);
+ g_object_unref (cal);
+}
+
+static void
+action_event_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ ECal *cal = NULL;
+ ECalSourceType source_type;
+ ESourceList *source_list;
+ EShellSettings *shell_settings;
+ EShell *shell;
+ gchar *uid;
+
+ /* This callback is used for both appointments and meetings. */
+
+ source_type = E_CAL_SOURCE_TYPE_EVENT;
+
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ if (!e_cal_get_sources (&source_list, source_type, NULL)) {
+ g_warning ("Could not get calendar sources from GConf!");
+ return;
+ }
+
+ uid = e_shell_settings_get_string (
+ shell_settings, "cal-primary-calendar");
+
+ if (uid != NULL) {
+ ESource *source;
+
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ if (source != NULL)
+ cal = auth_new_cal_from_source (source, source_type);
+ g_free (uid);
+ }
+
+ if (cal == NULL)
+ cal = auth_new_cal_from_default (source_type);
+
+ g_return_if_fail (cal != NULL);
+
+ g_signal_connect (
+ cal, "cal-opened",
+ G_CALLBACK (cal_shell_backend_cal_opened_cb), action);
+
+ e_cal_open_async (cal, FALSE);
+}
+
+static void
+action_calendar_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ calendar_setup_new_calendar (GTK_WINDOW (shell_window));
+}
+
+static GtkActionEntry item_entries[] = {
+
+ { "event-new",
+ "appointment-new",
+ NC_("New", "_Appointment"),
+ "<Shift><Control>a",
+ N_("Create a new appointment"),
+ G_CALLBACK (action_event_new_cb) },
+
+ { "event-all-day-new",
+ "stock_new-24h-appointment",
+ NC_("New", "All Day A_ppointment"),
+ NULL,
+ N_("Create a new all-day appointment"),
+ G_CALLBACK (action_event_new_cb) },
+
+ { "event-meeting-new",
+ "stock_new-meeting",
+ NC_("New", "M_eeting"),
+ "<Shift><Control>e",
+ N_("Create a new meeting request"),
+ G_CALLBACK (action_event_new_cb) }
+};
+
+static GtkActionEntry source_entries[] = {
+
+ { "calendar-new",
+ "x-office-calendar",
+ NC_("New", "Cale_ndar"),
+ NULL,
+ N_("Create a new calendar"),
+ G_CALLBACK (action_calendar_new_cb) }
+};
+
+static void
+cal_shell_backend_init_hooks (void)
+{
+ e_plugin_hook_register_type (e_cal_config_hook_get_type ());
+ e_plugin_hook_register_type (e_cal_event_hook_get_type ());
+}
+
+static void
+cal_shell_backend_init_importers (void)
+{
+ EImportClass *import_class;
+ EImportImporter *importer;
+
+ import_class = g_type_class_ref (e_import_get_type ());
+
+ importer = gnome_calendar_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+
+ importer = ical_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+
+ importer = vcal_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+}
+
+static void
+cal_shell_backend_init_preferences (EShell *shell)
+{
+ GtkWidget *preferences_window;
+
+ preferences_window = e_shell_get_preferences_window (shell);
+
+ e_preferences_window_add_page (
+ E_PREFERENCES_WINDOW (preferences_window),
+ "calendar-and-tasks",
+ "preferences-calendar-and-tasks",
+ _("Calendar and Tasks"),
+ calendar_prefs_dialog_new (shell),
+ 600);
+}
+
+static gboolean
+cal_shell_backend_handle_uri_cb (EShellBackend *shell_backend,
+ const gchar *uri)
+{
+ /* FIXME */
+ return FALSE;
+}
+
+static void
+cal_shell_backend_window_created_cb (EShellBackend *shell_backend,
+ GtkWindow *window)
+{
+ const gchar *backend_name;
+
+ if (!E_IS_SHELL_WINDOW (window))
+ return;
+
+ backend_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name;
+
+ e_shell_window_register_new_item_actions (
+ E_SHELL_WINDOW (window), backend_name,
+ item_entries, G_N_ELEMENTS (item_entries));
+
+ e_shell_window_register_new_source_actions (
+ E_SHELL_WINDOW (window), backend_name,
+ source_entries, G_N_ELEMENTS (source_entries));
+}
+
+static void
+cal_shell_backend_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SOURCE_LIST:
+ g_value_set_object (
+ value,
+ e_cal_shell_backend_get_source_list (
+ E_CAL_SHELL_BACKEND (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+cal_shell_backend_dispose (GObject *object)
+{
+ ECalShellBackendPrivate *priv;
+
+ priv = E_CAL_SHELL_BACKEND_GET_PRIVATE (object);
+
+ if (priv->source_list != NULL) {
+ g_object_unref (priv->source_list);
+ priv->source_list = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+cal_shell_backend_constructed (GObject *object)
+{
+ EShell *shell;
+ EShellBackend *shell_backend;
+
+ shell_backend = E_SHELL_BACKEND (object);
+ shell = e_shell_backend_get_shell (shell_backend);
+
+ cal_shell_backend_ensure_sources (shell_backend);
+
+ g_signal_connect_swapped (
+ shell, "handle-uri",
+ G_CALLBACK (cal_shell_backend_handle_uri_cb),
+ shell_backend);
+
+ g_signal_connect_swapped (
+ shell, "window-created",
+ G_CALLBACK (cal_shell_backend_window_created_cb),
+ shell_backend);
+
+ cal_shell_backend_init_hooks ();
+ cal_shell_backend_init_importers ();
+
+ /* Initialize settings before initializing preferences,
+ * since the preferences bind to the shell settings. */
+ e_cal_shell_backend_init_settings (shell);
+ cal_shell_backend_init_preferences (shell);
+
+ e_attachment_handler_calendar_get_type ();
+}
+
+static void
+cal_shell_backend_class_init (ECalShellBackendClass *class)
+{
+ GObjectClass *object_class;
+ EShellBackendClass *shell_backend_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ECalShellBackendPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = cal_shell_backend_get_property;
+ object_class->dispose = cal_shell_backend_dispose;
+ object_class->constructed = cal_shell_backend_constructed;
+
+ shell_backend_class = E_SHELL_BACKEND_CLASS (class);
+ shell_backend_class->shell_view_type = E_TYPE_CAL_SHELL_VIEW;
+ shell_backend_class->name = "calendar";
+ shell_backend_class->aliases = "";
+ shell_backend_class->schemes = "calendar";
+ shell_backend_class->sort_order = 400;
+ shell_backend_class->start = NULL;
+ shell_backend_class->is_busy = NULL;
+ shell_backend_class->shutdown = NULL;
+ shell_backend_class->migrate = e_cal_shell_backend_migrate;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SOURCE_LIST,
+ g_param_spec_object (
+ "source-list",
+ _("Source List"),
+ _("The registry of calendars"),
+ E_TYPE_SOURCE_LIST,
+ G_PARAM_READABLE));
+}
+
+static void
+cal_shell_backend_init (ECalShellBackend *cal_shell_backend)
+{
+ cal_shell_backend->priv =
+ E_CAL_SHELL_BACKEND_GET_PRIVATE (cal_shell_backend);
+}
+
+GType
+e_cal_shell_backend_get_type (void)
+{
+ return cal_shell_backend_type;
+}
+
+void
+e_cal_shell_backend_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (ECalShellBackendClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) cal_shell_backend_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (ECalShellBackend),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) cal_shell_backend_init,
+ NULL /* value_table */
+ };
+
+ cal_shell_backend_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_BACKEND,
+ "ECalShellBackend", &type_info, 0);
+}
+
+ESourceList *
+e_cal_shell_backend_get_source_list (ECalShellBackend *cal_shell_backend)
+{
+ g_return_val_if_fail (
+ E_IS_CAL_SHELL_BACKEND (cal_shell_backend), NULL);
+
+ return cal_shell_backend->priv->source_list;
+}
diff --git a/modules/calendar/e-cal-shell-backend.h b/modules/calendar/e-cal-shell-backend.h
new file mode 100644
index 0000000000..497e200490
--- /dev/null
+++ b/modules/calendar/e-cal-shell-backend.h
@@ -0,0 +1,70 @@
+/*
+ * e-cal-shell-backend.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_CAL_SHELL_BACKEND_H
+#define E_CAL_SHELL_BACKEND_H
+
+#include <shell/e-shell-backend.h>
+#include <libedataserver/e-source-list.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CAL_SHELL_BACKEND \
+ (e_cal_shell_backend_get_type ())
+#define E_CAL_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_CAL_SHELL_BACKEND, ECalShellBackend))
+#define E_CAL_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_CAL_SHELL_BACKEND, ECalShellBackendClass))
+#define E_IS_CAL_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_CAL_SHELL_BACKEND))
+#define E_IS_CAL_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_CAL_SHELL_BACKEND))
+#define E_CAL_SHELL_BACKEND_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_CAL_SHELL_BACKEND, ECalShellBackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECalShellBackend ECalShellBackend;
+typedef struct _ECalShellBackendClass ECalShellBackendClass;
+typedef struct _ECalShellBackendPrivate ECalShellBackendPrivate;
+
+struct _ECalShellBackend {
+ EShellBackend parent;
+ ECalShellBackendPrivate *priv;
+};
+
+struct _ECalShellBackendClass {
+ EShellBackendClass parent_class;
+};
+
+GType e_cal_shell_backend_get_type (void);
+void e_cal_shell_backend_register_type
+ (GTypeModule *type_module);
+ESourceList * e_cal_shell_backend_get_source_list
+ (ECalShellBackend *cal_shell_backend);
+
+G_END_DECLS
+
+#endif /* E_CAL_SHELL_BACKEND_H */
diff --git a/modules/calendar/e-cal-shell-content.c b/modules/calendar/e-cal-shell-content.c
new file mode 100644
index 0000000000..6afb40dce7
--- /dev/null
+++ b/modules/calendar/e-cal-shell-content.c
@@ -0,0 +1,827 @@
+/*
+ * e-cal-shell-content.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-content.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "e-util/gconf-bridge.h"
+
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/e-cal-list-view-config.h"
+#include "calendar/gui/e-cal-model-calendar.h"
+#include "calendar/gui/e-calendar-table.h"
+#include "calendar/gui/e-calendar-table-config.h"
+#include "calendar/gui/e-day-view-config.h"
+#include "calendar/gui/e-memo-table-config.h"
+#include "calendar/gui/e-week-view-config.h"
+
+#include "widgets/menus/gal-view-etable.h"
+
+#define E_CAL_SHELL_CONTENT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentPrivate))
+
+struct _ECalShellContentPrivate {
+ GtkWidget *hpaned;
+ GtkWidget *notebook;
+ GtkWidget *vpaned;
+
+ GtkWidget *day_view;
+ GtkWidget *work_week_view;
+ GtkWidget *week_view;
+ GtkWidget *month_view;
+ GtkWidget *list_view;
+ GtkWidget *task_table;
+ GtkWidget *memo_table;
+
+ EDayViewConfig *day_view_config;
+ EDayViewConfig *work_week_view_config;
+ EWeekViewConfig *week_view_config;
+ EWeekViewConfig *month_view_config;
+ ECalListViewConfig *list_view_config;
+ ECalendarTableConfig *task_table_config;
+ EMemoTableConfig *memo_table_config;
+
+ GalViewInstance *view_instance;
+
+ guint paned_binding_id;
+};
+
+enum {
+ PROP_0
+};
+
+/* Used to indicate who has the focus within the calendar view. */
+typedef enum {
+ FOCUS_CALENDAR,
+ FOCUS_MEMO_TABLE,
+ FOCUS_TASK_TABLE,
+ FOCUS_OTHER
+} FocusLocation;
+
+static gpointer parent_class;
+static GType cal_shell_content_type;
+
+static void
+cal_shell_content_display_view_cb (ECalShellContent *cal_shell_content,
+ GalView *gal_view)
+{
+ /* FIXME */
+}
+
+static void
+cal_shell_content_notify_view_id_cb (ECalShellContent *cal_shell_content)
+{
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ GConfBridge *bridge;
+ GtkWidget *paned;
+ guint binding_id;
+ const gchar *key;
+ const gchar *view_id;
+
+ bridge = gconf_bridge_get ();
+ paned = cal_shell_content->priv->hpaned;
+ binding_id = cal_shell_content->priv->paned_binding_id;
+
+ shell_content = E_SHELL_CONTENT (cal_shell_content);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ view_id = e_shell_view_get_view_id (shell_view);
+
+ if (binding_id > 0)
+ gconf_bridge_unbind (bridge, binding_id);
+
+ if (view_id != NULL && strcmp (view_id, "Month_View") == 0)
+ key = "/apps/evolution/calendar/display/month_hpane_position";
+ else
+ key = "/apps/evolution/calendar/display/hpane_position";
+
+ binding_id = gconf_bridge_bind_property_delayed (
+ bridge, key, G_OBJECT (paned), "position");
+
+ cal_shell_content->priv->paned_binding_id = binding_id;
+}
+
+static FocusLocation
+cal_shell_content_get_focus_location (ECalShellContent *cal_shell_content)
+{
+ return FOCUS_OTHER;
+#if 0 /* TEMPORARILY DISABLED */
+ GtkWidget *widget;
+ GnomeCalendar *calendar;
+ ECalendarTable *task_table;
+ EMemoTable *memo_table;
+ ETable *table;
+ ECalendarView *calendar_view;
+
+ calendar = GNOME_CALENDAR (cal_shell_content->priv->calendar);
+ widget = gnome_calendar_get_current_view_widget (calendar);
+
+ memo_table = E_MEMO_TABLE (cal_shell_content->priv->memo_table);
+ task_table = E_CALENDAR_TABLE (cal_shell_content->priv->task_table);
+
+ table = e_memo_table_get_table (memo_table);
+ if (GTK_WIDGET_HAS_FOCUS (table->table_canvas))
+ return FOCUS_MEMO_TABLE;
+
+ table = e_calendar_table_get_table (task_table);
+ if (GTK_WIDGET_HAS_FOCUS (table->table_canvas))
+ return FOCUS_TASK_TABLE;
+
+ if (E_IS_DAY_VIEW (widget)) {
+ EDayView *view = E_DAY_VIEW (widget);
+
+ if (GTK_WIDGET_HAS_FOCUS (view->top_canvas))
+ return FOCUS_CALENDAR;
+
+ if (GNOME_CANVAS (view->top_canvas)->focused_item != NULL)
+ return FOCUS_CALENDAR;
+
+ if (GTK_WIDGET_HAS_FOCUS (view->main_canvas))
+ return FOCUS_CALENDAR;
+
+ if (GNOME_CANVAS (view->main_canvas)->focused_item != NULL)
+ return FOCUS_CALENDAR;
+
+ } else if (E_IS_WEEK_VIEW (widget)) {
+ EWeekView *view = E_WEEK_VIEW (widget);
+
+ if (GTK_WIDGET_HAS_FOCUS (view->main_canvas))
+ return FOCUS_CALENDAR;
+
+ if (GNOME_CANVAS (view->main_canvas)->focused_item != NULL)
+ return FOCUS_CALENDAR;
+
+ } else if (E_IS_CAL_LIST_VIEW (widget)) {
+ ECalListView *view = E_CAL_LIST_VIEW (widget);
+
+ table = e_table_scrolled_get_table (view->table_scrolled);
+ if (GTK_WIDGET_HAS_FOCUS (table))
+ return FOCUS_CALENDAR;
+ }
+
+ return FOCUS_OTHER;
+#endif
+}
+
+static void
+cal_shell_content_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+cal_shell_content_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+cal_shell_content_dispose (GObject *object)
+{
+ ECalShellContentPrivate *priv;
+
+ priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (object);
+
+ if (priv->hpaned != NULL) {
+ g_object_unref (priv->hpaned);
+ priv->hpaned = NULL;
+ }
+
+ if (priv->notebook != NULL) {
+ g_object_unref (priv->notebook);
+ priv->notebook = NULL;
+ }
+
+ if (priv->vpaned != NULL) {
+ g_object_unref (priv->vpaned);
+ priv->vpaned = NULL;
+ }
+
+ if (priv->day_view != NULL) {
+ g_object_unref (priv->day_view);
+ priv->day_view = NULL;
+ }
+
+ if (priv->work_week_view != NULL) {
+ g_object_unref (priv->work_week_view);
+ priv->work_week_view = NULL;
+ }
+
+ if (priv->week_view != NULL) {
+ g_object_unref (priv->week_view);
+ priv->week_view = NULL;
+ }
+
+ if (priv->month_view != NULL) {
+ g_object_unref (priv->month_view);
+ priv->month_view = NULL;
+ }
+
+ if (priv->list_view != NULL) {
+ g_object_unref (priv->list_view);
+ priv->list_view = NULL;
+ }
+
+ if (priv->task_table != NULL) {
+ g_object_unref (priv->task_table);
+ priv->task_table = NULL;
+ }
+
+ if (priv->memo_table != NULL) {
+ g_object_unref (priv->memo_table);
+ priv->memo_table = NULL;
+ }
+
+ if (priv->day_view_config != NULL) {
+ g_object_unref (priv->day_view_config);
+ priv->day_view_config = NULL;
+ }
+
+ if (priv->work_week_view_config != NULL) {
+ g_object_unref (priv->work_week_view_config);
+ priv->work_week_view_config = NULL;
+ }
+
+ if (priv->week_view_config != NULL) {
+ g_object_unref (priv->week_view_config);
+ priv->week_view_config = NULL;
+ }
+
+ if (priv->month_view_config != NULL) {
+ g_object_unref (priv->month_view_config);
+ priv->month_view_config = NULL;
+ }
+
+ if (priv->list_view_config != NULL) {
+ g_object_unref (priv->list_view_config);
+ priv->list_view_config = NULL;
+ }
+
+ if (priv->task_table_config != NULL) {
+ g_object_unref (priv->task_table_config);
+ priv->task_table_config = NULL;
+ }
+
+ if (priv->memo_table_config != NULL) {
+ g_object_unref (priv->memo_table_config);
+ priv->memo_table_config = NULL;
+ }
+
+ if (priv->view_instance != NULL) {
+ g_object_unref (priv->view_instance);
+ priv->view_instance = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+cal_shell_content_finalize (GObject *object)
+{
+ ECalShellContentPrivate *priv;
+
+ priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (object);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+cal_shell_content_constructed (GObject *object)
+{
+ ECalShellContentPrivate *priv;
+ ECalModelCalendar *cal_model;
+ ECalModel *memo_model;
+ ECalModel *task_model;
+ EShellContent *shell_content;
+ EShellBackend *shell_backend;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellContent *foreign_content;
+ EShellView *foreign_view;
+ GalViewInstance *view_instance;
+ GConfBridge *bridge;
+ GtkWidget *container;
+ GtkWidget *widget;
+ const gchar *config_dir;
+ const gchar *key;
+ gchar *filename;
+ gchar *markup;
+ gint page_num;
+
+ priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ shell_content = E_SHELL_CONTENT (object);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ config_dir = e_shell_backend_get_config_dir (shell_backend);
+
+ /* Calendar model for the views. */
+ cal_model = e_cal_model_calendar_new ();
+ e_cal_model_set_flags (
+ E_CAL_MODEL (cal_model),
+ E_CAL_MODEL_FLAGS_EXPAND_RECURRENCES);
+
+ /* We borrow the memopad and taskpad models from the memo
+ * and task views, loading the views if necessary. */
+
+ foreign_view = e_shell_window_get_shell_view (shell_window, "memos");
+ foreign_content = e_shell_view_get_shell_content (foreign_view);
+ g_object_get (foreign_content, "model", &memo_model, NULL);
+
+ foreign_view = e_shell_window_get_shell_view (shell_window, "tasks");
+ foreign_content = e_shell_view_get_shell_content (foreign_view);
+ g_object_get (foreign_content, "model", &task_model, NULL);
+
+ /* Build content widgets. */
+
+ container = GTK_WIDGET (object);
+
+ /* FIXME Need to deal with saving and restoring the position.
+ * Month view has its own position. */
+ widget = gtk_hpaned_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->hpaned = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = priv->hpaned;
+
+ widget = gtk_notebook_new ();
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
+ gtk_paned_pack1 (GTK_PANED (container), widget, FALSE, TRUE);
+ priv->notebook = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* FIXME Need to deal with saving and restoring the position.
+ * Month view has its own position. */
+ widget = gtk_vpaned_new ();
+ gtk_paned_pack2 (GTK_PANED (container), widget, TRUE, TRUE);
+ priv->vpaned = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = priv->notebook;
+
+ /* Add views in the order defined by GnomeCalendarViewType, such
+ * that the notebook page number corresponds to the view type.
+ * The assertions below ensure that stays true. */
+
+#if 0 /* Not so fast... get the memo/task pads working first. */
+ /* FIXME Need to establish a calendar and timezone first. */
+ widget = e_day_view_new (E_CAL_MODEL (cal_model));
+ e_calendar_view_set_calendar (
+ E_CALENDAR_VIEW (widget), GNOME_CALENDAR (priv->calendar));
+ e_calendar_view_set_timezone (
+ E_CALENDAR_VIEW (widget), priv->timezone);
+ page_num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (widget));
+ gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+ g_return_if_fail (page_num == GNOME_CAL_DAY_VIEW);
+ priv->day_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* FIXME Need to establish a calendar and timezone first. */
+ widget = e_day_view_new (E_CAL_MODEL (cal_model));
+ e_day_view_set_work_week_view (E_DAY_VIEW (widget), TRUE);
+ e_day_view_set_days_shown (E_DAY_VIEW (widget), 5);
+ e_calendar_view_set_calendar (
+ E_CALENDAR_VIEW (widget), GNOME_CALENDAR (priv->calendar));
+ e_calendar_view_set_timezone (
+ E_CALENDAR_VIEW (widget), priv->timezone);
+ page_num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (widget));
+ gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+ g_return_if_fail (page_num == GNOME_CAL_WORK_WEEK_VIEW);
+ priv->work_week_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* FIXME Need to establish a calendar and timezone first. */
+ widget = e_week_view_new (E_CAL_MODEL (cal_model));
+ e_calendar_view_set_calendar (
+ E_CALENDAR_VIEW (widget), GNOME_CALENDAR (priv->calendar));
+ e_calendar_view_set_timezone (
+ E_CALENDAR_VIEW (widget), priv->timezone);
+ page_num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (widget));
+ gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+ g_return_if_fail (page_num == GNOME_CAL_WEEK_VIEW);
+ priv->week_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* FIXME Need to establish a calendar and timezone first. */
+ widget = e_week_view_new (E_CAL_MODEL (cal_model));
+ e_week_view_set_multi_week_view (E_WEEK_VIEW (widget), TRUE);
+ e_week_view_set_weeks_shown (E_WEEK_VIEW (widget), 6);
+ e_calendar_view_set_calendar (
+ E_CALENDAR_VIEW (widget), GNOME_CALENDAR (priv->calendar));
+ e_calendar_view_set_timezone (
+ E_CALENDAR_VIEW (widget), priv->timezone);
+ page_num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (widget));
+ gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+ g_return_if_fail (page_num == GNOME_CAL_MONTH_VIEW);
+ priv->month_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* FIXME Need to establish a calendar and timezone first. */
+ widget = e_cal_list_view_new (E_CAL_MODEL (cal_model));
+ e_calendar_view_set_calendar (
+ E_CALENDAR_VIEW (widget), GNOME_CALENDAR (priv->calendar));
+ e_calendar_view_set_timezone (
+ E_CALENDAR_VIEW (widget), priv->timezone);
+ page_num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (widget));
+ gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+ g_return_if_fail (page_num == GNOME_CAL_LIST_VIEW);
+ priv->list_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+#endif
+
+ container = priv->vpaned;
+
+ widget = gtk_vbox_new (FALSE, 0);
+ gtk_paned_pack1 (GTK_PANED (container), widget, FALSE, FALSE);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ markup = g_strdup_printf ("<b>%s</b>", _("Tasks"));
+ gtk_label_set_markup (GTK_LABEL (widget), markup);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+ gtk_widget_show (widget);
+ g_free (markup);
+
+ widget = e_calendar_table_new (shell_view, task_model);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ priv->task_table = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ filename = g_build_filename (config_dir, "TaskPad", NULL);
+ e_calendar_table_load_state (E_CALENDAR_TABLE (widget), filename);
+ g_free (filename);
+
+ container = priv->vpaned;
+
+ widget = gtk_vbox_new (FALSE, 0);
+ gtk_paned_pack2 (GTK_PANED (container), widget, TRUE, FALSE);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_label_new (NULL);
+ markup = g_strdup_printf ("<b>%s</b>", _("Memos"));
+ gtk_label_set_markup (GTK_LABEL (widget), markup);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+ gtk_widget_show (widget);
+ g_free (markup);
+
+ widget = e_memo_table_new (shell_view, memo_model);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ priv->memo_table = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ filename = g_build_filename (config_dir, "MemoPad", NULL);
+ e_memo_table_load_state (E_MEMO_TABLE (widget), filename);
+ g_free (filename);
+
+ /* Configuration managers for views and tables. */
+ priv->day_view_config = e_day_view_config_new (
+ E_DAY_VIEW (priv->day_view));
+ priv->work_week_view_config = e_day_view_config_new (
+ E_DAY_VIEW (priv->work_week_view));
+ priv->week_view_config = e_week_view_config_new (
+ E_WEEK_VIEW (priv->week_view));
+ priv->month_view_config = e_week_view_config_new (
+ E_WEEK_VIEW (priv->month_view));
+ priv->list_view_config = e_cal_list_view_config_new (
+ E_CAL_LIST_VIEW (priv->list_view));
+ priv->task_table_config = e_calendar_table_config_new (
+ E_CALENDAR_TABLE (priv->task_table));
+ priv->memo_table_config = e_memo_table_config_new (
+ E_MEMO_TABLE (priv->memo_table));
+
+ /* Load the view instance. */
+
+ view_instance = e_shell_view_new_view_instance (shell_view, NULL);
+ g_signal_connect_swapped (
+ view_instance, "display-view",
+ G_CALLBACK (cal_shell_content_display_view_cb),
+ object);
+ gal_view_instance_load (view_instance);
+ priv->view_instance = view_instance;
+
+ g_signal_connect_swapped (
+ shell_view, "notify::view-id",
+ G_CALLBACK (cal_shell_content_notify_view_id_cb),
+ object);
+
+ /* Bind GObject properties to GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (priv->vpaned);
+ key = "/apps/evolution/calendar/display/vpane_position";
+ gconf_bridge_bind_property_delayed (bridge, key, object, "position");
+
+ g_object_unref (memo_model);
+ g_object_unref (task_model);
+}
+
+static void
+cal_shell_content_class_init (ECalShellContentClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ECalShellContentPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = cal_shell_content_set_property;
+ object_class->get_property = cal_shell_content_get_property;
+ object_class->dispose = cal_shell_content_dispose;
+ object_class->finalize = cal_shell_content_finalize;
+ object_class->constructed = cal_shell_content_constructed;
+}
+
+static void
+cal_shell_content_init (ECalShellContent *cal_shell_content)
+{
+ cal_shell_content->priv =
+ E_CAL_SHELL_CONTENT_GET_PRIVATE (cal_shell_content);
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_cal_shell_content_get_type (void)
+{
+ return cal_shell_content_type;
+}
+
+void
+e_cal_shell_content_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (ECalShellContentClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) cal_shell_content_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (ECalShellContent),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) cal_shell_content_init,
+ NULL /* value_table */
+ };
+
+ cal_shell_content_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_CONTENT,
+ "ECalShellContent", &type_info, 0);
+}
+
+GtkWidget *
+e_cal_shell_content_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_CAL_SHELL_CONTENT,
+ "shell-view", shell_view, NULL);
+}
+
+GnomeCalendar *
+e_cal_shell_content_get_calendar (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ /* FIXME */
+ /*return GNOME_CALENDAR (cal_shell_content->priv->calendar);*/
+ return NULL;
+}
+
+EMemoTable *
+e_cal_shell_content_get_memo_table (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ return E_MEMO_TABLE (cal_shell_content->priv->memo_table);
+}
+
+ECalendarTable *
+e_cal_shell_content_get_task_table (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ return E_CALENDAR_TABLE (cal_shell_content->priv->task_table);
+}
+
+icaltimezone *
+e_cal_shell_content_get_timezone (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ /* FIXME */
+ /*return cal_shell_content->priv->timezone;*/
+ return NULL;
+}
+
+GalViewInstance *
+e_cal_shell_content_get_view_instance (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ return cal_shell_content->priv->view_instance;
+}
+
+void
+e_cal_shell_content_copy_clipboard (ECalShellContent *cal_shell_content)
+{
+#if 0
+ GnomeCalendar *calendar;
+ EMemoTable *memo_table;
+ ECalendarTable *task_table;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ switch (cal_shell_content_get_focus_location (cal_shell_content)) {
+ case FOCUS_CALENDAR:
+ gnome_calendar_copy_clipboard (calendar);
+ break;
+
+ case FOCUS_MEMO_TABLE:
+ e_memo_table_copy_clipboard (memo_table);
+ break;
+
+ case FOCUS_TASK_TABLE:
+ e_calendar_table_copy_clipboard (task_table);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+#endif
+}
+
+void
+e_cal_shell_content_cut_clipboard (ECalShellContent *cal_shell_content)
+{
+#if 0
+ GnomeCalendar *calendar;
+ EMemoTable *memo_table;
+ ECalendarTable *task_table;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ switch (cal_shell_content_get_focus_location (cal_shell_content)) {
+ case FOCUS_CALENDAR:
+ gnome_calendar_cut_clipboard (calendar);
+ break;
+
+ case FOCUS_MEMO_TABLE:
+ e_memo_table_copy_clipboard (memo_table);
+ break;
+
+ case FOCUS_TASK_TABLE:
+ e_calendar_table_copy_clipboard (task_table);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+#endif
+}
+
+void
+e_cal_shell_content_paste_clipboard (ECalShellContent *cal_shell_content)
+{
+#if 0
+ GnomeCalendar *calendar;
+ EMemoTable *memo_table;
+ ECalendarTable *task_table;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ switch (cal_shell_content_get_focus_location (cal_shell_content)) {
+ case FOCUS_CALENDAR:
+ gnome_calendar_paste_clipboard (calendar);
+ break;
+
+ case FOCUS_MEMO_TABLE:
+ e_memo_table_copy_clipboard (memo_table);
+ break;
+
+ case FOCUS_TASK_TABLE:
+ e_calendar_table_copy_clipboard (task_table);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+#endif
+}
+
+void
+e_cal_shell_content_delete_selection (ECalShellContent *cal_shell_content)
+{
+#if 0
+ GnomeCalendar *calendar;
+ EMemoTable *memo_table;
+ ECalendarTable *task_table;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ switch (cal_shell_content_get_focus_location (cal_shell_content)) {
+ case FOCUS_CALENDAR:
+ gnome_calendar_delete_selection (calendar);
+ break;
+
+ case FOCUS_MEMO_TABLE:
+ e_memo_table_delete_selected (memo_table);
+ break;
+
+ case FOCUS_TASK_TABLE:
+ e_calendar_table_delete_selected (task_table);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+#endif
+}
+
+void
+e_cal_shell_content_delete_selected_occurrence (ECalShellContent *cal_shell_content)
+{
+#if 0
+ GnomeCalendar *calendar;
+ FocusLocation focus;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ focus = cal_shell_content_get_focus_location (cal_shell_content);
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+
+ if (focus == FOCUS_CALENDAR)
+ gnome_calendar_delete_selected_occurrence (calendar);
+#endif
+}
diff --git a/modules/calendar/e-cal-shell-content.h b/modules/calendar/e-cal-shell-content.h
new file mode 100644
index 0000000000..44e13f733c
--- /dev/null
+++ b/modules/calendar/e-cal-shell-content.h
@@ -0,0 +1,108 @@
+/*
+ * e-cal-shell-content.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_CAL_SHELL_CONTENT_H
+#define E_CAL_SHELL_CONTENT_H
+
+#include <shell/e-shell-content.h>
+#include <shell/e-shell-view.h>
+
+#include <calendar/gui/e-memo-table.h>
+#include <calendar/gui/gnome-cal.h>
+#include <menus/gal-view-instance.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CAL_SHELL_CONTENT \
+ (e_cal_shell_content_get_type ())
+#define E_CAL_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContent))
+#define E_CAL_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentClass))
+#define E_IS_CAL_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_CAL_SHELL_CONTENT))
+#define E_IS_CAL_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_CAL_SHELL_CONTENT))
+#define E_CAL_SHELL_CONTENT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECalShellContent ECalShellContent;
+typedef struct _ECalShellContentClass ECalShellContentClass;
+typedef struct _ECalShellContentPrivate ECalShellContentPrivate;
+
+enum {
+ E_CAL_SHELL_CONTENT_SELECTION_SINGLE = 1 << 0,
+ E_CAL_SHELL_CONTENT_SELECTION_MULTIPLE = 1 << 1,
+ E_CAL_SHELL_CONTENT_SELECTION_IS_ASSIGNABLE = 1 << 2,
+ E_CAL_SHELL_CONTENT_SELECTION_IS_COMPLETE = 1 << 3,
+ E_CAL_SHELL_CONTENT_SELECTION_IS_EDITABLE = 1 << 4,
+ E_CAL_SHELL_CONTENT_SELECTION_IS_MEETING = 1 << 5,
+ E_CAL_SHELL_CONTENT_SELECTION_IS_ORGANIZER = 1 << 6,
+ E_CAL_SHELL_CONTENT_SELECTION_IS_RECURRING = 1 << 7,
+ E_CAL_SHELL_CONTENT_SELECTION_CAN_ACCEPT = 1 << 8,
+ E_CAL_SHELL_CONTENT_SELECTION_CAN_DELEGATE = 1 << 9,
+ E_CAL_SHELL_CONTENT_SELECTION_CAN_SAVE = 1 << 10
+};
+
+struct _ECalShellContent {
+ EShellContent parent;
+ ECalShellContentPrivate *priv;
+};
+
+struct _ECalShellContentClass {
+ EShellContentClass parent_class;
+};
+
+GType e_cal_shell_content_get_type (void);
+void e_cal_shell_content_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_cal_shell_content_new (EShellView *shell_view);
+GnomeCalendar * e_cal_shell_content_get_calendar
+ (ECalShellContent *cal_shell_content);
+EMemoTable * e_cal_shell_content_get_memo_table
+ (ECalShellContent *cal_shell_content);
+ECalendarTable *e_cal_shell_content_get_task_table
+ (ECalShellContent *cal_shell_content);
+icaltimezone * e_cal_shell_content_get_timezone
+ (ECalShellContent *cal_shell_content);
+GalViewInstance *
+ e_cal_shell_content_get_view_instance
+ (ECalShellContent *cal_shell_content);
+void e_cal_shell_content_copy_clipboard
+ (ECalShellContent *cal_shell_content);
+void e_cal_shell_content_cut_clipboard
+ (ECalShellContent *cal_shell_content);
+void e_cal_shell_content_paste_clipboard
+ (ECalShellContent *cal_shell_content);
+void e_cal_shell_content_delete_selection
+ (ECalShellContent *cal_shell_content);
+void e_cal_shell_content_delete_selected_occurrence
+ (ECalShellContent *cal_shell_content);
+
+G_END_DECLS
+
+#endif /* E_CAL_SHELL_CONTENT_H */
diff --git a/modules/calendar/e-cal-shell-migrate.c b/modules/calendar/e-cal-shell-migrate.c
new file mode 100644
index 0000000000..9887a5332c
--- /dev/null
+++ b/modules/calendar/e-cal-shell-migrate.c
@@ -0,0 +1,796 @@
+/*
+ * e-cal-shell-backend-migrate.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-migrate.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <libebackend/e-dbhash.h>
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-group.h>
+#include <libedataserver/e-source-list.h>
+#include <libedataserver/e-xml-hash-utils.h>
+
+#include "e-util/e-bconf-map.h"
+#include "e-util/e-folder-map.h"
+#include "e-util/e-util-private.h"
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/calendar-config-keys.h"
+#include "calendar/gui/e-cal-event.h"
+#include "shell/e-shell.h"
+
+#define WEBCAL_BASE_URI "webcal://"
+#define CONTACTS_BASE_URI "contacts://"
+#define BAD_CONTACTS_BASE_URI "contact://"
+#define PERSONAL_RELATIVE_URI "system"
+
+static e_gconf_map_t calendar_display_map[] = {
+ /* /Calendar/Display */
+ { "Timezone", "calendar/display/timezone", E_GCONF_MAP_STRING },
+ { "Use24HourFormat", "calendar/display/use_24hour_format", E_GCONF_MAP_BOOL },
+ { "WeekStartDay", "calendar/display/week_start_day", E_GCONF_MAP_INT },
+ { "DayStartHour", "calendar/display/day_start_hour", E_GCONF_MAP_INT },
+ { "DayStartMinute", "calendar/display/day_start_minute", E_GCONF_MAP_INT },
+ { "DayEndHour", "calendar/display/day_end_hour", E_GCONF_MAP_INT },
+ { "DayEndMinute", "calendar/display/day_end_minute", E_GCONF_MAP_INT },
+ { "TimeDivisions", "calendar/display/time_divisions", E_GCONF_MAP_INT },
+ { "View", "calendar/display/default_view", E_GCONF_MAP_INT },
+ { "HPanePosition", "calendar/display/hpane_position", E_GCONF_MAP_FLOAT },
+ { "VPanePosition", "calendar/display/vpane_position", E_GCONF_MAP_FLOAT },
+ { "MonthHPanePosition", "calendar/display/month_hpane_position", E_GCONF_MAP_FLOAT },
+ { "MonthVPanePosition", "calendar/display/month_vpane_position", E_GCONF_MAP_FLOAT },
+ { "CompressWeekend", "calendar/display/compress_weekend", E_GCONF_MAP_BOOL },
+ { "ShowEventEndTime", "calendar/display/show_event_end", E_GCONF_MAP_BOOL },
+ { "WorkingDays", "calendar/display/working_days", E_GCONF_MAP_INT },
+ { NULL },
+};
+
+static e_gconf_map_t calendar_other_map[] = {
+ /* /Calendar/Other */
+ { "ConfirmDelete", "calendar/prompts/confirm_delete", E_GCONF_MAP_BOOL },
+ { "ConfirmExpunge", "calendar/prompts/confirm_purge", E_GCONF_MAP_BOOL },
+ { "UseDefaultReminder", "calendar/other/use_default_reminder", E_GCONF_MAP_BOOL },
+ { "DefaultReminderInterval", "calendar/other/default_reminder_interval", E_GCONF_MAP_INT },
+ { "DefaultReminderUnits", "calendar/other/default_reminder_units", E_GCONF_MAP_STRING },
+ { NULL },
+};
+
+static e_gconf_map_t calendar_datenavigator_map[] = {
+ /* /Calendar/DateNavigator */
+ { "ShowWeekNumbers", "calendar/date_navigator/show_week_numbers", E_GCONF_MAP_BOOL },
+ { NULL },
+};
+
+static e_gconf_map_t calendar_alarmnotify_map[] = {
+ /* /Calendar/AlarmNotify */
+ { "LastNotificationTime", "calendar/notify/last_notification_time", E_GCONF_MAP_INT },
+ { "CalendarToLoad%i", "calendar/notify/calendars", E_GCONF_MAP_STRING|E_GCONF_MAP_LIST },
+ { "BlessedProgram%i", "calendar/notify/programs", E_GCONF_MAP_STRING|E_GCONF_MAP_LIST },
+ { NULL },
+};
+
+static e_gconf_map_list_t calendar_remap_list[] = {
+
+ { "/Calendar/Display", calendar_display_map },
+ { "/Calendar/Other/Map", calendar_other_map },
+ { "/Calendar/DateNavigator", calendar_datenavigator_map },
+ { "/Calendar/AlarmNotify", calendar_alarmnotify_map },
+
+ { NULL },
+};
+
+static GtkWidget *window;
+static GtkLabel *label;
+static GtkProgressBar *progress;
+
+#ifndef G_OS_WIN32
+
+/* No previous versions have been available on Win32, so don't
+ * bother with upgrade support from 1.x on Win32.
+ */
+
+static void
+setup_progress_dialog (void)
+{
+ GtkWidget *vbox, *hbox, *w;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title ((GtkWindow *) window, _("Migrating..."));
+ gtk_window_set_modal ((GtkWindow *) window, TRUE);
+ gtk_container_set_border_width ((GtkContainer *) window, 6);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (vbox);
+ gtk_container_add ((GtkContainer *) window, vbox);
+
+ w = gtk_label_new (_("The location and hierarchy of the Evolution calendar "
+ "folders has changed since Evolution 1.x.\n\nPlease be "
+ "patient while Evolution migrates your folders..."));
+
+ gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
+ gtk_widget_show (w);
+ gtk_box_pack_start_defaults ((GtkBox *) vbox, w);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_widget_show (hbox);
+ gtk_box_pack_start_defaults ((GtkBox *) vbox, hbox);
+
+ label = (GtkLabel *) gtk_label_new ("");
+ gtk_widget_show ((GtkWidget *) label);
+ gtk_box_pack_start_defaults ((GtkBox *) hbox, (GtkWidget *) label);
+
+ progress = (GtkProgressBar *) gtk_progress_bar_new ();
+ gtk_widget_show ((GtkWidget *) progress);
+ gtk_box_pack_start_defaults ((GtkBox *) hbox, (GtkWidget *) progress);
+
+ gtk_widget_show (window);
+}
+
+static void
+dialog_close (void)
+{
+ gtk_widget_destroy ((GtkWidget *) window);
+}
+
+static void
+dialog_set_folder_name (const gchar *folder_name)
+{
+ gchar *text;
+
+ text = g_strdup_printf (_("Migrating '%s':"), folder_name);
+ gtk_label_set_text (label, text);
+ g_free (text);
+
+ gtk_progress_bar_set_fraction (progress, 0.0);
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static void
+dialog_set_progress (double percent)
+{
+ gchar text[5];
+
+ snprintf (text, sizeof (text), "%d%%", (gint) (percent * 100.0f));
+
+ gtk_progress_bar_set_fraction (progress, percent);
+ gtk_progress_bar_set_text (progress, text);
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static gboolean
+check_for_conflict (ESourceGroup *group, gchar *name)
+{
+ GSList *sources;
+ GSList *s;
+
+ sources = e_source_group_peek_sources (group);
+
+ for (s = sources; s; s = s->next) {
+ ESource *source = E_SOURCE (s->data);
+
+ if (!strcmp (e_source_peek_name (source), name))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+get_source_name (ESourceGroup *group, const gchar *path)
+{
+ gchar **p = g_strsplit (path, "/", 0);
+ gint i, j, starting_index;
+ gint num_elements;
+ gboolean conflict;
+ GString *s = g_string_new (NULL);
+
+ for (i = 0; p[i]; i ++);
+
+ num_elements = i;
+ i--;
+
+ /* p[i] is now the last path element */
+
+ /* check if it conflicts */
+ starting_index = i;
+ do {
+ for (j = starting_index; j < num_elements; j += 2) {
+ if (j != starting_index)
+ g_string_append_c (s, '_');
+ g_string_append (s, p[j]);
+ }
+
+ conflict = check_for_conflict (group, s->str);
+
+
+ /* if there was a conflict back up 2 levels (skipping the /subfolder/ element) */
+ if (conflict)
+ starting_index -= 2;
+
+ /* we always break out if we can't go any further,
+ regardless of whether or not we conflict. */
+ if (starting_index < 0)
+ break;
+
+ } while (conflict);
+ g_strfreev (p);
+
+ return g_string_free (s, FALSE);
+}
+
+static gboolean
+migrate_ical (ECal *old_ecal, ECal *new_ecal)
+{
+ GList *l, *objects;
+ gint num_added = 0;
+ gint num_objects;
+ gboolean retval = TRUE;
+
+ /* both ecals are loaded, start the actual migration */
+ if (!e_cal_get_object_list (old_ecal, "#t", &objects, NULL))
+ return FALSE;
+
+ num_objects = g_list_length (objects);
+ for (l = objects; l; l = l->next) {
+ icalcomponent *ical_comp = l->data;
+ GError *error = NULL;
+
+ if (!e_cal_create_object (new_ecal, ical_comp, NULL, &error)) {
+ g_warning ("Migration of object failed: %s", error->message);
+ retval = FALSE;
+ }
+
+ g_clear_error (&error);
+
+ num_added ++;
+ dialog_set_progress ((double)num_added / num_objects);
+ }
+
+ g_list_foreach (objects, (GFunc) icalcomponent_free, NULL);
+ g_list_free (objects);
+
+ return retval;
+}
+
+static gboolean
+migrate_ical_folder_to_source (gchar *old_path, ESource *new_source, ECalSourceType type)
+{
+ ECal *old_ecal = NULL, *new_ecal = NULL;
+ ESource *old_source;
+ ESourceGroup *group;
+ gchar *old_uri = g_strdup_printf ("file://%s", old_path);
+ GError *error = NULL;
+ gboolean retval = FALSE;
+
+ group = e_source_group_new ("", old_uri);
+ old_source = e_source_new ("", "");
+ e_source_group_add_source (group, old_source, -1);
+
+ dialog_set_folder_name (e_source_peek_name (new_source));
+
+ if (!(old_ecal = e_cal_new (old_source, type))) {
+ g_warning ("could not find a backend for '%s'", e_source_get_uri (old_source));
+ goto finish;
+ }
+ if (!e_cal_open (old_ecal, FALSE, &error)) {
+ g_warning ("failed to load source ecal for migration: '%s' (%s)", error->message,
+ e_source_get_uri (old_source));
+ goto finish;
+ }
+
+ if (!(new_ecal = e_cal_new (new_source, type))) {
+ g_warning ("could not find a backend for '%s'", e_source_get_uri (new_source));
+ goto finish;
+ }
+ if (!e_cal_open (new_ecal, FALSE, &error)) {
+ g_warning ("failed to load destination ecal for migration: '%s' (%s)", error->message,
+ e_source_get_uri (new_source));
+ goto finish;
+ }
+
+ retval = migrate_ical (old_ecal, new_ecal);
+
+finish:
+ g_clear_error (&error);
+ if (old_ecal)
+ g_object_unref (old_ecal);
+ g_object_unref (group);
+ if (new_ecal)
+ g_object_unref (new_ecal);
+ g_free (old_uri);
+
+ return retval;
+}
+
+static gboolean
+migrate_ical_folder (gchar *old_path, ESourceGroup *dest_group, gchar *source_name, ECalSourceType type)
+{
+ ESource *new_source;
+ gboolean retval;
+
+ new_source = e_source_new (source_name, source_name);
+ e_source_set_relative_uri (new_source, e_source_peek_uid (new_source));
+ e_source_group_add_source (dest_group, new_source, -1);
+
+ retval = migrate_ical_folder_to_source (old_path, new_source, type);
+
+ g_object_unref (new_source);
+
+ return retval;
+}
+
+#endif /* !G_OS_WIN32 */
+
+#ifndef G_OS_WIN32
+
+static void
+migrate_pilot_db_key (const gchar *key, gpointer user_data)
+{
+ EXmlHash *xmlhash = user_data;
+
+ e_xmlhash_add (xmlhash, key, "");
+}
+
+static void
+migrate_pilot_data (const gchar *component, const gchar *conduit, const gchar *old_path, const gchar *new_path)
+{
+ gchar *changelog, *map;
+ const gchar *dent;
+ const gchar *ext;
+ gchar *filename;
+ GDir *dir;
+
+ if (!(dir = g_dir_open (old_path, 0, NULL)))
+ return;
+
+ map = g_alloca (12 + strlen (conduit));
+ sprintf (map, "pilot-map-%s-", conduit);
+
+ changelog = g_alloca (24 + strlen (conduit));
+ sprintf (changelog, "pilot-sync-evolution-%s-", conduit);
+
+ while ((dent = g_dir_read_name (dir))) {
+ if (!strncmp (dent, map, strlen (map)) &&
+ ((ext = strrchr (dent, '.')) && !strcmp (ext, ".xml"))) {
+ /* pilot map file - src and dest file formats are identical */
+ guchar inbuf[4096];
+ gsize nread, nwritten;
+ gint fd0, fd1;
+ gssize n;
+
+ filename = g_build_filename (old_path, dent, NULL);
+ if ((fd0 = g_open (filename, O_RDONLY|O_BINARY, 0)) == -1) {
+ g_free (filename);
+ continue;
+ }
+
+ g_free (filename);
+ filename = g_build_filename (new_path, dent, NULL);
+ if ((fd1 = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) == -1) {
+ g_free (filename);
+ close (fd0);
+ continue;
+ }
+
+ do {
+ do {
+ n = read (fd0, inbuf, sizeof (inbuf));
+ } while (n == -1 && errno == EINTR);
+
+ if (n < 1)
+ break;
+
+ nread = n;
+ nwritten = 0;
+ do {
+ do {
+ n = write (fd1, inbuf + nwritten, nread - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } while (nwritten < nread && n != -1);
+
+ if (n == -1)
+ break;
+ } while (1);
+
+ if (n != -1)
+ n = fsync (fd1);
+
+ if (n == -1) {
+ g_warning ("Failed to migrate %s: %s", dent, strerror (errno));
+ g_unlink (filename);
+ }
+
+ close (fd0);
+ close (fd1);
+ g_free (filename);
+ } else if (!strncmp (dent, changelog, strlen (changelog)) &&
+ ((ext = strrchr (dent, '.')) && !strcmp (ext, ".db"))) {
+ /* src and dest formats differ, src format is db3 while dest format is xml */
+ EXmlHash *xmlhash;
+ EDbHash *dbhash;
+ struct stat st;
+
+ filename = g_build_filename (old_path, dent, NULL);
+ if (g_stat (filename, &st) == -1) {
+ g_free (filename);
+ continue;
+ }
+
+ dbhash = e_dbhash_new (filename);
+ g_free (filename);
+
+ filename = g_strdup_printf ("%s/%s.ics-%s", new_path, component, dent);
+ if (g_stat (filename, &st) != -1)
+ g_unlink (filename);
+ xmlhash = e_xmlhash_new (filename);
+ g_free (filename);
+
+ e_dbhash_foreach_key (dbhash, migrate_pilot_db_key, xmlhash);
+
+ e_dbhash_destroy (dbhash);
+
+ e_xmlhash_write (xmlhash);
+ e_xmlhash_destroy (xmlhash);
+ }
+ }
+
+ g_dir_close (dir);
+}
+
+#endif
+
+static ESourceGroup *
+create_calendar_contact_source (ESourceList *source_list)
+{
+ ESourceGroup *group;
+ ESource *source;
+
+ /* Create the contacts group */
+ group = e_source_group_new (_("Contacts"), CONTACTS_BASE_URI);
+ e_source_list_add_group (source_list, group, -1);
+
+ source = e_source_new (_("Birthdays & Anniversaries"), "/");
+ e_source_group_add_source (group, source, -1);
+ g_object_unref (source);
+
+ e_source_set_color_spec (source, "#FED4D3");
+ e_source_group_set_readonly (group, TRUE);
+
+ return group;
+}
+
+static void
+create_calendar_sources (EShellBackend *shell_backend,
+ ESourceList *source_list,
+ ESourceGroup **on_this_computer,
+ ESource **personal_source,
+ ESourceGroup **on_the_web,
+ ESourceGroup **contacts)
+{
+ EShell *shell;
+ EShellSettings *shell_settings;
+ GSList *groups;
+ ESourceGroup *group;
+ gchar *base_uri, *base_uri_proto;
+ const gchar *base_dir;
+
+ *on_this_computer = NULL;
+ *on_the_web = NULL;
+ *contacts = NULL;
+ *personal_source = NULL;
+
+ shell = e_shell_backend_get_shell (shell_backend);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ base_dir = e_shell_backend_get_config_dir (shell_backend);
+ base_uri = g_build_filename (base_dir, "local", NULL);
+
+ base_uri_proto = g_filename_to_uri (base_uri, NULL, NULL);
+
+ groups = e_source_list_peek_groups (source_list);
+ if (groups) {
+ /* groups are already there, we need to search for things... */
+ GSList *g;
+
+ for (g = groups; g; g = g->next) {
+
+ group = E_SOURCE_GROUP (g->data);
+
+ if (!strcmp (BAD_CONTACTS_BASE_URI, e_source_group_peek_base_uri (group)))
+ e_source_group_set_base_uri (group, CONTACTS_BASE_URI);
+
+ if (!strcmp (base_uri, e_source_group_peek_base_uri (group)))
+ e_source_group_set_base_uri (group, base_uri_proto);
+
+ if (!*on_this_computer && !strcmp (base_uri_proto, e_source_group_peek_base_uri (group)))
+ *on_this_computer = g_object_ref (group);
+ else if (!*on_the_web && !strcmp (WEBCAL_BASE_URI, e_source_group_peek_base_uri (group)))
+ *on_the_web = g_object_ref (group);
+ else if (!*contacts && !strcmp (CONTACTS_BASE_URI, e_source_group_peek_base_uri (group)))
+ *contacts = g_object_ref (group);
+ }
+ }
+
+ if (*on_this_computer) {
+ /* make sure "Personal" shows up as a source under
+ this group */
+ GSList *sources = e_source_group_peek_sources (*on_this_computer);
+ GSList *s;
+ for (s = sources; s; s = s->next) {
+ ESource *source = E_SOURCE (s->data);
+ const gchar *relative_uri;
+
+ relative_uri = e_source_peek_relative_uri (source);
+ if (relative_uri == NULL)
+ continue;
+ if (!strcmp (PERSONAL_RELATIVE_URI, relative_uri)) {
+ *personal_source = g_object_ref (source);
+ break;
+ }
+ }
+ } else {
+ /* create the local source group */
+ group = e_source_group_new (_("On This Computer"), base_uri_proto);
+ e_source_list_add_group (source_list, group, -1);
+
+ *on_this_computer = group;
+ }
+
+ if (!*personal_source) {
+ gchar *primary_calendar;
+
+ /* Create the default Person calendar */
+ ESource *source = e_source_new (_("Personal"), PERSONAL_RELATIVE_URI);
+ e_source_group_add_source (*on_this_computer, source, -1);
+
+ primary_calendar = e_shell_settings_get_string (
+ shell_settings, "cal-primary-calendar");
+
+ if (!primary_calendar && !calendar_config_get_calendars_selected ()) {
+ GSList selected;
+
+ e_shell_settings_set_string (
+ shell_settings, "cal-primary-calendar",
+ e_source_peek_uid (source));
+
+ selected.data = (gpointer)e_source_peek_uid (source);
+ selected.next = NULL;
+ calendar_config_set_calendars_selected (&selected);
+ }
+
+ g_free (primary_calendar);
+ e_source_set_color_spec (source, "#BECEDD");
+ *personal_source = source;
+ }
+
+ if (!*on_the_web) {
+ /* Create the Webcal source group */
+ group = e_source_group_new (_("On The Web"), WEBCAL_BASE_URI);
+ e_source_list_add_group (source_list, group, -1);
+
+ *on_the_web = group;
+ }
+
+ if (!*contacts) {
+ group = create_calendar_contact_source (source_list);
+
+ *contacts = group;
+ }
+
+ g_free (base_uri_proto);
+ g_free (base_uri);
+}
+
+gboolean
+e_cal_shell_backend_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error)
+{
+ ESourceGroup *on_this_computer = NULL, *on_the_web = NULL, *contacts = NULL;
+ ESource *personal_source = NULL;
+ ESourceList *source_list;
+ ECalEvent *ece;
+ ECalEventTargetModule *target;
+ gboolean retval = FALSE;
+
+ source_list = g_object_get_data (
+ G_OBJECT (shell_backend), "source-list");
+
+ /* we call this unconditionally now - create_groups either
+ creates the groups/sources or it finds the necessary
+ groups/sources. */
+ create_calendar_sources (
+ shell_backend, source_list, &on_this_computer,
+ &personal_source, &on_the_web, &contacts);
+
+#ifndef G_OS_WIN32
+ if (major == 1) {
+ xmlDocPtr config_doc = NULL;
+ gchar *conf_file;
+ struct stat st;
+
+ conf_file = g_build_filename (g_get_home_dir (), "evolution", "config.xmldb", NULL);
+ if (lstat (conf_file, &st) == 0 && S_ISREG (st.st_mode))
+ config_doc = xmlParseFile (conf_file);
+ g_free (conf_file);
+
+ if (config_doc && minor <= 2) {
+ GConfClient *gconf;
+ gint res = 0;
+
+ /* move bonobo config to gconf */
+ gconf = gconf_client_get_default ();
+
+ res = e_bconf_import (gconf, config_doc, calendar_remap_list);
+
+ g_object_unref (gconf);
+
+ xmlFreeDoc(config_doc);
+
+ if (res != 0) {
+ /* FIXME: set proper domain/code */
+ g_set_error(error, 0, 0, _("Unable to migrate old settings from evolution/config.xmldb"));
+ goto fail;
+ }
+ }
+
+ if (minor <= 4) {
+ GSList *migration_dirs, *l;
+ gchar *path, *local_cal_folder;
+
+ setup_progress_dialog ();
+
+ path = g_build_filename (g_get_home_dir (), "evolution", "local", NULL);
+ migration_dirs = e_folder_map_local_folders (path, "calendar");
+ local_cal_folder = g_build_filename (path, "Calendar", NULL);
+ g_free (path);
+
+ if (personal_source)
+ migrate_ical_folder_to_source (local_cal_folder, personal_source, E_CAL_SOURCE_TYPE_EVENT);
+
+ for (l = migration_dirs; l; l = l->next) {
+ gchar *source_name;
+
+ if (personal_source && !strcmp ((gchar *)l->data, local_cal_folder))
+ continue;
+
+ source_name = get_source_name (on_this_computer, (gchar *)l->data);
+
+ if (!migrate_ical_folder (l->data, on_this_computer, source_name, E_CAL_SOURCE_TYPE_EVENT)) {
+ /* FIXME: domain/code */
+ g_set_error(error, 0, 0, _("Unable to migrate calendar `%s'"), source_name);
+ g_free(source_name);
+ goto fail;
+ }
+
+ g_free (source_name);
+ }
+
+ g_free (local_cal_folder);
+
+ dialog_close ();
+ }
+
+ if (minor <= 4 || (minor == 5 && micro < 5)) {
+ GConfClient *gconf;
+ GConfValue *gconf_val;
+ gint i;
+ const gchar *keys[] = {
+ CALENDAR_CONFIG_HPANE_POS,
+ CALENDAR_CONFIG_VPANE_POS,
+ CALENDAR_CONFIG_MONTH_HPANE_POS,
+ CALENDAR_CONFIG_MONTH_VPANE_POS,
+ NULL
+ };
+
+ gconf = gconf_client_get_default ();
+
+ for (i = 0; keys[i]; i++) {
+ gconf_val = gconf_client_get (gconf, keys[i], NULL);
+ if (gconf_val) {
+ if (gconf_val->type != GCONF_VALUE_INT)
+ gconf_client_unset (gconf, keys[i], NULL);
+ gconf_value_free (gconf_val);
+ }
+ }
+
+ g_object_unref (gconf);
+ }
+
+ if (minor < 5 || (minor == 5 && micro <= 10)) {
+ gchar *old_path, *new_path;
+
+ old_path = g_build_filename (g_get_home_dir (), "evolution", "local", "Calendar", NULL);
+ new_path = g_build_filename (e_shell_backend_get_config_dir (shell_backend),
+ "local", "system", NULL);
+ migrate_pilot_data ("calendar", "calendar", old_path, new_path);
+ g_free (new_path);
+ g_free (old_path);
+ }
+
+ /* we only need to do this next step if people ran
+ older versions of 1.5. We need to clear out the
+ absolute URI's that were assigned to ESources
+ during one phase of development, as they take
+ precedent over relative uris (but aren't updated
+ when editing an ESource). */
+ if (minor == 5 && micro <= 11) {
+ GSList *g;
+ for (g = e_source_list_peek_groups (source_list); g; g = g->next) {
+ ESourceGroup *group = g->data;
+ GSList *s;
+
+ for (s = e_source_group_peek_sources (group); s; s = s->next) {
+ ESource *source = s->data;
+ e_source_set_absolute_uri (source, NULL);
+ }
+ }
+ }
+
+ }
+#endif /* !G_OS_WIN32 */
+
+ e_source_list_sync (source_list, NULL);
+
+ /** @Event: component.migration
+ * @Title: Migration step in component initialization
+ * @Target: ECalEventTargetComponent
+ *
+ * component.migration is emitted during the calendar component
+ * initialization process. This allows new calendar backend types
+ * to be distributed as an e-d-s backend and a plugin without
+ * reaching their grubby little fingers into migration.c
+ */
+ /* Fire off migration event */
+ ece = e_cal_event_peek ();
+ target = e_cal_event_target_new_module (ece, shell_backend, 0);
+ e_event_emit ((EEvent *) ece, "module.migration", (EEventTarget *) target);
+
+ retval = TRUE;
+fail:
+ if (on_this_computer)
+ g_object_unref (on_this_computer);
+ if (on_the_web)
+ g_object_unref (on_the_web);
+ if (contacts)
+ g_object_unref (contacts);
+ if (personal_source)
+ g_object_unref (personal_source);
+
+ return retval;
+}
+
diff --git a/modules/calendar/e-cal-shell-migrate.h b/modules/calendar/e-cal-shell-migrate.h
new file mode 100644
index 0000000000..359ca5ea22
--- /dev/null
+++ b/modules/calendar/e-cal-shell-migrate.h
@@ -0,0 +1,38 @@
+/*
+ * e-cal-shell-backend-migrate.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_CAL_SHELL_BACKEND_MIGRATE_H
+#define E_CAL_SHELL_BACKEND_MIGRATE_H
+
+#include <glib.h>
+#include <shell/e-shell-backend.h>
+
+G_BEGIN_DECLS
+
+gboolean e_cal_shell_backend_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_CAL_SHELL_BACKEND_MIGRATE_H */
diff --git a/modules/calendar/e-cal-shell-settings.c b/modules/calendar/e-cal-shell-settings.c
new file mode 100644
index 0000000000..03af4aeebc
--- /dev/null
+++ b/modules/calendar/e-cal-shell-settings.c
@@ -0,0 +1,59 @@
+/*
+ * e-cal-shell-backend-settings.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-settings.h"
+
+#include <gconf/gconf-client.h>
+
+void
+e_cal_shell_backend_init_settings (EShell *shell)
+{
+ EShellSettings *shell_settings;
+
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ /* XXX Default values should match the GConf schema.
+ * Yes it's redundant, but we're stuck with GConf. */
+
+ e_shell_settings_install_property (
+ g_param_spec_string (
+ "cal-primary-calendar",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "cal-primary-calendar",
+ "/apps/evolution/calendar/display/primary_calendar");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "cal-use-system-timezone",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "cal-use-system-timezone",
+ "/apps/evolution/calendar/display/use_system_timezone");
+}
diff --git a/modules/calendar/e-cal-shell-settings.h b/modules/calendar/e-cal-shell-settings.h
new file mode 100644
index 0000000000..de8b22888b
--- /dev/null
+++ b/modules/calendar/e-cal-shell-settings.h
@@ -0,0 +1,33 @@
+/*
+ * e-cal-shell-backend-settings.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_CAL_SHELL_BACKEND_SETTINGS_H
+#define E_CAL_SHELL_BACKEND_SETTINGS_H
+
+#include <shell/e-shell.h>
+
+G_BEGIN_DECLS
+
+void e_cal_shell_backend_init_settings (EShell *shell);
+
+G_END_DECLS
+
+#endif /* E_CAL_SHELL_BACKEND_SETTINGS_H */
diff --git a/modules/calendar/e-cal-shell-sidebar.c b/modules/calendar/e-cal-shell-sidebar.c
new file mode 100644
index 0000000000..dc7df596d6
--- /dev/null
+++ b/modules/calendar/e-cal-shell-sidebar.c
@@ -0,0 +1,759 @@
+/*
+ * e-cal-shell-sidebar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-sidebar.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "e-util/e-error.h"
+#include "e-util/gconf-bridge.h"
+#include "calendar/common/authentication.h"
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/e-calendar-selector.h"
+#include "calendar/gui/e-mini-calendar-config.h"
+#include "calendar/gui/misc.h"
+
+#include "e-cal-shell-backend.h"
+#include "e-cal-shell-view.h"
+
+#define E_CAL_SHELL_SIDEBAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_CAL_SHELL_SIDEBAR, ECalShellSidebarPrivate))
+
+struct _ECalShellSidebarPrivate {
+ GtkWidget *paned;
+ GtkWidget *selector;
+ GtkWidget *mini_calendar;
+
+ /* UID -> Client */
+ GHashTable *client_table;
+
+ EMiniCalendarConfig *mini_calendar_config;
+};
+
+enum {
+ PROP_0,
+ PROP_MINI_CALENDAR,
+ PROP_SELECTOR
+};
+
+enum {
+ CLIENT_ADDED,
+ CLIENT_REMOVED,
+ STATUS_MESSAGE,
+ LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+static GType cal_shell_sidebar_type;
+
+static void
+cal_shell_sidebar_emit_client_added (ECalShellSidebar *cal_shell_sidebar,
+ ECal *client)
+{
+ guint signal_id = signals[CLIENT_ADDED];
+
+ g_signal_emit (cal_shell_sidebar, signal_id, 0, client);
+}
+
+static void
+cal_shell_sidebar_emit_client_removed (ECalShellSidebar *cal_shell_sidebar,
+ ECal *client)
+{
+ guint signal_id = signals[CLIENT_REMOVED];
+
+ g_signal_emit (cal_shell_sidebar, signal_id, 0, client);
+}
+
+static void
+cal_shell_sidebar_emit_status_message (ECalShellSidebar *cal_shell_sidebar,
+ const gchar *status_message)
+{
+ guint signal_id = signals[STATUS_MESSAGE];
+
+ g_signal_emit (cal_shell_sidebar, signal_id, 0, status_message);
+}
+
+static void
+cal_shell_sidebar_backend_died_cb (ECalShellSidebar *cal_shell_sidebar,
+ ECal *client)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ GHashTable *client_table;
+ ESource *source;
+ const gchar *uid;
+
+ client_table = cal_shell_sidebar->priv->client_table;
+
+ shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ source = e_cal_get_source (client);
+ uid = e_source_peek_uid (source);
+
+ g_object_ref (source);
+
+ g_hash_table_remove (client_table, uid);
+ cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL);
+
+ e_error_run (
+ GTK_WINDOW (shell_window),
+ "calendar:calendar-crashed", NULL);
+
+ g_object_unref (source);
+}
+
+static void
+cal_shell_sidebar_backend_error_cb (ECalShellSidebar *cal_shell_sidebar,
+ const gchar *message,
+ ECal *client)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ GtkWidget *dialog;
+ const gchar *uri;
+ gchar *uri_no_passwd;
+
+ shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ uri = e_cal_get_uri (client);
+ uri_no_passwd = get_uri_without_password (uri);
+
+ dialog = gtk_message_dialog_new (
+ GTK_WINDOW (shell_window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Error on %s\n%s"),
+ uri_no_passwd, message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_free (uri_no_passwd);
+}
+
+static void
+cal_shell_sidebar_client_opened_cb (ECalShellSidebar *cal_shell_sidebar,
+ ECalendarStatus status,
+ ECal *client)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ ESource *source;
+
+ source = e_cal_get_source (client);
+
+ shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ if (status == E_CALENDAR_STATUS_AUTHENTICATION_FAILED ||
+ status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED)
+ auth_cal_forget_password (client);
+
+ switch (status) {
+ case E_CALENDAR_STATUS_OK:
+ g_signal_handlers_disconnect_matched (
+ client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+ cal_shell_sidebar_client_opened_cb, NULL);
+
+ cal_shell_sidebar_emit_status_message (
+ cal_shell_sidebar, _("Loading calendars"));
+ cal_shell_sidebar_emit_client_added (
+ cal_shell_sidebar, client);
+ cal_shell_sidebar_emit_status_message (
+ cal_shell_sidebar, NULL);
+ break;
+
+ case E_CALENDAR_STATUS_AUTHENTICATION_FAILED:
+ e_cal_open_async (client, FALSE);
+ break;
+
+ case E_CALENDAR_STATUS_BUSY:
+ break;
+
+ case E_CALENDAR_STATUS_REPOSITORY_OFFLINE:
+ e_error_run (
+ GTK_WINDOW (shell_window),
+ "calendar:prompt-no-contents-offline-calendar",
+ NULL);
+ break;
+
+ default:
+ cal_shell_sidebar_emit_client_removed (
+ cal_shell_sidebar, client);
+ break;
+ }
+}
+
+static void
+cal_shell_sidebar_row_changed_cb (ECalShellSidebar *cal_shell_sidebar,
+ GtkTreePath *tree_path,
+ GtkTreeIter *tree_iter,
+ GtkTreeModel *tree_model)
+{
+ ESourceSelector *selector;
+ ESource *source;
+
+ /* XXX ESourceSelector's underlying tree store has only one
+ * column: ESource objects. While we're not supposed to
+ * know this, listening for "row-changed" signals from
+ * the model is easier to deal with than the selector's
+ * "selection-changed" signal, which doesn't tell you
+ * _which_ row changed. */
+
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+ gtk_tree_model_get (tree_model, tree_iter, 0, &source, -1);
+
+ /* XXX This signal gets emitted a lot while the model is being
+ * rebuilt, during which time we won't get a valid ESource.
+ * ESourceSelector should probably block this signal while
+ * rebuilding the model, but we'll be forgiving and not
+ * emit a warning. */
+ if (!E_IS_SOURCE (source))
+ return;
+
+ if (e_source_selector_source_is_selected (selector, source))
+ e_cal_shell_sidebar_add_source (cal_shell_sidebar, source);
+ else
+ e_cal_shell_sidebar_remove_source (cal_shell_sidebar, source);
+}
+
+static void
+cal_shell_sidebar_selection_changed_cb (ECalShellSidebar *cal_shell_sidebar,
+ ESourceSelector *selector)
+{
+ GSList *list, *iter;
+
+ /* This signal is emitted less frequently than "row-changed",
+ * especially when the model is being rebuilt. So we'll take
+ * it easy on poor GConf. */
+
+ list = e_source_selector_get_selection (selector);
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+
+ iter->data = (gpointer) e_source_peek_uid (source);
+ g_object_unref (source);
+ }
+
+ calendar_config_set_calendars_selected (list);
+
+ g_slist_free (list);
+}
+
+static void
+cal_shell_sidebar_primary_selection_changed_cb (ECalShellSidebar *cal_shell_sidebar,
+ ESourceSelector *selector)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ EShellSettings *shell_settings;
+ ESource *source;
+
+ /* XXX ESourceSelector needs a "primary-selection-uid" property
+ * so we can just bind the property with GConfBridge. */
+
+ source = e_source_selector_peek_primary_selection (selector);
+ if (source == NULL)
+ return;
+
+ shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ e_shell_settings_set_string (
+ shell_settings, "cal-primary-calendar",
+ e_source_peek_uid (source));
+}
+
+static void
+cal_shell_sidebar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_MINI_CALENDAR:
+ g_value_set_object (
+ value, e_cal_shell_sidebar_get_mini_calendar (
+ E_CAL_SHELL_SIDEBAR (object)));
+ return;
+
+ case PROP_SELECTOR:
+ g_value_set_object (
+ value, e_cal_shell_sidebar_get_selector (
+ E_CAL_SHELL_SIDEBAR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+cal_shell_sidebar_dispose (GObject *object)
+{
+ ECalShellSidebarPrivate *priv;
+
+ priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ if (priv->paned != NULL) {
+ g_object_unref (priv->paned);
+ priv->paned = NULL;
+ }
+
+ if (priv->selector != NULL) {
+ g_object_unref (priv->selector);
+ priv->selector = NULL;
+ }
+
+ if (priv->mini_calendar != NULL) {
+ g_object_unref (priv->mini_calendar);
+ priv->mini_calendar = NULL;
+ }
+
+ g_hash_table_remove_all (priv->client_table);
+
+ if (priv->mini_calendar_config != NULL) {
+ g_object_unref (priv->mini_calendar_config);
+ priv->mini_calendar = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+cal_shell_sidebar_finalize (GObject *object)
+{
+ ECalShellSidebarPrivate *priv;
+
+ priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->client_table);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+cal_shell_sidebar_constructed (GObject *object)
+{
+ ECalShellSidebarPrivate *priv;
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellBackend *shell_backend;
+ EShellSidebar *shell_sidebar;
+ EShellSettings *shell_settings;
+ ESourceSelector *selector;
+ ESourceList *source_list;
+ ESource *source;
+ ECalendarItem *calitem;
+ GConfBridge *bridge;
+ GtkTreeModel *model;
+ GtkWidget *container;
+ GtkWidget *widget;
+ AtkObject *a11y;
+ GSList *list, *iter;
+ const gchar *key;
+ gchar *uid;
+
+ priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ shell_sidebar = E_SHELL_SIDEBAR (object);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ source_list = e_cal_shell_backend_get_source_list (
+ E_CAL_SHELL_BACKEND (shell_backend));
+
+ container = GTK_WIDGET (shell_sidebar);
+
+ widget = gtk_vpaned_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->paned = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_paned_add1 (GTK_PANED (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_calendar_selector_new (source_list);
+ e_source_selector_set_select_new (E_SOURCE_SELECTOR (widget), TRUE);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ a11y = gtk_widget_get_accessible (widget);
+ atk_object_set_name (a11y, _("Calendar Selector"));
+ priv->selector = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = priv->paned;
+
+ widget = e_calendar_new ();
+ calitem = E_CALENDAR (widget)->calitem;
+ e_calendar_item_set_days_start_week_sel (calitem, 9);
+ e_calendar_item_set_max_days_sel (calitem, 42);
+ gtk_paned_add2 (GTK_PANED (container), widget);
+ priv->mini_calendar = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ priv->mini_calendar_config =
+ e_mini_calendar_config_new (E_CALENDAR (widget));
+
+ /* Restore the selector state from the last session. */
+
+ selector = E_SOURCE_SELECTOR (priv->selector);
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
+
+ g_signal_connect_swapped (
+ model, "row-changed",
+ G_CALLBACK (cal_shell_sidebar_row_changed_cb),
+ object);
+
+ source = NULL;
+ uid = e_shell_settings_get_string (
+ shell_settings, "cal-primary-calendar");
+ if (uid != NULL)
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ if (source == NULL)
+ source = e_source_list_peek_source_any (source_list);
+ if (source != NULL)
+ e_source_selector_set_primary_selection (selector, source);
+ g_free (uid);
+
+ list = calendar_config_get_calendars_selected ();
+ for (iter = list; iter != NULL; iter = iter->next) {
+ uid = iter->data;
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ g_free (uid);
+
+ if (source == NULL)
+ continue;
+
+ e_source_selector_select_source (selector, source);
+ }
+ g_slist_free (list);
+
+ /* Listen for subsequent changes to the selector. */
+
+ g_signal_connect_swapped (
+ selector, "selection-changed",
+ G_CALLBACK (cal_shell_sidebar_selection_changed_cb),
+ object);
+
+ g_signal_connect_swapped (
+ selector, "primary-selection-changed",
+ G_CALLBACK (cal_shell_sidebar_primary_selection_changed_cb),
+ object);
+
+ /* Bind GObject properties to GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (priv->paned);
+ key = "/apps/evolution/calendar/display/date_navigator_vpane_position";
+ gconf_bridge_bind_property_delayed (bridge, key, object, "position");
+}
+
+static void
+cal_shell_sidebar_client_removed (ECalShellSidebar *cal_shell_sidebar,
+ ECal *client)
+{
+ ESourceSelector *selector;
+ GHashTable *client_table;
+ ESource *source;
+ const gchar *uid;
+
+ client_table = cal_shell_sidebar->priv->client_table;
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+
+ g_signal_handlers_disconnect_matched (
+ client, G_SIGNAL_MATCH_DATA, 0, 0,
+ NULL, NULL, cal_shell_sidebar);
+
+ source = e_cal_get_source (client);
+ e_source_selector_unselect_source (selector, source);
+
+ uid = e_source_peek_uid (source);
+ g_hash_table_remove (client_table, uid);
+
+ cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL);
+}
+
+static void
+cal_shell_sidebar_class_init (ECalShellSidebarClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ECalShellSidebarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = cal_shell_sidebar_get_property;
+ object_class->dispose = cal_shell_sidebar_dispose;
+ object_class->finalize = cal_shell_sidebar_finalize;
+ object_class->constructed = cal_shell_sidebar_constructed;
+
+ class->client_removed = cal_shell_sidebar_client_removed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MINI_CALENDAR,
+ g_param_spec_object (
+ "mini-calendar",
+ _("Mini-Calendar Widget"),
+ _("This widget displays a miniature calendar"),
+ E_TYPE_CALENDAR,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SELECTOR,
+ g_param_spec_object (
+ "selector",
+ _("Source Selector Widget"),
+ _("This widget displays groups of calendars"),
+ E_TYPE_SOURCE_SELECTOR,
+ G_PARAM_READABLE));
+
+ signals[CLIENT_ADDED] = g_signal_new (
+ "client-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ECalShellSidebarClass, client_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_CAL);
+
+ signals[CLIENT_REMOVED] = g_signal_new (
+ "client-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ECalShellSidebarClass, client_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_CAL);
+
+ signals[STATUS_MESSAGE] = g_signal_new (
+ "status-message",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (ECalShellSidebarClass, status_message),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+}
+
+static void
+cal_shell_sidebar_init (ECalShellSidebar *cal_shell_sidebar)
+{
+ GHashTable *client_table;
+
+ client_table = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ cal_shell_sidebar->priv =
+ E_CAL_SHELL_SIDEBAR_GET_PRIVATE (cal_shell_sidebar);
+
+ cal_shell_sidebar->priv->client_table = client_table;
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_cal_shell_sidebar_get_type (void)
+{
+ return cal_shell_sidebar_type;
+}
+
+void
+e_cal_shell_sidebar_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (ECalShellSidebarClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) cal_shell_sidebar_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (ECalShellSidebar),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) cal_shell_sidebar_init,
+ NULL /* value_table */
+ };
+
+ cal_shell_sidebar_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_SIDEBAR,
+ "ECalShellSidebar", &type_info, 0);
+}
+
+GtkWidget *
+e_cal_shell_sidebar_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_CAL_SHELL_SIDEBAR,
+ "shell-view", shell_view, NULL);
+}
+
+GList *
+e_cal_shell_sidebar_get_clients (ECalShellSidebar *cal_shell_sidebar)
+{
+ GHashTable *client_table;
+
+ g_return_val_if_fail (
+ E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL);
+
+ client_table = cal_shell_sidebar->priv->client_table;
+
+ return g_hash_table_get_values (client_table);
+}
+
+ECalendar *
+e_cal_shell_sidebar_get_mini_calendar (ECalShellSidebar *cal_shell_sidebar)
+{
+ g_return_val_if_fail (
+ E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL);
+
+ return E_CALENDAR (cal_shell_sidebar->priv->mini_calendar);
+}
+
+ESourceSelector *
+e_cal_shell_sidebar_get_selector (ECalShellSidebar *cal_shell_sidebar)
+{
+ g_return_val_if_fail (
+ E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL);
+
+ return E_SOURCE_SELECTOR (cal_shell_sidebar->priv->selector);
+}
+
+void
+e_cal_shell_sidebar_add_source (ECalShellSidebar *cal_shell_sidebar,
+ ESource *source)
+{
+ ESourceSelector *selector;
+ GHashTable *client_table;
+ ECal *client;
+ const gchar *uid;
+ const gchar *uri;
+ gchar *message;
+
+ g_return_if_fail (E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ client_table = cal_shell_sidebar->priv->client_table;
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+
+ uid = e_source_peek_uid (source);
+ client = g_hash_table_lookup (client_table, uid);
+
+ if (client != NULL)
+ return;
+
+ client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_EVENT);
+ g_return_if_fail (client != NULL);
+
+ g_signal_connect_swapped (
+ client, "backend-died",
+ G_CALLBACK (cal_shell_sidebar_backend_died_cb),
+ cal_shell_sidebar);
+
+ g_signal_connect_swapped (
+ client, "backend-error",
+ G_CALLBACK (cal_shell_sidebar_backend_error_cb),
+ cal_shell_sidebar);
+
+ g_hash_table_insert (client_table, g_strdup (uid), client);
+ e_source_selector_select_source (selector, source);
+
+ uri = e_cal_get_uri (client);
+ message = g_strdup_printf (_("Opening calendar at %s"), uri);
+ cal_shell_sidebar_emit_status_message (cal_shell_sidebar, message);
+ g_free (message);
+
+ g_signal_connect_swapped (
+ client, "cal-opened",
+ G_CALLBACK (cal_shell_sidebar_client_opened_cb),
+ cal_shell_sidebar);
+
+ e_cal_open_async (client, FALSE);
+}
+
+void
+e_cal_shell_sidebar_remove_source (ECalShellSidebar *cal_shell_sidebar,
+ ESource *source)
+{
+ ESourceSelector *selector;
+ GHashTable *client_table;
+ ECal *client;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ client_table = cal_shell_sidebar->priv->client_table;
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+
+ uid = e_source_peek_uid (source);
+ client = g_hash_table_lookup (client_table, uid);
+
+ if (client == NULL)
+ return;
+
+ cal_shell_sidebar_emit_client_removed (cal_shell_sidebar, client);
+}
diff --git a/modules/calendar/e-cal-shell-sidebar.h b/modules/calendar/e-cal-shell-sidebar.h
new file mode 100644
index 0000000000..c555537abf
--- /dev/null
+++ b/modules/calendar/e-cal-shell-sidebar.h
@@ -0,0 +1,101 @@
+/*
+ * e-cal-shell-sidebar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_CAL_SHELL_SIDEBAR_H
+#define E_CAL_SHELL_SIDEBAR_H
+
+#include <libecal/e-cal.h>
+#include <libedataserverui/e-source-selector.h>
+
+#include <shell/e-shell-sidebar.h>
+#include <shell/e-shell-view.h>
+#include <misc/e-calendar.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CAL_SHELL_SIDEBAR \
+ (e_cal_shell_sidebar_get_type ())
+#define E_CAL_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_CAL_SHELL_SIDEBAR, ECalShellSidebar))
+#define E_CAL_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_CAL_SHELL_SIDEBAR, ECalShellSidebarClass))
+#define E_IS_CAL_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_CAL_SHELL_SIDEBAR))
+#define E_IS_CAL_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_CAL_SHELL_SIDEBAR))
+#define E_CAL_SHELL_SIDEBAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_CAL_SHELL_SIDEBAR, ECalShellSidebarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECalShellSidebar ECalShellSidebar;
+typedef struct _ECalShellSidebarClass ECalShellSidebarClass;
+typedef struct _ECalShellSidebarPrivate ECalShellSidebarPrivate;
+
+enum {
+ E_CAL_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE = 1 << 0,
+ E_CAL_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_EMPTY = 1 << 1,
+ E_CAL_SHELL_SIDEBAR_SOURCE_CAN_GO_OFFLINE = 1 << 2,
+ E_CAL_SHELL_SIDEBAR_SOURCE_CAN_DELETE = 1 << 3
+};
+
+struct _ECalShellSidebar {
+ EShellSidebar parent;
+ ECalShellSidebarPrivate *priv;
+};
+
+struct _ECalShellSidebarClass {
+ EShellSidebarClass parent_class;
+
+ /* Signals */
+ void (*client_added) (ECalShellSidebar *cal_shell_sidebar,
+ ECal *client);
+ void (*client_removed) (ECalShellSidebar *cal_shell_sidebar,
+ ECal *client);
+ void (*status_message) (ECalShellSidebar *cal_shell_sidebar,
+ const gchar *status_message);
+};
+
+GType e_cal_shell_sidebar_get_type (void);
+void e_cal_shell_sidebar_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_cal_shell_sidebar_new (EShellView *shell_view);
+GList * e_cal_shell_sidebar_get_clients
+ (ECalShellSidebar *cal_shell_sidebar);
+ECalendar * e_cal_shell_sidebar_get_mini_calendar
+ (ECalShellSidebar *cal_shell_sidebar);
+ESourceSelector *
+ e_cal_shell_sidebar_get_selector
+ (ECalShellSidebar *cal_shell_sidebar);
+void e_cal_shell_sidebar_add_source
+ (ECalShellSidebar *cal_shell_sidebar,
+ ESource *source);
+void e_cal_shell_sidebar_remove_source
+ (ECalShellSidebar *cal_shell_sidebar,
+ ESource *source);
+
+G_END_DECLS
+
+#endif /* E_CAL_SHELL_SIDEBAR_H */
diff --git a/modules/calendar/e-cal-shell-view-actions.c b/modules/calendar/e-cal-shell-view-actions.c
new file mode 100644
index 0000000000..02b7af609e
--- /dev/null
+++ b/modules/calendar/e-cal-shell-view-actions.c
@@ -0,0 +1,1202 @@
+/*
+ * e-cal-shell-view-actions.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-view-private.h"
+
+static void
+action_calendar_copy_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellSidebar *cal_shell_sidebar;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ ESourceSelector *selector;
+ ESource *source;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar;
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ copy_source_dialog (
+ GTK_WINDOW (shell_window),
+ source, E_CAL_SOURCE_TYPE_EVENT);
+}
+
+static void
+action_calendar_delete_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ ECalShellSidebar *cal_shell_sidebar;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ ECalendarView *calendar_view;
+ GnomeCalendarViewType view_type;
+ ECalModel *model;
+ ESourceSelector *selector;
+ ESourceGroup *source_group;
+ ESourceList *source_list;
+ ESource *source;
+ gint response;
+ gchar *uri;
+ GError *error = NULL;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ cal_shell_content = cal_shell_content->priv->cal_shell_content;
+ view_type = e_cal_shell_content_get_current_view (cal_shell_content);
+ calendar_view = e_cal_shell_content_get_calendar_view (
+ cal_shell_content, view_type);
+ model = e_calendar_view_get_model (calendar_view);
+
+ cal_shell_sidebar = cal_shell_sidebar->priv->cal_shell_sidebar;
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ /* Ask for confirmation. */
+ response = e_error_run (
+ GTK_WINDOW (shell_window),
+ "calendar:prompt-delete-calendar",
+ e_source_peek_name (source));
+ if (response != GTK_RESPONSE_YES)
+ return;
+
+ uri = e_source_get_uri (source);
+ client = e_cal_model_get_client_for_uri (model, uri);
+ if (client == NULL)
+ client = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_EVENT);
+ g_free (uri);
+
+ g_return_if_fail (client != NULL);
+
+ if (!e_cal_remove (client, &error)) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (e_source_selector_source_is_selected (selector, source)) {
+ e_cal_shell_sidebar_remove_source (
+ cal_shell_sidebar, source);
+ e_source_selector_unselect_source (selector, source);
+ }
+
+ source_group = e_source_peek_group (source);
+ e_source_group_remove_source (source_group, source);
+
+ source_list = cal_shell_view->priv->source_list;
+ if (!e_source_list_sync (source_list, &error)) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+#endif
+}
+
+static void
+action_calendar_go_back_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ GnomeCalendar *calendar;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+
+ gnome_calendar_previous (calendar);
+#endif
+}
+
+static void
+action_calendar_go_forward_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ GnomeCalendar *calendar;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+
+ gnome_calendar_next (calendar);
+#endif
+}
+
+static void
+action_calendar_go_today_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ GnomeCalendar *calendar;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+
+ gnome_calendar_goto_today (calendar);
+#endif
+}
+
+static void
+action_calendar_jump_to_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ GnomeCalendar *calendar;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+
+ goto_dialog (calendar);
+#endif
+}
+
+static void
+action_calendar_new_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ calendar_setup_new_calendar (GTK_WINDOW (shell_window));
+#endif
+}
+
+static void
+action_calendar_print_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ GnomeCalendar *calendar;
+ GtkPrintOperationAction print_action;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
+
+ if (gnome_calendar_get_view (calendar) == GNOME_CAL_LIST_VIEW) {
+ ECalListView *list_view;
+ GtkWidget *widget;
+ ETable *table;
+
+ widget = gnome_calendar_get_current_view_widget (calendar);
+ list_view = E_CAL_LIST_VIEW (widget);
+ table = e_table_scrolled_get_table (list_view->table_scrolled);
+ print_table (table, _("Print"), _("Calendar"), action);
+ } else {
+ time_t start;
+
+ gnome_calendar_get_current_time_range (calendar, &start, NULL);
+ print_calendar (calendar, action, start);
+ }
+#endif
+}
+
+static void
+action_calendar_print_preview_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ GnomeCalendar *calendar;
+ GtkPrintOperationAction print_action;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW;
+
+ if (gnome_calendar_get_view (calendar) == GNOME_CAL_LIST_VIEW) {
+ ECalListView *list_view;
+ GtkWidget *widget;
+ ETable *table;
+
+ widget = gnome_calendar_get_current_view_widget (calendar);
+ list_view = E_CAL_LIST_VIEW (widget);
+ table = e_table_scrolled_get_table (list_view->table_scrolled);
+ print_table (table, _("Print"), _("Calendar"), action);
+ } else {
+ time_t start;
+
+ gnome_calendar_get_current_time_range (calendar, &start, NULL);
+ print_calendar (calendar, action, start);
+ }
+#endif
+}
+
+static void
+action_calendar_properties_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellSidebar *cal_shell_sidebar;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ESource *source;
+ ESourceSelector *selector;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar;
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ calendar_setup_edit_calendar (GTK_WINDOW (shell_window), source);
+#endif
+}
+
+static void
+action_calendar_purge_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_calendar_rename_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellSidebar *cal_shell_sidebar;
+ ESourceSelector *selector;
+
+ cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar;
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+
+ e_source_selector_edit_primary_selection (selector);
+}
+
+static void
+action_calendar_search_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+ EShellContent *shell_content;
+ const gchar *search_hint;
+
+ /* XXX Figure out a way to handle this in EShellContent
+ * instead of every shell view having to handle it.
+ * The problem is EShellContent does not know what
+ * the search option actions are for this view. It
+ * would have to dig up the popup menu and retrieve
+ * the action for each menu item. Seems messy. */
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+
+ search_hint = gtk_action_get_label (GTK_ACTION (current));
+ e_shell_content_set_search_hint (shell_content, search_hint);
+}
+
+static void
+action_calendar_select_one_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellSidebar *cal_shell_sidebar;
+ ESourceSelector *selector;
+ ESource *primary;
+ GSList *list, *iter;
+
+ /* XXX ESourceSelector should provide a function for this. */
+
+ cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar;
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+ primary = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (primary != NULL);
+
+ list = e_source_selector_get_selection (selector);
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+
+ if (source == primary)
+ continue;
+
+ e_source_selector_unselect_source (selector, source);
+ }
+ e_source_selector_free_selection (list);
+
+ e_source_selector_select_source (selector, primary);
+}
+
+static void
+action_calendar_view_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+ GnomeCalendarViewType view_type;
+ const gchar *view_id;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ view_type = gtk_radio_action_get_current_value (action);
+
+ switch (view_type) {
+ case GNOME_CAL_DAY_VIEW:
+ view_id = "Day_View";
+ break;
+
+ case GNOME_CAL_WORK_WEEK_VIEW:
+ view_id = "Work_Week_View";
+ break;
+
+ case GNOME_CAL_WEEK_VIEW:
+ view_id = "Week_View";
+ break;
+
+ case GNOME_CAL_MONTH_VIEW:
+ view_id = "Month_View";
+ break;
+
+ case GNOME_CAL_LIST_VIEW:
+ view_id = "List_View";
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ e_shell_view_set_view_id (shell_view, view_id);
+}
+
+static void
+action_event_all_day_new_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_clipboard_copy_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ e_cal_shell_content_copy_clipboard (cal_shell_content);
+}
+
+static void
+action_event_clipboard_cut_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ e_cal_shell_content_cut_clipboard (cal_shell_content);
+}
+
+static void
+action_event_clipboard_paste_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ e_cal_shell_content_paste_clipboard (cal_shell_content);
+}
+
+static void
+action_event_copy_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_delegate_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_delete_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ e_cal_shell_content_delete_selection (cal_shell_content);
+}
+
+static void
+action_event_delete_occurrence_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ e_cal_shell_content_delete_selected_occurrence (cal_shell_content);
+}
+
+static void
+action_event_delete_occurrence_all_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+
+ /* XXX Same as "event-delete". */
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ e_cal_shell_content_delete_selection (cal_shell_content);
+}
+
+static void
+action_event_forward_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_meeting_new_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_move_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_new_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_occurrence_movable_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_open_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ GnomeCalendar *calendar;
+ GtkWidget *widget;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ widget = gnome_calendar_get_current_view_widget (calendar);
+
+ e_calendar_view_open_event (E_CALENDAR_VIEW (widget));
+#endif
+}
+
+static void
+action_event_print_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_reply_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_reply_all_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_save_as_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_event_schedule_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+static void
+action_gal_save_custom_view_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EShellView *shell_view;
+ GalViewInstance *view_instance;
+
+ /* All shell views respond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with saving the custom view. */
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ view_instance = e_cal_shell_content_get_view_instance (cal_shell_content);
+ gal_view_instance_save_as (view_instance);
+}
+
+static void
+action_search_execute_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+
+ /* All shell views respond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with executing the search. */
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ e_cal_shell_view_execute_search (cal_shell_view);
+}
+
+static void
+action_search_filter_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ gtk_action_activate (ACTION (SEARCH_EXECUTE));
+}
+
+static GtkActionEntry calendar_entries[] = {
+
+ { "calendar-copy",
+ GTK_STOCK_COPY,
+ N_("_Copy..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_copy_cb) },
+
+ { "calendar-delete",
+ GTK_STOCK_DELETE,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_delete_cb) },
+
+ { "calendar-go-back",
+ GTK_STOCK_GO_BACK,
+ N_("Previous"),
+ NULL,
+ N_("Go Back"),
+ G_CALLBACK (action_calendar_go_back_cb) },
+
+ { "calendar-go-forward",
+ GTK_STOCK_GO_FORWARD,
+ N_("Next"),
+ NULL,
+ N_("Go Forward"),
+ G_CALLBACK (action_calendar_go_forward_cb) },
+
+ { "calendar-go-today",
+ "go-today",
+ N_("Select _Today"),
+ "<Control>t",
+ N_("Select today"),
+ G_CALLBACK (action_calendar_go_today_cb) },
+
+ { "calendar-jump-to",
+ GTK_STOCK_JUMP_TO,
+ N_("Select _Date"),
+ "<Control>g",
+ N_("Select a specific date"),
+ G_CALLBACK (action_calendar_jump_to_cb) },
+
+ { "calendar-new",
+ "x-office-calendar",
+ N_("_New Calendar"),
+ NULL,
+ N_("Create a new calendar"),
+ G_CALLBACK (action_calendar_new_cb) },
+
+ { "calendar-properties",
+ GTK_STOCK_PROPERTIES,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_properties_cb) },
+
+ { "calendar-purge",
+ NULL,
+ N_("Purg_e"),
+ "<Control>e",
+ N_("Purge old appointments and meetings"),
+ G_CALLBACK (action_calendar_purge_cb) },
+
+ { "calendar-rename",
+ NULL,
+ N_("_Rename..."),
+ "F2",
+ N_("Rename the selected calendar"),
+ G_CALLBACK (action_calendar_rename_cb) },
+
+ { "calendar-select-one",
+ "stock_check-filled",
+ N_("Show _Only This Calendar"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_select_one_cb) },
+
+ { "event-clipboard-copy",
+ GTK_STOCK_COPY,
+ NULL,
+ NULL,
+ N_("Copy the selection"),
+ G_CALLBACK (action_event_clipboard_copy_cb) },
+
+ { "event-clipboard-cut",
+ GTK_STOCK_CUT,
+ NULL,
+ NULL,
+ N_("Cut the selection"),
+ G_CALLBACK (action_event_clipboard_cut_cb) },
+
+ { "event-clipboard-paste",
+ GTK_STOCK_PASTE,
+ NULL,
+ NULL,
+ N_("Paste the clipboard"),
+ G_CALLBACK (action_event_clipboard_paste_cb) },
+
+ { "event-copy",
+ NULL,
+ N_("Cop_y to Calendar..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_copy_cb) },
+
+ { "event-delegate",
+ NULL,
+ N_("_Delegate Meeting..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_delegate_cb) },
+
+ { "event-delete",
+ GTK_STOCK_DELETE,
+ NULL,
+ NULL,
+ N_("Delete the appointment"),
+ G_CALLBACK (action_event_delete_cb) },
+
+ { "event-delete-occurrence",
+ GTK_STOCK_DELETE,
+ N_("Delete This _Occurrence"),
+ NULL,
+ N_("Delete this occurrence"),
+ G_CALLBACK (action_event_delete_occurrence_cb) },
+
+ { "event-delete-occurrence-all",
+ GTK_STOCK_DELETE,
+ N_("Delete _All Occurrences"),
+ NULL,
+ N_("Delete all occurrences"),
+ G_CALLBACK (action_event_delete_occurrence_all_cb) },
+
+ { "event-all-day-new",
+ NULL,
+ N_("New All Day _Event..."),
+ NULL,
+ N_("Create a new all day event"),
+ G_CALLBACK (action_event_all_day_new_cb) },
+
+ { "event-forward",
+ "mail-forward",
+ N_("_Forward as iCalendar..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_forward_cb) },
+
+ { "event-meeting-new",
+ NULL,
+ N_("New _Meeting..."),
+ NULL,
+ N_("Create a new meeting"),
+ G_CALLBACK (action_event_meeting_new_cb) },
+
+ { "event-move",
+ NULL,
+ N_("Mo_ve to Calendar..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_move_cb) },
+
+ { "event-new",
+ NULL,
+ N_("New _Appointment..."),
+ NULL,
+ N_("Create a new appointment"),
+ G_CALLBACK (action_event_new_cb) },
+
+ { "event-occurrence-movable",
+ NULL,
+ N_("Make this Occurrence _Movable"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_occurrence_movable_cb) },
+
+ { "event-open",
+ NULL,
+ N_("_Open Appointment"),
+ "<Control>o",
+ N_("View the current appointment"),
+ G_CALLBACK (action_event_open_cb) },
+
+ { "event-reply",
+ "mail-reply-sender",
+ N_("_Reply"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_reply_cb) },
+
+ { "event-reply-all",
+ "mail-reply-all",
+ N_("Reply to _All"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_reply_all_cb) },
+
+ { "event-save-as",
+ GTK_STOCK_SAVE_AS,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_save_as_cb) },
+
+ { "event-schedule",
+ NULL,
+ N_("_Schedule Meeting..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_schedule_cb) },
+
+ /*** Menus ***/
+
+ { "calendar-actions-menu",
+ NULL,
+ N_("_Actions"),
+ NULL,
+ NULL,
+ NULL }
+};
+
+static EPopupActionEntry calendar_popup_entries[] = {
+
+ /* FIXME No equivalent main menu items for the any of the calendar
+ * popup menu items and for many of the event popup menu items.
+ * This is an accessibility issue. */
+
+ { "calendar-popup-copy",
+ NULL,
+ "calendar-copy" },
+
+ { "calendar-popup-delete",
+ NULL,
+ "calendar-delete" },
+
+ { "calendar-popup-go-today",
+ NULL,
+ "calendar-go-today" },
+
+ { "calendar-popup-jump-to",
+ NULL,
+ "calendar-jump-to" },
+
+ { "calendar-popup-properties",
+ NULL,
+ "calendar-properties" },
+
+ { "calendar-popup-rename",
+ NULL,
+ "calendar-rename" },
+
+ { "calendar-popup-select-one",
+ NULL,
+ "calendar-select-one" },
+
+ { "event-popup-clipboard-copy",
+ NULL,
+ "event-clipboard-copy" },
+
+ { "event-popup-clipboard-cut",
+ NULL,
+ "event-clipboard-cut" },
+
+ { "event-popup-clipboard-paste",
+ NULL,
+ "event-clipboard-paste" },
+
+ { "event-popup-copy",
+ NULL,
+ "event-copy" },
+
+ { "event-popup-delegate",
+ NULL,
+ "event-delegate" },
+
+ { "event-popup-delete",
+ NULL,
+ "event-delete" },
+
+ { "event-popup-delete-occurrence",
+ NULL,
+ "event-delete-occurrence" },
+
+ { "event-popup-delete-occurrence-all",
+ NULL,
+ "event-delete-occurrence-all" },
+
+ { "event-popup-forward",
+ NULL,
+ "event-forward" },
+
+ { "event-popup-move",
+ NULL,
+ "event-move" },
+
+ { "event-popup-occurrence-movable",
+ NULL,
+ "event-occurrence-movable" },
+
+ { "event-popup-open",
+ NULL,
+ "event-open" },
+
+ { "event-popup-reply",
+ NULL,
+ "event-reply" },
+
+ { "event-popup-reply-all",
+ NULL,
+ "event-reply-all" },
+
+ { "event-popup-save-as",
+ NULL,
+ "event-save-as" },
+
+ { "event-popup-schedule",
+ NULL,
+ "event-schedule" }
+};
+
+static GtkRadioActionEntry calendar_view_entries[] = {
+
+ { "calendar-view-day",
+ "view-calendar-day",
+ N_("Day"),
+ NULL,
+ N_("Show one day"),
+ GNOME_CAL_DAY_VIEW },
+
+ { "calendar-view-list",
+ "view-calendar-list",
+ N_("List"),
+ NULL,
+ N_("Show as list"),
+ GNOME_CAL_LIST_VIEW },
+
+ { "calendar-view-month",
+ "view-calendar-month",
+ N_("Month"),
+ NULL,
+ N_("Show one month"),
+ GNOME_CAL_MONTH_VIEW },
+
+ { "calendar-view-week",
+ "view-calendar-week",
+ N_("Week"),
+ NULL,
+ N_("Show one week"),
+ GNOME_CAL_WEEK_VIEW },
+
+ { "calendar-view-workweek",
+ "view-calendar-workweek",
+ N_("Work Week"),
+ NULL,
+ N_("Show one work week"),
+ GNOME_CAL_WORK_WEEK_VIEW }
+};
+
+static GtkRadioActionEntry calendar_filter_entries[] = {
+
+ { "calendar-filter-active-appointments",
+ NULL,
+ N_("Active Appointements"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CALENDAR_FILTER_ACTIVE_APPOINTMENTS },
+
+ { "calendar-filter-any-category",
+ NULL,
+ N_("Any Category"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CALENDAR_FILTER_ANY_CATEGORY },
+
+ { "calendar-filter-next-7-days-appointments",
+ NULL,
+ N_("Next 7 Days' Appointments"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS },
+
+ { "calendar-filter-unmatched",
+ NULL,
+ N_("Unmatched"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CALENDAR_FILTER_UNMATCHED }
+};
+
+static GtkRadioActionEntry calendar_search_entries[] = {
+
+ { "calendar-search-any-field-contains",
+ NULL,
+ N_("Any field contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CALENDAR_SEARCH_ANY_FIELD_CONTAINS },
+
+ { "calendar-search-description-contains",
+ NULL,
+ N_("Description contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CALENDAR_SEARCH_DESCRIPTION_CONTAINS },
+
+ { "calendar-search-summary-contains",
+ NULL,
+ N_("Summary contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ CALENDAR_SEARCH_SUMMARY_CONTAINS }
+};
+
+static GtkActionEntry lockdown_printing_entries[] = {
+
+ { "calendar-print",
+ GTK_STOCK_PRINT,
+ NULL,
+ "<Control>p",
+ N_("Print this calendar"),
+ G_CALLBACK (action_calendar_print_cb) },
+
+ { "calendar-print-preview",
+ GTK_STOCK_PRINT_PREVIEW,
+ NULL,
+ NULL,
+ N_("Preview the calendar to be printed"),
+ G_CALLBACK (action_calendar_print_preview_cb) },
+
+ { "event-print",
+ GTK_STOCK_PRINT,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_event_print_cb) }
+};
+
+static EPopupActionEntry lockdown_printing_popup_entries[] = {
+
+ { "event-popup-print",
+ NULL,
+ "event-print" }
+};
+
+void
+e_cal_shell_view_actions_init (ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ GtkActionGroup *action_group;
+ GtkAction *action;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ /* Calendar Actions */
+ action_group = ACTION_GROUP (CALENDAR);
+ gtk_action_group_add_actions (
+ action_group, calendar_entries,
+ G_N_ELEMENTS (calendar_entries), cal_shell_view);
+ e_action_group_add_popup_actions (
+ action_group, calendar_popup_entries,
+ G_N_ELEMENTS (calendar_popup_entries));
+ gtk_action_group_add_radio_actions (
+ action_group, calendar_view_entries,
+ G_N_ELEMENTS (calendar_view_entries), GNOME_CAL_DAY_VIEW,
+ G_CALLBACK (action_calendar_view_cb), cal_shell_view);
+ gtk_action_group_add_radio_actions (
+ action_group, calendar_search_entries,
+ G_N_ELEMENTS (calendar_search_entries),
+ CALENDAR_SEARCH_SUMMARY_CONTAINS,
+ G_CALLBACK (action_calendar_search_cb), cal_shell_view);
+
+ /* Lockdown Printing Actions */
+ action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
+ gtk_action_group_add_actions (
+ action_group, lockdown_printing_entries,
+ G_N_ELEMENTS (lockdown_printing_entries), cal_shell_view);
+ e_action_group_add_popup_actions (
+ action_group, lockdown_printing_popup_entries,
+ G_N_ELEMENTS (lockdown_printing_popup_entries));
+
+ /* Fine tuning. */
+
+ action = ACTION (CALENDAR_GO_TODAY);
+ g_object_set (action, "short-label", _("Today"), NULL);
+
+ action = ACTION (CALENDAR_JUMP_TO);
+ g_object_set (action, "short-label", _("Go To"), NULL);
+
+ action = ACTION (EVENT_DELETE);
+ g_object_set (action, "short-label", _("Delete"), NULL);
+
+ g_signal_connect (
+ ACTION (GAL_SAVE_CUSTOM_VIEW), "activate",
+ G_CALLBACK (action_gal_save_custom_view_cb), cal_shell_view);
+
+ g_signal_connect (
+ ACTION (SEARCH_EXECUTE), "activate",
+ G_CALLBACK (action_search_execute_cb), cal_shell_view);
+
+ /* Initialize the memo and task pad actions. */
+ e_cal_shell_view_memopad_actions_init (cal_shell_view);
+ e_cal_shell_view_taskpad_actions_init (cal_shell_view);
+}
+
+void
+e_cal_shell_view_update_search_filter (ECalShellView *cal_shell_view)
+{
+ EShellContent *shell_content;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ GtkActionGroup *action_group;
+ GtkRadioAction *radio_action;
+ GList *list, *iter;
+ GSList *group;
+ gint ii;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ action_group = ACTION_GROUP (CALENDAR_FILTER);
+ e_action_group_remove_all_actions (action_group);
+
+ /* Add the standard filter actions. */
+ gtk_action_group_add_radio_actions (
+ action_group, calendar_filter_entries,
+ G_N_ELEMENTS (calendar_filter_entries),
+ CALENDAR_FILTER_ANY_CATEGORY,
+ G_CALLBACK (action_search_filter_cb),
+ cal_shell_view);
+
+ /* Retrieve the radio group from an action we just added. */
+ list = gtk_action_group_list_actions (action_group);
+ radio_action = GTK_RADIO_ACTION (list->data);
+ group = gtk_radio_action_get_group (radio_action);
+ g_list_free (list);
+
+ /* Build the category actions. */
+
+ list = e_categories_get_list ();
+ for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) {
+ const gchar *category_name = iter->data;
+ const gchar *filename;
+ GtkAction *action;
+ gchar *action_name;
+
+ action_name = g_strdup_printf (
+ "calendar-filter-category-%d", ii);
+ radio_action = gtk_radio_action_new (
+ action_name, category_name, NULL, NULL, ii);
+ g_free (action_name);
+
+ /* Convert the category icon file to a themed icon name. */
+ filename = e_categories_get_icon_file_for (category_name);
+ if (filename != NULL && *filename != '\0') {
+ gchar *basename;
+ gchar *cp;
+
+ basename = g_path_get_basename (filename);
+
+ /* Lose the file extension. */
+ if ((cp = strrchr (basename, '.')) != NULL)
+ *cp = '\0';
+
+ g_object_set (
+ radio_action, "icon-name", basename, NULL);
+
+ g_free (basename);
+ }
+
+ gtk_radio_action_set_group (radio_action, group);
+ group = gtk_radio_action_get_group (radio_action);
+
+ /* The action group takes ownership of the action. */
+ action = GTK_ACTION (radio_action);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (radio_action);
+ }
+ g_list_free (list);
+
+ /* Use any action in the group; doesn't matter which. */
+ e_shell_content_set_filter_action (shell_content, radio_action);
+
+ ii = CALENDAR_FILTER_UNMATCHED;
+ e_shell_content_add_filter_separator_after (shell_content, ii);
+
+ ii = CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS;
+ e_shell_content_add_filter_separator_after (shell_content, ii);
+}
diff --git a/modules/calendar/e-cal-shell-view-actions.h b/modules/calendar/e-cal-shell-view-actions.h
new file mode 100644
index 0000000000..b02906f179
--- /dev/null
+++ b/modules/calendar/e-cal-shell-view-actions.h
@@ -0,0 +1,153 @@
+/*
+ * e-cal-shell-view-actions.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_CAL_SHELL_VIEW_ACTIONS_H
+#define E_CAL_SHELL_VIEW_ACTIONS_H
+
+#include <shell/e-shell-window-actions.h>
+
+/* Calendar Actions */
+#define E_SHELL_WINDOW_ACTION_CALENDAR_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-copy")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-delete")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_GO_BACK(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-go-back")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_GO_FORWARD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-go-forward")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_GO_TODAY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-go-today")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_JUMP_TO(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-jump-to")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-new")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_PRINT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-print")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_PRINT_PREVIEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-print-preview")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_PROPERTIES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-properties")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_PURGE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-purge")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_RENAME(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-rename")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_SELECT_ONE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-select-one")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_DAY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-view-day")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_LIST(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-view-list")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_MONTH(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-view-month")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_WEEK(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-view-week")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_WORKWEEK(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-view-workweek")
+
+/* Event Actions */
+#define E_SHELL_WINDOW_ACTION_EVENT_CLIPBOARD_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "event-clipboard-copy")
+#define E_SHELL_WINDOW_ACTION_EVENT_CLIPBOARD_CUT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "event-clipboard-cut")
+#define E_SHELL_WINDOW_ACTION_EVENT_CLIPBOARD_PASTE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "event-clipboard-paste")
+#define E_SHELL_WINDOW_ACTION_EVENT_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "event-delete")
+#define E_SHELL_WINDOW_ACTION_EVENT_DELETE_OCCURRENCE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "event-delete-occurrence")
+#define E_SHELL_WINDOW_ACTION_EVENT_DELETE_OCCURRENCE_ALL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "event-delete-occurrence-all")
+#define E_SHELL_WINDOW_ACTION_EVENT_OPEN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "event-open")
+
+/* Memo Pad Actions */
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_CLIPBOARD_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-clipboard-copy")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_CLIPBOARD_CUT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-clipboard-cut")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_CLIPBOARD_PASTE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-clipboard-paste")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-delete")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_FORWARD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-forward")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-new")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_OPEN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-open")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_OPEN_URL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-open-url")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_PRINT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-print")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_SAVE_AS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-save-as")
+
+/* Task Pad Actions */
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_ASSIGN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-assign")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_CLIPBOARD_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-clipboard-copy")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_CLIPBOARD_CUT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-clipboard-cut")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_CLIPBOARD_PASTE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-clipboard-paste")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-delete")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_FORWARD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-forward")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_MARK_COMPLETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-mark-complete")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_MARK_INCOMPLETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-mark-incomplete")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-new")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_OPEN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-open")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_OPEN_URL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-open-url")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_PRINT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-print")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_SAVE_AS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-save-as")
+
+/* Calendar Query Actions */
+#define E_SHELL_WINDOW_ACTION_CALENDAR_FILTER_ACTIVE_APPOINTMENTS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-filter-active-appointments")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_FILTER_ANY_CATEGORY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-filter-any-category")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-filter-next-7-days-appointments")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_FILTER_UNMATCHED(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-filter-unmatched")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_SEARCH_ANY_FIELD_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-search-any-field-contains")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_SEARCH_DESCRIPTION_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-search-description-contains")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_SEARCH_SUMMARY_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "calendar-search-summary-contains")
+
+/* Action Groups */
+#define E_SHELL_WINDOW_ACTION_GROUP_CALENDAR(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "calendar")
+#define E_SHELL_WINDOW_ACTION_GROUP_CALENDAR_FILTER(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "calendar-filter")
+
+#endif /* E_CAL_SHELL_VIEW_ACTIONS_H */
diff --git a/modules/calendar/e-cal-shell-view-memopad.c b/modules/calendar/e-cal-shell-view-memopad.c
new file mode 100644
index 0000000000..ecee72d8b5
--- /dev/null
+++ b/modules/calendar/e-cal-shell-view-memopad.c
@@ -0,0 +1,526 @@
+/*
+ * e-cal-shell-view-memopad.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-view-private.h"
+
+/* Much of this file is based on e-memo-shell-view-actions.c. */
+
+static void
+action_calendar_memopad_clipboard_copy_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ e_memo_table_copy_clipboard (memo_table);
+}
+
+static void
+action_calendar_memopad_clipboard_cut_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ e_memo_table_cut_clipboard (memo_table);
+}
+
+static void
+action_calendar_memopad_clipboard_paste_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ e_memo_table_paste_clipboard (memo_table);
+}
+
+static void
+action_calendar_memopad_delete_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ e_cal_shell_view_memopad_set_status_message (
+ cal_shell_view, _("Deleting selected memos..."), -1.0);
+ e_memo_table_delete_selected (memo_table);
+ e_cal_shell_view_memopad_set_status_message (
+ cal_shell_view, NULL, -1.0);
+}
+
+static void
+action_calendar_memopad_forward_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ GSList *list;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only forward the first selected memo. */
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_component_set_icalcomponent (comp, clone);
+ itip_send_comp (
+ E_CAL_COMPONENT_METHOD_PUBLISH, comp,
+ comp_data->client, NULL, NULL, NULL, TRUE);
+ g_object_unref (comp);
+}
+
+static void
+action_calendar_memopad_new_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ ECal *client;
+ ECalComponent *comp;
+ CompEditor *editor;
+ GSList *list;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ client = comp_data->client;
+ editor = memo_editor_new (client, shell, COMP_EDITOR_NEW_ITEM);
+ comp = cal_comp_memo_new_with_defaults (client);
+ comp_editor_edit_comp (editor, comp);
+
+ gtk_window_present (GTK_WINDOW (editor));
+
+ g_object_unref (comp);
+ g_object_unref (client);
+}
+
+static void
+action_calendar_memopad_open_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only open the first selected memo. */
+ e_cal_shell_view_memopad_open_memo (cal_shell_view, comp_data);
+}
+
+static void
+action_calendar_memopad_open_url_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ icalproperty *prop;
+ const gchar *uri;
+ GSList *list;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only open the URI of the first selected memo. */
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_URL_PROPERTY);
+ g_return_if_fail (prop == NULL);
+
+ uri = icalproperty_get_url (prop);
+ e_show_uri (GTK_WINDOW (shell_window), uri);
+}
+
+static void
+action_calendar_memopad_print_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ GtkPrintOperationAction print_action;
+ GSList *list;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only print the first selected memo. */
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
+ e_cal_component_set_icalcomponent (comp, clone);
+ print_comp (comp, comp_data->client, print_action);
+ g_object_unref (comp);
+}
+
+static void
+action_calendar_memopad_save_as_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+ gchar *filename;
+ gchar *string;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ filename = e_file_dialog_save (_("Save as..."), NULL);
+ if (filename == NULL)
+ return;
+
+ /* XXX We only save the first selected memo. */
+ string = e_cal_get_component_as_string (
+ comp_data->client, comp_data->icalcomp);
+ if (string == NULL) {
+ g_warning ("Could not convert memo to a string.");
+ return;
+ }
+
+ e_write_file_uri (filename, string);
+
+ g_free (filename);
+ g_free (string);
+}
+
+static GtkActionEntry calendar_memopad_entries[] = {
+
+ { "calendar-memopad-clipboard-copy",
+ GTK_STOCK_COPY,
+ NULL,
+ NULL,
+ N_("Copy selected memo"),
+ G_CALLBACK (action_calendar_memopad_clipboard_copy_cb) },
+
+ { "calendar-memopad-clipboard-cut",
+ GTK_STOCK_CUT,
+ NULL,
+ NULL,
+ N_("Cut selected memo"),
+ G_CALLBACK (action_calendar_memopad_clipboard_cut_cb) },
+
+ { "calendar-memopad-clipboard-paste",
+ GTK_STOCK_PASTE,
+ NULL,
+ NULL,
+ N_("Paste memo from the clipboard"),
+ G_CALLBACK (action_calendar_memopad_clipboard_paste_cb) },
+
+ { "calendar-memopad-delete",
+ GTK_STOCK_DELETE,
+ N_("_Delete Memo"),
+ NULL,
+ N_("Delete selected memos"),
+ G_CALLBACK (action_calendar_memopad_delete_cb) },
+
+ { "calendar-memopad-forward",
+ "mail-forward",
+ N_("_Forward as iCalendar..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_memopad_forward_cb) },
+
+ { "calendar-memopad-new",
+ "stock_insert-note",
+ N_("New _Memo"),
+ NULL,
+ N_("Create a new memo"),
+ G_CALLBACK (action_calendar_memopad_new_cb) },
+
+ { "calendar-memopad-open",
+ GTK_STOCK_OPEN,
+ N_("_Open Memo"),
+ NULL,
+ N_("View the selected memo"),
+ G_CALLBACK (action_calendar_memopad_open_cb) },
+
+ { "calendar-memopad-open-url",
+ "applications-internet",
+ N_("Open _Web Page"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_memopad_open_url_cb) },
+
+ { "calendar-memopad-save-as",
+ GTK_STOCK_SAVE_AS,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_memopad_save_as_cb) }
+};
+
+static GtkActionEntry lockdown_printing_entries[] = {
+
+ { "calendar-memopad-print",
+ GTK_STOCK_PRINT,
+ NULL,
+ NULL,
+ N_("Print the selected memo"),
+ G_CALLBACK (action_calendar_memopad_print_cb) }
+};
+
+void
+e_cal_shell_view_memopad_actions_init (ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ GtkActionGroup *action_group;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ /* Calendar Actions */
+ action_group = ACTION_GROUP (CALENDAR);
+ gtk_action_group_add_actions (
+ action_group, calendar_memopad_entries,
+ G_N_ELEMENTS (calendar_memopad_entries), cal_shell_view);
+
+ /* Lockdown Printing Actions */
+ action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
+ gtk_action_group_add_actions (
+ action_group, lockdown_printing_entries,
+ G_N_ELEMENTS (lockdown_printing_entries), cal_shell_view);
+}
+
+void
+e_cal_shell_view_memopad_actions_update (ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ EMemoTable *memo_table;
+ ETable *table;
+ GtkAction *action;
+ GSList *list, *iter;
+ const gchar *label;
+ gboolean editable = TRUE;
+ gboolean has_url = FALSE;
+ gboolean sensitive;
+ gint n_selected;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+
+ table = e_memo_table_get_table (memo_table);
+ n_selected = e_table_selected_count (table);
+
+ list = e_memo_table_get_selected (memo_table);
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ECalModelComponent *comp_data = iter->data;
+ icalproperty *prop;
+ gboolean read_only;
+
+ e_cal_is_read_only (comp_data->client, &read_only, NULL);
+ editable &= !read_only;
+
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_URL_PROPERTY);
+ has_url |= (prop != NULL);
+ }
+ g_slist_free (list);
+
+ action = ACTION (CALENDAR_MEMOPAD_CLIPBOARD_COPY);
+ sensitive = (n_selected > 0);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_MEMOPAD_CLIPBOARD_CUT);
+ sensitive = (n_selected > 0) && editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_MEMOPAD_CLIPBOARD_PASTE);
+ sensitive = editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_MEMOPAD_DELETE);
+ sensitive = (n_selected > 0) && editable;
+ gtk_action_set_sensitive (action, sensitive);
+ label = ngettext ("Delete Memo", "Delete Memos", n_selected);
+ g_object_set (action, "label", label, NULL);
+
+ action = ACTION (CALENDAR_MEMOPAD_FORWARD);
+ sensitive = (n_selected == 1);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_MEMOPAD_OPEN);
+ sensitive = (n_selected == 1);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_MEMOPAD_OPEN_URL);
+ sensitive = (n_selected == 1) && has_url;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_MEMOPAD_PRINT);
+ sensitive = (n_selected == 1);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_MEMOPAD_SAVE_AS);
+ sensitive = (n_selected == 1);
+ gtk_action_set_sensitive (action, sensitive);
+}
+
+void
+e_cal_shell_view_memopad_open_memo (ECalShellView *cal_shell_view,
+ ECalModelComponent *comp_data)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view));
+ g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data));
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ uid = icalcomponent_get_uid (comp_data->icalcomp);
+ editor = comp_editor_find_instance (uid);
+
+ if (editor != NULL)
+ goto exit;
+
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_component_set_icalcomponent (comp, clone);
+
+ if (e_cal_component_has_organizer (comp))
+ flags |= COMP_EDITOR_IS_SHARED;
+
+ if (itip_organizer_is_user (comp, comp_data->client))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ editor = memo_editor_new (comp_data->client, shell, flags);
+ comp_editor_edit_comp (editor, comp);
+
+ g_object_unref (comp);
+
+exit:
+ gtk_window_present (GTK_WINDOW (editor));
+}
+
+void
+e_cal_shell_view_memopad_set_status_message (ECalShellView *cal_shell_view,
+ const gchar *status_message,
+ gdouble percent)
+{
+ EActivity *activity;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+
+ g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view));
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+
+ activity = cal_shell_view->priv->memopad_activity;
+
+ if (status_message == NULL || *status_message == '\0') {
+ if (activity != NULL) {
+ e_activity_complete (activity);
+ g_object_unref (activity);
+ activity = NULL;
+ }
+
+ } else if (activity == NULL) {
+ activity = e_activity_new (status_message);
+ e_activity_set_percent (activity, percent);
+ e_shell_backend_add_activity (shell_backend, activity);
+
+ } else {
+ e_activity_set_percent (activity, percent);
+ e_activity_set_primary_text (activity, status_message);
+ }
+
+ cal_shell_view->priv->memopad_activity = activity;
+}
diff --git a/modules/calendar/e-cal-shell-view-private.c b/modules/calendar/e-cal-shell-view-private.c
new file mode 100644
index 0000000000..f681a2c2f3
--- /dev/null
+++ b/modules/calendar/e-cal-shell-view-private.c
@@ -0,0 +1,650 @@
+/*
+ * e-cal-shell-view-private.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-view-private.h"
+
+#include "calendar/gui/calendar-view-factory.h"
+#include "widgets/menus/gal-view-factory-etable.h"
+
+static void
+cal_shell_view_process_completed_tasks (ECalShellView *cal_shell_view,
+ gboolean config_changed)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ e_calendar_table_process_completed_tasks (
+ task_table, clients, config_changed);
+#endif
+}
+
+static void
+cal_shell_view_config_timezone_changed_cb (GConfClient *client,
+ guint id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ ECalShellView *cal_shell_view = user_data;
+
+ e_cal_shell_view_update_timezone (cal_shell_view);
+}
+
+static struct tm
+cal_shell_view_get_current_time (ECalendarItem *calitem,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ struct icaltimetype tt;
+ icaltimezone *timezone;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ timezone = e_cal_shell_content_get_timezone (cal_shell_content);
+
+ tt = icaltime_from_timet_with_zone (time (NULL), FALSE, timezone);
+
+ return icaltimetype_to_tm (&tt);
+}
+
+static void
+cal_shell_view_mini_calendar_date_range_changed_cb (ECalShellView *cal_shell_view,
+ ECalendarItem *calitem)
+{
+ /* FIXME gnome-calendar.c calls update_query() here. */
+}
+
+static void
+cal_shell_view_mini_calendar_selection_changed_cb (ECalShellView *cal_shell_view,
+ ECalendarItem *calitem)
+{
+ /* FIXME */
+}
+
+static void
+cal_shell_view_mini_calendar_scroll_event_cb (ECalShellView *cal_shell_view,
+ GdkEventScroll *event,
+ ECalendar *mini_calendar)
+{
+ ECalendarItem *calitem;
+ GDate start_date, end_date;
+
+ calitem = mini_calendar->calitem;
+ if (!e_calendar_item_get_selection (calitem, &start_date, &end_date))
+ return;
+
+ switch (event->direction) {
+ case GDK_SCROLL_UP:
+ g_date_subtract_months (&start_date, 1);
+ g_date_subtract_months (&end_date, 1);
+ break;
+
+ case GDK_SCROLL_DOWN:
+ g_date_add_months (&start_date, 1);
+ g_date_add_months (&end_date, 1);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ /* XXX Does ECalendarItem emit a signal for this? If so, maybe
+ * we could move this handler into ECalShellSidebar. */
+ e_calendar_item_set_selection (calitem, &start_date, &end_date);
+
+ cal_shell_view_mini_calendar_date_range_changed_cb (
+ cal_shell_view, calitem);
+}
+
+static gboolean
+cal_shell_view_selector_popup_event_cb (EShellView *shell_view,
+ ESource *primary_source,
+ GdkEventButton *event)
+{
+ const gchar *widget_path;
+
+ widget_path = "/calendar-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+
+ return TRUE;
+}
+
+static void
+cal_shell_view_memopad_popup_event_cb (EShellView *shell_view,
+ GdkEventButton *event)
+{
+ const gchar *widget_path;
+
+ widget_path = "/calendar-memopad-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+}
+
+static void
+cal_shell_view_taskpad_popup_event_cb (EShellView *shell_view,
+ GdkEventButton *event)
+{
+ const gchar *widget_path;
+
+ widget_path = "/calendar-taskpad-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+}
+
+static void
+cal_shell_view_load_view_collection (EShellViewClass *shell_view_class)
+{
+ GalViewCollection *collection;
+ GalViewFactory *factory;
+ ETableSpecification *spec;
+ const gchar *base_dir;
+ gchar *filename;
+
+ collection = shell_view_class->view_collection;
+
+ base_dir = EVOLUTION_ETSPECDIR;
+ spec = e_table_specification_new ();
+ filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL);
+ if (!e_table_specification_load_from_file (spec, filename))
+ g_critical ("Unable to load ETable specification file "
+ "for calendars");
+ g_free (filename);
+
+ factory = calendar_view_factory_new (GNOME_CAL_DAY_VIEW);
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+
+ factory = calendar_view_factory_new (GNOME_CAL_WORK_WEEK_VIEW);
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+
+ factory = calendar_view_factory_new (GNOME_CAL_WEEK_VIEW);
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+
+ factory = calendar_view_factory_new (GNOME_CAL_MONTH_VIEW);
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+
+ factory = gal_view_factory_etable_new (spec);
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+ g_object_unref (spec);
+
+ gal_view_collection_load (collection);
+}
+
+static void
+cal_shell_view_notify_view_id_cb (ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ GalViewInstance *view_instance;
+ const gchar *view_id;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ view_instance =
+ e_cal_shell_content_get_view_instance (cal_shell_content);
+ view_id = e_shell_view_get_view_id (E_SHELL_VIEW (cal_shell_view));
+
+ /* A NULL view ID implies we're in a custom view. But you can
+ * only get to a custom view via the "Define Views" dialog, which
+ * would have already modified the view instance appropriately.
+ * Furthermore, there's no way to refer to a custom view by ID
+ * anyway, since custom views have no IDs. */
+ if (view_id == NULL)
+ return;
+
+ gal_view_instance_set_current_view_id (view_instance, view_id);
+}
+
+void
+e_cal_shell_view_private_init (ECalShellView *cal_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ if (!gal_view_collection_loaded (shell_view_class->view_collection))
+ cal_shell_view_load_view_collection (shell_view_class);
+
+ g_signal_connect (
+ cal_shell_view, "notify::view-id",
+ G_CALLBACK (cal_shell_view_notify_view_id_cb), NULL);
+}
+
+void
+e_cal_shell_view_private_constructed (ECalShellView *cal_shell_view)
+{
+ ECalShellViewPrivate *priv = cal_shell_view->priv;
+ ECalShellContent *cal_shell_content;
+ ECalShellSidebar *cal_shell_sidebar;
+ EShellBackend *shell_backend;
+ EShellContent *shell_content;
+ EShellSidebar *shell_sidebar;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ GnomeCalendar *calendar;
+ ECalendar *mini_calendar;
+ EMemoTable *memo_table;
+ ECalendarTable *task_table;
+ ESourceSelector *selector;
+ guint id;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ e_shell_window_add_action_group (shell_window, "calendar");
+ e_shell_window_add_action_group (shell_window, "calendar-filter");
+
+ /* Cache these to avoid lots of awkward casting. */
+ priv->cal_shell_backend = g_object_ref (shell_backend);
+ priv->cal_shell_content = g_object_ref (shell_content);
+ priv->cal_shell_sidebar = g_object_ref (shell_sidebar);
+
+ cal_shell_content = E_CAL_SHELL_CONTENT (shell_content);
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ cal_shell_sidebar = E_CAL_SHELL_SIDEBAR (shell_sidebar);
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+ mini_calendar = e_cal_shell_sidebar_get_mini_calendar (cal_shell_sidebar);
+
+ e_calendar_item_set_get_time_callback (
+ mini_calendar->calitem, (ECalendarItemGetTimeCallback)
+ cal_shell_view_get_current_time, cal_shell_view, NULL);
+
+#if 0 /* KILL-BONOBO */
+ g_signal_connect_swapped (
+ calendar, "dates-shown-changed",
+ G_CALLBACK (e_cal_shell_view_update_sidebar),
+ cal_shell_view);
+#endif
+
+ g_signal_connect_swapped (
+ mini_calendar, "scroll-event",
+ G_CALLBACK (cal_shell_view_mini_calendar_scroll_event_cb),
+ cal_shell_view);
+
+ g_signal_connect_swapped (
+ mini_calendar->calitem, "date-range-changed",
+ G_CALLBACK (cal_shell_view_mini_calendar_date_range_changed_cb),
+ cal_shell_view);
+
+ g_signal_connect_swapped (
+ mini_calendar->calitem, "selection-changed",
+ G_CALLBACK (cal_shell_view_mini_calendar_selection_changed_cb),
+ cal_shell_view);
+
+ g_signal_connect_swapped (
+ selector, "popup-event",
+ G_CALLBACK (cal_shell_view_selector_popup_event_cb),
+ cal_shell_view);
+
+ g_signal_connect_swapped (
+ memo_table, "popup-event",
+ G_CALLBACK (cal_shell_view_memopad_popup_event_cb),
+ cal_shell_view);
+
+ g_signal_connect_swapped (
+ memo_table, "status-message",
+ G_CALLBACK (e_cal_shell_view_memopad_set_status_message),
+ cal_shell_view);
+
+ g_signal_connect_swapped (
+ task_table, "popup-event",
+ G_CALLBACK (cal_shell_view_taskpad_popup_event_cb),
+ cal_shell_view);
+
+ g_signal_connect_swapped (
+ task_table, "status-message",
+ G_CALLBACK (e_cal_shell_view_taskpad_set_status_message),
+ cal_shell_view);
+
+ g_signal_connect_swapped (
+ e_memo_table_get_table (memo_table), "selection-change",
+ G_CALLBACK (e_cal_shell_view_memopad_actions_update),
+ cal_shell_view);
+
+ g_signal_connect_swapped (
+ e_calendar_table_get_table (task_table), "selection-change",
+ G_CALLBACK (e_cal_shell_view_taskpad_actions_update),
+ cal_shell_view);
+
+ e_categories_register_change_listener (
+ G_CALLBACK (e_cal_shell_view_update_search_filter),
+ cal_shell_view);
+
+ /* Listen for configuration changes. */
+
+ /* Timezone */
+ id = calendar_config_add_notification_timezone (
+ cal_shell_view_config_timezone_changed_cb, cal_shell_view);
+ priv->notifications = g_list_prepend (
+ priv->notifications, GUINT_TO_POINTER (id));
+
+ e_cal_shell_view_actions_init (cal_shell_view);
+ e_cal_shell_view_update_sidebar (cal_shell_view);
+ e_cal_shell_view_update_search_filter (cal_shell_view);
+ e_cal_shell_view_update_timezone (cal_shell_view);
+}
+
+void
+e_cal_shell_view_private_dispose (ECalShellView *cal_shell_view)
+{
+ ECalShellViewPrivate *priv = cal_shell_view->priv;
+ GList *iter;
+
+ DISPOSE (priv->cal_shell_backend);
+ DISPOSE (priv->cal_shell_content);
+ DISPOSE (priv->cal_shell_sidebar);
+
+ if (priv->calendar_activity != NULL) {
+ /* XXX Activity is not cancellable. */
+ e_activity_complete (priv->calendar_activity);
+ g_object_unref (priv->calendar_activity);
+ priv->calendar_activity = NULL;
+ }
+
+ if (priv->memopad_activity != NULL) {
+ /* XXX Activity is not cancellable. */
+ e_activity_complete (priv->memopad_activity);
+ g_object_unref (priv->memopad_activity);
+ priv->memopad_activity = NULL;
+ }
+
+ if (priv->taskpad_activity != NULL) {
+ /* XXX Activity is not cancellable. */
+ e_activity_complete (priv->taskpad_activity);
+ g_object_unref (priv->taskpad_activity);
+ priv->taskpad_activity = NULL;
+ }
+
+ for (iter = priv->notifications; iter != NULL; iter = iter->next) {
+ guint notification_id = GPOINTER_TO_UINT (iter->data);
+ calendar_config_remove_notification (notification_id);
+ }
+ g_list_free (priv->notifications);
+ priv->notifications = NULL;
+}
+
+void
+e_cal_shell_view_private_finalize (ECalShellView *cal_shell_view)
+{
+ /* XXX Nothing to do? */
+}
+
+void
+e_cal_shell_view_execute_search (ECalShellView *cal_shell_view)
+{
+ /* FIXME */
+}
+
+void
+e_cal_shell_view_open_event (ECalShellView *cal_shell_view,
+ ECalModelComponent *comp_data)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ icalproperty *prop;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view));
+ g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data));
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ uid = icalcomponent_get_uid (comp_data->icalcomp);
+ editor = comp_editor_find_instance (uid);
+
+ if (editor != NULL)
+ goto exit;
+
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_component_set_icalcomponent (comp, clone);
+
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_ATTENDEE_PROPERTY);
+ if (prop != NULL)
+ flags |= COMP_EDITOR_MEETING;
+
+ if (itip_organizer_is_user (comp, comp_data->client))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ if (itip_sentby_is_user (comp, comp_data->client))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ if (!e_cal_component_has_attendees (comp))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ editor = event_editor_new (comp_data->client, shell, flags);
+ comp_editor_edit_comp (editor, comp);
+
+ g_object_ref (comp);
+
+exit:
+ gtk_window_present (GTK_WINDOW (editor));
+}
+
+void
+e_cal_shell_view_set_status_message (ECalShellView *cal_shell_view,
+ const gchar *status_message,
+ gdouble percent)
+{
+ EActivity *activity;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+
+ g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view));
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+
+ activity = cal_shell_view->priv->calendar_activity;
+
+ if (status_message == NULL || *status_message == '\0') {
+ if (activity != NULL) {
+ e_activity_complete (activity);
+ g_object_unref (activity);
+ activity = NULL;
+ }
+
+ } else if (activity == NULL) {
+ activity = e_activity_new (status_message);
+ e_activity_set_percent (activity, percent);
+ e_shell_backend_add_activity (shell_backend, activity);
+
+ } else {
+ e_activity_set_percent (activity, percent);
+ e_activity_set_primary_text (activity, status_message);
+ }
+
+ cal_shell_view->priv->calendar_activity = activity;
+}
+
+void
+e_cal_shell_view_update_sidebar (ECalShellView *cal_shell_view)
+{
+#if 0 /* KILL-BONOBO */
+ EShellView *shell_view;
+ EShellSidebar *shell_sidebar;
+ GnomeCalendar *calendar;
+ GnomeCalendarViewType view;
+ time_t start_time, end_time;
+ struct tm start_tm, end_tm;
+ struct icaltimetype start_tt, end_tt;
+ icaltimezone *timezone;
+ gchar buffer[512];
+ gchar end_buffer[512];
+
+ g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view));
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+
+ calendar = e_cal_shell_view_get_calendar (cal_shell_view);
+
+ gnome_calendar_get_visible_time_range (
+ calendar, &start_time, &end_time);
+ timezone = gnome_calendar_get_timezone (calendar);
+ view = gnome_calendar_get_view (calendar);
+
+ start_tt = icaltime_from_timet_with_zone (start_time, FALSE, timezone);
+ start_tm.tm_year = start_tt.year - 1900;
+ start_tm.tm_mon = start_tt.month - 1;
+ start_tm.tm_mday = start_tt.day;
+ start_tm.tm_hour = start_tt.hour;
+ start_tm.tm_min = start_tt.minute;
+ start_tm.tm_sec = start_tt.second;
+ start_tm.tm_isdst = -1;
+ start_tm.tm_wday = time_day_of_week (
+ start_tt.day, start_tt.month - 1, start_tt.year);
+
+ /* Subtract one from end_time so we don't get an extra day. */
+ end_tt = icaltime_from_timet_with_zone (end_time - 1, FALSE, timezone);
+ end_tm.tm_year = end_tt.year - 1900;
+ end_tm.tm_mon = end_tt.month - 1;
+ end_tm.tm_mday = end_tt.day;
+ end_tm.tm_hour = end_tt.hour;
+ end_tm.tm_min = end_tt.minute;
+ end_tm.tm_sec = end_tt.second;
+ end_tm.tm_isdst = -1;
+ end_tm.tm_wday = time_day_of_week (
+ end_tt.day, end_tt.month - 1, end_tt.year);
+
+ switch (view) {
+ case GNOME_CAL_DAY_VIEW:
+ case GNOME_CAL_WORK_WEEK_VIEW:
+ case GNOME_CAL_WEEK_VIEW:
+ if (start_tm.tm_year == end_tm.tm_year &&
+ start_tm.tm_mon == end_tm.tm_mon &&
+ start_tm.tm_mday == end_tm.tm_mday) {
+ e_utf8_strftime (
+ buffer, sizeof (buffer),
+ _("%A %d %b %Y"), &start_tm);
+ } else if (start_tm.tm_year == end_tm.tm_year) {
+ e_utf8_strftime (
+ buffer, sizeof (buffer),
+ _("%a %d %b"), &start_tm);
+ e_utf8_strftime (
+ end_buffer, sizeof (end_buffer),
+ _("%a %d %b %Y"), &end_tm);
+ strcat (buffer, " - ");
+ strcat (buffer, end_buffer);
+ } else {
+ e_utf8_strftime (
+ buffer, sizeof (buffer),
+ _("%a %d %b %Y"), &start_tm);
+ e_utf8_strftime (
+ end_buffer, sizeof (end_buffer),
+ _("%a %d %b %Y"), &end_tm);
+ strcat (buffer, " - ");
+ strcat (buffer, end_buffer);
+ }
+ break;
+
+ case GNOME_CAL_MONTH_VIEW:
+ case GNOME_CAL_LIST_VIEW:
+ if (start_tm.tm_year == end_tm.tm_year) {
+ if (start_tm.tm_mon == end_tm.tm_mon) {
+ e_utf8_strftime (
+ buffer,
+ sizeof (buffer),
+ "%d", &start_tm);
+ e_utf8_strftime (
+ end_buffer,
+ sizeof (end_buffer),
+ _("%d %b %Y"), &end_tm);
+ strcat (buffer, " - ");
+ strcat (buffer, end_buffer);
+ } else {
+ e_utf8_strftime (
+ buffer,
+ sizeof (buffer),
+ _("%d %b"), &start_tm);
+ e_utf8_strftime (
+ end_buffer,
+ sizeof (end_buffer),
+ _("%d %b %Y"), &end_tm);
+ strcat (buffer, " - ");
+ strcat (buffer, end_buffer);
+ }
+ } else {
+ e_utf8_strftime (
+ buffer, sizeof (buffer),
+ _("%d %b %Y"), &start_tm);
+ e_utf8_strftime (
+ end_buffer, sizeof (end_buffer),
+ _("%d %b %Y"), &end_tm);
+ strcat (buffer, " - ");
+ strcat (buffer, end_buffer);
+ }
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ e_shell_sidebar_set_secondary_text (shell_sidebar, buffer);
+#endif
+}
+
+void
+e_cal_shell_view_update_timezone (ECalShellView *cal_shell_view)
+{
+#if 0
+ ECalShellContent *cal_shell_content;
+ ECalShellSidebar *cal_shell_sidebar;
+ GnomeCalendarViewType view_type;
+ ECalendarView *calendar_view;
+ icaltimezone *timezone;
+ GList *clients, *iter;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ view_type = e_cal_shell_content_get_current_view (cal_shell_content);
+ calendar_view = e_cal_shell_content_get_calendar_view (
+ cal_shell_content, view_type);
+
+ cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar;
+ clients = e_cal_shell_sidebar_get_clients (cal_shell_sidebar);
+
+ timezone = calendar_config_get_icaltimezone ();
+
+ for (iter = clients; iter != NULL; iter = iter->next) {
+ ECal *client = iter->data;
+
+ if (e_cal_get_load_state (client) == E_CAL_LOAD_LOADED)
+ e_cal_set_default_timezone (client, timezone, NULL);
+ }
+
+ e_calendar_view_set_icaltimezone (calendar_view, timezone);
+
+ g_list_free (clients);
+#endif
+}
diff --git a/modules/calendar/e-cal-shell-view-private.h b/modules/calendar/e-cal-shell-view-private.h
new file mode 100644
index 0000000000..8308e0bf7f
--- /dev/null
+++ b/modules/calendar/e-cal-shell-view-private.h
@@ -0,0 +1,170 @@
+/*
+ * e-cal-shell-view-private.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_CAL_SHELL_VIEW_PRIVATE_H
+#define E_CAL_SHELL_VIEW_PRIVATE_H
+
+#include "e-cal-shell-view.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libecal/e-cal-time-util.h>
+#include <libedataserver/e-categories.h>
+#include <libedataserver/e-data-server-util.h>
+
+#include "e-util/e-util.h"
+#include "e-util/e-dialog-utils.h"
+#include "widgets/misc/e-popup-action.h"
+
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/comp-util.h"
+#include "calendar/gui/e-cal-list-view.h"
+#include "calendar/gui/e-cal-model-tasks.h"
+#include "calendar/gui/e-calendar-view.h"
+#include "calendar/gui/gnome-cal.h"
+/*#include "calendar/gui/goto.h"*/
+#include "calendar/gui/print.h"
+#include "calendar/gui/dialogs/copy-source-dialog.h"
+#include "calendar/gui/dialogs/event-editor.h"
+#include "calendar/gui/dialogs/memo-editor.h"
+#include "calendar/gui/dialogs/task-editor.h"
+
+#include "e-cal-shell-backend.h"
+#include "e-cal-shell-content.h"
+#include "e-cal-shell-sidebar.h"
+#include "e-cal-shell-view-actions.h"
+
+#define E_CAL_SHELL_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_CAL_SHELL_VIEW, ECalShellViewPrivate))
+
+/* Shorthand, requires a variable named "shell_window". */
+#define ACTION(name) \
+ (E_SHELL_WINDOW_ACTION_##name (shell_window))
+#define ACTION_GROUP(name) \
+ (E_SHELL_WINDOW_ACTION_GROUP_##name (shell_window))
+
+/* For use in dispose() methods. */
+#define DISPOSE(obj) \
+ G_STMT_START { \
+ if ((obj) != NULL) { g_object_unref (obj); (obj) = NULL; } \
+ } G_STMT_END
+
+/* ETable Specifications */
+#define ETSPEC_FILENAME "e-calendar-table.etspec"
+
+G_BEGIN_DECLS
+
+/* Filter items are displayed in ascending order.
+ * Non-negative values are reserved for categories. */
+enum {
+ CALENDAR_FILTER_ANY_CATEGORY = -4,
+ CALENDAR_FILTER_UNMATCHED = -3,
+ CALENDAR_FILTER_ACTIVE_APPOINTMENTS = -2,
+ CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS = -1
+};
+
+/* Search items are displayed in ascending order. */
+enum {
+ CALENDAR_SEARCH_SUMMARY_CONTAINS,
+ CALENDAR_SEARCH_DESCRIPTION_CONTAINS,
+ CALENDAR_SEARCH_ANY_FIELD_CONTAINS
+};
+
+struct _ECalShellViewPrivate {
+
+ /* These are just for convenience. */
+ ECalShellBackend *cal_shell_backend;
+ ECalShellContent *cal_shell_content;
+ ECalShellSidebar *cal_shell_sidebar;
+
+ /* The last time explicitly selected by the user. */
+ time_t base_view_time;
+
+ EActivity *calendar_activity;
+ EActivity *memopad_activity;
+ EActivity *taskpad_activity;
+
+ /* GConf notification IDs */
+ GList *notifications;
+};
+
+void e_cal_shell_view_private_init
+ (ECalShellView *cal_shell_view,
+ EShellViewClass *shell_view_class);
+void e_cal_shell_view_private_constructed
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_private_dispose
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_private_finalize
+ (ECalShellView *cal_shell_view);
+
+/* Private Utilities */
+
+void e_cal_shell_view_actions_init
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_execute_search
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_open_event
+ (ECalShellView *cal_shell_view,
+ ECalModelComponent *comp_data);
+void e_cal_shell_view_set_status_message
+ (ECalShellView *cal_shell_view,
+ const gchar *status_message,
+ gdouble percent);
+void e_cal_shell_view_update_sidebar
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_update_search_filter
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_update_timezone
+ (ECalShellView *cal_shell_view);
+
+/* Memo Pad Utilities */
+
+void e_cal_shell_view_memopad_actions_init
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_memopad_actions_update
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_memopad_open_memo
+ (ECalShellView *cal_shell_view,
+ ECalModelComponent *comp_data);
+void e_cal_shell_view_memopad_set_status_message
+ (ECalShellView *cal_shell_view,
+ const gchar *status_message,
+ gdouble percent);
+
+/* Task Pad Utilities */
+
+void e_cal_shell_view_taskpad_actions_init
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_taskpad_actions_update
+ (ECalShellView *cal_shell_view);
+void e_cal_shell_view_taskpad_open_task
+ (ECalShellView *cal_shell_view,
+ ECalModelComponent *comp_data);
+void e_cal_shell_view_taskpad_set_status_message
+ (ECalShellView *cal_shell_view,
+ const gchar *status_message,
+ gdouble percent);
+
+G_END_DECLS
+
+#endif /* E_CAL_SHELL_VIEW_PRIVATE_H */
diff --git a/modules/calendar/e-cal-shell-view-taskpad.c b/modules/calendar/e-cal-shell-view-taskpad.c
new file mode 100644
index 0000000000..bdd136b6f7
--- /dev/null
+++ b/modules/calendar/e-cal-shell-view-taskpad.c
@@ -0,0 +1,654 @@
+/*
+ * e-cal-shell-view-taskpad.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-view-private.h"
+
+/* Much of this file is based on e-task-shell-view-actions.c. */
+
+static void
+action_calendar_taskpad_assign_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only open the first selected task. */
+ e_cal_shell_view_taskpad_open_task (cal_shell_view, comp_data);
+
+ /* FIXME Need to actually assign the task. */
+}
+
+static void
+action_calendar_taskpad_clipboard_copy_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ e_calendar_table_copy_clipboard (task_table);
+}
+
+static void
+action_calendar_taskpad_clipboard_cut_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ e_calendar_table_cut_clipboard (task_table);
+}
+
+static void
+action_calendar_taskpad_clipboard_paste_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ e_calendar_table_paste_clipboard (task_table);
+}
+
+static void
+action_calendar_taskpad_delete_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ e_cal_shell_view_taskpad_set_status_message (
+ cal_shell_view, _("Deleting selected tasks..."), -1.0);
+ e_calendar_table_delete_selected (task_table);
+ e_cal_shell_view_taskpad_set_status_message (
+ cal_shell_view, NULL, -1.0);
+}
+
+static void
+action_calendar_taskpad_forward_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ GSList *list;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only forward the first selected task. */
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_component_set_icalcomponent (comp, clone);
+ itip_send_comp (
+ E_CAL_COMPONENT_METHOD_PUBLISH, comp,
+ comp_data->client, NULL, NULL, NULL, TRUE);
+ g_object_unref (comp);
+}
+
+static void
+action_calendar_taskpad_mark_complete_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+ ECalModel *model;
+ GSList *list, *iter;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+ list = e_calendar_table_get_selected (task_table);
+ model = e_calendar_table_get_model (task_table);
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ECalModelComponent *comp_data = iter->data;
+ e_cal_model_tasks_mark_comp_complete (
+ E_CAL_MODEL_TASKS (model), comp_data);
+ }
+
+ g_slist_free (list);
+}
+
+static void
+action_calendar_taskpad_mark_incomplete_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+ ECalModel *model;
+ GSList *list, *iter;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+ list = e_calendar_table_get_selected (task_table);
+ model = e_calendar_table_get_model (task_table);
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ECalModelComponent *comp_data = iter->data;
+ e_cal_model_tasks_mark_comp_incomplete (
+ E_CAL_MODEL_TASKS (model), comp_data);
+ }
+
+ g_slist_free (list);
+}
+
+static void
+action_calendar_taskpad_new_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ ECal *client;
+ ECalComponent *comp;
+ CompEditor *editor;
+ GSList *list;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ client = comp_data->client;
+ editor = task_editor_new (client, shell, COMP_EDITOR_NEW_ITEM);
+ comp = cal_comp_task_new_with_defaults (client);
+ comp_editor_edit_comp (editor, comp);
+
+ gtk_window_present (GTK_WINDOW (editor));
+
+ g_object_unref (comp);
+ g_object_unref (client);
+}
+
+static void
+action_calendar_taskpad_open_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only open the first selected task. */
+ e_cal_shell_view_taskpad_open_task (cal_shell_view, comp_data);
+}
+
+static void
+action_calendar_taskpad_open_url_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ icalproperty *prop;
+ const gchar *uri;
+ GSList *list;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+
+ /* XXX We only open the URI of the first selected task. */
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_URL_PROPERTY);
+ g_return_if_fail (prop == NULL);
+
+ uri = icalproperty_get_url (prop);
+ e_show_uri (GTK_WINDOW (shell_window), uri);
+}
+
+static void
+action_calendar_taskpad_print_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ GtkPrintOperationAction print_action;
+ GSList *list;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only print the first selected task. */
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
+ e_cal_component_set_icalcomponent (comp, clone);
+ print_comp (comp, comp_data->client, print_action);
+ g_object_unref (comp);
+}
+
+static void
+action_calendar_taskpad_save_as_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+ gchar *filename;
+ gchar *string;
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ filename = e_file_dialog_save (_("Save as..."), NULL);
+ if (filename == NULL)
+ return;
+
+ string = e_cal_get_component_as_string (
+ comp_data->client, comp_data->icalcomp);
+ if (string == NULL) {
+ g_warning ("Could not convert task to a string");
+ return;
+ }
+
+ e_write_file_uri (filename, string);
+
+ g_free (filename);
+ g_free (string);
+}
+
+static GtkActionEntry calendar_taskpad_entries[] = {
+
+ { "calendar-taskpad-assign",
+ NULL,
+ N_("_Assign Task"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_taskpad_assign_cb) },
+
+ { "calendar-taskpad-clipboard-copy",
+ GTK_STOCK_COPY,
+ NULL,
+ NULL,
+ N_("Copy selected tasks"),
+ G_CALLBACK (action_calendar_taskpad_clipboard_copy_cb) },
+
+ { "calendar-taskpad-clipboard-cut",
+ GTK_STOCK_CUT,
+ NULL,
+ NULL,
+ N_("Cut selected tasks"),
+ G_CALLBACK (action_calendar_taskpad_clipboard_cut_cb) },
+
+ { "calendar-taskpad-clipboard-paste",
+ GTK_STOCK_PASTE,
+ NULL,
+ NULL,
+ N_("Paste tasks from the clipboard"),
+ G_CALLBACK (action_calendar_taskpad_clipboard_paste_cb) },
+
+ { "calendar-taskpad-delete",
+ GTK_STOCK_DELETE,
+ N_("_Delete Task"),
+ NULL,
+ N_("Delete selected tasks"),
+ G_CALLBACK (action_calendar_taskpad_delete_cb) },
+
+ { "calendar-taskpad-forward",
+ "mail-forward",
+ N_("_Forward as iCalendar..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_taskpad_forward_cb) },
+
+ { "calendar-taskpad-mark-complete",
+ NULL,
+ N_("_Mark as Complete"),
+ NULL,
+ N_("Mark selected tasks as complete"),
+ G_CALLBACK (action_calendar_taskpad_mark_complete_cb) },
+
+ { "calendar-taskpad-mark-incomplete",
+ NULL,
+ N_("_Mar_k as Incomplete"),
+ NULL,
+ N_("Mark selected tasks as incomplete"),
+ G_CALLBACK (action_calendar_taskpad_mark_incomplete_cb) },
+
+ { "calendar-taskpad-new",
+ "stock_task",
+ N_("New _Task"),
+ NULL,
+ N_("Create a new task"),
+ G_CALLBACK (action_calendar_taskpad_new_cb) },
+
+ { "calendar-taskpad-open",
+ GTK_STOCK_OPEN,
+ N_("_Open Task"),
+ NULL,
+ N_("View the selected task"),
+ G_CALLBACK (action_calendar_taskpad_open_cb) },
+
+ { "calendar-taskpad-open-url",
+ "applications-internet",
+ N_("Open _Web Page"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_taskpad_open_url_cb) },
+
+ { "calendar-taskpad-save-as",
+ GTK_STOCK_SAVE_AS,
+ N_("_Save as iCalendar..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_calendar_taskpad_save_as_cb) }
+};
+
+static GtkActionEntry lockdown_printing_entries[] = {
+
+ { "calendar-taskpad-print",
+ GTK_STOCK_PRINT,
+ NULL,
+ NULL,
+ N_("Print the selected task"),
+ G_CALLBACK (action_calendar_taskpad_print_cb) }
+};
+
+void
+e_cal_shell_view_taskpad_actions_init (ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ GtkActionGroup *action_group;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ /* Calendar Actions */
+ action_group = ACTION_GROUP (CALENDAR);
+ gtk_action_group_add_actions (
+ action_group, calendar_taskpad_entries,
+ G_N_ELEMENTS (calendar_taskpad_entries), cal_shell_view);
+
+ /* Lockdown Printing Actions */
+ action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
+ gtk_action_group_add_actions (
+ action_group, lockdown_printing_entries,
+ G_N_ELEMENTS (lockdown_printing_entries), cal_shell_view);
+}
+
+void
+e_cal_shell_view_taskpad_actions_update (ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ ECalendarTable *task_table;
+ ETable *table;
+ GtkAction *action;
+ GSList *list, *iter;
+ const gchar *label;
+ gboolean assignable = TRUE;
+ gboolean editable = TRUE;
+ gboolean has_url = FALSE;
+ gboolean sensitive;
+ gint n_selected;
+ gint n_complete = 0;
+ gint n_incomplete = 0;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ task_table = e_cal_shell_content_get_task_table (cal_shell_content);
+
+ table = e_calendar_table_get_table (task_table);
+ n_selected = e_table_selected_count (table);
+
+ list = e_calendar_table_get_selected (task_table);
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ECalModelComponent *comp_data = iter->data;
+ icalproperty *prop;
+ const gchar *cap;
+ gboolean read_only;
+
+ e_cal_is_read_only (comp_data->client, &read_only, NULL);
+ editable &= !read_only;
+
+ cap = CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT;
+ if (e_cal_get_static_capability (comp_data->client, cap))
+ assignable = FALSE;
+
+ cap = CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK;
+ if (e_cal_get_static_capability (comp_data->client, cap))
+ assignable = FALSE;
+
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_URL_PROPERTY);
+ has_url |= (prop != NULL);
+
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_COMPLETED_PROPERTY);
+ if (prop != NULL)
+ n_complete++;
+ else
+ n_incomplete++;
+ }
+ g_slist_free (list);
+
+ action = ACTION (CALENDAR_TASKPAD_ASSIGN);
+ sensitive = (n_selected == 1) && editable && assignable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_CLIPBOARD_COPY);
+ sensitive = (n_selected > 0);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_CLIPBOARD_CUT);
+ sensitive = (n_selected > 0) && editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_CLIPBOARD_PASTE);
+ sensitive = editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_DELETE);
+ sensitive = (n_selected > 0) && editable;
+ gtk_action_set_sensitive (action, sensitive);
+ label = ngettext ("Delete Task", "Delete Tasks", n_selected);
+ g_object_set (action, "label", label, NULL);
+
+ action = ACTION (CALENDAR_TASKPAD_FORWARD);
+ sensitive = (n_selected == 1);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_MARK_COMPLETE);
+ sensitive = (n_selected > 0) && editable && (n_incomplete > 0);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_MARK_INCOMPLETE);
+ sensitive = (n_selected > 0) && editable && (n_complete > 0);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_OPEN);
+ sensitive = (n_selected == 1);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_OPEN_URL);
+ sensitive = (n_selected == 1) && has_url;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_PRINT);
+ sensitive = (n_selected == 1);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_TASKPAD_SAVE_AS);
+ sensitive = (n_selected == 1);
+ gtk_action_set_sensitive (action, sensitive);
+}
+
+void
+e_cal_shell_view_taskpad_open_task (ECalShellView *cal_shell_view,
+ ECalModelComponent *comp_data)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ icalproperty *prop;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view));
+ g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data));
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ uid = icalcomponent_get_uid (comp_data->icalcomp);
+ editor = comp_editor_find_instance (uid);
+
+ if (editor != NULL)
+ goto exit;
+
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_component_set_icalcomponent (comp, clone);
+
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_ATTENDEE_PROPERTY);
+ if (prop != NULL)
+ flags |= COMP_EDITOR_IS_ASSIGNED;
+
+ if (itip_organizer_is_user (comp, comp_data->client))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ if (!e_cal_component_has_attendees (comp))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ editor = task_editor_new (comp_data->client, shell, flags);
+ comp_editor_edit_comp (editor, comp);
+
+ g_object_ref (comp);
+
+ if (flags & COMP_EDITOR_IS_ASSIGNED)
+ task_editor_show_assignment (TASK_EDITOR (editor));
+
+exit:
+ gtk_window_present (GTK_WINDOW (editor));
+}
+
+void
+e_cal_shell_view_taskpad_set_status_message (ECalShellView *cal_shell_view,
+ const gchar *status_message,
+ gdouble percent)
+{
+ EActivity *activity;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+
+ g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view));
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+
+ activity = cal_shell_view->priv->taskpad_activity;
+
+ if (status_message == NULL || *status_message == '\0') {
+ if (activity != NULL) {
+ e_activity_complete (activity);
+ g_object_unref (activity);
+ activity = NULL;
+ }
+
+ } else if (activity == NULL) {
+ activity = e_activity_new (status_message);
+ e_activity_set_percent (activity, percent);
+ e_shell_backend_add_activity (shell_backend, activity);
+
+ } else {
+ e_activity_set_percent (activity, percent);
+ e_activity_set_primary_text (activity, status_message);
+ }
+
+ cal_shell_view->priv->taskpad_activity = activity;
+}
diff --git a/modules/calendar/e-cal-shell-view.c b/modules/calendar/e-cal-shell-view.c
new file mode 100644
index 0000000000..5bf1c749a1
--- /dev/null
+++ b/modules/calendar/e-cal-shell-view.c
@@ -0,0 +1,226 @@
+/*
+ * e-cal-shell-view.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-view-private.h"
+
+static gpointer parent_class;
+static GType cal_shell_view_type;
+
+static void
+cal_shell_view_dispose (GObject *object)
+{
+ e_cal_shell_view_private_dispose (E_CAL_SHELL_VIEW (object));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+cal_shell_view_finalize (GObject *object)
+{
+ e_cal_shell_view_private_finalize (E_CAL_SHELL_VIEW (object));
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+cal_shell_view_constructed (GObject *object)
+{
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ e_cal_shell_view_private_constructed (E_CAL_SHELL_VIEW (object));
+}
+
+static void
+cal_shell_view_update_actions (EShellView *shell_view)
+{
+#if 0
+ ECalShellViewPrivate *priv;
+ ECalShellContent *cal_shell_content;
+ ECalShellSidebar *cal_shell_sidebar;
+ EShellWindow *shell_window;
+ GnomeCalendar *calendar;
+ ECalModel *model;
+ ESourceSelector *selector;
+ ESource *source;
+ GtkAction *action;
+ GtkWidget *widget;
+ GList *list, *iter;
+ const gchar *uri = NULL;
+ gboolean user_created_source;
+ gboolean editable = TRUE;
+ gboolean recurring = FALSE;
+ gboolean sensitive;
+ gint n_selected;
+
+ priv = E_CAL_SHELL_VIEW_GET_PRIVATE (shell_view);
+
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ cal_shell_content = priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ widget = gnome_calendar_get_current_view_widget (calendar);
+ model = e_calendar_view_get_model (E_CALENDAR_VIEW (widget));
+
+ cal_shell_sidebar = priv->cal_shell_sidebar;
+ selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
+
+ list = e_calendar_view_get_selected_events (E_CALENDAR_VIEW (widget));
+ n_selected = g_list_length (list);
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ECalModelComponent *comp_data = iter->data;
+ gboolean read_only;
+
+ e_cal_is_read_only (comp_data->client, &read_only, NULL);
+ editable &= !read_only;
+
+ if (e_cal_util_component_has_recurrences (comp_data->icalcomp))
+ recurring |= TRUE;
+ else if (e_cal_util_component_is_instance (comp_data->icalcomp))
+ recurring |= TRUE;
+ }
+
+ source = e_source_selector_peek_primary_selection (selector);
+ if (source != NULL)
+ uri = e_source_peek_relative_uri (source);
+ user_created_source = (uri != NULL && strcmp (uri, "system") != 0);
+
+ action = ACTION (CALENDAR_COPY);
+ sensitive = (source != NULL);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_DELETE);
+ sensitive = user_created_source;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_PROPERTIES);
+ sensitive = (source != NULL);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (CALENDAR_RENAME);
+ sensitive = has_primary_source;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (EVENT_CLIPBOARD_COPY);
+ sensitive = (n_selected > 0);
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (EVENT_CLIPBOARD_CUT);
+ sensitive = (n_selected > 0) && editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (EVENT_CLIPBOARD_PASTE);
+ sensitive = editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (EVENT_DELETE);
+ sensitive = (n_selected > 0) && editable && !recurring;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (EVENT_DELETE_OCCURRENCE);
+ sensitive = (n_selected > 0) && editable && recurring;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (EVENT_DELETE_OCCURRENCE_ALL);
+ sensitive = (n_selected > 0) && editable && recurring;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (EVENT_OPEN);
+ sensitive = (n_selected == 1);
+ gtk_action_set_sensitive (action, sensitive);
+#endif
+}
+
+static void
+cal_shell_view_class_init (ECalShellViewClass *class,
+ GTypeModule *type_module)
+{
+ GObjectClass *object_class;
+ EShellViewClass *shell_view_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ECalShellViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = cal_shell_view_dispose;
+ object_class->finalize = cal_shell_view_finalize;
+ object_class->constructed = cal_shell_view_constructed;
+
+ shell_view_class = E_SHELL_VIEW_CLASS (class);
+ shell_view_class->label = _("Calendar");
+ shell_view_class->icon_name = "x-office-calendar";
+ shell_view_class->ui_definition = "evolution-calendars.ui";
+ shell_view_class->ui_manager_id = "org.gnome.evolution.calendars";
+ shell_view_class->search_options = "/calendar-search-options";
+ shell_view_class->search_rules = "caltypes.xml";
+ shell_view_class->new_shell_content = e_cal_shell_content_new;
+ shell_view_class->new_shell_sidebar = e_cal_shell_sidebar_new;
+ shell_view_class->update_actions = cal_shell_view_update_actions;
+}
+
+static void
+cal_shell_view_init (ECalShellView *cal_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ cal_shell_view->priv =
+ E_CAL_SHELL_VIEW_GET_PRIVATE (cal_shell_view);
+
+ e_cal_shell_view_private_init (cal_shell_view, shell_view_class);
+}
+
+GType
+e_cal_shell_view_get_type (void)
+{
+ return cal_shell_view_type;
+}
+
+void
+e_cal_shell_view_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (ECalShellViewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) cal_shell_view_class_init,
+ (GClassFinalizeFunc) NULL,
+ type_module,
+ sizeof (ECalShellView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) cal_shell_view_init,
+ NULL /* value_table */
+ };
+
+ cal_shell_view_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_VIEW,
+ "ECalShellView", &type_info, 0);
+}
+
+GnomeCalendar *
+e_cal_shell_view_get_calendar (ECalShellView *cal_shell_view)
+{
+ g_return_val_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view), NULL);
+
+ /* FIXME */
+ return NULL;
+}
diff --git a/modules/calendar/e-cal-shell-view.h b/modules/calendar/e-cal-shell-view.h
new file mode 100644
index 0000000000..67fa15220c
--- /dev/null
+++ b/modules/calendar/e-cal-shell-view.h
@@ -0,0 +1,69 @@
+/*
+ * e-cal-shell-view.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_CAL_SHELL_VIEW_H
+#define E_CAL_SHELL_VIEW_H
+
+#include <shell/e-shell-view.h>
+#include <calendar/gui/gnome-cal.h>
+#include <libedataserver/e-source-list.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CAL_SHELL_VIEW \
+ (e_cal_shell_view_get_type ())
+#define E_CAL_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_CAL_SHELL_VIEW, ECalShellView))
+#define E_CAL_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_CAL_SHELL_VIEW, ECalShellViewClass))
+#define E_IS_CAL_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_CAL_SHELL_VIEW))
+#define E_IS_CAL_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_CAL_SHELL_VIEW))
+#define E_CAL_SHELL_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_CAL_SHELL_VIEW, ECalShellViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECalShellView ECalShellView;
+typedef struct _ECalShellViewClass ECalShellViewClass;
+typedef struct _ECalShellViewPrivate ECalShellViewPrivate;
+
+struct _ECalShellView {
+ EShellView parent;
+ ECalShellViewPrivate *priv;
+};
+
+struct _ECalShellViewClass {
+ EShellViewClass parent_class;
+};
+
+GType e_cal_shell_view_get_type (void);
+void e_cal_shell_view_register_type (GTypeModule *type_module);
+GnomeCalendar * e_cal_shell_view_get_calendar (ECalShellView *cal_shell_view);
+
+G_END_DECLS
+
+#endif /* E_CAL_SHELL_VIEW_H */
diff --git a/modules/calendar/e-memo-shell-backend.c b/modules/calendar/e-memo-shell-backend.c
new file mode 100644
index 0000000000..d2734e066a
--- /dev/null
+++ b/modules/calendar/e-memo-shell-backend.c
@@ -0,0 +1,613 @@
+/*
+ * e-memo-shell-backend.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-memo-shell-backend.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libecal/e-cal.h>
+#include <libedataserver/e-url.h>
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-list.h>
+#include <libedataserver/e-source-group.h>
+
+#include "shell/e-shell.h"
+#include "shell/e-shell-backend.h"
+#include "shell/e-shell-window.h"
+
+#include "calendar/common/authentication.h"
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/comp-util.h"
+#include "calendar/gui/dialogs/calendar-setup.h"
+#include "calendar/gui/dialogs/memo-editor.h"
+
+#include "e-memo-shell-migrate.h"
+#include "e-memo-shell-view.h"
+
+#define E_MEMO_SHELL_BACKEND_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MEMO_SHELL_BACKEND, EMemoShellBackendPrivate))
+
+#define WEB_BASE_URI "webcal://"
+#define PERSONAL_RELATIVE_URI "system"
+
+struct _EMemoShellBackendPrivate {
+ ESourceList *source_list;
+};
+
+enum {
+ PROP_0,
+ PROP_SOURCE_LIST
+};
+
+static gpointer parent_class;
+static GType memo_shell_backend_type;
+
+static void
+memo_module_ensure_sources (EShellBackend *shell_backend)
+{
+ /* XXX This is basically the same algorithm across all modules.
+ * Maybe we could somehow integrate this into EShellBackend? */
+
+ EMemoShellBackendPrivate *priv;
+ ESourceGroup *on_this_computer;
+ ESourceGroup *on_the_web;
+ ESource *personal;
+ GSList *groups, *iter;
+ const gchar *data_dir;
+ const gchar *name;
+ gchar *base_uri;
+ gchar *filename;
+
+ on_this_computer = NULL;
+ on_the_web = NULL;
+ personal = NULL;
+
+ priv = E_MEMO_SHELL_BACKEND_GET_PRIVATE (shell_backend);
+
+ if (!e_cal_get_sources (&priv->source_list, E_CAL_SOURCE_TYPE_JOURNAL, NULL)) {
+ g_warning ("Could not get memo sources from GConf!");
+ return;
+ }
+
+ data_dir = e_shell_backend_get_data_dir (shell_backend);
+ filename = g_build_filename (data_dir, "local", NULL);
+ base_uri = g_filename_to_uri (filename, NULL, NULL);
+ g_free (filename);
+
+ groups = e_source_list_peek_groups (priv->source_list);
+ for (iter = groups; iter != NULL; iter = iter->next) {
+ ESourceGroup *source_group = iter->data;
+ const gchar *group_base_uri;
+
+ group_base_uri = e_source_group_peek_base_uri (source_group);
+
+ /* Compare only "file://" part. If the user's home
+ * changes, we do not want to create another group. */
+ if (on_this_computer == NULL &&
+ strncmp (base_uri, group_base_uri, 7) == 0)
+ on_this_computer = source_group;
+
+ else if (on_the_web == NULL &&
+ strcmp (WEB_BASE_URI, group_base_uri) == 0)
+ on_the_web = source_group;
+ }
+
+ name = _("On This Computer");
+
+ if (on_this_computer != NULL) {
+ GSList *sources;
+ const gchar *group_base_uri;
+
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (on_this_computer, name);
+
+ sources = e_source_group_peek_sources (on_this_computer);
+ group_base_uri = e_source_group_peek_base_uri (on_this_computer);
+
+ /* Make sure this group includes a "Personal" source. */
+ for (iter = sources; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+ const gchar *relative_uri;
+
+ relative_uri = e_source_peek_relative_uri (source);
+ if (relative_uri == NULL)
+ continue;
+
+ if (strcmp (PERSONAL_RELATIVE_URI, relative_uri) != 0)
+ continue;
+
+ personal = source;
+ break;
+ }
+
+ /* Make sure we have the correct base URI. This can
+ * change when the user's home directory changes. */
+ if (strcmp (base_uri, group_base_uri) != 0) {
+ e_source_group_set_base_uri (
+ on_this_computer, base_uri);
+
+ /* XXX We shouldn't need this sync call here as
+ * set_base_uri() results in synching to GConf,
+ * but that happens in an idle loop and too late
+ * to prevent the user from seeing a "Cannot
+ * Open ... because of invalid URI" error. */
+ e_source_list_sync (priv->source_list, NULL);
+ }
+
+ } else {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, base_uri);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+ }
+
+ name = _("Personal");
+
+ if (personal == NULL) {
+ ESource *source;
+ GSList *selected;
+ gchar *primary;
+
+ source = e_source_new (name, PERSONAL_RELATIVE_URI);
+ e_source_group_add_source (on_this_computer, source, -1);
+ g_object_unref (source);
+
+ primary = calendar_config_get_primary_memos ();
+ selected = calendar_config_get_memos_selected ();
+
+ if (primary == NULL && selected == NULL) {
+ const gchar *uid;
+
+ uid = e_source_peek_uid (source);
+ selected = g_slist_prepend (NULL, g_strdup (uid));
+
+ calendar_config_set_primary_memos (uid);
+ calendar_config_set_memos_selected (selected);
+ }
+
+ g_slist_foreach (selected, (GFunc) g_free, NULL);
+ g_slist_free (selected);
+ g_free (primary);
+ } else {
+ /* Force the source name to the current locale. */
+ e_source_set_name (personal, name);
+ }
+
+ name = _("On The Web");
+
+ if (on_the_web == NULL) {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, WEB_BASE_URI);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+ } else {
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (on_the_web, name);
+ }
+
+ g_free (base_uri);
+}
+
+static void
+memo_module_cal_opened_cb (ECal *cal,
+ ECalendarStatus status,
+ GtkAction *action)
+{
+ EShell *shell;
+ ECalComponent *comp;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ const gchar *action_name;
+
+ /* FIXME Pass this in. */
+ shell = e_shell_get_default ();
+
+ /* XXX Handle errors better. */
+ if (status != E_CALENDAR_STATUS_OK)
+ return;
+
+ action_name = gtk_action_get_name (action);
+
+ flags |= COMP_EDITOR_NEW_ITEM;
+ if (strcmp (action_name, "memo-shared-new") == 0) {
+ flags |= COMP_EDITOR_IS_SHARED;
+ flags |= COMP_EDITOR_USER_ORG;
+ }
+
+ editor = memo_editor_new (cal, shell, flags);
+ comp = cal_comp_memo_new_with_defaults (cal);
+ comp_editor_edit_comp (editor, comp);
+
+ gtk_window_present (GTK_WINDOW (editor));
+
+ g_object_unref (comp);
+ g_object_unref (cal);
+}
+
+static void
+action_memo_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ ECal *cal = NULL;
+ ECalSourceType source_type;
+ ESourceList *source_list;
+ gchar *uid;
+
+ /* This callback is used for both memos and shared memos. */
+
+ source_type = E_CAL_SOURCE_TYPE_JOURNAL;
+
+ if (!e_cal_get_sources (&source_list, source_type, NULL)) {
+ g_warning ("Could not get memo sources from GConf!");
+ return;
+ }
+
+ uid = calendar_config_get_primary_memos ();
+
+ if (uid != NULL) {
+ ESource *source;
+
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ if (source != NULL)
+ cal = auth_new_cal_from_source (source, source_type);
+ g_free (uid);
+ }
+
+ if (cal == NULL)
+ cal = auth_new_cal_from_default (source_type);
+
+ g_return_if_fail (cal != NULL);
+
+ g_signal_connect (
+ cal, "cal-opened",
+ G_CALLBACK (memo_module_cal_opened_cb), action);
+
+ e_cal_open_async (cal, FALSE);
+}
+
+static void
+action_memo_list_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ calendar_setup_new_memo_list (GTK_WINDOW (shell_window));
+}
+
+static GtkActionEntry item_entries[] = {
+
+ { "memo-new",
+ "stock_insert-note",
+ NC_("New", "Mem_o"),
+ "<Shift><Control>o",
+ N_("Create a new memo"),
+ G_CALLBACK (action_memo_new_cb) },
+
+ { "memo-shared-new",
+ "stock_insert-note",
+ N_("_Shared Memo"),
+ "<Shift><Control>h",
+ N_("Create a new shared memo"),
+ G_CALLBACK (action_memo_new_cb) }
+};
+
+static GtkActionEntry source_entries[] = {
+
+ { "memo-list-new",
+ "stock_notes",
+ NC_("New", "Memo Li_st"),
+ NULL,
+ N_("Create a new memo list"),
+ G_CALLBACK (action_memo_list_new_cb) }
+};
+
+static gboolean
+memo_module_handle_uri_cb (EShellBackend *shell_backend,
+ const gchar *uri)
+{
+ EShell *shell;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ ECal *client;
+ ECalComponent *comp;
+ ESource *source;
+ ESourceList *source_list;
+ ECalSourceType source_type;
+ EUri *euri;
+ icalcomponent *icalcomp;
+ const gchar *cp;
+ gchar *source_uid = NULL;
+ gchar *comp_uid = NULL;
+ gchar *comp_rid = NULL;
+ gboolean handled = FALSE;
+ GError *error = NULL;
+
+ source_type = E_CAL_SOURCE_TYPE_JOURNAL;
+ shell = e_shell_backend_get_shell (shell_backend);
+
+ if (strncmp (uri, "memo:", 5) != 0)
+ return FALSE;
+
+ euri = e_uri_new (uri);
+ cp = euri->query;
+ if (cp == NULL)
+ goto exit;
+
+ while (*cp != '\0') {
+ gchar *header;
+ gchar *content;
+ gsize header_len;
+ gsize content_len;
+
+ header_len = strcspn (cp, "=&");
+
+ /* If it's malformed, give up. */
+ if (cp[header_len] != '=')
+ break;
+
+ header = (gchar *) cp;
+ header[header_len] = '\0';
+ cp += header_len + 1;
+
+ content_len = strcspn (cp, "&");
+
+ content = g_strndup (cp, content_len);
+ if (g_ascii_strcasecmp (header, "source-uid") == 0)
+ source_uid = g_strdup (content);
+ else if (g_ascii_strcasecmp (header, "comp-uid") == 0)
+ comp_uid = g_strdup (content);
+ else if (g_ascii_strcasecmp (header, "comp-rid") == 0)
+ comp_rid = g_strdup (content);
+ g_free (content);
+
+ cp += content_len;
+ if (*cp == '&') {
+ cp++;
+ if (strcmp (cp, "amp;") == 0)
+ cp += 4;
+ }
+ }
+
+ if (source_uid == NULL || comp_uid == NULL)
+ goto exit;
+
+ /* URI is valid, so consider it handled. Whether
+ * we successfully open it is another matter... */
+ handled = TRUE;
+
+ if (!e_cal_get_sources (&source_list, source_type, NULL)) {
+ g_printerr ("Could not get memo sources from GConf!\n");
+ goto exit;
+ }
+
+ source = e_source_list_peek_source_by_uid (source_list, source_uid);
+ if (source == NULL) {
+ g_printerr ("No source for UID `%s'\n", source_uid);
+ g_object_unref (source_list);
+ goto exit;
+ }
+
+ client = auth_new_cal_from_source (source, source_type);
+ if (client == NULL || !e_cal_open (client, TRUE, &error)) {
+ g_printerr ("%s\n", error->message);
+ g_object_unref (source_list);
+ g_error_free (error);
+ goto exit;
+ }
+
+ /* XXX Copied from e_memo_shell_view_open_memo().
+ * Clearly a new utility function is needed. */
+
+ editor = comp_editor_find_instance (comp_uid);
+
+ if (editor != NULL)
+ goto present;
+
+ if (!e_cal_get_object (client, comp_uid, comp_rid, &icalcomp, &error)) {
+ g_printerr ("%s\n", error->message);
+ g_object_unref (source_list);
+ g_error_free (error);
+ goto exit;
+ }
+
+ comp = e_cal_component_new ();
+ e_cal_component_set_icalcomponent (comp, icalcomp);
+
+ if (e_cal_component_has_organizer (comp))
+ flags |= COMP_EDITOR_IS_SHARED;
+
+ if (itip_organizer_is_user (comp, client))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ editor = memo_editor_new (client, shell, flags);
+ comp_editor_edit_comp (editor, comp);
+
+ g_object_unref (comp);
+
+present:
+ gtk_window_present (GTK_WINDOW (editor));
+
+ g_object_unref (source_list);
+ g_object_unref (client);
+
+exit:
+ g_free (source_uid);
+ g_free (comp_uid);
+ g_free (comp_rid);
+
+ e_uri_free (euri);
+
+ return handled;
+}
+
+static void
+memo_module_window_created_cb (EShellBackend *shell_backend,
+ GtkWindow *window)
+{
+ const gchar *module_name;
+
+ if (!E_IS_SHELL_WINDOW (window))
+ return;
+
+ module_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name;
+
+ e_shell_window_register_new_item_actions (
+ E_SHELL_WINDOW (window), module_name,
+ item_entries, G_N_ELEMENTS (item_entries));
+
+ e_shell_window_register_new_source_actions (
+ E_SHELL_WINDOW (window), module_name,
+ source_entries, G_N_ELEMENTS (source_entries));
+}
+
+static void
+memo_shell_backend_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SOURCE_LIST:
+ g_value_set_object (
+ value,
+ e_memo_shell_backend_get_source_list (
+ E_MEMO_SHELL_BACKEND (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+memo_shell_backend_dispose (GObject *object)
+{
+ EMemoShellBackendPrivate *priv;
+
+ priv = E_MEMO_SHELL_BACKEND_GET_PRIVATE (object);
+
+ if (priv->source_list != NULL) {
+ g_object_unref (priv->source_list);
+ priv->source_list = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+memo_shell_backend_constructed (GObject *object)
+{
+ EShell *shell;
+ EShellBackend *shell_backend;
+
+ shell_backend = E_SHELL_BACKEND (object);
+ shell = e_shell_backend_get_shell (shell_backend);
+
+ memo_module_ensure_sources (shell_backend);
+
+ g_signal_connect_swapped (
+ shell, "handle-uri",
+ G_CALLBACK (memo_module_handle_uri_cb), shell_backend);
+
+ g_signal_connect_swapped (
+ shell, "window-created",
+ G_CALLBACK (memo_module_window_created_cb), shell_backend);
+}
+
+static void
+memo_shell_backend_class_init (EMemoShellBackendClass *class)
+{
+ GObjectClass *object_class;
+ EShellBackendClass *shell_backend_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMemoShellBackendPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = memo_shell_backend_get_property;
+ object_class->dispose = memo_shell_backend_dispose;
+ object_class->constructed = memo_shell_backend_constructed;
+
+ shell_backend_class = E_SHELL_BACKEND_CLASS (class);
+ shell_backend_class->shell_view_type = E_TYPE_MEMO_SHELL_VIEW;
+ shell_backend_class->name = "memos";
+ shell_backend_class->aliases = "";
+ shell_backend_class->schemes = "memo";
+ shell_backend_class->sort_order = 500;
+ shell_backend_class->start = NULL;
+ shell_backend_class->is_busy = NULL;
+ shell_backend_class->shutdown = NULL;
+ shell_backend_class->migrate = e_memo_shell_backend_migrate;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SOURCE_LIST,
+ g_param_spec_object (
+ "source-list",
+ _("Source List"),
+ _("The registry of memo lists"),
+ E_TYPE_SOURCE_LIST,
+ G_PARAM_READABLE));
+}
+
+static void
+memo_shell_backend_init (EMemoShellBackend *memo_shell_backend)
+{
+ memo_shell_backend->priv =
+ E_MEMO_SHELL_BACKEND_GET_PRIVATE (memo_shell_backend);
+}
+
+GType
+e_memo_shell_backend_get_type (void)
+{
+ return memo_shell_backend_type;
+}
+
+void
+e_memo_shell_backend_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (EMemoShellBackendClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) memo_shell_backend_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMemoShellBackend),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) memo_shell_backend_init,
+ NULL /* value_table */
+ };
+
+ memo_shell_backend_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_BACKEND,
+ "EMemoShellBackend", &type_info, 0);
+}
+
+ESourceList *
+e_memo_shell_backend_get_source_list (EMemoShellBackend *memo_shell_backend)
+{
+ g_return_val_if_fail (
+ E_IS_MEMO_SHELL_BACKEND (memo_shell_backend), NULL);
+
+ return memo_shell_backend->priv->source_list;
+}
diff --git a/modules/calendar/e-memo-shell-backend.h b/modules/calendar/e-memo-shell-backend.h
new file mode 100644
index 0000000000..37fe41a784
--- /dev/null
+++ b/modules/calendar/e-memo-shell-backend.h
@@ -0,0 +1,70 @@
+/*
+ * e-memo-shell-backend.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MEMO_SHELL_BACKEND_H
+#define E_MEMO_SHELL_BACKEND_H
+
+#include <shell/e-shell-backend.h>
+#include <libedataserver/e-source-list.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MEMO_SHELL_BACKEND \
+ (e_memo_shell_backend_get_type ())
+#define E_MEMO_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MEMO_SHELL_BACKEND, EMemoShellBackend))
+#define E_MEMO_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MEMO_SHELL_BACKEND, EMemoShellBackendClass))
+#define E_IS_MEMO_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MEMO_SHELL_BACKEND))
+#define E_IS_MEMO_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MEMO_SHELL_BACKEND))
+#define E_MEMO_SHELL_BACKEND_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MEMO_SHELL_BACKEND, EMemoShellBackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMemoShellBackend EMemoShellBackend;
+typedef struct _EMemoShellBackendClass EMemoShellBackendClass;
+typedef struct _EMemoShellBackendPrivate EMemoShellBackendPrivate;
+
+struct _EMemoShellBackend {
+ EShellBackend parent;
+ EMemoShellBackendPrivate *priv;
+};
+
+struct _EMemoShellBackendClass {
+ EShellBackendClass parent_class;
+};
+
+GType e_memo_shell_backend_get_type (void);
+void e_memo_shell_backend_register_type
+ (GTypeModule *type_module);
+ESourceList * e_memo_shell_backend_get_source_list
+ (EMemoShellBackend *memo_shell_backend);
+
+G_END_DECLS
+
+#endif /* E_MEMO_SHELL_BACKEND_H */
diff --git a/modules/calendar/e-memo-shell-content.c b/modules/calendar/e-memo-shell-content.c
new file mode 100644
index 0000000000..ce020158ae
--- /dev/null
+++ b/modules/calendar/e-memo-shell-content.c
@@ -0,0 +1,676 @@
+/*
+ * e-memo-shell-content.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-memo-shell-content.h"
+
+#include <glib/gi18n.h>
+
+#include "e-util/gconf-bridge.h"
+
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/comp-util.h"
+#include "calendar/gui/e-cal-model-memos.h"
+#include "calendar/gui/e-memo-table.h"
+#include "calendar/gui/e-memo-table-config.h"
+
+#include "widgets/menus/gal-view-etable.h"
+
+#define E_MEMO_SHELL_CONTENT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MEMO_SHELL_CONTENT, EMemoShellContentPrivate))
+
+#define E_MEMO_TABLE_DEFAULT_STATE \
+ "<?xml version=\"1.0\"?>" \
+ "<ETableState>" \
+ " <column source=\"1\"/>" \
+ " <column source=\"0\"/>" \
+ " <column source=\"2\"/>" \
+ " <grouping/>" \
+ "</ETableState>"
+
+struct _EMemoShellContentPrivate {
+ GtkWidget *paned;
+ GtkWidget *memo_table;
+ GtkWidget *memo_preview;
+
+ ECalModel *memo_model;
+ EMemoTableConfig *table_config;
+ GalViewInstance *view_instance;
+
+ gchar *current_uid;
+};
+
+enum {
+ PROP_0,
+ PROP_MODEL,
+ PROP_PREVIEW_VISIBLE
+};
+
+enum {
+ TARGET_VCALENDAR
+};
+
+static GtkTargetEntry drag_types[] = {
+ { (gchar *) "text/calendar", 0, TARGET_VCALENDAR },
+ { (gchar *) "text/x-calendar", 0, TARGET_VCALENDAR }
+};
+
+static gpointer parent_class;
+static GType memo_shell_content_type;
+
+static void
+memo_shell_content_display_view_cb (EMemoShellContent *memo_shell_content,
+ GalView *gal_view)
+{
+ EMemoTable *memo_table;
+ ETable *table;
+
+ if (!GAL_IS_VIEW_ETABLE (gal_view))
+ return;
+
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ table = e_memo_table_get_table (memo_table);
+
+ gal_view_etable_attach_table (GAL_VIEW_ETABLE (gal_view), table);
+}
+
+static void
+memo_shell_content_table_foreach_cb (gint model_row,
+ gpointer user_data)
+{
+ ECalModelComponent *comp_data;
+ icalcomponent *clone;
+ icalcomponent *vcal;
+ gchar *string;
+
+ struct {
+ ECalModel *model;
+ GSList *list;
+ } *foreach_data = user_data;
+
+ comp_data = e_cal_model_get_component_at (
+ foreach_data->model, model_row);
+
+ vcal = e_cal_util_new_top_level ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_util_add_timezones_from_component (vcal, comp_data->icalcomp);
+ icalcomponent_add_component (vcal, clone);
+
+ /* String is owned by libical; do not free. */
+ string = icalcomponent_as_ical_string (vcal);
+ if (string != NULL) {
+ ESource *source;
+ const gchar *source_uid;
+
+ source = e_cal_get_source (comp_data->client);
+ source_uid = e_source_peek_uid (source);
+
+ foreach_data->list = g_slist_prepend (
+ foreach_data->list,
+ g_strdup_printf ("%s\n%s", source_uid, string));
+ }
+
+ icalcomponent_free (vcal);
+}
+
+static void
+memo_shell_content_table_drag_data_get_cb (EMemoShellContent *memo_shell_content,
+ gint row,
+ gint col,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ EMemoTable *memo_table;
+ ETable *table;
+
+ struct {
+ ECalModel *model;
+ GSList *list;
+ } foreach_data;
+
+ if (info != TARGET_VCALENDAR)
+ return;
+
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ table = e_memo_table_get_table (memo_table);
+
+ foreach_data.model = e_memo_table_get_model (memo_table);
+ foreach_data.list = NULL;
+
+ e_table_selected_row_foreach (
+ table, memo_shell_content_table_foreach_cb,
+ &foreach_data);
+
+ if (foreach_data.list != NULL) {
+ cal_comp_selection_set_string_list (
+ selection_data, foreach_data.list);
+ g_slist_foreach (foreach_data.list, (GFunc) g_free, NULL);
+ g_slist_free (foreach_data.list);
+ }
+}
+
+static void
+memo_shell_content_table_drag_data_delete_cb (EMemoShellContent *memo_shell_content,
+ gint row,
+ gint col,
+ GdkDragContext *context)
+{
+ /* Moved components are deleted from source immediately when moved,
+ * because some of them can be part of destination source, and we
+ * don't want to delete not-moved memos. There is no such information
+ * which event has been moved and which not, so skip this method. */
+}
+
+static void
+memo_shell_content_cursor_change_cb (EMemoShellContent *memo_shell_content,
+ gint row,
+ ETable *table)
+{
+ ECalComponentPreview *memo_preview;
+ EMemoTable *memo_table;
+ ECalModel *memo_model;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ const gchar *uid;
+
+ memo_model = e_memo_shell_content_get_memo_model (memo_shell_content);
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ memo_preview = e_memo_shell_content_get_memo_preview (memo_shell_content);
+
+ if (e_table_selected_count (table) != 1) {
+ e_cal_component_preview_clear (memo_preview);
+ return;
+ }
+
+ row = e_table_get_cursor_row (table);
+ comp_data = e_cal_model_get_component_at (memo_model, row);
+
+ comp = e_cal_component_new ();
+ e_cal_component_set_icalcomponent (
+ comp, icalcomponent_new_clone (comp_data->icalcomp));
+ e_cal_component_preview_display (
+ memo_preview, comp_data->client, comp);
+
+ e_cal_component_get_uid (comp, &uid);
+ g_free (memo_shell_content->priv->current_uid);
+ memo_shell_content->priv->current_uid = g_strdup (uid);
+
+ g_object_unref (comp);
+}
+
+static void
+memo_shell_content_selection_change_cb (EMemoShellContent *memo_shell_content,
+ ETable *table)
+{
+ ECalComponentPreview *memo_preview;
+
+ memo_preview = e_memo_shell_content_get_memo_preview (memo_shell_content);
+
+ /* XXX Old code emits a "selection-changed" signal here. */
+
+ if (e_table_selected_count (table) != 1)
+ e_cal_component_preview_clear (memo_preview);
+}
+
+static void
+memo_shell_content_model_row_changed_cb (EMemoShellContent *memo_shell_content,
+ gint row,
+ ETableModel *model)
+{
+ ECalModelComponent *comp_data;
+ EMemoTable *memo_table;
+ ETable *table;
+ const gchar *current_uid;
+ const gchar *uid;
+
+ current_uid = memo_shell_content->priv->current_uid;
+ if (current_uid == NULL)
+ return;
+
+ comp_data = e_cal_model_get_component_at (E_CAL_MODEL (model), row);
+ if (comp_data == NULL)
+ return;
+
+ uid = icalcomponent_get_uid (comp_data->icalcomp);
+ if (g_strcmp0 (uid, current_uid) != 0)
+ return;
+
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ table = e_memo_table_get_table (memo_table);
+
+ memo_shell_content_cursor_change_cb (memo_shell_content, 0, table);
+}
+
+static void
+memo_shell_content_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_PREVIEW_VISIBLE:
+ e_memo_shell_content_set_preview_visible (
+ E_MEMO_SHELL_CONTENT (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+memo_shell_content_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_MODEL:
+ g_value_set_object (
+ value, e_memo_shell_content_get_memo_model (
+ E_MEMO_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_PREVIEW_VISIBLE:
+ g_value_set_boolean (
+ value, e_memo_shell_content_get_preview_visible (
+ E_MEMO_SHELL_CONTENT (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+memo_shell_content_dispose (GObject *object)
+{
+ EMemoShellContentPrivate *priv;
+
+ priv = E_MEMO_SHELL_CONTENT_GET_PRIVATE (object);
+
+ if (priv->paned != NULL) {
+ g_object_unref (priv->paned);
+ priv->paned = NULL;
+ }
+
+ if (priv->memo_table != NULL) {
+ g_object_unref (priv->memo_table);
+ priv->memo_table = NULL;
+ }
+
+ if (priv->memo_preview != NULL) {
+ g_object_unref (priv->memo_preview);
+ priv->memo_preview = NULL;
+ }
+
+ if (priv->memo_model != NULL) {
+ g_object_unref (priv->memo_model);
+ priv->memo_model = NULL;
+ }
+
+ if (priv->table_config != NULL) {
+ g_object_unref (priv->table_config);
+ priv->table_config = NULL;
+ }
+
+ if (priv->view_instance != NULL) {
+ g_object_unref (priv->view_instance);
+ priv->view_instance = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+memo_shell_content_finalize (GObject *object)
+{
+ EMemoShellContentPrivate *priv;
+
+ priv = E_MEMO_SHELL_CONTENT_GET_PRIVATE (object);
+
+ g_free (priv->current_uid);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+memo_shell_content_constructed (GObject *object)
+{
+ EMemoShellContentPrivate *priv;
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ GalViewInstance *view_instance;
+ ETable *table;
+ GConfBridge *bridge;
+ GtkWidget *container;
+ GtkWidget *widget;
+ const gchar *key;
+
+ priv = E_MEMO_SHELL_CONTENT_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ shell_content = E_SHELL_CONTENT (object);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+
+ /* Build content widgets. */
+
+ container = GTK_WIDGET (object);
+
+ widget = gtk_vpaned_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->paned = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_memo_table_new (shell_view, priv->memo_model);
+ gtk_paned_add1 (GTK_PANED (container), widget);
+ priv->memo_table = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_paned_add2 (GTK_PANED (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_cal_component_preview_new ();
+ e_cal_component_preview_set_default_timezone (
+ E_CAL_COMPONENT_PREVIEW (widget),
+ calendar_config_get_icaltimezone ());
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->memo_preview = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* Configure the memo table. */
+
+ widget = E_MEMO_TABLE (priv->memo_table)->etable;
+ table = e_table_scrolled_get_table (E_TABLE_SCROLLED (widget));
+
+ priv->table_config = e_memo_table_config_new (
+ E_MEMO_TABLE (priv->memo_table));
+
+ e_table_set_state (table, E_MEMO_TABLE_DEFAULT_STATE);
+
+ e_table_drag_source_set (
+ table, GDK_BUTTON1_MASK,
+ drag_types, G_N_ELEMENTS (drag_types),
+ GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_ASK);
+
+ g_signal_connect_swapped (
+ table, "table-drag-data-get",
+ G_CALLBACK (memo_shell_content_table_drag_data_get_cb),
+ object);
+
+ g_signal_connect_swapped (
+ table, "table-drag-data-delete",
+ G_CALLBACK (memo_shell_content_table_drag_data_delete_cb),
+ object);
+
+ g_signal_connect_swapped (
+ table, "cursor-change",
+ G_CALLBACK (memo_shell_content_cursor_change_cb),
+ object);
+
+ g_signal_connect_swapped (
+ table, "selection-change",
+ G_CALLBACK (memo_shell_content_selection_change_cb),
+ object);
+
+ g_signal_connect_swapped (
+ priv->memo_model, "model-row-changed",
+ G_CALLBACK (memo_shell_content_model_row_changed_cb),
+ object);
+
+ /* Load the view instance. */
+
+ view_instance = e_shell_view_new_view_instance (shell_view, NULL);
+ g_signal_connect_swapped (
+ view_instance, "display-view",
+ G_CALLBACK (memo_shell_content_display_view_cb),
+ object);
+ gal_view_instance_load (view_instance);
+ priv->view_instance = view_instance;
+
+ /* Bind GObject properties to GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (priv->paned);
+ key = "/apps/evolution/calendar/display/memo_vpane_position";
+ gconf_bridge_bind_property_delayed (bridge, key, object, "position");
+}
+
+static guint32
+memo_shell_content_check_state (EShellContent *shell_content)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ETable *table;
+ GSList *list, *iter;
+ gboolean editable = TRUE;
+ gboolean has_url = FALSE;
+ gint n_selected;
+ guint32 state = 0;
+
+ memo_shell_content = E_MEMO_SHELL_CONTENT (shell_content);
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ table = e_memo_table_get_table (memo_table);
+ n_selected = e_table_selected_count (table);
+
+ list = e_memo_table_get_selected (memo_table);
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ECalModelComponent *comp_data = iter->data;
+ icalproperty *prop;
+ gboolean read_only;
+
+ e_cal_is_read_only (comp_data->client, &read_only, NULL);
+ editable &= !read_only;
+
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_URL_PROPERTY);
+ has_url |= (prop != NULL);
+ }
+ g_slist_free (list);
+
+ if (n_selected == 1)
+ state |= E_MEMO_SHELL_CONTENT_SELECTION_SINGLE;
+ if (n_selected > 1)
+ state |= E_MEMO_SHELL_CONTENT_SELECTION_MULTIPLE;
+ if (editable)
+ state |= E_MEMO_SHELL_CONTENT_SELECTION_CAN_EDIT;
+ if (has_url)
+ state |= E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL;
+
+ return state;
+}
+
+static void
+memo_shell_content_class_init (EMemoShellContentClass *class)
+{
+ GObjectClass *object_class;
+ EShellContentClass *shell_content_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMemoShellContentPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = memo_shell_content_set_property;
+ object_class->get_property = memo_shell_content_get_property;
+ object_class->dispose = memo_shell_content_dispose;
+ object_class->finalize = memo_shell_content_finalize;
+ object_class->constructed = memo_shell_content_constructed;
+
+ shell_content_class = E_SHELL_CONTENT_CLASS (class);
+ shell_content_class->check_state = memo_shell_content_check_state;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MODEL,
+ g_param_spec_object (
+ "model",
+ _("Model"),
+ _("The memo table model"),
+ E_TYPE_CAL_MODEL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PREVIEW_VISIBLE,
+ g_param_spec_boolean (
+ "preview-visible",
+ _("Preview is Visible"),
+ _("Whether the preview pane is visible"),
+ TRUE,
+ G_PARAM_READWRITE));
+}
+
+static void
+memo_shell_content_init (EMemoShellContent *memo_shell_content)
+{
+ memo_shell_content->priv =
+ E_MEMO_SHELL_CONTENT_GET_PRIVATE (memo_shell_content);
+
+ memo_shell_content->priv->memo_model = e_cal_model_memos_new ();
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_memo_shell_content_get_type (void)
+{
+ return memo_shell_content_type;
+}
+
+void
+e_memo_shell_content_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (EMemoShellContentClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) memo_shell_content_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMemoShellContent),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) memo_shell_content_init,
+ NULL /* value_table */
+ };
+
+ memo_shell_content_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_CONTENT,
+ "EMemoShellContent", &type_info, 0);
+}
+
+GtkWidget *
+e_memo_shell_content_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_MEMO_SHELL_CONTENT,
+ "shell-view", shell_view, NULL);
+}
+
+ECalModel *
+e_memo_shell_content_get_memo_model (EMemoShellContent *memo_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_MEMO_SHELL_CONTENT (memo_shell_content), NULL);
+
+ return memo_shell_content->priv->memo_model;
+}
+
+ECalComponentPreview *
+e_memo_shell_content_get_memo_preview (EMemoShellContent *memo_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_MEMO_SHELL_CONTENT (memo_shell_content), NULL);
+
+ return E_CAL_COMPONENT_PREVIEW (
+ memo_shell_content->priv->memo_preview);
+}
+
+EMemoTable *
+e_memo_shell_content_get_memo_table (EMemoShellContent *memo_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_MEMO_SHELL_CONTENT (memo_shell_content), NULL);
+
+ return E_MEMO_TABLE (memo_shell_content->priv->memo_table);
+}
+
+GalViewInstance *
+e_memo_shell_content_get_view_instance (EMemoShellContent *memo_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_MEMO_SHELL_CONTENT (memo_shell_content), NULL);
+
+ return memo_shell_content->priv->view_instance;
+}
+
+gboolean
+e_memo_shell_content_get_preview_visible (EMemoShellContent *memo_shell_content)
+{
+ GtkPaned *paned;
+ GtkWidget *child;
+
+ g_return_val_if_fail (
+ E_IS_MEMO_SHELL_CONTENT (memo_shell_content), FALSE);
+
+ paned = GTK_PANED (memo_shell_content->priv->paned);
+ child = gtk_paned_get_child2 (paned);
+
+ return GTK_WIDGET_VISIBLE (child);
+}
+
+void
+e_memo_shell_content_set_preview_visible (EMemoShellContent *memo_shell_content,
+ gboolean preview_visible)
+{
+ GtkPaned *paned;
+ GtkWidget *child;
+
+ g_return_if_fail (E_IS_MEMO_SHELL_CONTENT (memo_shell_content));
+
+ paned = GTK_PANED (memo_shell_content->priv->paned);
+ child = gtk_paned_get_child2 (paned);
+
+ if (preview_visible)
+ gtk_widget_show (child);
+ else
+ gtk_widget_hide (child);
+
+ g_object_notify (G_OBJECT (memo_shell_content), "preview-visible");
+}
diff --git a/modules/calendar/e-memo-shell-content.h b/modules/calendar/e-memo-shell-content.h
new file mode 100644
index 0000000000..84c22d6ce4
--- /dev/null
+++ b/modules/calendar/e-memo-shell-content.h
@@ -0,0 +1,96 @@
+/*
+ * e-memo-shell-content.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MEMO_SHELL_CONTENT_H
+#define E_MEMO_SHELL_CONTENT_H
+
+#include <shell/e-shell-content.h>
+#include <shell/e-shell-view.h>
+
+#include <calendar/gui/e-memo-table.h>
+#include <calendar/gui/e-cal-component-preview.h>
+
+#include <menus/gal-view-instance.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MEMO_SHELL_CONTENT \
+ (e_memo_shell_content_get_type ())
+#define E_MEMO_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MEMO_SHELL_CONTENT, EMemoShellContent))
+#define E_MEMO_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MEMO_SHELL_CONTENT, EMemoShellContentClass))
+#define E_IS_MEMO_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MEMO_SHELL_CONTENT))
+#define E_IS_MEMO_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MEMO_SHELL_CONTENT))
+#define E_MEMO_SHELL_CONTENT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MEMO_SHELL_CONTENT, EMemoShellContentClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMemoShellContent EMemoShellContent;
+typedef struct _EMemoShellContentClass EMemoShellContentClass;
+typedef struct _EMemoShellContentPrivate EMemoShellContentPrivate;
+
+enum {
+ E_MEMO_SHELL_CONTENT_SELECTION_SINGLE = 1 << 0,
+ E_MEMO_SHELL_CONTENT_SELECTION_MULTIPLE = 1 << 1,
+ E_MEMO_SHELL_CONTENT_SELECTION_CAN_EDIT = 1 << 2,
+ E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL = 1 << 3
+};
+
+struct _EMemoShellContent {
+ EShellContent parent;
+ EMemoShellContentPrivate *priv;
+};
+
+struct _EMemoShellContentClass {
+ EShellContentClass parent_class;
+};
+
+GType e_memo_shell_content_get_type (void);
+void e_memo_shell_content_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_memo_shell_content_new(EShellView *shell_view);
+ECalModel * e_memo_shell_content_get_memo_model
+ (EMemoShellContent *memo_shell_conent);
+ECalComponentPreview *
+ e_memo_shell_content_get_memo_preview
+ (EMemoShellContent *memo_shell_content);
+EMemoTable * e_memo_shell_content_get_memo_table
+ (EMemoShellContent *memo_shell_content);
+GalViewInstance *
+ e_memo_shell_content_get_view_instance
+ (EMemoShellContent *memo_shell_content);
+gboolean e_memo_shell_content_get_preview_visible
+ (EMemoShellContent *memo_shell_content);
+void e_memo_shell_content_set_preview_visible
+ (EMemoShellContent *memo_shell_content,
+ gboolean preview_visible);
+
+G_END_DECLS
+
+#endif /* E_MEMO_SHELL_CONTENT_H */
diff --git a/modules/calendar/e-memo-shell-migrate.c b/modules/calendar/e-memo-shell-migrate.c
new file mode 100644
index 0000000000..d2e7df6d1b
--- /dev/null
+++ b/modules/calendar/e-memo-shell-migrate.c
@@ -0,0 +1,257 @@
+/*
+ * e-memo-shell-migrate.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-memo-shell-migrate.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <camel/camel-url.h>
+#include <libedataserver/e-account.h>
+#include <libedataserver/e-account-list.h>
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-group.h>
+#include <libedataserver/e-source-list.h>
+
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/calendar-config-keys.h"
+
+#define WEBCAL_BASE_URI "webcal://"
+#define PERSONAL_RELATIVE_URI "system"
+#define GROUPWISE_BASE_URI "groupwise://"
+
+static void
+create_memo_sources (EShellBackend *shell_backend,
+ ESourceList *source_list,
+ ESourceGroup **on_this_computer,
+ ESourceGroup **on_the_web,
+ ESource **personal_source)
+{
+ GSList *groups;
+ ESourceGroup *group;
+ gchar *base_uri, *base_uri_proto;
+ const gchar *base_dir;
+
+ *on_this_computer = NULL;
+ *on_the_web = NULL;
+ *personal_source = NULL;
+
+ base_dir = e_shell_backend_get_config_dir (shell_backend);
+ base_uri = g_build_filename (base_dir, "local", NULL);
+
+ base_uri_proto = g_filename_to_uri (base_uri, NULL, NULL);
+
+ groups = e_source_list_peek_groups (source_list);
+ if (groups) {
+ /* groups are already there, we need to search for things... */
+ GSList *g;
+
+ for (g = groups; g; g = g->next) {
+
+ group = E_SOURCE_GROUP (g->data);
+
+ if (!*on_this_computer && !strcmp (base_uri_proto, e_source_group_peek_base_uri (group)))
+ *on_this_computer = g_object_ref (group);
+ else if (!*on_the_web && !strcmp (WEBCAL_BASE_URI, e_source_group_peek_base_uri (group)))
+ *on_the_web = g_object_ref (group);
+ }
+ }
+
+ if (*on_this_computer) {
+ /* make sure "Personal" shows up as a source under
+ this group */
+ GSList *sources = e_source_group_peek_sources (*on_this_computer);
+ GSList *s;
+ for (s = sources; s; s = s->next) {
+ ESource *source = E_SOURCE (s->data);
+ const gchar *relative_uri;
+
+ relative_uri = e_source_peek_relative_uri (source);
+ if (relative_uri == NULL)
+ continue;
+ if (!strcmp (PERSONAL_RELATIVE_URI, relative_uri)) {
+ *personal_source = g_object_ref (source);
+ break;
+ }
+ }
+ } else {
+ /* create the local source group */
+ group = e_source_group_new (_("On This Computer"), base_uri_proto);
+ e_source_list_add_group (source_list, group, -1);
+
+ *on_this_computer = group;
+ }
+
+ if (!*personal_source) {
+ /* Create the default Person task list */
+ ESource *source = e_source_new (_("Personal"), PERSONAL_RELATIVE_URI);
+ e_source_group_add_source (*on_this_computer, source, -1);
+
+ if (!calendar_config_get_primary_memos () && !calendar_config_get_memos_selected ()) {
+ GSList selected;
+
+ calendar_config_set_primary_memos (e_source_peek_uid (source));
+
+ selected.data = (gpointer)e_source_peek_uid (source);
+ selected.next = NULL;
+ calendar_config_set_memos_selected (&selected);
+ }
+
+ e_source_set_color_spec (source, "#BECEDD");
+ *personal_source = source;
+ }
+
+ if (!*on_the_web) {
+ /* Create the Webcal source group */
+ group = e_source_group_new (_("On The Web"), WEBCAL_BASE_URI);
+ e_source_list_add_group (source_list, group, -1);
+
+ *on_the_web = group;
+ }
+
+ g_free (base_uri_proto);
+ g_free (base_uri);
+}
+
+static gboolean
+is_groupwise_account (EAccount *account)
+{
+ if (account->source->url != NULL) {
+ return g_str_has_prefix (account->source->url, GROUPWISE_BASE_URI);
+ } else {
+ return FALSE;
+ }
+}
+
+static void
+add_gw_esource (ESourceList *source_list, const gchar *group_name, const gchar *source_name, CamelURL *url, GConfClient *client)
+{
+ ESourceGroup *group;
+ ESource *source;
+ GSList *ids, *temp;
+ GError *error = NULL;
+ gchar *relative_uri;
+ const gchar *soap_port;
+ const gchar * use_ssl;
+ const gchar *poa_address;
+ const gchar *offline_sync;
+
+
+ poa_address = url->host;
+ if (!poa_address || strlen (poa_address) ==0)
+ return;
+ soap_port = camel_url_get_param (url, "soap_port");
+
+ if (!soap_port || strlen (soap_port) == 0)
+ soap_port = "7191";
+
+ use_ssl = camel_url_get_param (url, "use_ssl");
+ offline_sync = camel_url_get_param (url, "offline_sync");
+
+ group = e_source_group_new (group_name, GROUPWISE_BASE_URI);
+ if (!e_source_list_add_group (source_list, group, -1))
+ return;
+ relative_uri = g_strdup_printf ("%s@%s/", url->user, poa_address);
+
+ source = e_source_new (source_name, relative_uri);
+ e_source_set_property (source, "auth", "1");
+ e_source_set_property (source, "username", url->user);
+ e_source_set_property (source, "port", camel_url_get_param (url, "soap_port"));
+ e_source_set_property (source, "auth-domain", "Groupwise");
+ e_source_set_property (source, "use_ssl", use_ssl);
+ e_source_set_property (source, "offline_sync", offline_sync ? "1" : "0" );
+
+ e_source_set_color_spec (source, "#EEBC60");
+ e_source_group_add_source (group, source, -1);
+
+ ids = gconf_client_get_list (client, CALENDAR_CONFIG_MEMOS_SELECTED_MEMOS, GCONF_VALUE_STRING, &error);
+ if ( error != NULL ) {
+ g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message);
+ g_error_free(error);
+ }
+ ids = g_slist_append (ids, g_strdup (e_source_peek_uid (source)));
+ gconf_client_set_list (client, CALENDAR_CONFIG_MEMOS_SELECTED_MEMOS, GCONF_VALUE_STRING, ids, NULL);
+ temp = ids;
+ for (; temp != NULL; temp = g_slist_next (temp))
+ g_free (temp->data);
+
+ g_slist_free (ids);
+ g_object_unref (source);
+ g_object_unref (group);
+ g_free (relative_uri);
+}
+
+gboolean
+e_memo_shell_backend_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint revision,
+ GError **error)
+{
+ ESourceGroup *on_this_computer = NULL;
+ ESourceGroup *on_the_web = NULL;
+ ESource *personal_source = NULL;
+ ESourceList *source_list = NULL;
+ gboolean retval = FALSE;
+
+ source_list = g_object_get_data (
+ G_OBJECT (shell_backend), "source-list");
+
+ /* we call this unconditionally now - create_groups either
+ creates the groups/sources or it finds the necessary
+ groups/sources. */
+ create_memo_sources (
+ shell_backend, source_list, &on_this_computer,
+ &on_the_web, &personal_source);
+
+ /* Migration for Gw accounts between versions < 2.8 */
+ if (major == 2 && minor < 8) {
+ EAccountList *al;
+ EAccount *a;
+ CamelURL *url;
+ EIterator *it;
+ GConfClient *gconf_client = gconf_client_get_default ();
+ al = e_account_list_new (gconf_client);
+ for (it = e_list_get_iterator((EList *)al);
+ e_iterator_is_valid(it);
+ e_iterator_next(it)) {
+ a = (EAccount *) e_iterator_get(it);
+ if (!a->enabled || !is_groupwise_account (a))
+ continue;
+ url = camel_url_new (a->source->url, NULL);
+ add_gw_esource (source_list, a->name, _("Notes"), url, gconf_client);
+ camel_url_free (url);
+ }
+ g_object_unref (al);
+ g_object_unref (gconf_client);
+ }
+
+ e_source_list_sync (source_list, NULL);
+ retval = TRUE;
+
+ if (on_this_computer)
+ g_object_unref (on_this_computer);
+ if (on_the_web)
+ g_object_unref (on_the_web);
+ if (personal_source)
+ g_object_unref (personal_source);
+
+ return retval;
+}
diff --git a/modules/calendar/e-memo-shell-migrate.h b/modules/calendar/e-memo-shell-migrate.h
new file mode 100644
index 0000000000..ba163c6950
--- /dev/null
+++ b/modules/calendar/e-memo-shell-migrate.h
@@ -0,0 +1,38 @@
+/*
+ * e-memo-shell-backend-migrate.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MEMO_SHELL_BACKEND_MIGRATE_H
+#define E_MEMO_SHELL_BACKEND_MIGRATE_H
+
+#include <glib.h>
+#include <shell/e-shell-backend.h>
+
+G_BEGIN_DECLS
+
+gboolean e_memo_shell_backend_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_MEMO_SHELL_BACKEND_MIGRATE_H */
diff --git a/modules/calendar/e-memo-shell-sidebar.c b/modules/calendar/e-memo-shell-sidebar.c
new file mode 100644
index 0000000000..ca5d05c40b
--- /dev/null
+++ b/modules/calendar/e-memo-shell-sidebar.c
@@ -0,0 +1,718 @@
+/*
+ * e-memo-shell-sidebar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-memo-shell-sidebar.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libecal/e-cal.h>
+
+#include "e-util/e-error.h"
+#include "e-util/e-util.h"
+#include "calendar/common/authentication.h"
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/e-memo-list-selector.h"
+#include "calendar/gui/misc.h"
+
+#include "e-memo-shell-view.h"
+#include "e-memo-shell-backend.h"
+
+#define E_MEMO_SHELL_SIDEBAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MEMO_SHELL_SIDEBAR, EMemoShellSidebarPrivate))
+
+struct _EMemoShellSidebarPrivate {
+ GtkWidget *selector;
+
+ /* UID -> Client */
+ GHashTable *client_table;
+};
+
+enum {
+ PROP_0,
+ PROP_SELECTOR
+};
+
+enum {
+ CLIENT_ADDED,
+ CLIENT_REMOVED,
+ STATUS_MESSAGE,
+ LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+static GType memo_shell_sidebar_type;
+
+static void
+memo_shell_sidebar_emit_client_added (EMemoShellSidebar *memo_shell_sidebar,
+ ECal *client)
+{
+ guint signal_id = signals[CLIENT_ADDED];
+
+ g_signal_emit (memo_shell_sidebar, signal_id, 0, client);
+}
+
+static void
+memo_shell_sidebar_emit_client_removed (EMemoShellSidebar *memo_shell_sidebar,
+ ECal *client)
+{
+ guint signal_id = signals[CLIENT_REMOVED];
+
+ g_signal_emit (memo_shell_sidebar, signal_id, 0, client);
+}
+
+static void
+memo_shell_sidebar_emit_status_message (EMemoShellSidebar *memo_shell_sidebar,
+ const gchar *status_message)
+{
+ guint signal_id = signals[STATUS_MESSAGE];
+
+ g_signal_emit (memo_shell_sidebar, signal_id, 0, status_message, -1.0);
+}
+
+static void
+memo_shell_sidebar_update_timezone (EMemoShellSidebar *memo_shell_sidebar)
+{
+ GHashTable *client_table;
+ icaltimezone *zone;
+ GList *values;
+
+ zone = calendar_config_get_icaltimezone ();
+ client_table = memo_shell_sidebar->priv->client_table;
+ values = g_hash_table_get_values (client_table);
+
+ while (values != NULL) {
+ ECal *client = values->data;
+
+ if (e_cal_get_load_state (client) == E_CAL_LOAD_LOADED)
+ e_cal_set_default_timezone (client, zone, NULL);
+
+ values = g_list_delete_link (values, values);
+ }
+
+ /* XXX Need to call e_cal_component_preview_set_default_timezone()
+ * here but the sidebar is not really supposed to access content
+ * stuff. I guess we could emit an "update-timezone" signal
+ * here, but that feels wrong. Maybe this whole thing should
+ * be in EMemoShellView instead. */
+}
+
+static void
+memo_shell_sidebar_backend_died_cb (EMemoShellSidebar *memo_shell_sidebar,
+ ECal *client)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ GHashTable *client_table;
+ ESource *source;
+ const gchar *uid;
+
+ client_table = memo_shell_sidebar->priv->client_table;
+
+ shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ source = e_cal_get_source (client);
+ uid = e_source_peek_uid (source);
+
+ g_object_ref (source);
+
+ g_hash_table_remove (client_table, uid);
+ memo_shell_sidebar_emit_status_message (memo_shell_sidebar, NULL);
+
+ e_error_run (
+ GTK_WINDOW (shell_window),
+ "calendar:memos-crashed", NULL);
+
+ g_object_unref (source);
+}
+
+static void
+memo_shell_sidebar_backend_error_cb (EMemoShellSidebar *memo_shell_sidebar,
+ const gchar *message,
+ ECal *client)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ GtkWidget *dialog;
+ const gchar *uri;
+ gchar *uri_no_passwd;
+
+ shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ uri = e_cal_get_uri (client);
+ uri_no_passwd = get_uri_without_password (uri);
+
+ dialog = gtk_message_dialog_new (
+ GTK_WINDOW (shell_window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Error on %s\n%s"),
+ uri_no_passwd, message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_free (uri_no_passwd);
+}
+
+static void
+memo_shell_sidebar_client_opened_cb (EMemoShellSidebar *memo_shell_sidebar,
+ ECalendarStatus status,
+ ECal *client)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ ESource *source;
+
+ source = e_cal_get_source (client);
+
+ shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ if (status == E_CALENDAR_STATUS_AUTHENTICATION_FAILED ||
+ status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED)
+ auth_cal_forget_password (client);
+
+ switch (status) {
+ case E_CALENDAR_STATUS_OK:
+ g_signal_handlers_disconnect_matched (
+ client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+ memo_shell_sidebar_client_opened_cb, NULL);
+
+ memo_shell_sidebar_emit_status_message (
+ memo_shell_sidebar, _("Loading memos"));
+ memo_shell_sidebar_emit_client_added (
+ memo_shell_sidebar, client);
+ memo_shell_sidebar_emit_status_message (
+ memo_shell_sidebar, NULL);
+ break;
+
+ case E_CALENDAR_STATUS_AUTHENTICATION_FAILED:
+ e_cal_open_async (client, FALSE);
+ break;
+
+ case E_CALENDAR_STATUS_BUSY:
+ break;
+
+ case E_CALENDAR_STATUS_REPOSITORY_OFFLINE:
+ e_error_run (
+ GTK_WINDOW (shell_window),
+ "calendar:prompt-no-contents-offline-memos",
+ NULL);
+ break;
+
+ default:
+ memo_shell_sidebar_emit_client_removed (
+ memo_shell_sidebar, client);
+ break;
+ }
+}
+
+static void
+memo_shell_sidebar_row_changed_cb (EMemoShellSidebar *memo_shell_sidebar,
+ GtkTreePath *tree_path,
+ GtkTreeIter *tree_iter,
+ GtkTreeModel *tree_model)
+{
+ ESourceSelector *selector;
+ ESource *source;
+
+ /* XXX ESourceSelector's underlying tree store has only one
+ * column: ESource objects. While we're not supposed to
+ * know this, listening for "row-changed" signals from
+ * the model is easier to deal with than the selector's
+ * "selection-changed" signal, which doesn't tell you
+ * _which_ row changed. */
+
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+ gtk_tree_model_get (tree_model, tree_iter, 0, &source, -1);
+
+ /* XXX This signal gets emitted a lot while the model is being
+ * rebuilt, during which time we won't get a valid ESource.
+ * ESourceSelector should probably block this signal while
+ * rebuilding the model, but we'll be forgiving and not
+ * emit a warning. */
+ if (!E_IS_SOURCE (source))
+ return;
+
+ if (e_source_selector_source_is_selected (selector, source))
+ e_memo_shell_sidebar_add_source (memo_shell_sidebar, source);
+ else
+ e_memo_shell_sidebar_remove_source (memo_shell_sidebar, source);
+}
+
+static void
+memo_shell_sidebar_selection_changed_cb (EMemoShellSidebar *memo_shell_sidebar,
+ ESourceSelector *selector)
+{
+ GSList *list, *iter;
+
+ /* This signal is emitted less frequently than "row-changed",
+ * especially when the model is being rebuilt. So we'll take
+ * it easy on poor GConf. */
+
+ list = e_source_selector_get_selection (selector);
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+
+ iter->data = (gpointer) e_source_peek_uid (source);
+ g_object_unref (source);
+ }
+
+ calendar_config_set_memos_selected (list);
+
+ g_slist_free (list);
+}
+
+static void
+memo_shell_sidebar_primary_selection_changed_cb (EMemoShellSidebar *memo_shell_sidebar,
+ ESourceSelector *selector)
+{
+ ESource *source;
+ const gchar *uid;
+
+ /* XXX ESourceSelector needs a "primary-selection-uid" property
+ * so we can just bind the property with GConfBridge. */
+
+ source = e_source_selector_peek_primary_selection (selector);
+ if (source == NULL)
+ return;
+
+ uid = e_source_peek_uid (source);
+ calendar_config_set_primary_memos (uid);
+}
+
+static void
+memo_shell_sidebar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SELECTOR:
+ g_value_set_object (
+ value, e_memo_shell_sidebar_get_selector (
+ E_MEMO_SHELL_SIDEBAR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+memo_shell_sidebar_dispose (GObject *object)
+{
+ EMemoShellSidebarPrivate *priv;
+
+ priv = E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ if (priv->selector != NULL) {
+ g_object_unref (priv->selector);
+ priv->selector = NULL;
+ }
+
+ g_hash_table_remove_all (priv->client_table);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+memo_shell_sidebar_finalize (GObject *object)
+{
+ EMemoShellSidebarPrivate *priv;
+
+ priv = E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->client_table);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+memo_shell_sidebar_constructed (GObject *object)
+{
+ EMemoShellSidebarPrivate *priv;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+ EShellSidebar *shell_sidebar;
+ ESourceSelector *selector;
+ ESourceList *source_list;
+ ESource *source;
+ GtkContainer *container;
+ GtkTreeModel *model;
+ GtkWidget *widget;
+ AtkObject *a11y;
+ GSList *list, *iter;
+ gchar *uid;
+
+ priv = E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ shell_sidebar = E_SHELL_SIDEBAR (object);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+
+ source_list = e_memo_shell_backend_get_source_list (
+ E_MEMO_SHELL_BACKEND (shell_backend));
+
+ container = GTK_CONTAINER (shell_sidebar);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_container_add (container, widget);
+ gtk_widget_show (widget);
+
+ container = GTK_CONTAINER (widget);
+
+ widget = e_memo_list_selector_new (source_list);
+ e_source_selector_set_select_new (E_SOURCE_SELECTOR (widget), TRUE);
+ gtk_container_add (container, widget);
+ a11y = gtk_widget_get_accessible (widget);
+ atk_object_set_name (a11y, _("Memo List Selector"));
+ priv->selector = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* Restore the selector state from the last session. */
+
+ selector = E_SOURCE_SELECTOR (priv->selector);
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+
+ g_signal_connect_swapped (
+ model, "row-changed",
+ G_CALLBACK (memo_shell_sidebar_row_changed_cb),
+ object);
+
+ source = NULL;
+ uid = calendar_config_get_primary_memos ();
+ if (uid != NULL)
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ if (source == NULL)
+ source = e_source_list_peek_source_any (source_list);
+ if (source != NULL)
+ e_source_selector_set_primary_selection (selector, source);
+ g_free (uid);
+
+ list = calendar_config_get_memos_selected ();
+ for (iter = list; iter != NULL; iter = iter->next) {
+ uid = iter->data;
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ g_free (uid);
+
+ if (source == NULL)
+ continue;
+
+ e_source_selector_select_source (selector, source);
+ }
+ g_slist_free (list);
+
+ /* Listen for subsequent changes to the selector. */
+
+ g_signal_connect_swapped (
+ widget, "selection-changed",
+ G_CALLBACK (memo_shell_sidebar_selection_changed_cb),
+ object);
+
+ g_signal_connect_swapped (
+ widget, "primary-selection-changed",
+ G_CALLBACK (memo_shell_sidebar_primary_selection_changed_cb),
+ object);
+}
+
+static guint32
+memo_shell_sidebar_check_state (EShellSidebar *shell_sidebar)
+{
+ EMemoShellSidebar *memo_shell_sidebar;
+ ESourceSelector *selector;
+ ESource *source;
+ gboolean is_system = FALSE;
+ guint32 state = 0;
+
+ memo_shell_sidebar = E_MEMO_SHELL_SIDEBAR (shell_sidebar);
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+
+ if (source != NULL) {
+ const gchar *uri;
+
+ uri = e_source_peek_relative_uri (source);
+ is_system = (uri == NULL || strcmp (uri, "system") == 0);
+ }
+
+ if (source != NULL)
+ state |= E_MEMO_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE;
+ if (is_system)
+ state |= E_MEMO_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM;
+
+ return state;
+}
+
+static void
+memo_shell_sidebar_client_added (EMemoShellSidebar *memo_shell_sidebar,
+ ECal *client)
+{
+ memo_shell_sidebar_update_timezone (memo_shell_sidebar);
+}
+
+static void
+memo_shell_sidebar_client_removed (EMemoShellSidebar *memo_shell_sidebar,
+ ECal *client)
+{
+ ESourceSelector *selector;
+ GHashTable *client_table;
+ ESource *source;
+ const gchar *uid;
+
+ client_table = memo_shell_sidebar->priv->client_table;
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+
+ g_signal_handlers_disconnect_matched (
+ client, G_SIGNAL_MATCH_DATA, 0, 0,
+ NULL, NULL, memo_shell_sidebar);
+
+ source = e_cal_get_source (client);
+ e_source_selector_unselect_source (selector, source);
+
+ uid = e_source_peek_uid (source);
+ g_hash_table_remove (client_table, uid);
+
+ memo_shell_sidebar_emit_status_message (memo_shell_sidebar, NULL);
+}
+
+static void
+memo_shell_sidebar_class_init (EMemoShellSidebarClass *class)
+{
+ GObjectClass *object_class;
+ EShellSidebarClass *shell_sidebar_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMemoShellSidebarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = memo_shell_sidebar_get_property;
+ object_class->dispose = memo_shell_sidebar_dispose;
+ object_class->finalize = memo_shell_sidebar_finalize;
+ object_class->constructed = memo_shell_sidebar_constructed;
+
+ shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class);
+ shell_sidebar_class->check_state = memo_shell_sidebar_check_state;
+
+ class->client_added = memo_shell_sidebar_client_added;
+ class->client_removed = memo_shell_sidebar_client_removed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SELECTOR,
+ g_param_spec_object (
+ "selector",
+ _("Source Selector Widget"),
+ _("This widget displays groups of memo lists"),
+ E_TYPE_SOURCE_SELECTOR,
+ G_PARAM_READABLE));
+
+ signals[CLIENT_ADDED] = g_signal_new (
+ "client-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMemoShellSidebarClass, client_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_CAL);
+
+ signals[CLIENT_REMOVED] = g_signal_new (
+ "client-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMemoShellSidebarClass, client_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_CAL);
+
+ signals[STATUS_MESSAGE] = g_signal_new (
+ "status-message",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EMemoShellSidebarClass, status_message),
+ NULL, NULL,
+ e_marshal_VOID__STRING_DOUBLE,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_DOUBLE);
+}
+
+static void
+memo_shell_sidebar_init (EMemoShellSidebar *memo_shell_sidebar)
+{
+ GHashTable *client_table;
+
+ client_table = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ memo_shell_sidebar->priv =
+ E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (memo_shell_sidebar);
+
+ memo_shell_sidebar->priv->client_table = client_table;
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_memo_shell_sidebar_get_type (void)
+{
+ return memo_shell_sidebar_type;
+}
+
+void
+e_memo_shell_sidebar_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (EMemoShellSidebarClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) memo_shell_sidebar_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMemoShellSidebar),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) memo_shell_sidebar_init,
+ NULL /* value_table */
+ };
+
+ memo_shell_sidebar_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_SIDEBAR,
+ "EMemoShellSidebar", &type_info, 0);
+}
+
+GtkWidget *
+e_memo_shell_sidebar_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_MEMO_SHELL_SIDEBAR,
+ "shell-view", shell_view, NULL);
+}
+
+ESourceSelector *
+e_memo_shell_sidebar_get_selector (EMemoShellSidebar *memo_shell_sidebar)
+{
+ g_return_val_if_fail (
+ E_IS_MEMO_SHELL_SIDEBAR (memo_shell_sidebar), NULL);
+
+ return E_SOURCE_SELECTOR (memo_shell_sidebar->priv->selector);
+}
+
+void
+e_memo_shell_sidebar_add_source (EMemoShellSidebar *memo_shell_sidebar,
+ ESource *source)
+{
+ ESourceSelector *selector;
+ GHashTable *client_table;
+ ECal *client;
+ const gchar *uid;
+ const gchar *uri;
+ gchar *message;
+
+ g_return_if_fail (E_IS_MEMO_SHELL_SIDEBAR (memo_shell_sidebar));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ client_table = memo_shell_sidebar->priv->client_table;
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+
+ uid = e_source_peek_uid (source);
+ client = g_hash_table_lookup (client_table, uid);
+
+ if (client != NULL)
+ return;
+
+ client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_JOURNAL);
+ g_return_if_fail (client != NULL);
+
+ g_signal_connect_swapped (
+ client, "backend-died",
+ G_CALLBACK (memo_shell_sidebar_backend_died_cb),
+ memo_shell_sidebar);
+
+ g_signal_connect_swapped (
+ client, "backend-error",
+ G_CALLBACK (memo_shell_sidebar_backend_error_cb),
+ memo_shell_sidebar);
+
+ g_hash_table_insert (client_table, g_strdup (uid), client);
+ e_source_selector_select_source (selector, source);
+
+ uri = e_cal_get_uri (client);
+ message = g_strdup_printf (_("Opening memos at %s"), uri);
+ memo_shell_sidebar_emit_status_message (memo_shell_sidebar, message);
+ g_free (message);
+
+ g_signal_connect_swapped (
+ client, "cal-opened",
+ G_CALLBACK (memo_shell_sidebar_client_opened_cb),
+ memo_shell_sidebar);
+
+ e_cal_open_async (client, FALSE);
+}
+
+void
+e_memo_shell_sidebar_remove_source (EMemoShellSidebar *memo_shell_sidebar,
+ ESource *source)
+{
+ ESourceSelector *selector;
+ GHashTable *client_table;
+ ECal *client;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_MEMO_SHELL_SIDEBAR (memo_shell_sidebar));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ client_table = memo_shell_sidebar->priv->client_table;
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+
+ uid = e_source_peek_uid (source);
+ client = g_hash_table_lookup (client_table, uid);
+
+ if (client == NULL)
+ return;
+
+ memo_shell_sidebar_emit_client_removed (memo_shell_sidebar, client);
+}
diff --git a/modules/calendar/e-memo-shell-sidebar.h b/modules/calendar/e-memo-shell-sidebar.h
new file mode 100644
index 0000000000..665c8f218a
--- /dev/null
+++ b/modules/calendar/e-memo-shell-sidebar.h
@@ -0,0 +1,95 @@
+/*
+ * e-memo-shell-sidebar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MEMO_SHELL_SIDEBAR_H
+#define E_MEMO_SHELL_SIDEBAR_H
+
+#include <libecal/e-cal.h>
+#include <libedataserverui/e-source-selector.h>
+
+#include <shell/e-shell-sidebar.h>
+#include <shell/e-shell-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MEMO_SHELL_SIDEBAR \
+ (e_memo_shell_sidebar_get_type ())
+#define E_MEMO_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MEMO_SHELL_SIDEBAR, EMemoShellSidebar))
+#define E_MEMO_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MEMO_SHELL_SIDEBAR, EMemoShellSidebarClass))
+#define E_IS_MEMO_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MEMO_SHELL_SIDEBAR))
+#define E_IS_MEMO_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MEMO_SHELL_SIDEBAR))
+#define E_MEMO_SHELL_SIDEBAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MEMO_SHELL_SIDEBAR, EMemoShellSidebarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMemoShellSidebar EMemoShellSidebar;
+typedef struct _EMemoShellSidebarClass EMemoShellSidebarClass;
+typedef struct _EMemoShellSidebarPrivate EMemoShellSidebarPrivate;
+
+enum {
+ E_MEMO_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE = 1 << 0,
+ E_MEMO_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM = 1 << 1
+};
+
+struct _EMemoShellSidebar {
+ EShellSidebar parent;
+ EMemoShellSidebarPrivate *priv;
+};
+
+struct _EMemoShellSidebarClass {
+ EShellSidebarClass parent_class;
+
+ /* Signals */
+ void (*client_added) (EMemoShellSidebar *memo_shell_sidebar,
+ ECal *client);
+ void (*client_removed) (EMemoShellSidebar *memo_shell_sidebar,
+ ECal *client);
+ void (*status_message) (EMemoShellSidebar *memo_shell_sidebar,
+ const gchar *status_message,
+ gdouble percent);
+};
+
+GType e_memo_shell_sidebar_get_type (void);
+void e_memo_shell_sidebar_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_memo_shell_sidebar_new(EShellView *shell_view);
+ESourceSelector *
+ e_memo_shell_sidebar_get_selector
+ (EMemoShellSidebar *memo_shell_sidebar);
+void e_memo_shell_sidebar_add_source
+ (EMemoShellSidebar *memo_shell_sidebar,
+ ESource *source);
+void e_memo_shell_sidebar_remove_source
+ (EMemoShellSidebar *memo_shell_sidebar,
+ ESource *source);
+
+G_END_DECLS
+
+#endif /* E_MEMO_SHELL_SIDEBAR_H */
diff --git a/modules/calendar/e-memo-shell-view-actions.c b/modules/calendar/e-memo-shell-view-actions.c
new file mode 100644
index 0000000000..1736606abe
--- /dev/null
+++ b/modules/calendar/e-memo-shell-view-actions.c
@@ -0,0 +1,949 @@
+/*
+ * e-memo-shell-view-actions.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-memo-shell-view-private.h"
+
+static void
+action_gal_save_custom_view_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EShellView *shell_view;
+ GalViewInstance *view_instance;
+
+ /* All shell views respond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with saving the custom view. */
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ view_instance = e_memo_shell_content_get_view_instance (memo_shell_content);
+ gal_view_instance_save_as (view_instance);
+}
+
+static void
+action_memo_clipboard_copy_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ e_memo_table_copy_clipboard (memo_table);
+}
+
+static void
+action_memo_clipboard_cut_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ e_memo_table_cut_clipboard (memo_table);
+}
+
+static void
+action_memo_clipboard_paste_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ e_memo_table_paste_clipboard (memo_table);
+}
+
+static void
+action_memo_delete_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ ECalComponentPreview *memo_preview;
+ EMemoTable *memo_table;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ memo_preview = e_memo_shell_content_get_memo_preview (memo_shell_content);
+
+ e_memo_shell_view_set_status_message (
+ memo_shell_view, _("Deleting selected memos..."), -1.0);
+ e_memo_table_delete_selected (memo_table);
+ e_memo_shell_view_set_status_message (memo_shell_view, NULL, -1.0);
+
+ e_cal_component_preview_clear (memo_preview);
+}
+
+static void
+action_memo_forward_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ GSList *list;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only forward the first selected memo. */
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_component_set_icalcomponent (comp, clone);
+ itip_send_comp (
+ E_CAL_COMPONENT_METHOD_PUBLISH, comp,
+ comp_data->client, NULL, NULL, NULL, TRUE);
+ g_object_unref (comp);
+}
+
+static void
+action_memo_list_copy_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellSidebar *memo_shell_sidebar;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ ESourceSelector *selector;
+ ESource *source;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar;
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ copy_source_dialog (
+ GTK_WINDOW (shell_window),
+ source, E_CAL_SOURCE_TYPE_JOURNAL);
+}
+
+static void
+action_memo_list_delete_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellBackend *memo_shell_backend;
+ EMemoShellContent *memo_shell_content;
+ EMemoShellSidebar *memo_shell_sidebar;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ EMemoTable *memo_table;
+ ECal *client;
+ ECalModel *model;
+ ESourceSelector *selector;
+ ESourceGroup *source_group;
+ ESourceList *source_list;
+ ESource *source;
+ gint response;
+ gchar *uri;
+ GError *error = NULL;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ memo_shell_backend = memo_shell_view->priv->memo_shell_backend;
+ source_list = e_memo_shell_backend_get_source_list (memo_shell_backend);
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ model = e_memo_table_get_model (memo_table);
+
+ memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar;
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ /* Ask for confirmation. */
+ response = e_error_run (
+ GTK_WINDOW (shell_window),
+ "calendar:prompt-delete-memo-list",
+ e_source_peek_name (source));
+ if (response != GTK_RESPONSE_YES)
+ return;
+
+ uri = e_source_get_uri (source);
+ client = e_cal_model_get_client_for_uri (model, uri);
+ if (client == NULL)
+ client = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_JOURNAL);
+ g_free (uri);
+
+ g_return_if_fail (client != NULL);
+
+ if (!e_cal_remove (client, &error)) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (e_source_selector_source_is_selected (selector, source)) {
+ e_memo_shell_sidebar_remove_source (
+ memo_shell_sidebar, source);
+ e_source_selector_unselect_source (selector, source);
+ }
+
+ source_group = e_source_peek_group (source);
+ e_source_group_remove_source (source_group, source);
+
+ if (!e_source_list_sync (source_list, &error)) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+action_memo_list_new_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ calendar_setup_new_memo_list (GTK_WINDOW (shell_window));
+}
+
+static void
+action_memo_list_print_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ETable *table;
+ GtkPrintOperationAction print_action;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ table = e_memo_table_get_table (memo_table);
+
+ print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
+ print_table (table, _("Print Memos"), _("Memos"), print_action);
+}
+
+static void
+action_memo_list_print_preview_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ETable *table;
+ GtkPrintOperationAction print_action;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ table = e_memo_table_get_table (memo_table);
+
+ print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW;
+ print_table (table, _("Print Memos"), _("Memos"), print_action);
+}
+
+static void
+action_memo_list_properties_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellSidebar *memo_shell_sidebar;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ESource *source;
+ ESourceSelector *selector;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar;
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ calendar_setup_edit_memo_list (GTK_WINDOW (shell_window), source);
+}
+
+static void
+action_memo_list_rename_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellSidebar *memo_shell_sidebar;
+ ESourceSelector *selector;
+
+ memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar;
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+
+ e_source_selector_edit_primary_selection (selector);
+}
+
+static void
+action_memo_list_select_one_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellSidebar *memo_shell_sidebar;
+ ESourceSelector *selector;
+ ESource *primary;
+ GSList *list, *iter;
+
+ /* XXX ESourceSelector should provide a function for this. */
+
+ memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar;
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+ primary = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (primary != NULL);
+
+ list = e_source_selector_get_selection (selector);
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+
+ if (source == primary)
+ continue;
+
+ e_source_selector_unselect_source (selector, source);
+ }
+ e_source_selector_free_selection (list);
+
+ e_source_selector_select_source (selector, primary);
+}
+
+static void
+action_memo_new_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ ECal *client;
+ ECalComponent *comp;
+ CompEditor *editor;
+ GSList *list;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ client = comp_data->client;
+ editor = memo_editor_new (client, shell, COMP_EDITOR_NEW_ITEM);
+ comp = cal_comp_memo_new_with_defaults (client);
+ comp_editor_edit_comp (editor, comp);
+
+ gtk_window_present (GTK_WINDOW (editor));
+
+ g_object_unref (comp);
+ g_object_unref (client);
+}
+
+static void
+action_memo_open_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only open the first selected memo. */
+ e_memo_shell_view_open_memo (memo_shell_view, comp_data);
+}
+
+static void
+action_memo_open_url_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ icalproperty *prop;
+ const gchar *uri;
+ GSList *list;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only open the URI of the first selected memo. */
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_URL_PROPERTY);
+ g_return_if_fail (prop == NULL);
+
+ uri = icalproperty_get_url (prop);
+ e_show_uri (GTK_WINDOW (shell_window), uri);
+}
+
+static void
+action_memo_preview_cb (GtkToggleAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ gboolean visible;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ visible = gtk_toggle_action_get_active (action);
+ e_memo_shell_content_set_preview_visible (memo_shell_content, visible);
+}
+
+static void
+action_memo_print_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ GtkPrintOperationAction print_action;
+ GSList *list;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only print the first selected memo. */
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
+ e_cal_component_set_icalcomponent (comp, clone);
+ print_comp (comp, comp_data->client, print_action);
+ g_object_unref (comp);
+}
+
+static void
+action_memo_save_as_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+ gchar *filename;
+ gchar *string;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ list = e_memo_table_get_selected (memo_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ filename = e_file_dialog_save (_("Save as..."), NULL);
+ if (filename == NULL)
+ return;
+
+ /* XXX We only save the first selected memo. */
+ string = e_cal_get_component_as_string (
+ comp_data->client, comp_data->icalcomp);
+ if (string == NULL) {
+ g_warning ("Could not convert memo to a string");
+ return;
+ }
+
+ e_write_file_uri (filename, string);
+
+ g_free (filename);
+ g_free (string);
+}
+
+static void
+action_memo_search_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EMemoShellView *memo_shell_view)
+{
+ EShellView *shell_view;
+ EShellContent *shell_content;
+ const gchar *search_hint;
+
+ /* XXX Figure out a way to handle this in EShellContent
+ * instead of every shell view having to handle it.
+ * The problem is EShellContent does not know what
+ * the search option actions are for this view. It
+ * would have to dig up the popup menu and retrieve
+ * the action for each menu item. Seems messy. */
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+
+ search_hint = gtk_action_get_label (GTK_ACTION (current));
+ e_shell_content_set_search_hint (shell_content, search_hint);
+}
+
+static void
+action_search_execute_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EShellView *shell_view;
+
+ /* All shell views respond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with executing the search. */
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ e_memo_shell_view_execute_search (memo_shell_view);
+}
+
+static void
+action_search_filter_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EMemoShellView *memo_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ gtk_action_activate (ACTION (SEARCH_EXECUTE));
+}
+
+static GtkActionEntry memo_entries[] = {
+
+ { "memo-clipboard-copy",
+ GTK_STOCK_COPY,
+ NULL,
+ NULL,
+ N_("Copy selected memo"),
+ G_CALLBACK (action_memo_clipboard_copy_cb) },
+
+ { "memo-clipboard-cut",
+ GTK_STOCK_CUT,
+ NULL,
+ NULL,
+ N_("Cut selected memo"),
+ G_CALLBACK (action_memo_clipboard_cut_cb) },
+
+ { "memo-clipboard-paste",
+ GTK_STOCK_PASTE,
+ NULL,
+ NULL,
+ N_("Paste memo from the clipboard"),
+ G_CALLBACK (action_memo_clipboard_paste_cb) },
+
+ { "memo-delete",
+ GTK_STOCK_DELETE,
+ N_("_Delete Memo"),
+ NULL,
+ N_("Delete selected memos"),
+ G_CALLBACK (action_memo_delete_cb) },
+
+ { "memo-forward",
+ "mail-forward",
+ N_("_Forward as iCalendar..."),
+ "<Control>f",
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_memo_forward_cb) },
+
+ { "memo-list-copy",
+ GTK_STOCK_COPY,
+ N_("_Copy..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_memo_list_copy_cb) },
+
+ { "memo-list-delete",
+ GTK_STOCK_DELETE,
+ N_("_Delete"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_memo_list_delete_cb) },
+
+ { "memo-list-new",
+ "stock_notes",
+ N_("_New Memo List"),
+ NULL,
+ N_("Create a new memo list"),
+ G_CALLBACK (action_memo_list_new_cb) },
+
+ { "memo-list-properties",
+ GTK_STOCK_PROPERTIES,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_memo_list_properties_cb) },
+
+ { "memo-list-rename",
+ NULL,
+ N_("_Rename..."),
+ "F2",
+ N_("Rename the selected memo list"),
+ G_CALLBACK (action_memo_list_rename_cb) },
+
+ { "memo-list-select-one",
+ "stock_check-filled",
+ N_("Show _Only This Memo List"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_memo_list_select_one_cb) },
+
+ { "memo-new",
+ "stock_insert-note",
+ N_("New _Memo"),
+ NULL,
+ N_("Create a new memo"),
+ G_CALLBACK (action_memo_new_cb) },
+
+ { "memo-open",
+ GTK_STOCK_OPEN,
+ N_("_Open Memo"),
+ "<Control>o",
+ N_("View the selected memo"),
+ G_CALLBACK (action_memo_open_cb) },
+
+ { "memo-open-url",
+ "applications-internet",
+ N_("Open _Web Page"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_memo_open_url_cb) },
+
+ { "memo-save-as",
+ GTK_STOCK_SAVE_AS,
+ N_("_Save as iCalendar..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_memo_save_as_cb) }
+};
+
+static EPopupActionEntry memo_popup_entries[] = {
+
+ { "memo-list-popup-copy",
+ NULL,
+ "memo-list-copy" },
+
+ { "memo-list-popup-delete",
+ NULL,
+ "memo-list-delete" },
+
+ { "memo-list-popup-properties",
+ NULL,
+ "memo-list-properties" },
+
+ { "memo-list-popup-rename",
+ NULL,
+ "memo-list-rename" },
+
+ { "memo-list-popup-select-one",
+ NULL,
+ "memo-list-select-one" },
+
+ { "memo-popup-clipboard-copy",
+ NULL,
+ "memo-clipboard-copy" },
+
+ { "memo-popup-clipboard-cut",
+ NULL,
+ "memo-clipboard-cut" },
+
+ { "memo-popup-clipboard-paste",
+ NULL,
+ "memo-clipboard-paste" },
+
+ { "memo-popup-delete",
+ NULL,
+ "memo-delete" },
+
+ { "memo-popup-forward",
+ NULL,
+ "memo-forward" },
+
+ { "memo-popup-open",
+ NULL,
+ "memo-open" },
+
+ { "memo-popup-open-url",
+ NULL,
+ "memo-open-url" },
+
+ { "memo-popup-save-as",
+ NULL,
+ "memo-save-as" }
+};
+
+static GtkToggleActionEntry memo_toggle_entries[] = {
+
+ { "memo-preview",
+ NULL,
+ N_("Memo _Preview"),
+ "<Control>m",
+ N_("Show memo preview pane"),
+ G_CALLBACK (action_memo_preview_cb),
+ TRUE }
+};
+
+static GtkRadioActionEntry memo_filter_entries[] = {
+
+ { "memo-filter-any-category",
+ NULL,
+ N_("Any Category"),
+ NULL,
+ NULL,
+ MEMO_FILTER_ANY_CATEGORY },
+
+ { "memo-filter-unmatched",
+ NULL,
+ N_("Unmatched"),
+ NULL,
+ NULL,
+ MEMO_FILTER_UNMATCHED }
+};
+
+static GtkRadioActionEntry memo_search_entries[] = {
+
+ { "memo-search-any-field-contains",
+ NULL,
+ N_("Any field contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MEMO_SEARCH_ANY_FIELD_CONTAINS },
+
+ { "memo-search-description-contains",
+ NULL,
+ N_("Description contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MEMO_SEARCH_DESCRIPTION_CONTAINS },
+
+ { "memo-search-summary-contains",
+ NULL,
+ N_("Summary contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MEMO_SEARCH_SUMMARY_CONTAINS }
+};
+
+static GtkActionEntry lockdown_printing_entries[] = {
+
+ { "memo-list-print",
+ GTK_STOCK_PRINT,
+ NULL,
+ "<Control>p",
+ N_("Print the list of memos"),
+ G_CALLBACK (action_memo_list_print_cb) },
+
+ { "memo-list-print-preview",
+ GTK_STOCK_PRINT_PREVIEW,
+ NULL,
+ NULL,
+ N_("Preview the list of memos to be printed"),
+ G_CALLBACK (action_memo_list_print_preview_cb) },
+
+ { "memo-print",
+ GTK_STOCK_PRINT,
+ NULL,
+ NULL,
+ N_("Print the selected memo"),
+ G_CALLBACK (action_memo_print_cb) }
+};
+
+static EPopupActionEntry lockdown_printing_popup_entries[] = {
+
+ { "memo-popup-print",
+ NULL,
+ "memo-print" }
+};
+
+void
+e_memo_shell_view_actions_init (EMemoShellView *memo_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ GtkActionGroup *action_group;
+ GConfBridge *bridge;
+ GtkAction *action;
+ GObject *object;
+ const gchar *key;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ /* Memo Actions */
+ action_group = ACTION_GROUP (MEMOS);
+ gtk_action_group_add_actions (
+ action_group, memo_entries,
+ G_N_ELEMENTS (memo_entries), memo_shell_view);
+ e_action_group_add_popup_actions (
+ action_group, memo_popup_entries,
+ G_N_ELEMENTS (memo_popup_entries));
+ gtk_action_group_add_toggle_actions (
+ action_group, memo_toggle_entries,
+ G_N_ELEMENTS (memo_toggle_entries), memo_shell_view);
+ gtk_action_group_add_radio_actions (
+ action_group, memo_search_entries,
+ G_N_ELEMENTS (memo_search_entries),
+ MEMO_SEARCH_SUMMARY_CONTAINS,
+ G_CALLBACK (action_memo_search_cb), memo_shell_view);
+
+ /* Lockdown Printing Actions */
+ action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
+ gtk_action_group_add_actions (
+ action_group, lockdown_printing_entries,
+ G_N_ELEMENTS (lockdown_printing_entries), memo_shell_view);
+ e_action_group_add_popup_actions (
+ action_group, lockdown_printing_popup_entries,
+ G_N_ELEMENTS (lockdown_printing_popup_entries));
+
+ /* Bind GObject properties to GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (ACTION (MEMO_PREVIEW));
+ key = "/apps/evolution/calendar/display/show_memo_preview";
+ gconf_bridge_bind_property (bridge, key, object, "active");
+
+ /* Fine tuning. */
+
+ action = ACTION (MEMO_DELETE);
+ g_object_set (action, "short-label", _("Delete"), NULL);
+
+ g_signal_connect (
+ ACTION (GAL_SAVE_CUSTOM_VIEW), "activate",
+ G_CALLBACK (action_gal_save_custom_view_cb), memo_shell_view);
+
+ g_signal_connect (
+ ACTION (SEARCH_EXECUTE), "activate",
+ G_CALLBACK (action_search_execute_cb), memo_shell_view);
+}
+
+void
+e_memo_shell_view_update_search_filter (EMemoShellView *memo_shell_view)
+{
+ EShellContent *shell_content;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ GtkActionGroup *action_group;
+ GtkRadioAction *radio_action;
+ GList *list, *iter;
+ GSList *group;
+ gint ii;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ action_group = ACTION_GROUP (MEMOS_FILTER);
+ e_action_group_remove_all_actions (action_group);
+
+ /* Add the standard filter actions. */
+ gtk_action_group_add_radio_actions (
+ action_group, memo_filter_entries,
+ G_N_ELEMENTS (memo_filter_entries),
+ MEMO_FILTER_ANY_CATEGORY,
+ G_CALLBACK (action_search_filter_cb),
+ memo_shell_view);
+
+ /* Retrieve the radio group from an action we just added. */
+ list = gtk_action_group_list_actions (action_group);
+ radio_action = GTK_RADIO_ACTION (list->data);
+ group = gtk_radio_action_get_group (radio_action);
+ g_list_free (list);
+
+ /* Build the category actions. */
+
+ list = e_categories_get_list ();
+ for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) {
+ const gchar *category_name = iter->data;
+ const gchar *filename;
+ GtkAction *action;
+ gchar *action_name;
+
+ action_name = g_strdup_printf (
+ "memo-filter-category-%d", ii);
+ radio_action = gtk_radio_action_new (
+ action_name, category_name, NULL, NULL, ii);
+ g_free (action_name);
+
+ /* Convert the category icon file to a themed icon name. */
+ filename = e_categories_get_icon_file_for (category_name);
+ if (filename != NULL && *filename != '\0') {
+ gchar *basename;
+ gchar *cp;
+
+ basename = g_path_get_basename (filename);
+
+ /* Lose the file extension. */
+ if ((cp = strrchr (basename, '.')) != NULL)
+ *cp = '\0';
+
+ g_object_set (
+ radio_action, "icon-name", basename, NULL);
+
+ g_free (basename);
+ }
+
+ gtk_radio_action_set_group (radio_action, group);
+ group = gtk_radio_action_get_group (radio_action);
+
+ /* The action group takes ownership of the action. */
+ action = GTK_ACTION (radio_action);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (radio_action);
+ }
+ g_list_free (list);
+
+ /* Use any action in the group; doesn't matter which. */
+ e_shell_content_set_filter_action (shell_content, radio_action);
+
+ ii = MEMO_FILTER_UNMATCHED;
+ e_shell_content_add_filter_separator_after (shell_content, ii);
+}
diff --git a/modules/calendar/e-memo-shell-view-actions.h b/modules/calendar/e-memo-shell-view-actions.h
new file mode 100644
index 0000000000..d6fd3ca514
--- /dev/null
+++ b/modules/calendar/e-memo-shell-view-actions.h
@@ -0,0 +1,87 @@
+/*
+ * e-memo-shell-view-actions.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MEMO_SHELL_VIEW_ACTIONS_H
+#define E_MEMO_SHELL_VIEW_ACTIONS_H
+
+#include <shell/e-shell-window-actions.h>
+
+/* Memo Actions */
+#define E_SHELL_WINDOW_ACTION_MEMO_CLIPBOARD_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-clipboard-copy")
+#define E_SHELL_WINDOW_ACTION_MEMO_CLIPBOARD_CUT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-clipboard-cut")
+#define E_SHELL_WINDOW_ACTION_MEMO_CLIPBOARD_PASTE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-clipboard-paste")
+#define E_SHELL_WINDOW_ACTION_MEMO_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-delete")
+#define E_SHELL_WINDOW_ACTION_MEMO_FORWARD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-forward")
+#define E_SHELL_WINDOW_ACTION_MEMO_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-new")
+#define E_SHELL_WINDOW_ACTION_MEMO_OPEN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-open")
+#define E_SHELL_WINDOW_ACTION_MEMO_OPEN_URL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-open-url")
+#define E_SHELL_WINDOW_ACTION_MEMO_PREVIEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-preview")
+#define E_SHELL_WINDOW_ACTION_MEMO_PRINT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-print")
+#define E_SHELL_WINDOW_ACTION_MEMO_SAVE_AS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-save-as")
+
+/* Memo List Actions */
+#define E_SHELL_WINDOW_ACTION_MEMO_LIST_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-list-copy")
+#define E_SHELL_WINDOW_ACTION_MEMO_LIST_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-list-delete")
+#define E_SHELL_WINDOW_ACTION_MEMO_LIST_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-list-new")
+#define E_SHELL_WINDOW_ACTION_MEMO_LIST_PRINT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-list-print")
+#define E_SHELL_WINDOW_ACTION_MEMO_LIST_PRINT_PREVIEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-list-print-preview")
+#define E_SHELL_WINDOW_ACTION_MEMO_LIST_PROPERTIES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-list-properties")
+#define E_SHELL_WINDOW_ACTION_MEMO_LIST_RENAME(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-list-rename")
+#define E_SHELL_WINDOW_ACTION_MEMO_LIST_SELECT_ONE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-list-select-one")
+
+/* Memo Query Actions */
+#define E_SHELL_WINDOW_ACTION_MEMO_FILTER_ANY_CATEGORY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-filter-any-category")
+#define E_SHELL_WINDOW_ACTION_MEMO_FILTER_UNMATCHED(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-filter-unmatched")
+#define E_SHELL_WINDOW_ACTION_MEMO_SEARCH_ANY_FIELD_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-search-any-field-contains")
+#define E_SHELL_WINDOW_ACTION_MEMO_SEARCH_DESCRIPTION_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-search-description-contains")
+#define E_SHELL_WINDOW_ACTION_MEMO_SEARCH_SUMMARY_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "memo-search-summary-contains")
+
+/* Action Groups */
+#define E_SHELL_WINDOW_ACTION_GROUP_MEMOS(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "memos")
+#define E_SHELL_WINDOW_ACTION_GROUP_MEMOS_FILTER(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "memos-filter")
+
+#endif /* E_MEMO_SHELL_VIEW_ACTIONS_H */
diff --git a/modules/calendar/e-memo-shell-view-private.c b/modules/calendar/e-memo-shell-view-private.c
new file mode 100644
index 0000000000..69d6c0afe2
--- /dev/null
+++ b/modules/calendar/e-memo-shell-view-private.c
@@ -0,0 +1,524 @@
+/*
+ * e-memo-shell-view-private.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-memo-shell-view-private.h"
+
+#include "widgets/menus/gal-view-factory-etable.h"
+
+static void
+memo_shell_view_table_popup_event_cb (EShellView *shell_view,
+ GdkEventButton *event)
+{
+ const gchar *widget_path;
+
+ widget_path = "/memo-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+}
+
+static void
+memo_shell_view_table_user_created_cb (EMemoShellView *memo_shell_view,
+ EMemoTable *memo_table)
+{
+ EMemoShellSidebar *memo_shell_sidebar;
+ ECalModel *model;
+ ECal *client;
+ ESource *source;
+
+ /* This is the "Click to Add" handler. */
+
+ model = e_memo_table_get_model (memo_table);
+ client = e_cal_model_get_default_client (model);
+ source = e_cal_get_source (client);
+
+ memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar;
+ e_memo_shell_sidebar_add_source (memo_shell_sidebar, source);
+
+ e_cal_model_add_client (model, client);
+}
+
+static void
+memo_shell_view_selector_client_added_cb (EMemoShellView *memo_shell_view,
+ ECal *client)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ECalModel *model;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ model = e_memo_table_get_model (memo_table);
+
+ e_cal_model_add_client (model, client);
+}
+
+static void
+memo_shell_view_selector_client_removed_cb (EMemoShellView *memo_shell_view,
+ ECal *client)
+{
+ EMemoShellContent *memo_shell_content;
+ EMemoTable *memo_table;
+ ECalModel *model;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ model = e_memo_table_get_model (memo_table);
+
+ e_cal_model_remove_client (model, client);
+}
+
+static gboolean
+memo_shell_view_selector_popup_event_cb (EShellView *shell_view,
+ ESource *primary_source,
+ GdkEventButton *event)
+{
+ const gchar *widget_path;
+
+ widget_path = "/memo-list-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+
+ return TRUE;
+}
+
+static void
+memo_shell_view_load_view_collection (EShellViewClass *shell_view_class)
+{
+ GalViewCollection *collection;
+ GalViewFactory *factory;
+ ETableSpecification *spec;
+ const gchar *base_dir;
+ gchar *filename;
+
+ collection = shell_view_class->view_collection;
+
+ base_dir = EVOLUTION_ETSPECDIR;
+ spec = e_table_specification_new ();
+ filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL);
+ if (!e_table_specification_load_from_file (spec, filename))
+ g_critical ("Unable to load ETable specification file "
+ "for memos");
+ g_free (filename);
+
+ factory = gal_view_factory_etable_new (spec);
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+ g_object_unref (spec);
+
+ gal_view_collection_load (collection);
+}
+
+static void
+memo_shell_view_notify_view_id_cb (EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ GalViewInstance *view_instance;
+ const gchar *view_id;
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ view_instance =
+ e_memo_shell_content_get_view_instance (memo_shell_content);
+ view_id = e_shell_view_get_view_id (E_SHELL_VIEW (memo_shell_view));
+
+ /* A NULL view ID implies we're in a custom view. But you can
+ * only get to a custom view via the "Define Views" dialog, which
+ * would have already modified the view instance appropriately.
+ * Furthermore, there's no way to refer to a custom view by ID
+ * anyway, since custom views have no IDs. */
+ if (view_id == NULL)
+ return;
+
+ gal_view_instance_set_current_view_id (view_instance, view_id);
+}
+
+void
+e_memo_shell_view_private_init (EMemoShellView *memo_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ if (!gal_view_collection_loaded (shell_view_class->view_collection))
+ memo_shell_view_load_view_collection (shell_view_class);
+
+ g_signal_connect (
+ memo_shell_view, "notify::view-id",
+ G_CALLBACK (memo_shell_view_notify_view_id_cb), NULL);
+}
+
+void
+e_memo_shell_view_private_constructed (EMemoShellView *memo_shell_view)
+{
+ EMemoShellViewPrivate *priv = memo_shell_view->priv;
+ EMemoShellContent *memo_shell_content;
+ EMemoShellSidebar *memo_shell_sidebar;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+ EShellContent *shell_content;
+ EShellSidebar *shell_sidebar;
+ EShellWindow *shell_window;
+ EMemoTable *memo_table;
+ ECalModel *model;
+ ETable *table;
+ ESourceSelector *selector;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ e_shell_window_add_action_group (shell_window, "memos");
+ e_shell_window_add_action_group (shell_window, "memos-filter");
+
+ /* Cache these to avoid lots of awkward casting. */
+ priv->memo_shell_backend = g_object_ref (shell_backend);
+ priv->memo_shell_content = g_object_ref (shell_content);
+ priv->memo_shell_sidebar = g_object_ref (shell_sidebar);
+
+ memo_shell_content = E_MEMO_SHELL_CONTENT (shell_content);
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ model = e_memo_table_get_model (memo_table);
+ table = e_memo_table_get_table (memo_table);
+
+ memo_shell_sidebar = E_MEMO_SHELL_SIDEBAR (shell_sidebar);
+ selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar);
+
+ g_signal_connect_swapped (
+ memo_table, "open-component",
+ G_CALLBACK (e_memo_shell_view_open_memo),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ memo_table, "popup-event",
+ G_CALLBACK (memo_shell_view_table_popup_event_cb),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ memo_table, "status-message",
+ G_CALLBACK (e_memo_shell_view_set_status_message),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ memo_table, "user-created",
+ G_CALLBACK (memo_shell_view_table_user_created_cb),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ model, "model-changed",
+ G_CALLBACK (e_memo_shell_view_update_sidebar),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ model, "model-rows-deleted",
+ G_CALLBACK (e_memo_shell_view_update_sidebar),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ model, "model-rows-inserted",
+ G_CALLBACK (e_memo_shell_view_update_sidebar),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ table, "selection-change",
+ G_CALLBACK (e_memo_shell_view_update_sidebar),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ memo_shell_sidebar, "client-added",
+ G_CALLBACK (memo_shell_view_selector_client_added_cb),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ memo_shell_sidebar, "client-removed",
+ G_CALLBACK (memo_shell_view_selector_client_removed_cb),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ memo_shell_sidebar, "status-message",
+ G_CALLBACK (e_memo_shell_view_set_status_message),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ selector, "popup-event",
+ G_CALLBACK (memo_shell_view_selector_popup_event_cb),
+ memo_shell_view);
+
+ g_signal_connect_swapped (
+ selector, "primary-selection-changed",
+ G_CALLBACK (e_shell_view_update_actions),
+ memo_shell_view);
+
+ e_categories_register_change_listener (
+ G_CALLBACK (e_memo_shell_view_update_search_filter),
+ memo_shell_view);
+
+ e_memo_shell_view_actions_init (memo_shell_view);
+ e_memo_shell_view_update_sidebar (memo_shell_view);
+ e_memo_shell_view_update_search_filter (memo_shell_view);
+
+ e_memo_shell_view_execute_search (memo_shell_view);
+}
+
+void
+e_memo_shell_view_private_dispose (EMemoShellView *memo_shell_view)
+{
+ EMemoShellViewPrivate *priv = memo_shell_view->priv;
+
+ DISPOSE (priv->memo_shell_backend);
+ DISPOSE (priv->memo_shell_content);
+ DISPOSE (priv->memo_shell_sidebar);
+
+ if (memo_shell_view->priv->activity != NULL) {
+ /* XXX Activity is not cancellable. */
+ e_activity_complete (memo_shell_view->priv->activity);
+ g_object_unref (memo_shell_view->priv->activity);
+ memo_shell_view->priv->activity = NULL;
+ }
+}
+
+void
+e_memo_shell_view_private_finalize (EMemoShellView *memo_shell_view)
+{
+ /* XXX Nothing to do? */
+}
+
+void
+e_memo_shell_view_execute_search (EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellContent *shell_content;
+ GtkAction *action;
+ GString *string;
+ ECalComponentPreview *memo_preview;
+ EMemoTable *memo_table;
+ ECalModel *model;
+ FilterRule *rule;
+ const gchar *format;
+ const gchar *text;
+ gchar *query;
+ gchar *temp;
+ gint value;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ text = e_shell_content_get_search_text (shell_content);
+
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ action = ACTION (MEMO_SEARCH_ANY_FIELD_CONTAINS);
+ value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
+
+ if (text == NULL || *text == '\0') {
+ text = "";
+ value = MEMO_SEARCH_SUMMARY_CONTAINS;
+ }
+
+ switch (value) {
+ default:
+ text = "";
+ /* fall through */
+
+ case MEMO_SEARCH_SUMMARY_CONTAINS:
+ format = "(contains? \"summary\" %s)";
+ break;
+
+ case MEMO_SEARCH_DESCRIPTION_CONTAINS:
+ format = "(contains? \"description\" %s)";
+ break;
+
+ case MEMO_SEARCH_ANY_FIELD_CONTAINS:
+ format = "(contains? \"any\" %s)";
+ break;
+ }
+
+ /* Build the query. */
+ string = g_string_new ("");
+ e_sexp_encode_string (string, text);
+ query = g_strdup_printf (format, string->str);
+ g_string_free (string, TRUE);
+
+ /* Apply selected filter. */
+ value = e_shell_content_get_filter_value (shell_content);
+ switch (value) {
+ case MEMO_FILTER_ANY_CATEGORY:
+ break;
+
+ case MEMO_FILTER_UNMATCHED:
+ temp = g_strdup_printf (
+ "(and (has-categories? #f) %s", query);
+ g_free (query);
+ query = temp;
+ break;
+
+ default:
+ {
+ GList *categories;
+ const gchar *category_name;
+
+ categories = e_categories_get_list ();
+ category_name = g_list_nth_data (categories, value);
+ g_list_free (categories);
+
+ temp = g_strdup_printf (
+ "(and (has-categories? \"%s\") %s)",
+ category_name, query);
+ g_free (query);
+ query = temp;
+ }
+ }
+
+ /* XXX This is wrong. We need to programmatically construct a
+ * FilterRule, tell it to build code, and pass the resulting
+ * expression string to ECalModel. */
+ rule = filter_rule_new ();
+ e_shell_content_set_search_rule (shell_content, rule);
+ g_object_unref (rule);
+
+ /* Submit the query. */
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+ model = e_memo_table_get_model (memo_table);
+ e_cal_model_set_search_query (model, query);
+ g_free (query);
+
+ memo_preview =
+ e_memo_shell_content_get_memo_preview (memo_shell_content);
+ e_cal_component_preview_clear (memo_preview);
+}
+
+void
+e_memo_shell_view_open_memo (EMemoShellView *memo_shell_view,
+ ECalModelComponent *comp_data)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_MEMO_SHELL_VIEW (memo_shell_view));
+ g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data));
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ uid = icalcomponent_get_uid (comp_data->icalcomp);
+ editor = comp_editor_find_instance (uid);
+
+ if (editor != NULL)
+ goto exit;
+
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_component_set_icalcomponent (comp, clone);
+
+ if (e_cal_component_has_organizer (comp))
+ flags |= COMP_EDITOR_IS_SHARED;
+
+ if (itip_organizer_is_user (comp, comp_data->client))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ editor = memo_editor_new (comp_data->client, shell, flags);
+ comp_editor_edit_comp (editor, comp);
+
+ g_object_unref (comp);
+
+exit:
+ gtk_window_present (GTK_WINDOW (editor));
+}
+
+void
+e_memo_shell_view_set_status_message (EMemoShellView *memo_shell_view,
+ const gchar *status_message,
+ gdouble percent)
+{
+ EActivity *activity;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+
+ g_return_if_fail (E_IS_MEMO_SHELL_VIEW (memo_shell_view));
+
+ activity = memo_shell_view->priv->activity;
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+
+ if (status_message == NULL || *status_message == '\0') {
+ if (activity != NULL) {
+ e_activity_complete (activity);
+ g_object_unref (activity);
+ activity = NULL;
+ }
+
+ } else if (activity == NULL) {
+ activity = e_activity_new (status_message);
+ e_activity_set_percent (activity, percent);
+ e_shell_backend_add_activity (shell_backend, activity);
+
+ } else {
+ e_activity_set_percent (activity, percent);
+ e_activity_set_primary_text (activity, status_message);
+ }
+
+ memo_shell_view->priv->activity = activity;
+}
+
+void
+e_memo_shell_view_update_sidebar (EMemoShellView *memo_shell_view)
+{
+ EMemoShellContent *memo_shell_content;
+ EShellView *shell_view;
+ EShellSidebar *shell_sidebar;
+ EMemoTable *memo_table;
+ ECalModel *model;
+ ETable *table;
+ GString *string;
+ const gchar *format;
+ gint n_rows;
+ gint n_selected;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+
+ memo_shell_content = memo_shell_view->priv->memo_shell_content;
+ memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
+
+ model = e_memo_table_get_model (memo_table);
+ table = e_memo_table_get_table (memo_table);
+
+ n_rows = e_table_model_row_count (E_TABLE_MODEL (model));
+ n_selected = e_table_selected_count (table);
+
+ string = g_string_sized_new (64);
+
+ format = ngettext ("%d memo", "%d memos", n_rows);
+ g_string_append_printf (string, format, n_rows);
+
+ if (n_selected > 0) {
+ format = _("%d selected");
+ g_string_append_len (string, ", ", 2);
+ g_string_append_printf (string, format, n_selected);
+ }
+
+ e_shell_sidebar_set_secondary_text (shell_sidebar, string->str);
+
+ g_string_free (string, TRUE);
+}
diff --git a/modules/calendar/e-memo-shell-view-private.h b/modules/calendar/e-memo-shell-view-private.h
new file mode 100644
index 0000000000..c41eaed7c0
--- /dev/null
+++ b/modules/calendar/e-memo-shell-view-private.h
@@ -0,0 +1,126 @@
+/*
+ * e-memo-shell-view-private.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MEMO_SHELL_VIEW_PRIVATE_H
+#define E_MEMO_SHELL_VIEW_PRIVATE_H
+
+#include "e-memo-shell-view.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libedataserver/e-categories.h>
+#include <libedataserver/e-sexp.h>
+
+#include "e-util/e-dialog-utils.h"
+#include "e-util/e-error.h"
+#include "e-util/e-util.h"
+#include "e-util/gconf-bridge.h"
+#include "widgets/misc/e-popup-action.h"
+
+#include "calendar/gui/comp-util.h"
+#include "calendar/gui/e-cal-component-preview.h"
+#include "calendar/gui/e-calendar-selector.h"
+#include "calendar/gui/print.h"
+#include "calendar/gui/dialogs/calendar-setup.h"
+#include "calendar/gui/dialogs/copy-source-dialog.h"
+#include "calendar/gui/dialogs/memo-editor.h"
+
+#include "e-memo-shell-backend.h"
+#include "e-memo-shell-content.h"
+#include "e-memo-shell-sidebar.h"
+#include "e-memo-shell-view-actions.h"
+
+#define E_MEMO_SHELL_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MEMO_SHELL_VIEW, EMemoShellViewPrivate))
+
+/* Shorthand, requires a variable named "shell_window". */
+#define ACTION(name) \
+ (E_SHELL_WINDOW_ACTION_##name (shell_window))
+#define ACTION_GROUP(name) \
+ (E_SHELL_WINDOW_ACTION_GROUP_##name (shell_window))
+
+/* For use in dispose() methods. */
+#define DISPOSE(obj) \
+ G_STMT_START { \
+ if ((obj) != NULL) { g_object_unref (obj); (obj) = NULL; } \
+ } G_STMT_END
+
+/* ETable Specifications */
+#define ETSPEC_FILENAME "e-memo-table.etspec"
+
+G_BEGIN_DECLS
+
+/* Filter items are displayed in ascending order.
+ * Non-negative values are reserved for categories. */
+enum {
+ MEMO_FILTER_ANY_CATEGORY = -2,
+ MEMO_FILTER_UNMATCHED = -1
+};
+
+/* Search items are displayed in ascending order. */
+enum {
+ MEMO_SEARCH_SUMMARY_CONTAINS,
+ MEMO_SEARCH_DESCRIPTION_CONTAINS,
+ MEMO_SEARCH_ANY_FIELD_CONTAINS
+};
+
+struct _EMemoShellViewPrivate {
+
+ /* These are just for convenience. */
+ EMemoShellBackend *memo_shell_backend;
+ EMemoShellContent *memo_shell_content;
+ EMemoShellSidebar *memo_shell_sidebar;
+
+ EActivity *activity;
+};
+
+void e_memo_shell_view_private_init
+ (EMemoShellView *memo_shell_view,
+ EShellViewClass *shell_view_class);
+void e_memo_shell_view_private_constructed
+ (EMemoShellView *memo_shell_view);
+void e_memo_shell_view_private_dispose
+ (EMemoShellView *memo_shell_view);
+void e_memo_shell_view_private_finalize
+ (EMemoShellView *memo_shell_view);
+
+/* Private Utilities */
+
+void e_memo_shell_view_actions_init
+ (EMemoShellView *memo_shell_view);
+void e_memo_shell_view_execute_search
+ (EMemoShellView *memo_shell_view);
+void e_memo_shell_view_open_memo
+ (EMemoShellView *memo_shell_view,
+ ECalModelComponent *comp_data);
+void e_memo_shell_view_set_status_message
+ (EMemoShellView *memo_shell_view,
+ const gchar *status_message,
+ gdouble percent);
+void e_memo_shell_view_update_sidebar
+ (EMemoShellView *memo_shell_view);
+void e_memo_shell_view_update_search_filter
+ (EMemoShellView *memo_shell_view);
+
+G_END_DECLS
+
+#endif /* E_MEMO_SHELL_VIEW_PRIVATE_H */
diff --git a/modules/calendar/e-memo-shell-view.c b/modules/calendar/e-memo-shell-view.c
new file mode 100644
index 0000000000..e2964b061c
--- /dev/null
+++ b/modules/calendar/e-memo-shell-view.c
@@ -0,0 +1,222 @@
+/*
+ * e-memo-shell-view.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-memo-shell-view-private.h"
+
+static gpointer parent_class;
+static GType memo_shell_view_type;
+
+static void
+memo_shell_view_dispose (GObject *object)
+{
+ e_memo_shell_view_private_dispose (E_MEMO_SHELL_VIEW (object));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+memo_shell_view_finalize (GObject *object)
+{
+ e_memo_shell_view_private_finalize (E_MEMO_SHELL_VIEW (object));
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+memo_shell_view_constructed (GObject *object)
+{
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ e_memo_shell_view_private_constructed (E_MEMO_SHELL_VIEW (object));
+}
+
+static void
+memo_shell_view_update_actions (EShellView *shell_view)
+{
+ EMemoShellViewPrivate *priv;
+ EShellContent *shell_content;
+ EShellSidebar *shell_sidebar;
+ EShellWindow *shell_window;
+ GtkAction *action;
+ const gchar *label;
+ gboolean sensitive;
+ guint32 state;
+
+ /* Be descriptive. */
+ gboolean any_memos_selected;
+ gboolean has_primary_source;
+ gboolean multiple_memos_selected;
+ gboolean primary_source_is_system;
+ gboolean selection_has_url;
+ gboolean single_memo_selected;
+ gboolean sources_are_editable;
+
+ priv = E_MEMO_SHELL_VIEW_GET_PRIVATE (shell_view);
+
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ state = e_shell_content_check_state (shell_content);
+
+ single_memo_selected =
+ (state & E_MEMO_SHELL_CONTENT_SELECTION_SINGLE);
+ multiple_memos_selected =
+ (state & E_MEMO_SHELL_CONTENT_SELECTION_MULTIPLE);
+ sources_are_editable =
+ (state & E_MEMO_SHELL_CONTENT_SELECTION_CAN_EDIT);
+ selection_has_url =
+ (state & E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL);
+
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ state = e_shell_sidebar_check_state (shell_sidebar);
+
+ has_primary_source =
+ (state & E_MEMO_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE);
+ primary_source_is_system =
+ (state & E_MEMO_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM);
+
+ any_memos_selected =
+ (single_memo_selected || multiple_memos_selected);
+
+ action = ACTION (MEMO_CLIPBOARD_COPY);
+ sensitive = any_memos_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_CLIPBOARD_CUT);
+ sensitive = any_memos_selected && sources_are_editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_CLIPBOARD_PASTE);
+ sensitive = sources_are_editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_DELETE);
+ sensitive = any_memos_selected && sources_are_editable;
+ gtk_action_set_sensitive (action, sensitive);
+ if (multiple_memos_selected)
+ label = _("Delete Memos");
+ else
+ label = _("Delete Memo");
+ g_object_set (action, "label", label, NULL);
+
+ action = ACTION (MEMO_FORWARD);
+ sensitive = single_memo_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_LIST_COPY);
+ sensitive = has_primary_source;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_LIST_DELETE);
+ sensitive = has_primary_source && !primary_source_is_system;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_LIST_PROPERTIES);
+ sensitive = has_primary_source;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_LIST_RENAME);
+ sensitive = has_primary_source;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_OPEN);
+ sensitive = single_memo_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_OPEN_URL);
+ sensitive = single_memo_selected && selection_has_url;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_PRINT);
+ sensitive = single_memo_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MEMO_SAVE_AS);
+ sensitive = single_memo_selected;
+ gtk_action_set_sensitive (action, sensitive);
+}
+
+static void
+memo_shell_view_class_init (EMemoShellViewClass *class,
+ GTypeModule *type_module)
+{
+ GObjectClass *object_class;
+ EShellViewClass *shell_view_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMemoShellViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = memo_shell_view_dispose;
+ object_class->finalize = memo_shell_view_finalize;
+ object_class->constructed = memo_shell_view_constructed;
+
+ shell_view_class = E_SHELL_VIEW_CLASS (class);
+ shell_view_class->label = _("Memos");
+ shell_view_class->icon_name = "evolution-memos";
+ shell_view_class->ui_definition = "evolution-memos.ui";
+ shell_view_class->ui_manager_id = "org.gnome.evolution.memos";
+ shell_view_class->search_options = "/memo-search-options";
+ shell_view_class->search_rules = "memotypes.xml";
+ shell_view_class->new_shell_content = e_memo_shell_content_new;
+ shell_view_class->new_shell_sidebar = e_memo_shell_sidebar_new;
+ shell_view_class->update_actions = memo_shell_view_update_actions;
+}
+
+static void
+memo_shell_view_init (EMemoShellView *memo_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ memo_shell_view->priv =
+ E_MEMO_SHELL_VIEW_GET_PRIVATE (memo_shell_view);
+
+ e_memo_shell_view_private_init (memo_shell_view, shell_view_class);
+}
+
+GType
+e_memo_shell_view_get_type (void)
+{
+ return memo_shell_view_type;
+}
+
+void
+e_memo_shell_view_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (EMemoShellViewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) memo_shell_view_class_init,
+ (GClassFinalizeFunc) NULL,
+ type_module,
+ sizeof (EMemoShellView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) memo_shell_view_init,
+ NULL /* value_table */
+ };
+
+ memo_shell_view_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_VIEW,
+ "EMemoShellView", &type_info, 0);
+}
diff --git a/modules/calendar/e-memo-shell-view.h b/modules/calendar/e-memo-shell-view.h
new file mode 100644
index 0000000000..686ae734c9
--- /dev/null
+++ b/modules/calendar/e-memo-shell-view.h
@@ -0,0 +1,67 @@
+/*
+ * e-memo-shell-view.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MEMO_SHELL_VIEW_H
+#define E_MEMO_SHELL_VIEW_H
+
+#include <shell/e-shell-view.h>
+#include <libedataserver/e-source-list.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MEMO_SHELL_VIEW \
+ (e_memo_shell_view_get_type ())
+#define E_MEMO_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MEMO_SHELL_VIEW, EMemoShellView))
+#define E_MEMO_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MEMO_SHELL_VIEW, EMemoShellViewClass))
+#define E_IS_MEMO_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MEMO_SHELL_VIEW))
+#define E_IS_MEMO_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MEMO_SHELL_VIEW))
+#define E_MEMO_SHELL_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MEMO_SHELL_VIEW, EMemoShellViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMemoShellView EMemoShellView;
+typedef struct _EMemoShellViewClass EMemoShellViewClass;
+typedef struct _EMemoShellViewPrivate EMemoShellViewPrivate;
+
+struct _EMemoShellView {
+ EShellView parent;
+ EMemoShellViewPrivate *priv;
+};
+
+struct _EMemoShellViewClass {
+ EShellViewClass parent_class;
+};
+
+GType e_memo_shell_view_get_type (void);
+void e_memo_shell_view_register_type (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_MEMO_SHELL_VIEW_H */
diff --git a/modules/calendar/e-task-shell-backend.c b/modules/calendar/e-task-shell-backend.c
new file mode 100644
index 0000000000..6bded3e412
--- /dev/null
+++ b/modules/calendar/e-task-shell-backend.c
@@ -0,0 +1,621 @@
+/*
+ * e-task-shell-backend.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-task-shell-backend.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libecal/e-cal.h>
+#include <libedataserver/e-url.h>
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-list.h>
+#include <libedataserver/e-source-group.h>
+
+#include "shell/e-shell.h"
+#include "shell/e-shell-backend.h"
+#include "shell/e-shell-window.h"
+
+#include "calendar/common/authentication.h"
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/comp-util.h"
+#include "calendar/gui/dialogs/calendar-setup.h"
+#include "calendar/gui/dialogs/task-editor.h"
+
+#include "e-task-shell-content.h"
+#include "e-task-shell-migrate.h"
+#include "e-task-shell-sidebar.h"
+#include "e-task-shell-view.h"
+
+#define E_TASK_SHELL_BACKEND_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_TASK_SHELL_BACKEND, ETaskShellBackendPrivate))
+
+#define WEB_BASE_URI "webcal://"
+#define PERSONAL_RELATIVE_URI "system"
+
+struct _ETaskShellBackendPrivate {
+ ESourceList *source_list;
+};
+
+enum {
+ PROP_0,
+ PROP_SOURCE_LIST
+};
+
+static gpointer parent_class;
+static GType task_shell_backend_type;
+
+static void
+task_module_ensure_sources (EShellBackend *shell_backend)
+{
+ /* XXX This is basically the same algorithm across all modules.
+ * Maybe we could somehow integrate this into EShellBackend? */
+
+ ETaskShellBackendPrivate *priv;
+ ESourceGroup *on_this_computer;
+ ESourceGroup *on_the_web;
+ ESource *personal;
+ GSList *groups, *iter;
+ const gchar *data_dir;
+ const gchar *name;
+ gchar *base_uri;
+ gchar *filename;
+
+ on_this_computer = NULL;
+ on_the_web = NULL;
+ personal = NULL;
+
+ priv = E_TASK_SHELL_BACKEND_GET_PRIVATE (shell_backend);
+
+ if (!e_cal_get_sources (&priv->source_list, E_CAL_SOURCE_TYPE_TODO, NULL)) {
+ g_warning ("Could not get task sources from GConf!");
+ return;
+ }
+
+ data_dir = e_shell_backend_get_data_dir (shell_backend);
+ filename = g_build_filename (data_dir, "local", NULL);
+ base_uri = g_filename_to_uri (filename, NULL, NULL);
+ g_free (filename);
+
+ groups = e_source_list_peek_groups (priv->source_list);
+ for (iter = groups; iter != NULL; iter = iter->next) {
+ ESourceGroup *source_group = iter->data;
+ const gchar *group_base_uri;
+
+ group_base_uri = e_source_group_peek_base_uri (source_group);
+
+ /* Compare only "file://" part. If the user's home
+ * changes, we do not want to create another group. */
+ if (on_this_computer == NULL &&
+ strncmp (base_uri, group_base_uri, 7) == 0)
+ on_this_computer = source_group;
+
+ else if (on_the_web == NULL &&
+ strcmp (WEB_BASE_URI, group_base_uri) == 0)
+ on_the_web = source_group;
+ }
+
+ name = _("On This Computer");
+
+ if (on_this_computer != NULL) {
+ GSList *sources;
+ const gchar *group_base_uri;
+
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (on_this_computer, name);
+
+ sources = e_source_group_peek_sources (on_this_computer);
+ group_base_uri = e_source_group_peek_base_uri (on_this_computer);
+
+ /* Make sure this group includes a "Personal" source. */
+ for (iter = sources; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+ const gchar *relative_uri;
+
+ relative_uri = e_source_peek_relative_uri (source);
+ if (relative_uri == NULL)
+ continue;
+
+ if (strcmp (PERSONAL_RELATIVE_URI, relative_uri) != 0)
+ continue;
+
+ personal = source;
+ break;
+ }
+
+ /* Make sure we have the correct base URI. This can
+ * change when the user's home directory changes. */
+ if (strcmp (base_uri, group_base_uri) != 0) {
+ e_source_group_set_base_uri (
+ on_this_computer, base_uri);
+
+ /* XXX We shouldn't need this sync call here as
+ * set_base_uri() results in synching to GConf,
+ * but that happens in an idle loop and too late
+ * to prevent the user from seeing a "Cannot
+ * Open ... because of invalid URI" error. */
+ e_source_list_sync (priv->source_list, NULL);
+ }
+
+ } else {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, base_uri);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+ }
+
+ name = _("Personal");
+
+ if (personal == NULL) {
+ ESource *source;
+ GSList *selected;
+ gchar *primary;
+
+ source = e_source_new (name, PERSONAL_RELATIVE_URI);
+ e_source_group_add_source (on_this_computer, source, -1);
+ g_object_unref (source);
+
+ primary = calendar_config_get_primary_tasks ();
+ selected = calendar_config_get_tasks_selected ();
+
+ if (primary == NULL && selected == NULL) {
+ const gchar *uid;
+
+ uid = e_source_peek_uid (source);
+ selected = g_slist_prepend (NULL, g_strdup (uid));
+
+ calendar_config_set_primary_tasks (uid);
+ calendar_config_set_tasks_selected (selected);
+ }
+
+ g_slist_foreach (selected, (GFunc) g_free, NULL);
+ g_slist_free (selected);
+ g_free (primary);
+ } else {
+ /* Force the source name to the current locale. */
+ e_source_set_name (personal, name);
+ }
+
+ name = _("On The Web");
+
+ if (on_the_web == NULL) {
+ ESourceGroup *source_group;
+
+ source_group = e_source_group_new (name, WEB_BASE_URI);
+ e_source_list_add_group (priv->source_list, source_group, -1);
+ g_object_unref (source_group);
+ } else {
+ /* Force the group name to the current locale. */
+ e_source_group_set_name (on_the_web, name);
+ }
+
+ g_free (base_uri);
+}
+
+static void
+task_module_cal_opened_cb (ECal *cal,
+ ECalendarStatus status,
+ GtkAction *action)
+{
+ EShell *shell;
+ ECalComponent *comp;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ const gchar *action_name;
+
+ /* FIXME Pass this in. */
+ shell = e_shell_get_default ();
+
+ /* XXX Handle errors better. */
+ if (status != E_CALENDAR_STATUS_OK)
+ return;
+
+ action_name = gtk_action_get_name (action);
+
+ flags |= COMP_EDITOR_NEW_ITEM;
+ if (strcmp (action_name, "task-assigned-new") == 0) {
+ flags |= COMP_EDITOR_IS_ASSIGNED;
+ flags |= COMP_EDITOR_USER_ORG;
+ }
+
+ editor = task_editor_new (cal, shell, flags);
+ comp = cal_comp_task_new_with_defaults (cal);
+ comp_editor_edit_comp (editor, comp);
+
+ gtk_window_present (GTK_WINDOW (editor));
+
+ g_object_unref (comp);
+ g_object_unref (cal);
+}
+
+static void
+action_task_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ ECal *cal = NULL;
+ ECalSourceType source_type;
+ ESourceList *source_list;
+ gchar *uid;
+
+ /* This callback is used for both tasks and assigned tasks. */
+
+ source_type = E_CAL_SOURCE_TYPE_TODO;
+
+ if (!e_cal_get_sources (&source_list, source_type, NULL)) {
+ g_warning ("Could not get task sources from GConf!");
+ return;
+ }
+
+ uid = calendar_config_get_primary_tasks ();
+
+ if (uid != NULL) {
+ ESource *source;
+
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ if (source != NULL)
+ cal = auth_new_cal_from_source (source, source_type);
+ g_free (uid);
+ }
+
+ if (cal == NULL)
+ cal = auth_new_cal_from_default (source_type);
+
+ g_return_if_fail (cal != NULL);
+
+ g_signal_connect (
+ cal, "cal-opened",
+ G_CALLBACK (task_module_cal_opened_cb), action);
+
+ e_cal_open_async (cal, FALSE);
+}
+
+static void
+action_task_list_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ calendar_setup_new_task_list (GTK_WINDOW (shell_window));
+}
+
+static GtkActionEntry item_entries[] = {
+
+ { "task-new",
+ "stock_task",
+ NC_("New", "_Task"),
+ "<Shift><Control>t",
+ N_("Create a new task"),
+ G_CALLBACK (action_task_new_cb) },
+
+ { "task-assigned-new",
+ "stock_task",
+ N_("Assigne_d Task"),
+ NULL,
+ N_("Create a new assigned task"),
+ G_CALLBACK (action_task_new_cb) }
+};
+
+static GtkActionEntry source_entries[] = {
+
+ { "task-list-new",
+ "stock_todo",
+ NC_("New", "Tas_k List"),
+ NULL,
+ N_("Create a new task list"),
+ G_CALLBACK (action_task_list_new_cb) }
+};
+
+static gboolean
+task_module_handle_uri_cb (EShellBackend *shell_backend,
+ const gchar *uri)
+{
+ EShell *shell;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ ECal *client;
+ ECalComponent *comp;
+ ESource *source;
+ ESourceList *source_list;
+ ECalSourceType source_type;
+ EUri *euri;
+ icalcomponent *icalcomp;
+ icalproperty *icalprop;
+ const gchar *cp;
+ gchar *source_uid = NULL;
+ gchar *comp_uid = NULL;
+ gchar *comp_rid = NULL;
+ gboolean handled = FALSE;
+ GError *error = NULL;
+
+ source_type = E_CAL_SOURCE_TYPE_TODO;
+ shell = e_shell_backend_get_shell (shell_backend);
+
+ if (strncmp (uri, "task:", 5) != 0)
+ return FALSE;
+
+ euri = e_uri_new (uri);
+ cp = euri->query;
+ if (cp == NULL)
+ goto exit;
+
+ while (*cp != '\0') {
+ gchar *header;
+ gchar *content;
+ gsize header_len;
+ gsize content_len;
+
+ header_len = strcspn (cp, "=&");
+
+ /* If it's malformed, give up. */
+ if (cp[header_len] != '=')
+ break;
+
+ header = (gchar *) cp;
+ header[header_len] = '\0';
+ cp += header_len + 1;
+
+ content_len = strcspn (cp, "&");
+
+ content = g_strndup (cp, content_len);
+ if (g_ascii_strcasecmp (header, "source-uid") == 0)
+ source_uid = g_strdup (content);
+ else if (g_ascii_strcasecmp (header, "comp-uid") == 0)
+ comp_uid = g_strdup (content);
+ else if (g_ascii_strcasecmp (header, "comp-rid") == 0)
+ comp_rid = g_strdup (content);
+ g_free (content);
+
+ cp += content_len;
+ if (*cp == '&') {
+ cp++;
+ if (strcmp (cp, "amp;") == 0)
+ cp += 4;
+ }
+ }
+
+ if (source_uid != NULL || comp_uid != NULL)
+ goto exit;
+
+ /* URI is valid, so consider it handled. Whether
+ * we successfully open it is another matter... */
+ handled = TRUE;
+
+ if (!e_cal_get_sources (&source_list, source_type, NULL)) {
+ g_printerr ("Could not get task sources from GConf!\n");
+ goto exit;
+ }
+
+ source = e_source_list_peek_source_by_uid (source_list, source_uid);
+ if (source == NULL) {
+ g_printerr ("No source for UID `%s'\n", source_uid);
+ g_object_unref (source_list);
+ goto exit;
+ }
+
+ client = auth_new_cal_from_source (source, source_type);
+ if (client == NULL || !e_cal_open (client, TRUE, &error)) {
+ g_printerr ("%s\n", error->message);
+ g_object_unref (source_list);
+ g_error_free (error);
+ goto exit;
+ }
+
+ /* XXX Copied from e_task_shell_view_open_task().
+ * Clearly a new utility function is needed. */
+
+ editor = comp_editor_find_instance (comp_uid);
+
+ if (editor != NULL)
+ goto present;
+
+ if (!e_cal_get_object (client, comp_uid, comp_rid, &icalcomp, &error)) {
+ g_printerr ("%s\n", error->message);
+ g_object_unref (source_list);
+ g_error_free (error);
+ goto exit;
+ }
+
+ comp = e_cal_component_new ();
+ e_cal_component_set_icalcomponent (comp, icalcomp);
+
+ icalprop = icalcomponent_get_first_property (
+ icalcomp, ICAL_ATTENDEE_PROPERTY);
+ if (icalprop != NULL)
+ flags |= COMP_EDITOR_IS_ASSIGNED;
+
+ if (itip_organizer_is_user (comp, client))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ if (!e_cal_component_has_attendees (comp))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ editor = task_editor_new (client, shell, flags);
+ comp_editor_edit_comp (editor, comp);
+
+ g_object_unref (comp);
+
+present:
+ gtk_window_present (GTK_WINDOW (editor));
+
+ g_object_unref (source_list);
+ g_object_unref (client);
+
+exit:
+ g_free (source_uid);
+ g_free (comp_uid);
+ g_free (comp_rid);
+
+ e_uri_free (euri);
+
+ return handled;
+}
+
+static void
+task_module_window_created_cb (EShellBackend *shell_backend,
+ GtkWindow *window)
+{
+ const gchar *module_name;
+
+ if (!E_IS_SHELL_WINDOW (window))
+ return;
+
+ module_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name;
+
+ e_shell_window_register_new_item_actions (
+ E_SHELL_WINDOW (window), module_name,
+ item_entries, G_N_ELEMENTS (item_entries));
+
+ e_shell_window_register_new_source_actions (
+ E_SHELL_WINDOW (window), module_name,
+ source_entries, G_N_ELEMENTS (source_entries));
+}
+
+static void
+task_shell_backend_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SOURCE_LIST:
+ g_value_set_object (
+ value,
+ e_task_shell_backend_get_source_list (
+ E_TASK_SHELL_BACKEND (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+task_shell_backend_dispose (GObject *object)
+{
+ ETaskShellBackendPrivate *priv;
+
+ priv = E_TASK_SHELL_BACKEND_GET_PRIVATE (object);
+
+ if (priv->source_list != NULL) {
+ g_object_unref (priv->source_list);
+ priv->source_list = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+task_shell_backend_constructed (GObject *object)
+{
+ EShell *shell;
+ EShellBackend *shell_backend;
+
+ shell_backend = E_SHELL_BACKEND (object);
+ shell = e_shell_backend_get_shell (shell_backend);
+
+ task_module_ensure_sources (shell_backend);
+
+ g_signal_connect_swapped (
+ shell, "handle-uri",
+ G_CALLBACK (task_module_handle_uri_cb), shell_backend);
+
+ g_signal_connect_swapped (
+ shell, "window-created",
+ G_CALLBACK (task_module_window_created_cb), shell_backend);
+}
+
+static void
+task_shell_backend_class_init (ETaskShellBackendClass *class)
+{
+ GObjectClass *object_class;
+ EShellBackendClass *shell_backend_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ETaskShellBackendPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = task_shell_backend_get_property;
+ object_class->dispose = task_shell_backend_dispose;
+ object_class->constructed = task_shell_backend_constructed;
+
+ shell_backend_class = E_SHELL_BACKEND_CLASS (class);
+ shell_backend_class->shell_view_type = E_TYPE_TASK_SHELL_VIEW;
+ shell_backend_class->name = "tasks";
+ shell_backend_class->aliases = "";
+ shell_backend_class->schemes = "task";
+ shell_backend_class->sort_order = 600;
+ shell_backend_class->start = NULL;
+ shell_backend_class->is_busy = NULL;
+ shell_backend_class->shutdown = NULL;
+ shell_backend_class->migrate = e_task_shell_backend_migrate;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SOURCE_LIST,
+ g_param_spec_object (
+ "source-list",
+ _("Source List"),
+ _("The registry of task lists"),
+ E_TYPE_SOURCE_LIST,
+ G_PARAM_READABLE));
+}
+
+static void
+task_shell_backend_init (ETaskShellBackend *task_shell_backend)
+{
+ task_shell_backend->priv =
+ E_TASK_SHELL_BACKEND_GET_PRIVATE (task_shell_backend);
+}
+
+GType
+e_task_shell_backend_get_type (void)
+{
+ return task_shell_backend_type;
+}
+
+void
+e_task_shell_backend_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (ETaskShellBackendClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) task_shell_backend_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (ETaskShellBackend),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) task_shell_backend_init,
+ NULL /* value_table */
+ };
+
+ task_shell_backend_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_BACKEND,
+ "ETaskShellBackend", &type_info, 0);
+}
+
+ESourceList *
+e_task_shell_backend_get_source_list (ETaskShellBackend *task_shell_backend)
+{
+ g_return_val_if_fail (
+ E_IS_TASK_SHELL_BACKEND (task_shell_backend), NULL);
+
+ return task_shell_backend->priv->source_list;
+}
diff --git a/modules/calendar/e-task-shell-backend.h b/modules/calendar/e-task-shell-backend.h
new file mode 100644
index 0000000000..63b157ad85
--- /dev/null
+++ b/modules/calendar/e-task-shell-backend.h
@@ -0,0 +1,70 @@
+/*
+ * e-task-shell-backend.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_TASK_SHELL_BACKEND_H
+#define E_TASK_SHELL_BACKEND_H
+
+#include <shell/e-shell-backend.h>
+#include <libedataserver/e-source-list.h>
+
+/* Standard GObject macros */
+#define E_TYPE_TASK_SHELL_BACKEND \
+ (e_task_shell_backend_get_type ())
+#define E_TASK_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_TASK_SHELL_BACKEND, ETaskShellBackend))
+#define E_TASK_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_TASK_SHELL_BACKEND, ETaskShellBackendClass))
+#define E_IS_TASK_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_TASK_SHELL_BACKEND))
+#define E_IS_TASK_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_TASK_SHELL_BACKEND))
+#define E_TASK_SHELL_BACKEND_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_TASK_SHELL_BACKEND, ETaskShellBackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ETaskShellBackend ETaskShellBackend;
+typedef struct _ETaskShellBackendClass ETaskShellBackendClass;
+typedef struct _ETaskShellBackendPrivate ETaskShellBackendPrivate;
+
+struct _ETaskShellBackend {
+ EShellBackend parent;
+ ETaskShellBackendPrivate *priv;
+};
+
+struct _ETaskShellBackendClass {
+ EShellBackendClass parent_class;
+};
+
+GType e_task_shell_backend_get_type (void);
+void e_task_shell_backend_register_type
+ (GTypeModule *type_module);
+ESourceList * e_task_shell_backend_get_source_list
+ (ETaskShellBackend *task_shell_backend);
+
+G_END_DECLS
+
+#endif /* E_TASK_SHELL_BACKEND_H */
diff --git a/modules/calendar/e-task-shell-content.c b/modules/calendar/e-task-shell-content.c
new file mode 100644
index 0000000000..0177c6c3c3
--- /dev/null
+++ b/modules/calendar/e-task-shell-content.c
@@ -0,0 +1,700 @@
+/*
+ * e-task-shell-content.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-task-shell-content.h"
+
+#include <glib/gi18n.h>
+
+#include "e-util/gconf-bridge.h"
+
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/comp-util.h"
+#include "calendar/gui/e-cal-model-tasks.h"
+#include "calendar/gui/e-calendar-table.h"
+#include "calendar/gui/e-calendar-table-config.h"
+
+#include "widgets/menus/gal-view-etable.h"
+
+#define E_TASK_SHELL_CONTENT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_TASK_SHELL_CONTENT, ETaskShellContentPrivate))
+
+#define E_CALENDAR_TABLE_DEFAULT_STATE \
+ "<?xml version=\"1.0\"?>" \
+ "<ETableState>" \
+ " <column source=\"13\"/>" \
+ " <column source=\"14\"/>" \
+ " <column source=\"9\"/>" \
+ " <column source=\"5\"/>" \
+ " <grouping/>" \
+ "</ETableState>"
+
+struct _ETaskShellContentPrivate {
+ GtkWidget *paned;
+ GtkWidget *task_table;
+ GtkWidget *task_preview;
+
+ ECalModel *task_model;
+ ECalendarTableConfig *table_config;
+ GalViewInstance *view_instance;
+
+ gchar *current_uid;
+};
+
+enum {
+ PROP_0,
+ PROP_MODEL,
+ PROP_PREVIEW_VISIBLE
+};
+
+enum {
+ TARGET_VCALENDAR
+};
+
+static GtkTargetEntry drag_types[] = {
+ { (gchar *) "text/calendar", 0, TARGET_VCALENDAR },
+ { (gchar *) "text/x-calendar", 0, TARGET_VCALENDAR }
+};
+
+static gpointer parent_class;
+static GType task_shell_content_type;
+
+static void
+task_shell_content_display_view_cb (ETaskShellContent *task_shell_content,
+ GalView *gal_view)
+{
+ ECalendarTable *task_table;
+ ETable *table;
+
+ if (!GAL_IS_VIEW_ETABLE (gal_view))
+ return;
+
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ table = e_calendar_table_get_table (task_table);
+
+ gal_view_etable_attach_table (GAL_VIEW_ETABLE (gal_view), table);
+}
+
+static void
+task_shell_content_table_foreach_cb (gint model_row,
+ gpointer user_data)
+{
+ ECalModelComponent *comp_data;
+ icalcomponent *clone;
+ icalcomponent *vcal;
+ gchar *string;
+
+ struct {
+ ECalModel *model;
+ GSList *list;
+ } *foreach_data = user_data;
+
+ comp_data = e_cal_model_get_component_at (
+ foreach_data->model, model_row);
+
+ vcal = e_cal_util_new_top_level ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_util_add_timezones_from_component (vcal, comp_data->icalcomp);
+ icalcomponent_add_component (vcal, clone);
+
+ /* String is owned by libical; do not free. */
+ string = icalcomponent_as_ical_string (vcal);
+ if (string != NULL) {
+ ESource *source;
+ const gchar *source_uid;
+
+ source = e_cal_get_source (comp_data->client);
+ source_uid = e_source_peek_uid (source);
+
+ foreach_data->list = g_slist_prepend (
+ foreach_data->list,
+ g_strdup_printf ("%s\n%s", source_uid, string));
+ }
+
+ icalcomponent_free (vcal);
+}
+
+static void
+task_shell_content_table_drag_data_get_cb (ETaskShellContent *task_shell_content,
+ gint row,
+ gint col,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ ECalendarTable *task_table;
+ ETable *table;
+
+ struct {
+ ECalModel *model;
+ GSList *list;
+ } foreach_data;
+
+ if (info != TARGET_VCALENDAR)
+ return;
+
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ table = e_calendar_table_get_table (task_table);
+
+ foreach_data.model = e_calendar_table_get_model (task_table);
+ foreach_data.list = NULL;
+
+ e_table_selected_row_foreach (
+ table, task_shell_content_table_foreach_cb,
+ &foreach_data);
+
+ if (foreach_data.list != NULL) {
+ cal_comp_selection_set_string_list (
+ selection_data, foreach_data.list);
+ g_slist_foreach (foreach_data.list, (GFunc) g_free, NULL);
+ g_slist_free (foreach_data.list);
+ }
+}
+
+static void
+task_shell_content_table_drag_data_delete_cb (ETaskShellContent *task_shell_content,
+ gint row,
+ gint col,
+ GdkDragContext *context)
+{
+ /* Moved components are deleted from source immediately when moved,
+ * because some of them can be part of destination source, and we
+ * don't want to delete not-moved tasks. There is no such information
+ * which event has been moved and which not, so skip this method. */
+}
+
+static void
+task_shell_content_cursor_change_cb (ETaskShellContent *task_shell_content,
+ gint row,
+ ETable *table)
+{
+ ECalComponentPreview *task_preview;
+ ECalendarTable *task_table;
+ ECalModel *task_model;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ const gchar *uid;
+
+ task_model = e_task_shell_content_get_task_model (task_shell_content);
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ task_preview = e_task_shell_content_get_task_preview (task_shell_content);
+
+ if (e_table_selected_count (table) != 1) {
+ e_cal_component_preview_clear (task_preview);
+ return;
+ }
+
+ row = e_table_get_cursor_row (table);
+ comp_data = e_cal_model_get_component_at (task_model, row);
+
+ comp = e_cal_component_new ();
+ e_cal_component_set_icalcomponent (
+ comp, icalcomponent_new_clone (comp_data->icalcomp));
+ e_cal_component_preview_display (
+ task_preview, comp_data->client, comp);
+
+ e_cal_component_get_uid (comp, &uid);
+ g_free (task_shell_content->priv->current_uid);
+ task_shell_content->priv->current_uid = g_strdup (uid);
+
+ g_object_unref (comp);
+}
+
+static void
+task_shell_content_selection_change_cb (ETaskShellContent *task_shell_content,
+ ETable *table)
+{
+ ECalComponentPreview *task_preview;
+
+ task_preview = e_task_shell_content_get_task_preview (task_shell_content);
+
+ if (e_table_selected_count (table) != 1)
+ e_cal_component_preview_clear (task_preview);
+}
+
+static void
+task_shell_content_model_row_changed_cb (ETaskShellContent *task_shell_content,
+ gint row,
+ ETableModel *model)
+{
+ ECalModelComponent *comp_data;
+ ECalendarTable *task_table;
+ ETable *table;
+ const gchar *current_uid;
+ const gchar *uid;
+
+ current_uid = task_shell_content->priv->current_uid;
+ if (current_uid == NULL)
+ return;
+
+ comp_data = e_cal_model_get_component_at (E_CAL_MODEL (model), row);
+ if (comp_data == NULL)
+ return;
+
+ uid = icalcomponent_get_uid (comp_data->icalcomp);
+ if (g_strcmp0 (uid, current_uid) != 0)
+ return;
+
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ table = e_calendar_table_get_table (task_table);
+
+ task_shell_content_cursor_change_cb (task_shell_content, 0, table);
+}
+
+static void
+task_shell_content_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_PREVIEW_VISIBLE:
+ e_task_shell_content_set_preview_visible (
+ E_TASK_SHELL_CONTENT (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+task_shell_content_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_MODEL:
+ g_value_set_object (
+ value, e_task_shell_content_get_task_model (
+ E_TASK_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_PREVIEW_VISIBLE:
+ g_value_set_boolean (
+ value, e_task_shell_content_get_preview_visible (
+ E_TASK_SHELL_CONTENT (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+task_shell_content_dispose (GObject *object)
+{
+ ETaskShellContentPrivate *priv;
+
+ priv = E_TASK_SHELL_CONTENT_GET_PRIVATE (object);
+
+ if (priv->paned != NULL) {
+ g_object_unref (priv->paned);
+ priv->paned = NULL;
+ }
+
+ if (priv->task_table != NULL) {
+ g_object_unref (priv->task_table);
+ priv->task_table = NULL;
+ }
+
+ if (priv->task_preview != NULL) {
+ g_object_unref (priv->task_preview);
+ priv->task_preview = NULL;
+ }
+
+ if (priv->task_model != NULL) {
+ g_object_unref (priv->task_model);
+ priv->task_model = NULL;
+ }
+
+ if (priv->table_config != NULL) {
+ g_object_unref (priv->table_config);
+ priv->table_config = NULL;
+ }
+
+ if (priv->view_instance != NULL) {
+ g_object_unref (priv->view_instance);
+ priv->view_instance = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+task_shell_content_finalize (GObject *object)
+{
+ ETaskShellContentPrivate *priv;
+
+ priv = E_TASK_SHELL_CONTENT_GET_PRIVATE (object);
+
+ g_free (priv->current_uid);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+task_shell_content_constructed (GObject *object)
+{
+ ETaskShellContentPrivate *priv;
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ GalViewInstance *view_instance;
+ ETable *table;
+ GConfBridge *bridge;
+ GtkWidget *container;
+ GtkWidget *widget;
+ const gchar *key;
+
+ priv = E_TASK_SHELL_CONTENT_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ shell_content = E_SHELL_CONTENT (object);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+
+ /* Build content widgets. */
+
+ container = GTK_WIDGET (object);
+
+ widget = gtk_vpaned_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->paned = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_calendar_table_new (shell_view, priv->task_model);
+ gtk_paned_add1 (GTK_PANED (container), widget);
+ priv->task_table = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_paned_add2 (GTK_PANED (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_cal_component_preview_new ();
+ e_cal_component_preview_set_default_timezone (
+ E_CAL_COMPONENT_PREVIEW (widget),
+ calendar_config_get_icaltimezone ());
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->task_preview = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* Configure the task table. */
+
+ widget = E_CALENDAR_TABLE (priv->task_table)->etable;
+ table = e_table_scrolled_get_table (E_TABLE_SCROLLED (widget));
+
+ priv->table_config = e_calendar_table_config_new (
+ E_CALENDAR_TABLE (priv->task_table));
+
+ e_table_set_state (table, E_CALENDAR_TABLE_DEFAULT_STATE);
+
+ e_table_drag_source_set (
+ table, GDK_BUTTON1_MASK,
+ drag_types, G_N_ELEMENTS (drag_types),
+ GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_ASK);
+
+ g_signal_connect_swapped (
+ table, "table-drag-data-get",
+ G_CALLBACK (task_shell_content_table_drag_data_get_cb),
+ object);
+
+ g_signal_connect_swapped (
+ table, "table-drag-data-delete",
+ G_CALLBACK (task_shell_content_table_drag_data_delete_cb),
+ object);
+
+ g_signal_connect_swapped (
+ table, "cursor-change",
+ G_CALLBACK (task_shell_content_cursor_change_cb),
+ object);
+
+ g_signal_connect_swapped (
+ table, "selection-change",
+ G_CALLBACK (task_shell_content_selection_change_cb),
+ object);
+
+ g_signal_connect_swapped (
+ priv->task_model, "model-row-changed",
+ G_CALLBACK (task_shell_content_model_row_changed_cb),
+ object);
+
+ /* Load the view instance. */
+
+ view_instance = e_shell_view_new_view_instance (shell_view, NULL);
+ g_signal_connect_swapped (
+ view_instance, "display-view",
+ G_CALLBACK (task_shell_content_display_view_cb),
+ object);
+ gal_view_instance_load (view_instance);
+ priv->view_instance = view_instance;
+
+ /* Bind GObject properties to GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (priv->paned);
+ key = "/apps/evolution/calendar/display/task_vpane_position";
+ gconf_bridge_bind_property_delayed (bridge, key, object, "position");
+}
+
+static guint32
+task_shell_content_check_state (EShellContent *shell_content)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ETable *table;
+ GSList *list, *iter;
+ gboolean assignable = TRUE;
+ gboolean editable = TRUE;
+ gboolean has_url = FALSE;
+ gint n_selected;
+ gint n_complete = 0;
+ gint n_incomplete = 0;
+ guint32 state = 0;
+
+ task_shell_content = E_TASK_SHELL_CONTENT (shell_content);
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ table = e_calendar_table_get_table (task_table);
+ n_selected = e_table_selected_count (table);
+
+ list = e_calendar_table_get_selected (task_table);
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ECalModelComponent *comp_data = iter->data;
+ icalproperty *prop;
+ const gchar *cap;
+ gboolean read_only;
+
+ e_cal_is_read_only (comp_data->client, &read_only, NULL);
+ editable &= !read_only;
+
+ cap = CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT;
+ if (e_cal_get_static_capability (comp_data->client, cap))
+ assignable = FALSE;
+
+ cap = CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK;
+ if (e_cal_get_static_capability (comp_data->client, cap))
+ assignable = FALSE;
+
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_URL_PROPERTY);
+ has_url |= (prop != NULL);
+
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_COMPLETED_PROPERTY);
+ if (prop != NULL)
+ n_complete++;
+ else
+ n_incomplete++;
+ }
+ g_slist_free (list);
+
+ if (n_selected == 1)
+ state |= E_TASK_SHELL_CONTENT_SELECTION_SINGLE;
+ if (n_selected > 1)
+ state |= E_TASK_SHELL_CONTENT_SELECTION_MULTIPLE;
+ if (assignable)
+ state |= E_TASK_SHELL_CONTENT_SELECTION_CAN_ASSIGN;
+ if (editable)
+ state |= E_TASK_SHELL_CONTENT_SELECTION_CAN_EDIT;
+ if (n_complete > 0)
+ state |= E_TASK_SHELL_CONTENT_SELECTION_HAS_COMPLETE;
+ if (n_incomplete > 0)
+ state |= E_TASK_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE;
+ if (has_url)
+ state |= E_TASK_SHELL_CONTENT_SELECTION_HAS_URL;
+
+ return state;
+}
+
+static void
+task_shell_content_class_init (ETaskShellContentClass *class)
+{
+ GObjectClass *object_class;
+ EShellContentClass *shell_content_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ETaskShellContentPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = task_shell_content_set_property;
+ object_class->get_property = task_shell_content_get_property;
+ object_class->dispose = task_shell_content_dispose;
+ object_class->finalize = task_shell_content_finalize;
+ object_class->constructed = task_shell_content_constructed;
+
+ shell_content_class = E_SHELL_CONTENT_CLASS (class);
+ shell_content_class->check_state = task_shell_content_check_state;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MODEL,
+ g_param_spec_object (
+ "model",
+ _("Model"),
+ _("The task table model"),
+ E_TYPE_CAL_MODEL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PREVIEW_VISIBLE,
+ g_param_spec_boolean (
+ "preview-visible",
+ _("Preview is Visible"),
+ _("Whether the preview pane is visible"),
+ TRUE,
+ G_PARAM_READWRITE));
+}
+
+static void
+task_shell_content_init (ETaskShellContent *task_shell_content)
+{
+ task_shell_content->priv =
+ E_TASK_SHELL_CONTENT_GET_PRIVATE (task_shell_content);
+
+ task_shell_content->priv->task_model = e_cal_model_tasks_new ();
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_task_shell_content_get_type (void)
+{
+ return task_shell_content_type;
+}
+
+void
+e_task_shell_content_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (ETaskShellContentClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) task_shell_content_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (ETaskShellContent),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) task_shell_content_init,
+ NULL /* value_table */
+ };
+
+ task_shell_content_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_CONTENT,
+ "ETaskShellContent", &type_info, 0);
+}
+
+GtkWidget *
+e_task_shell_content_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_TASK_SHELL_CONTENT,
+ "shell-view", shell_view, NULL);
+}
+
+ECalModel *
+e_task_shell_content_get_task_model (ETaskShellContent *task_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_TASK_SHELL_CONTENT (task_shell_content), NULL);
+
+ return task_shell_content->priv->task_model;
+}
+
+ECalComponentPreview *
+e_task_shell_content_get_task_preview (ETaskShellContent *task_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_TASK_SHELL_CONTENT (task_shell_content), NULL);
+
+ return E_CAL_COMPONENT_PREVIEW (
+ task_shell_content->priv->task_preview);
+}
+
+ECalendarTable *
+e_task_shell_content_get_task_table (ETaskShellContent *task_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_TASK_SHELL_CONTENT (task_shell_content), NULL);
+
+ return E_CALENDAR_TABLE (task_shell_content->priv->task_table);
+}
+
+GalViewInstance *
+e_task_shell_content_get_view_instance (ETaskShellContent *task_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_TASK_SHELL_CONTENT (task_shell_content), NULL);
+
+ return task_shell_content->priv->view_instance;
+}
+
+gboolean
+e_task_shell_content_get_preview_visible (ETaskShellContent *task_shell_content)
+{
+ GtkPaned *paned;
+ GtkWidget *child;
+
+ g_return_val_if_fail (
+ E_IS_TASK_SHELL_CONTENT (task_shell_content), FALSE);
+
+ paned = GTK_PANED (task_shell_content->priv->paned);
+ child = gtk_paned_get_child2 (paned);
+
+ return GTK_WIDGET_VISIBLE (child);
+}
+
+void
+e_task_shell_content_set_preview_visible (ETaskShellContent *task_shell_content,
+ gboolean preview_visible)
+{
+ GtkPaned *paned;
+ GtkWidget *child;
+
+ g_return_if_fail (E_IS_TASK_SHELL_CONTENT (task_shell_content));
+
+ paned = GTK_PANED (task_shell_content->priv->paned);
+ child = gtk_paned_get_child2 (paned);
+
+ if (preview_visible)
+ gtk_widget_show (child);
+ else
+ gtk_widget_hide (child);
+
+ g_object_notify (G_OBJECT (task_shell_content), "preview-visible");
+}
diff --git a/modules/calendar/e-task-shell-content.h b/modules/calendar/e-task-shell-content.h
new file mode 100644
index 0000000000..7e0b2128a7
--- /dev/null
+++ b/modules/calendar/e-task-shell-content.h
@@ -0,0 +1,100 @@
+/*
+ * e-task-shell-content.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_TASK_SHELL_CONTENT_H
+#define E_TASK_SHELL_CONTENT_H
+
+#include <shell/e-shell-content.h>
+#include <shell/e-shell-view.h>
+
+#include <calendar/gui/e-cal-model.h>
+#include <calendar/gui/e-calendar-table.h>
+#include <calendar/gui/e-cal-component-preview.h>
+
+#include <menus/gal-view-instance.h>
+
+/* Standard GObject macros */
+#define E_TYPE_TASK_SHELL_CONTENT \
+ (e_task_shell_content_get_type ())
+#define E_TASK_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_TASK_SHELL_CONTENT, ETaskShellContent))
+#define E_TASK_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_TASK_SHELL_CONTENT, ETaskShellContentClass))
+#define E_IS_TASK_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_TASK_SHELL_CONTENT))
+#define E_IS_TASK_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_TASK_SHELL_CONTENT))
+#define E_TASK_SHELL_CONTENT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_TASK_SHELL_CONTENT, ETaskShellContentClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ETaskShellContent ETaskShellContent;
+typedef struct _ETaskShellContentClass ETaskShellContentClass;
+typedef struct _ETaskShellContentPrivate ETaskShellContentPrivate;
+
+enum {
+ E_TASK_SHELL_CONTENT_SELECTION_SINGLE = 1 << 0,
+ E_TASK_SHELL_CONTENT_SELECTION_MULTIPLE = 1 << 1,
+ E_TASK_SHELL_CONTENT_SELECTION_CAN_ASSIGN = 1 << 2,
+ E_TASK_SHELL_CONTENT_SELECTION_CAN_EDIT = 1 << 3,
+ E_TASK_SHELL_CONTENT_SELECTION_HAS_COMPLETE = 1 << 4,
+ E_TASK_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE = 1 << 5,
+ E_TASK_SHELL_CONTENT_SELECTION_HAS_URL = 1 << 6
+};
+
+struct _ETaskShellContent {
+ EShellContent parent;
+ ETaskShellContentPrivate *priv;
+};
+
+struct _ETaskShellContentClass {
+ EShellContentClass parent_class;
+};
+
+GType e_task_shell_content_get_type (void);
+void e_task_shell_content_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_task_shell_content_new(EShellView *shell_view);
+ECalModel * e_task_shell_content_get_task_model
+ (ETaskShellContent *task_shell_content);
+ECalComponentPreview *
+ e_task_shell_content_get_task_preview
+ (ETaskShellContent *task_shell_content);
+ECalendarTable *e_task_shell_content_get_task_table
+ (ETaskShellContent *task_shell_content);
+GalViewInstance *
+ e_task_shell_content_get_view_instance
+ (ETaskShellContent *task_shell_content);
+gboolean e_task_shell_content_get_preview_visible
+ (ETaskShellContent *task_shell_content);
+void e_task_shell_content_set_preview_visible
+ (ETaskShellContent *task_shell_content,
+ gboolean preview_visible);
+
+G_END_DECLS
+
+#endif /* E_TASK_SHELL_CONTENT_H */
diff --git a/modules/calendar/e-task-shell-migrate.c b/modules/calendar/e-task-shell-migrate.c
new file mode 100644
index 0000000000..ced54bafdf
--- /dev/null
+++ b/modules/calendar/e-task-shell-migrate.c
@@ -0,0 +1,664 @@
+/*
+ * e-task-shell-backend-migrate.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-task-shell-migrate.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <libebackend/e-dbhash.h>
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-group.h>
+#include <libedataserver/e-source-list.h>
+#include <libedataserver/e-xml-hash-utils.h>
+#include <libedataserver/e-xml-utils.h>
+
+#include "e-util/e-bconf-map.h"
+#include "e-util/e-folder-map.h"
+#include "e-util/e-util-private.h"
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/calendar-config-keys.h"
+
+#define WEBCAL_BASE_URI "webcal://"
+#define PERSONAL_RELATIVE_URI "system"
+
+static e_gconf_map_t calendar_tasks_map[] = {
+ /* /Calendar/Tasks */
+ { "HideCompletedTasks", "calendar/tasks/hide_completed", E_GCONF_MAP_BOOL },
+ { "HideCompletedTasksUnits", "calendar/tasks/hide_completed_units", E_GCONF_MAP_STRING },
+ { "HideCompletedTasksValue", "calendar/tasks/hide_completed_value", E_GCONF_MAP_INT },
+ { NULL },
+};
+
+static e_gconf_map_t calendar_tasks_colours_map[] = {
+ /* /Calendar/Tasks/Colors */
+ { "TasksDueToday", "calendar/tasks/colors/due_today", E_GCONF_MAP_STRING },
+ { "TasksOverDue", "calendar/tasks/colors/overdue", E_GCONF_MAP_STRING },
+ { "TasksDueToday", "calendar/tasks/colors/due_today", E_GCONF_MAP_STRING },
+ { NULL },
+};
+
+static e_gconf_map_list_t task_remap_list[] = {
+
+ { "/Calendar/Tasks", calendar_tasks_map },
+ { "/Calendar/Tasks/Colors", calendar_tasks_colours_map },
+
+ { NULL },
+};
+
+static GtkWidget *window;
+static GtkLabel *label;
+static GtkProgressBar *progress;
+
+#ifndef G_OS_WIN32
+
+/* No previous versions have been available on Win32, so don't
+ * bother with upgrade support from 1.x on Win32.
+ */
+
+static void
+setup_progress_dialog (void)
+{
+ GtkWidget *vbox, *hbox, *w;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title ((GtkWindow *) window, _("Migrating..."));
+ gtk_window_set_modal ((GtkWindow *) window, TRUE);
+ gtk_container_set_border_width ((GtkContainer *) window, 6);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (vbox);
+ gtk_container_add ((GtkContainer *) window, vbox);
+
+ w = gtk_label_new (_("The location and hierarchy of the Evolution task "
+ "folders has changed since Evolution 1.x.\n\nPlease be "
+ "patient while Evolution migrates your folders..."));
+
+ gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
+ gtk_widget_show (w);
+ gtk_box_pack_start_defaults ((GtkBox *) vbox, w);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_widget_show (hbox);
+ gtk_box_pack_start_defaults ((GtkBox *) vbox, hbox);
+
+ label = (GtkLabel *) gtk_label_new ("");
+ gtk_widget_show ((GtkWidget *) label);
+ gtk_box_pack_start_defaults ((GtkBox *) hbox, (GtkWidget *) label);
+
+ progress = (GtkProgressBar *) gtk_progress_bar_new ();
+ gtk_widget_show ((GtkWidget *) progress);
+ gtk_box_pack_start_defaults ((GtkBox *) hbox, (GtkWidget *) progress);
+
+ gtk_widget_show (window);
+}
+
+static void
+dialog_close (void)
+{
+ gtk_widget_destroy ((GtkWidget *) window);
+}
+
+static void
+dialog_set_folder_name (const gchar *folder_name)
+{
+ gchar *text;
+
+ text = g_strdup_printf (_("Migrating '%s':"), folder_name);
+ gtk_label_set_text (label, text);
+ g_free (text);
+
+ gtk_progress_bar_set_fraction (progress, 0.0);
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static void
+dialog_set_progress (double percent)
+{
+ gchar text[5];
+
+ snprintf (text, sizeof (text), "%d%%", (gint) (percent * 100.0f));
+
+ gtk_progress_bar_set_fraction (progress, percent);
+ gtk_progress_bar_set_text (progress, text);
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static gboolean
+check_for_conflict (ESourceGroup *group, gchar *name)
+{
+ GSList *sources;
+ GSList *s;
+
+ sources = e_source_group_peek_sources (group);
+
+ for (s = sources; s; s = s->next) {
+ ESource *source = E_SOURCE (s->data);
+
+ if (!strcmp (e_source_peek_name (source), name))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+get_source_name (ESourceGroup *group, const gchar *path)
+{
+ gchar **p = g_strsplit (path, "/", 0);
+ gint i, j, starting_index;
+ gint num_elements;
+ gboolean conflict;
+ GString *s = g_string_new (NULL);
+
+ for (i = 0; p[i]; i ++);
+
+ num_elements = i;
+ i--;
+
+ /* p[i] is now the last path element */
+
+ /* check if it conflicts */
+ starting_index = i;
+ do {
+ for (j = starting_index; j < num_elements; j += 2) {
+ if (j != starting_index)
+ g_string_append_c (s, '_');
+ g_string_append (s, p[j]);
+ }
+
+ conflict = check_for_conflict (group, s->str);
+
+
+ /* if there was a conflict back up 2 levels (skipping the /subfolder/ element) */
+ if (conflict)
+ starting_index -= 2;
+
+ /* we always break out if we can't go any further,
+ regardless of whether or not we conflict. */
+ if (starting_index < 0)
+ break;
+
+ } while (conflict);
+ g_strfreev (p);
+
+ return g_string_free (s, FALSE);
+}
+
+static gboolean
+migrate_ical (ECal *old_ecal, ECal *new_ecal)
+{
+ GList *l, *objects;
+ gint num_added = 0;
+ gint num_objects;
+ gboolean retval = TRUE;
+
+ /* both ecals are loaded, start the actual migration */
+ if (!e_cal_get_object_list (old_ecal, "#t", &objects, NULL))
+ return FALSE;
+
+ num_objects = g_list_length (objects);
+ for (l = objects; l; l = l->next) {
+ icalcomponent *ical_comp = l->data;
+ GError *error = NULL;
+
+ if (!e_cal_create_object (new_ecal, ical_comp, NULL, &error)) {
+ g_warning ("Migration of object failed: %s", error->message);
+ retval = FALSE;
+ }
+
+ g_clear_error (&error);
+
+ num_added ++;
+ dialog_set_progress ((double)num_added / num_objects);
+ }
+
+ g_list_foreach (objects, (GFunc) icalcomponent_free, NULL);
+ g_list_free (objects);
+
+ return retval;
+}
+
+static gboolean
+migrate_ical_folder_to_source (gchar *old_path, ESource *new_source, ECalSourceType type)
+{
+ ECal *old_ecal = NULL, *new_ecal = NULL;
+ ESource *old_source;
+ ESourceGroup *group;
+ gchar *old_uri = g_strdup_printf ("file://%s", old_path);
+ GError *error = NULL;
+ gboolean retval = FALSE;
+
+ group = e_source_group_new ("", old_uri);
+ old_source = e_source_new ("", "");
+ e_source_group_add_source (group, old_source, -1);
+
+ dialog_set_folder_name (e_source_peek_name (new_source));
+
+ if (!(old_ecal = e_cal_new (old_source, type))) {
+ g_warning ("could not find a backend for '%s'", e_source_get_uri (old_source));
+ goto finish;
+ }
+ if (!e_cal_open (old_ecal, FALSE, &error)) {
+ g_warning ("failed to load source ecal for migration: '%s' (%s)", error->message,
+ e_source_get_uri (old_source));
+ goto finish;
+ }
+
+ if (!(new_ecal = e_cal_new (new_source, type))) {
+ g_warning ("could not find a backend for '%s'", e_source_get_uri (new_source));
+ goto finish;
+ }
+ if (!e_cal_open (new_ecal, FALSE, &error)) {
+ g_warning ("failed to load destination ecal for migration: '%s' (%s)", error->message,
+ e_source_get_uri (new_source));
+ goto finish;
+ }
+
+ retval = migrate_ical (old_ecal, new_ecal);
+
+finish:
+ g_clear_error (&error);
+ if (old_ecal)
+ g_object_unref (old_ecal);
+ g_object_unref (group);
+ if (new_ecal)
+ g_object_unref (new_ecal);
+ g_free (old_uri);
+
+ return retval;
+}
+
+static gboolean
+migrate_ical_folder (gchar *old_path, ESourceGroup *dest_group, gchar *source_name, ECalSourceType type)
+{
+ ESource *new_source;
+ gboolean retval;
+
+ new_source = e_source_new (source_name, source_name);
+ e_source_set_relative_uri (new_source, e_source_peek_uid (new_source));
+ e_source_group_add_source (dest_group, new_source, -1);
+
+ retval = migrate_ical_folder_to_source (old_path, new_source, type);
+
+ g_object_unref (new_source);
+
+ return retval;
+}
+
+#endif /* !G_OS_WIN32 */
+
+#ifndef G_OS_WIN32
+
+static void
+migrate_pilot_db_key (const gchar *key, gpointer user_data)
+{
+ EXmlHash *xmlhash = user_data;
+
+ e_xmlhash_add (xmlhash, key, "");
+}
+
+static void
+migrate_pilot_data (const gchar *component, const gchar *conduit, const gchar *old_path, const gchar *new_path)
+{
+ gchar *changelog, *map;
+ const gchar *dent;
+ const gchar *ext;
+ gchar *filename;
+ GDir *dir;
+
+ if (!(dir = g_dir_open (old_path, 0, NULL)))
+ return;
+
+ map = g_alloca (12 + strlen (conduit));
+ sprintf (map, "pilot-map-%s-", conduit);
+
+ changelog = g_alloca (24 + strlen (conduit));
+ sprintf (changelog, "pilot-sync-evolution-%s-", conduit);
+
+ while ((dent = g_dir_read_name (dir))) {
+ if (!strncmp (dent, map, strlen (map)) &&
+ ((ext = strrchr (dent, '.')) && !strcmp (ext, ".xml"))) {
+ /* pilot map file - src and dest file formats are identical */
+ guchar inbuf[4096];
+ gsize nread, nwritten;
+ gint fd0, fd1;
+ gssize n;
+
+ filename = g_build_filename (old_path, dent, NULL);
+ if ((fd0 = g_open (filename, O_RDONLY|O_BINARY, 0)) == -1) {
+ g_free (filename);
+ continue;
+ }
+
+ g_free (filename);
+ filename = g_build_filename (new_path, dent, NULL);
+ if ((fd1 = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) == -1) {
+ g_free (filename);
+ close (fd0);
+ continue;
+ }
+
+ do {
+ do {
+ n = read (fd0, inbuf, sizeof (inbuf));
+ } while (n == -1 && errno == EINTR);
+
+ if (n < 1)
+ break;
+
+ nread = n;
+ nwritten = 0;
+ do {
+ do {
+ n = write (fd1, inbuf + nwritten, nread - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } while (nwritten < nread && n != -1);
+
+ if (n == -1)
+ break;
+ } while (1);
+
+ if (n != -1)
+ n = fsync (fd1);
+
+ if (n == -1) {
+ g_warning ("Failed to migrate %s: %s", dent, strerror (errno));
+ g_unlink (filename);
+ }
+
+ close (fd0);
+ close (fd1);
+ g_free (filename);
+ } else if (!strncmp (dent, changelog, strlen (changelog)) &&
+ ((ext = strrchr (dent, '.')) && !strcmp (ext, ".db"))) {
+ /* src and dest formats differ, src format is db3 while dest format is xml */
+ EXmlHash *xmlhash;
+ EDbHash *dbhash;
+ struct stat st;
+
+ filename = g_build_filename (old_path, dent, NULL);
+ if (g_stat (filename, &st) == -1) {
+ g_free (filename);
+ continue;
+ }
+
+ dbhash = e_dbhash_new (filename);
+ g_free (filename);
+
+ filename = g_strdup_printf ("%s/%s.ics-%s", new_path, component, dent);
+ if (g_stat (filename, &st) != -1)
+ g_unlink (filename);
+ xmlhash = e_xmlhash_new (filename);
+ g_free (filename);
+
+ e_dbhash_foreach_key (dbhash, migrate_pilot_db_key, xmlhash);
+
+ e_dbhash_destroy (dbhash);
+
+ e_xmlhash_write (xmlhash);
+ e_xmlhash_destroy (xmlhash);
+ }
+ }
+
+ g_dir_close (dir);
+}
+
+#endif
+
+static void
+create_task_sources (EShellBackend *shell_backend,
+ ESourceList *source_list,
+ ESourceGroup **on_this_computer,
+ ESourceGroup **on_the_web,
+ ESource **personal_source)
+{
+ GSList *groups;
+ ESourceGroup *group;
+ gchar *base_uri, *base_uri_proto;
+ const gchar *base_dir;
+
+ *on_this_computer = NULL;
+ *on_the_web = NULL;
+ *personal_source = NULL;
+
+ base_dir = e_shell_backend_get_config_dir (shell_backend);
+ base_uri = g_build_filename (base_dir, "local", NULL);
+
+ base_uri_proto = g_filename_to_uri (base_uri, NULL, NULL);
+
+ groups = e_source_list_peek_groups (source_list);
+ if (groups) {
+ /* groups are already there, we need to search for things... */
+ GSList *g;
+
+ for (g = groups; g; g = g->next) {
+
+ group = E_SOURCE_GROUP (g->data);
+
+ if (!*on_this_computer && !strcmp (base_uri_proto, e_source_group_peek_base_uri (group)))
+ *on_this_computer = g_object_ref (group);
+ else if (!*on_the_web && !strcmp (WEBCAL_BASE_URI, e_source_group_peek_base_uri (group)))
+ *on_the_web = g_object_ref (group);
+ }
+ }
+
+ if (*on_this_computer) {
+ /* make sure "Personal" shows up as a source under
+ this group */
+ GSList *sources = e_source_group_peek_sources (*on_this_computer);
+ GSList *s;
+ for (s = sources; s; s = s->next) {
+ ESource *source = E_SOURCE (s->data);
+ const gchar *relative_uri;
+
+ relative_uri = e_source_peek_relative_uri (source);
+ if (relative_uri == NULL)
+ continue;
+ if (!strcmp (PERSONAL_RELATIVE_URI, relative_uri)) {
+ *personal_source = g_object_ref (source);
+ break;
+ }
+ }
+ } else {
+ /* create the local source group */
+ group = e_source_group_new (_("On This Computer"), base_uri_proto);
+ e_source_list_add_group (source_list, group, -1);
+
+ *on_this_computer = group;
+ }
+
+ if (!*personal_source) {
+ /* Create the default Person task list */
+ ESource *source = e_source_new (_("Personal"), PERSONAL_RELATIVE_URI);
+ e_source_group_add_source (*on_this_computer, source, -1);
+
+ if (!calendar_config_get_primary_tasks () && !calendar_config_get_tasks_selected ()) {
+ GSList selected;
+
+ calendar_config_set_primary_tasks (e_source_peek_uid (source));
+
+ selected.data = (gpointer)e_source_peek_uid (source);
+ selected.next = NULL;
+ calendar_config_set_tasks_selected (&selected);
+ }
+
+ e_source_set_color_spec (source, "#BECEDD");
+ *personal_source = source;
+ }
+
+ if (!*on_the_web) {
+ /* Create the Webcal source group */
+ group = e_source_group_new (_("On The Web"), WEBCAL_BASE_URI);
+ e_source_list_add_group (source_list, group, -1);
+
+ *on_the_web = group;
+ }
+
+ g_free (base_uri_proto);
+ g_free (base_uri);
+}
+
+gboolean
+e_task_shell_backend_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error)
+{
+ ESourceGroup *on_this_computer = NULL;
+ ESourceGroup *on_the_web = NULL;
+ ESource *personal_source = NULL;
+ ESourceList *source_list;
+ gboolean retval = FALSE;
+
+ source_list = g_object_get_data (
+ G_OBJECT (source_list), "source-list");
+
+ /* we call this unconditionally now - create_groups either
+ creates the groups/sources or it finds the necessary
+ groups/sources. */
+ create_task_sources (
+ shell_backend, source_list, &on_this_computer,
+ &on_the_web, &personal_source);
+
+#ifndef G_OS_WIN32
+ if (major == 1) {
+ xmlDocPtr config_doc = NULL;
+ gchar *conf_file;
+
+ conf_file = g_build_filename (g_get_home_dir (), "evolution", "config.xmldb", NULL);
+ if (g_file_test (conf_file, G_FILE_TEST_IS_REGULAR))
+ config_doc = e_xml_parse_file (conf_file);
+ g_free (conf_file);
+
+ if (config_doc && minor <= 2) {
+ GConfClient *gconf;
+ gint res = 0;
+
+ /* move bonobo config to gconf */
+ gconf = gconf_client_get_default ();
+
+ res = e_bconf_import (gconf, config_doc, task_remap_list);
+
+ g_object_unref (gconf);
+
+ xmlFreeDoc(config_doc);
+
+ if (res != 0) {
+ g_set_error(error, 0, 0, _("Unable to migrate old settings from evolution/config.xmldb"));
+ goto fail;
+ }
+ }
+
+ if (minor <= 4) {
+ GSList *migration_dirs, *l;
+ gchar *path, *local_task_folder;
+
+ setup_progress_dialog ();
+
+ path = g_build_filename (g_get_home_dir (), "evolution", "local", NULL);
+ migration_dirs = e_folder_map_local_folders (path, "tasks");
+ local_task_folder = g_build_filename (path, "Tasks", NULL);
+ g_free (path);
+
+ if (personal_source)
+ migrate_ical_folder_to_source (local_task_folder, personal_source, E_CAL_SOURCE_TYPE_TODO);
+
+ for (l = migration_dirs; l; l = l->next) {
+ gchar *source_name;
+
+ if (personal_source && !strcmp ((gchar *)l->data, local_task_folder))
+ continue;
+
+ source_name = get_source_name (on_this_computer, (gchar *)l->data);
+
+ if (!migrate_ical_folder (l->data, on_this_computer, source_name, E_CAL_SOURCE_TYPE_TODO)) {
+ /* FIXME: domain/code */
+ g_set_error(error, 0, 0, _("Unable to migrate tasks `%s'"), source_name);
+ g_free(source_name);
+ goto fail;
+ }
+
+ g_free (source_name);
+ }
+
+ g_free (local_task_folder);
+
+ dialog_close ();
+ }
+
+ if (minor < 5 || (minor == 5 && micro <= 10)) {
+ gchar *old_path, *new_path;
+
+ old_path = g_build_filename (g_get_home_dir (), "evolution", "local", "Tasks", NULL);
+ new_path = g_build_filename (e_shell_backend_get_config_dir (shell_backend),
+ "local", "system", NULL);
+ migrate_pilot_data ("tasks", "todo", old_path, new_path);
+ g_free (new_path);
+ g_free (old_path);
+ }
+
+ /* we only need to do this next step if people ran
+ older versions of 1.5. We need to clear out the
+ absolute URI's that were assigned to ESources
+ during one phase of development, as they take
+ precedent over relative uris (but aren't updated
+ when editing an ESource). */
+ if (minor == 5 && micro <= 11) {
+ GSList *g;
+ for (g = e_source_list_peek_groups (source_list); g; g = g->next) {
+ ESourceGroup *group = g->data;
+ GSList *s;
+
+ for (s = e_source_group_peek_sources (group); s; s = s->next) {
+ ESource *source = s->data;
+ e_source_set_absolute_uri (source, NULL);
+ }
+ }
+ }
+ }
+#endif /* !G_OS_WIN32 */
+ e_source_list_sync (source_list, NULL);
+ retval = TRUE;
+fail:
+ if (on_this_computer)
+ g_object_unref (on_this_computer);
+ if (on_the_web)
+ g_object_unref (on_the_web);
+ if (personal_source)
+ g_object_unref (personal_source);
+
+ return retval;
+}
diff --git a/modules/calendar/e-task-shell-migrate.h b/modules/calendar/e-task-shell-migrate.h
new file mode 100644
index 0000000000..4cb91c9f4a
--- /dev/null
+++ b/modules/calendar/e-task-shell-migrate.h
@@ -0,0 +1,38 @@
+/*
+ * e-task-shell-backend-migrate.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_TASK_SHELL_BACKEND_MIGRATE_H
+#define E_TASK_SHELL_BACKEND_MIGRATE_H
+
+#include <glib.h>
+#include <shell/e-shell-backend.h>
+
+G_BEGIN_DECLS
+
+gboolean e_task_shell_backend_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_TASK_SHELL_BACKEND_MIGRATE_H */
diff --git a/modules/calendar/e-task-shell-sidebar.c b/modules/calendar/e-task-shell-sidebar.c
new file mode 100644
index 0000000000..827a0a037e
--- /dev/null
+++ b/modules/calendar/e-task-shell-sidebar.c
@@ -0,0 +1,696 @@
+/*
+ * e-task-shell-sidebar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-task-shell-sidebar.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libecal/e-cal.h>
+
+#include "e-util/e-error.h"
+#include "e-util/e-util.h"
+#include "calendar/common/authentication.h"
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/e-task-list-selector.h"
+#include "calendar/gui/misc.h"
+
+#include "e-task-shell-backend.h"
+#include "e-task-shell-view.h"
+
+#define E_TASK_SHELL_SIDEBAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_TASK_SHELL_SIDEBAR, ETaskShellSidebarPrivate))
+
+struct _ETaskShellSidebarPrivate {
+ GtkWidget *selector;
+
+ /* UID -> Client */
+ GHashTable *client_table;
+};
+
+enum {
+ PROP_0,
+ PROP_SELECTOR
+};
+
+enum {
+ CLIENT_ADDED,
+ CLIENT_REMOVED,
+ STATUS_MESSAGE,
+ LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+static GType task_shell_sidebar_type;
+
+static void
+task_shell_sidebar_emit_client_added (ETaskShellSidebar *task_shell_sidebar,
+ ECal *client)
+{
+ guint signal_id = signals[CLIENT_ADDED];
+
+ g_signal_emit (task_shell_sidebar, signal_id, 0, client);
+}
+
+static void
+task_shell_sidebar_emit_client_removed (ETaskShellSidebar *task_shell_sidebar,
+ ECal *client)
+{
+ guint signal_id = signals[CLIENT_REMOVED];
+
+ g_signal_emit (task_shell_sidebar, signal_id, 0, client);
+}
+
+static void
+task_shell_sidebar_emit_status_message (ETaskShellSidebar *task_shell_sidebar,
+ const gchar *status_message)
+{
+ guint signal_id = signals[STATUS_MESSAGE];
+
+ g_signal_emit (task_shell_sidebar, signal_id, 0, status_message, -1.0);
+}
+
+static void
+task_shell_sidebar_backend_died_cb (ETaskShellSidebar *task_shell_sidebar,
+ ECal *client)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ GHashTable *client_table;
+ ESource *source;
+ const gchar *uid;
+
+ client_table = task_shell_sidebar->priv->client_table;
+
+ shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ source = e_cal_get_source (client);
+ uid = e_source_peek_uid (source);
+
+ g_object_ref (source);
+
+ g_hash_table_remove (client_table, uid);
+ task_shell_sidebar_emit_status_message (task_shell_sidebar, NULL);
+
+ e_error_run (
+ GTK_WINDOW (shell_window),
+ "calendar:tasks-crashed", NULL);
+
+ g_object_unref (source);
+}
+
+static void
+task_shell_sidebar_backend_error_cb (ETaskShellSidebar *task_shell_sidebar,
+ const gchar *message,
+ ECal *client)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ GtkWidget *dialog;
+ const gchar *uri;
+ gchar *uri_no_passwd;
+
+ shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ uri = e_cal_get_uri (client);
+ uri_no_passwd = get_uri_without_password (uri);
+
+ dialog = gtk_message_dialog_new (
+ GTK_WINDOW (shell_window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Error on %s\n%s"),
+ uri_no_passwd, message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_free (uri_no_passwd);
+}
+
+static void
+task_shell_sidebar_client_opened_cb (ETaskShellSidebar *task_shell_sidebar,
+ ECalendarStatus status,
+ ECal *client)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ ESource *source;
+
+ source = e_cal_get_source (client);
+
+ shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ if (status == E_CALENDAR_STATUS_AUTHENTICATION_FAILED ||
+ status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED)
+ auth_cal_forget_password (client);
+
+ switch (status) {
+ case E_CALENDAR_STATUS_OK:
+ g_signal_handlers_disconnect_matched (
+ client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+ task_shell_sidebar_client_opened_cb, NULL);
+
+ task_shell_sidebar_emit_status_message (
+ task_shell_sidebar, _("Loading tasks"));
+ task_shell_sidebar_emit_client_added (
+ task_shell_sidebar, client);
+ task_shell_sidebar_emit_status_message (
+ task_shell_sidebar, NULL);
+ break;
+
+ case E_CALENDAR_STATUS_AUTHENTICATION_FAILED:
+ e_cal_open_async (client, FALSE);
+ break;
+
+ case E_CALENDAR_STATUS_BUSY:
+ break;
+
+ case E_CALENDAR_STATUS_REPOSITORY_OFFLINE:
+ e_error_run (
+ GTK_WINDOW (shell_window),
+ "calendar:prompt-no-contents-offline-tasks",
+ NULL);
+ break;
+
+ default:
+ task_shell_sidebar_emit_client_removed (
+ task_shell_sidebar, client);
+ break;
+ }
+}
+
+static void
+task_shell_sidebar_row_changed_cb (ETaskShellSidebar *task_shell_sidebar,
+ GtkTreePath *tree_path,
+ GtkTreeIter *tree_iter,
+ GtkTreeModel *tree_model)
+{
+ ESourceSelector *selector;
+ ESource *source;
+
+ /* XXX ESourceSelector's underlying tree store has only one
+ * column: ESource objects. While we're not supposed to
+ * know this, listening for "row-changed" signals from
+ * the model is easier to deal with than the selector's
+ * "selection-changed" signal, which doesn't tell you
+ * _which_ row changed. */
+
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+ gtk_tree_model_get (tree_model, tree_iter, 0, &source, -1);
+
+ /* XXX This signal gets emitted a lot while the model is being
+ * rebuilt, during which time we won't get a valid ESource.
+ * ESourceSelector should probably block this signal while
+ * rebuilding the model, but we'll be forgiving and not
+ * emit a warning. */
+ if (!E_IS_SOURCE (source))
+ return;
+
+ if (e_source_selector_source_is_selected (selector, source))
+ e_task_shell_sidebar_add_source (task_shell_sidebar, source);
+ else
+ e_task_shell_sidebar_remove_source (task_shell_sidebar, source);
+}
+
+static void
+task_shell_sidebar_selection_changed_cb (ETaskShellSidebar *task_shell_sidebar,
+ ESourceSelector *selector)
+{
+ GSList *list, *iter;
+
+ /* This signal is emitted less frequently than "row-changed",
+ * especially when the model is being rebuilt. So we'll take
+ * it easy on poor GConf. */
+
+ list = e_source_selector_get_selection (selector);
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+
+ iter->data = (gpointer) e_source_peek_uid (source);
+ g_object_unref (source);
+ }
+
+ calendar_config_set_tasks_selected (list);
+
+ g_slist_free (list);
+}
+
+static void
+task_shell_sidebar_primary_selection_changed_cb (ETaskShellSidebar *task_shell_sidebar,
+ ESourceSelector *selector)
+{
+ ESource *source;
+ const gchar *uid;
+
+ /* XXX ESourceSelector needs a "primary-selection-uid" property
+ * so we can just bind the property with GConfBridge. */
+
+ source = e_source_selector_peek_primary_selection (selector);
+ if (source == NULL)
+ return;
+
+ uid = e_source_peek_uid (source);
+ calendar_config_set_primary_tasks (uid);
+}
+
+static void
+task_shell_sidebar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SELECTOR:
+ g_value_set_object (
+ value, e_task_shell_sidebar_get_selector (
+ E_TASK_SHELL_SIDEBAR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+task_shell_sidebar_dispose (GObject *object)
+{
+ ETaskShellSidebarPrivate *priv;
+
+ priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ if (priv->selector != NULL) {
+ g_object_unref (priv->selector);
+ priv->selector = NULL;
+ }
+
+ g_hash_table_remove_all (priv->client_table);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+task_shell_sidebar_finalize (GObject *object)
+{
+ ETaskShellSidebarPrivate *priv;
+
+ priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->client_table);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+task_shell_sidebar_constructed (GObject *object)
+{
+ ETaskShellSidebarPrivate *priv;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+ EShellSidebar *shell_sidebar;
+ ESourceSelector *selector;
+ ESourceList *source_list;
+ ESource *source;
+ GtkContainer *container;
+ GtkTreeModel *model;
+ GtkWidget *widget;
+ AtkObject *a11y;
+ GSList *list, *iter;
+ gchar *uid;
+
+ priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ shell_sidebar = E_SHELL_SIDEBAR (object);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+
+ source_list = e_task_shell_backend_get_source_list (
+ E_TASK_SHELL_BACKEND (shell_backend));
+
+ container = GTK_CONTAINER (shell_sidebar);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_container_add (container, widget);
+ gtk_widget_show (widget);
+
+ container = GTK_CONTAINER (widget);
+
+ widget = e_task_list_selector_new (source_list);
+ e_source_selector_set_select_new (E_SOURCE_SELECTOR (widget), TRUE);
+ gtk_container_add (container, widget);
+ a11y = gtk_widget_get_accessible (widget);
+ atk_object_set_name (a11y, _("Task List Selector"));
+ priv->selector = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* Restore the selector state from the last session. */
+
+ selector = E_SOURCE_SELECTOR (priv->selector);
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+
+ g_signal_connect_swapped (
+ model, "row-changed",
+ G_CALLBACK (task_shell_sidebar_row_changed_cb),
+ object);
+
+ source = NULL;
+ uid = calendar_config_get_primary_tasks ();
+ if (uid != NULL)
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ if (source == NULL)
+ source = e_source_list_peek_source_any (source_list);
+ if (source != NULL)
+ e_source_selector_set_primary_selection (selector, source);
+ g_free (uid);
+
+ list = calendar_config_get_tasks_selected ();
+ for (iter = list; iter != NULL; iter = iter->next) {
+ uid = iter->data;
+ source = e_source_list_peek_source_by_uid (source_list, uid);
+ g_free (uid);
+
+ if (source == NULL)
+ continue;
+
+ e_source_selector_select_source (selector, source);
+ }
+ g_slist_free (list);
+
+ /* Listen for subsequent changes to the selector. */
+
+ g_signal_connect_swapped (
+ widget, "selection-changed",
+ G_CALLBACK (task_shell_sidebar_selection_changed_cb),
+ object);
+
+ g_signal_connect_swapped (
+ widget, "primary-selection-changed",
+ G_CALLBACK (task_shell_sidebar_primary_selection_changed_cb),
+ object);
+}
+
+static guint32
+task_shell_sidebar_check_state (EShellSidebar *shell_sidebar)
+{
+ ETaskShellSidebar *task_shell_sidebar;
+ ESourceSelector *selector;
+ ESource *source;
+ gboolean is_system = FALSE;
+ guint32 state = 0;
+
+ task_shell_sidebar = E_TASK_SHELL_SIDEBAR (shell_sidebar);
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+
+ if (source != NULL) {
+ const gchar *uri;
+
+ uri = e_source_peek_relative_uri (source);
+ is_system = (uri == NULL || strcmp (uri, "system") == 0);
+ }
+
+ if (source != NULL)
+ state |= E_TASK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE;
+ if (is_system)
+ state |= E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM;
+
+ return state;
+}
+
+static void
+task_shell_sidebar_client_removed (ETaskShellSidebar *task_shell_sidebar,
+ ECal *client)
+{
+ ESourceSelector *selector;
+ GHashTable *client_table;
+ ESource *source;
+ const gchar *uid;
+
+ client_table = task_shell_sidebar->priv->client_table;
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+
+ g_signal_handlers_disconnect_matched (
+ client, G_SIGNAL_MATCH_DATA, 0, 0,
+ NULL, NULL, task_shell_sidebar);
+
+ source = e_cal_get_source (client);
+ e_source_selector_unselect_source (selector, source);
+
+ uid = e_source_peek_uid (source);
+ g_hash_table_remove (client_table, uid);
+
+ task_shell_sidebar_emit_status_message (task_shell_sidebar, NULL);
+}
+
+static void
+task_shell_sidebar_class_init (ETaskShellSidebarClass *class)
+{
+ GObjectClass *object_class;
+ EShellSidebarClass *shell_sidebar_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ETaskShellSidebarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = task_shell_sidebar_get_property;
+ object_class->dispose = task_shell_sidebar_dispose;
+ object_class->finalize = task_shell_sidebar_finalize;
+ object_class->constructed = task_shell_sidebar_constructed;
+
+ shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class);
+ shell_sidebar_class->check_state = task_shell_sidebar_check_state;
+
+ class->client_removed = task_shell_sidebar_client_removed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SELECTOR,
+ g_param_spec_object (
+ "selector",
+ _("Source Selector Widget"),
+ _("This widget displays groups of task lists"),
+ E_TYPE_SOURCE_SELECTOR,
+ G_PARAM_READABLE));
+
+ signals[CLIENT_ADDED] = g_signal_new (
+ "client-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ETaskShellSidebarClass, client_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_CAL);
+
+ signals[CLIENT_REMOVED] = g_signal_new (
+ "client-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ETaskShellSidebarClass, client_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_CAL);
+
+ signals[STATUS_MESSAGE] = g_signal_new (
+ "status-message",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (ETaskShellSidebarClass, status_message),
+ NULL, NULL,
+ e_marshal_VOID__STRING_DOUBLE,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_DOUBLE);
+}
+
+static void
+task_shell_sidebar_init (ETaskShellSidebar *task_shell_sidebar)
+{
+ GHashTable *client_table;
+
+ client_table = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ task_shell_sidebar->priv =
+ E_TASK_SHELL_SIDEBAR_GET_PRIVATE (task_shell_sidebar);
+
+ task_shell_sidebar->priv->client_table = client_table;
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_task_shell_sidebar_get_type (void)
+{
+ return task_shell_sidebar_type;
+}
+
+void
+e_task_shell_sidebar_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (ETaskShellSidebarClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) task_shell_sidebar_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (ETaskShellSidebar),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) task_shell_sidebar_init,
+ NULL /* value_table */
+ };
+
+ task_shell_sidebar_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_SIDEBAR,
+ "ETaskShellSidebar", &type_info, 0);
+}
+
+GtkWidget *
+e_task_shell_sidebar_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_TASK_SHELL_SIDEBAR,
+ "shell-view", shell_view, NULL);
+}
+
+GList *
+e_task_shell_sidebar_get_clients (ETaskShellSidebar *task_shell_sidebar)
+{
+ GHashTable *client_table;
+
+ g_return_val_if_fail (
+ E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar), NULL);
+
+ client_table = task_shell_sidebar->priv->client_table;
+
+ return g_hash_table_get_values (client_table);
+}
+
+ESourceSelector *
+e_task_shell_sidebar_get_selector (ETaskShellSidebar *task_shell_sidebar)
+{
+ g_return_val_if_fail (
+ E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar), NULL);
+
+ return E_SOURCE_SELECTOR (task_shell_sidebar->priv->selector);
+}
+
+void
+e_task_shell_sidebar_add_source (ETaskShellSidebar *task_shell_sidebar,
+ ESource *source)
+{
+ ESourceSelector *selector;
+ GHashTable *client_table;
+ ECal *client;
+ const gchar *uid;
+ const gchar *uri;
+ gchar *message;
+
+ g_return_if_fail (E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ client_table = task_shell_sidebar->priv->client_table;
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+
+ uid = e_source_peek_uid (source);
+ client = g_hash_table_lookup (client_table, uid);
+
+ if (client != NULL)
+ return;
+
+ client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_TODO);
+ g_return_if_fail (client != NULL);
+
+ g_signal_connect_swapped (
+ client, "backend-died",
+ G_CALLBACK (task_shell_sidebar_backend_died_cb),
+ task_shell_sidebar);
+
+ g_signal_connect_swapped (
+ client, "backend-error",
+ G_CALLBACK (task_shell_sidebar_backend_error_cb),
+ task_shell_sidebar);
+
+ g_hash_table_insert (client_table, g_strdup (uid), client);
+ e_source_selector_select_source (selector, source);
+
+ uri = e_cal_get_uri (client);
+ message = g_strdup_printf (_("Opening tasks at %s"), uri);
+ task_shell_sidebar_emit_status_message (task_shell_sidebar, message);
+ g_free (message);
+
+ g_signal_connect_swapped (
+ client, "cal-opened",
+ G_CALLBACK (task_shell_sidebar_client_opened_cb),
+ task_shell_sidebar);
+
+ e_cal_open_async (client, FALSE);
+}
+
+void
+e_task_shell_sidebar_remove_source (ETaskShellSidebar *task_shell_sidebar,
+ ESource *source)
+{
+ ESourceSelector *selector;
+ GHashTable *client_table;
+ ECal *client;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ client_table = task_shell_sidebar->priv->client_table;
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+
+ uid = e_source_peek_uid (source);
+ client = g_hash_table_lookup (client_table, uid);
+
+ if (client == NULL)
+ return;
+
+ task_shell_sidebar_emit_client_removed (task_shell_sidebar, client);
+}
diff --git a/modules/calendar/e-task-shell-sidebar.h b/modules/calendar/e-task-shell-sidebar.h
new file mode 100644
index 0000000000..5d4c74fe11
--- /dev/null
+++ b/modules/calendar/e-task-shell-sidebar.h
@@ -0,0 +1,97 @@
+/*
+ * e-task-shell-sidebar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_TASK_SHELL_SIDEBAR_H
+#define E_TASK_SHELL_SIDEBAR_H
+
+#include <libecal/e-cal.h>
+#include <libedataserverui/e-source-selector.h>
+
+#include <shell/e-shell-sidebar.h>
+#include <shell/e-shell-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_TASK_SHELL_SIDEBAR \
+ (e_task_shell_sidebar_get_type ())
+#define E_TASK_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_TASK_SHELL_SIDEBAR, ETaskShellSidebar))
+#define E_TASK_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_TASK_SHELL_SIDEBAR, ETaskShellSidebarClass))
+#define E_IS_TASK_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_TASK_SHELL_SIDEBAR))
+#define E_IS_TASK_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_TASK_SHELL_SIDEBAR))
+#define E_TASK_SHELL_SIDEBAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_TASK_SHELL_SIDEBAR, ETaskShellSidebarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ETaskShellSidebar ETaskShellSidebar;
+typedef struct _ETaskShellSidebarClass ETaskShellSidebarClass;
+typedef struct _ETaskShellSidebarPrivate ETaskShellSidebarPrivate;
+
+enum {
+ E_TASK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE = 1 << 0,
+ E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM = 1 << 1
+};
+
+struct _ETaskShellSidebar {
+ EShellSidebar parent;
+ ETaskShellSidebarPrivate *priv;
+};
+
+struct _ETaskShellSidebarClass {
+ EShellSidebarClass parent_class;
+
+ /* Signals */
+ void (*client_added) (ETaskShellSidebar *task_shell_sidebar,
+ ECal *client);
+ void (*client_removed) (ETaskShellSidebar *task_shell_sidebar,
+ ECal *client);
+ void (*status_message) (ETaskShellSidebar *task_shell_sidebar,
+ const gchar *status_message,
+ gdouble percent);
+};
+
+GType e_task_shell_sidebar_get_type (void);
+void e_task_shell_sidebar_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_task_shell_sidebar_new(EShellView *shell_view);
+GList * e_task_shell_sidebar_get_clients
+ (ETaskShellSidebar *task_shell_sidebar);
+ESourceSelector *
+ e_task_shell_sidebar_get_selector
+ (ETaskShellSidebar *task_shell_sidebar);
+void e_task_shell_sidebar_add_source
+ (ETaskShellSidebar *task_shell_sidebar,
+ ESource *source);
+void e_task_shell_sidebar_remove_source
+ (ETaskShellSidebar *task_shell_sidebar,
+ ESource *source);
+
+G_END_DECLS
+
+#endif /* E_TASK_SHELL_SIDEBAR_H */
diff --git a/modules/calendar/e-task-shell-view-actions.c b/modules/calendar/e-task-shell-view-actions.c
new file mode 100644
index 0000000000..4deed9fdf0
--- /dev/null
+++ b/modules/calendar/e-task-shell-view-actions.c
@@ -0,0 +1,1154 @@
+/*
+ * e-task-shell-view-actions.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-task-shell-view-private.h"
+
+static void
+action_gal_save_custom_view_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ EShellView *shell_view;
+ GalViewInstance *view_instance;
+
+ /* All shell views respond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with saving the custom view. */
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ view_instance = e_task_shell_content_get_view_instance (task_shell_content);
+ gal_view_instance_save_as (view_instance);
+}
+
+static void
+action_search_execute_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ EShellView *shell_view;
+
+ /* All shell views respond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with executing the search. */
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ e_task_shell_view_execute_search (task_shell_view);
+}
+
+static void
+action_search_filter_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ ETaskShellView *task_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ gtk_action_activate (ACTION (SEARCH_EXECUTE));
+}
+
+static void
+action_task_assign_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only open the first selected task. */
+ e_task_shell_view_open_task (task_shell_view, comp_data);
+
+ /* FIXME Need to actually assign the task. */
+}
+
+static void
+action_task_clipboard_copy_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ e_calendar_table_copy_clipboard (task_table);
+}
+
+static void
+action_task_clipboard_cut_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ e_calendar_table_cut_clipboard (task_table);
+}
+
+static void
+action_task_clipboard_paste_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ e_calendar_table_paste_clipboard (task_table);
+}
+
+static void
+action_task_delete_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalComponentPreview *task_preview;
+ ECalendarTable *task_table;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ task_preview = e_task_shell_content_get_task_preview (task_shell_content);
+
+ e_task_shell_view_set_status_message (
+ task_shell_view, _("Deleting selected tasks..."), -1.0);
+ e_calendar_table_delete_selected (task_table);
+ e_task_shell_view_set_status_message (task_shell_view, NULL, -1.0);
+
+ e_cal_component_preview_clear (task_preview);
+}
+
+static void
+action_task_forward_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ GSList *list;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only forward the first selected task. */
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_component_set_icalcomponent (comp, clone);
+ itip_send_comp (
+ E_CAL_COMPONENT_METHOD_PUBLISH, comp,
+ comp_data->client, NULL, NULL, NULL, TRUE);
+ g_object_unref (comp);
+}
+
+static void
+action_task_list_copy_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellSidebar *task_shell_sidebar;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ ESourceSelector *selector;
+ ESource *source;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ copy_source_dialog (
+ GTK_WINDOW (shell_window),
+ source, E_CAL_SOURCE_TYPE_TODO);
+}
+
+static void
+action_task_list_delete_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellBackend *task_shell_backend;
+ ETaskShellContent *task_shell_content;
+ ETaskShellSidebar *task_shell_sidebar;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ ECalendarTable *task_table;
+ ECal *client;
+ ECalModel *model;
+ ESourceSelector *selector;
+ ESourceGroup *source_group;
+ ESourceList *source_list;
+ ESource *source;
+ gint response;
+ gchar *uri;
+ GError *error = NULL;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ task_shell_backend = task_shell_view->priv->task_shell_backend;
+ source_list = e_task_shell_backend_get_source_list (task_shell_backend);
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ model = e_calendar_table_get_model (task_table);
+
+ task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ /* Ask for confirmation. */
+ response = e_error_run (
+ GTK_WINDOW (shell_window),
+ "calendar:prompt-delete-task-list",
+ e_source_peek_name (source));
+ if (response != GTK_RESPONSE_YES)
+ return;
+
+ uri = e_source_get_uri (source);
+ client = e_cal_model_get_client_for_uri (model, uri);
+ if (client == NULL)
+ client = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_JOURNAL);
+ g_free (uri);
+
+ g_return_if_fail (client != NULL);
+
+ if (!e_cal_remove (client, &error)) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (e_source_selector_source_is_selected (selector, source)) {
+ e_task_shell_sidebar_remove_source (
+ task_shell_sidebar, source);
+ e_source_selector_unselect_source (selector, source);
+ }
+
+ source_group = e_source_peek_group (source);
+ e_source_group_remove_source (source_group, source);
+
+ if (!e_source_list_sync (source_list, &error)) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+action_task_list_new_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ calendar_setup_new_task_list (GTK_WINDOW (shell_window));
+}
+
+static void
+action_task_list_print_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ETable *table;
+ GtkPrintOperationAction print_action;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ table = e_calendar_table_get_table (task_table);
+
+ print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
+ print_table (table, _("Print Tasks"), _("Tasks"), print_action);
+}
+
+static void
+action_task_list_print_preview_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ETable *table;
+ GtkPrintOperationAction print_action;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ table = e_calendar_table_get_table (task_table);
+
+ print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW;
+ print_table (table, _("Print Tasks"), _("Tasks"), print_action);
+}
+
+static void
+action_task_list_properties_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellSidebar *task_shell_sidebar;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ESource *source;
+ ESourceSelector *selector;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+ source = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ calendar_setup_edit_task_list (GTK_WINDOW (shell_window), source);
+}
+
+static void
+action_task_list_rename_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellSidebar *task_shell_sidebar;
+ ESourceSelector *selector;
+
+ task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+
+ e_source_selector_edit_primary_selection (selector);
+}
+
+static void
+action_task_list_select_one_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellSidebar *task_shell_sidebar;
+ ESourceSelector *selector;
+ ESource *primary;
+ GSList *list, *iter;
+
+ /* XXX ESourceSelector should provide a function for this. */
+
+ task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+ primary = e_source_selector_peek_primary_selection (selector);
+ g_return_if_fail (primary != NULL);
+
+ list = e_source_selector_get_selection (selector);
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ESource *source = iter->data;
+
+ if (source == primary)
+ continue;
+
+ e_source_selector_unselect_source (selector, source);
+ }
+ e_source_selector_free_selection (list);
+
+ e_source_selector_select_source (selector, primary);
+}
+
+static void
+action_task_mark_complete_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModel *model;
+ GSList *list, *iter;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ list = e_calendar_table_get_selected (task_table);
+ model = e_calendar_table_get_model (task_table);
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ECalModelComponent *comp_data = iter->data;
+ e_cal_model_tasks_mark_comp_complete (
+ E_CAL_MODEL_TASKS (model), comp_data);
+ }
+
+ g_slist_free (list);
+}
+
+static void
+action_task_mark_incomplete_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModel *model;
+ GSList *list, *iter;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ list = e_calendar_table_get_selected (task_table);
+ model = e_calendar_table_get_model (task_table);
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ ECalModelComponent *comp_data = iter->data;
+ e_cal_model_tasks_mark_comp_incomplete (
+ E_CAL_MODEL_TASKS (model), comp_data);
+ }
+
+ g_slist_free (list);
+}
+
+static void
+action_task_new_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ ECal *client;
+ ECalComponent *comp;
+ CompEditor *editor;
+ GSList *list;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ client = comp_data->client;
+ editor = task_editor_new (client, shell, COMP_EDITOR_NEW_ITEM);
+ comp = cal_comp_task_new_with_defaults (client);
+ comp_editor_edit_comp (editor, comp);
+
+ gtk_window_present (GTK_WINDOW (editor));
+
+ g_object_unref (comp);
+ g_object_unref (client);
+}
+
+static void
+action_task_open_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only open the first selected task. */
+ e_task_shell_view_open_task (task_shell_view, comp_data);
+}
+
+static void
+action_task_open_url_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ icalproperty *prop;
+ const gchar *uri;
+ GSList *list;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+
+ /* XXX We only open the URI of the first selected task. */
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_URL_PROPERTY);
+ g_return_if_fail (prop == NULL);
+
+ uri = icalproperty_get_url (prop);
+ e_show_uri (GTK_WINDOW (shell_window), uri);
+}
+
+static void
+action_task_preview_cb (GtkToggleAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ gboolean visible;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ visible = gtk_toggle_action_get_active (action);
+ e_task_shell_content_set_preview_visible (task_shell_content, visible);
+}
+
+static void
+action_task_print_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ GtkPrintOperationAction print_action;
+ GSList *list;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ /* XXX We only print the first selected task. */
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
+ e_cal_component_set_icalcomponent (comp, clone);
+ print_comp (comp, comp_data->client, print_action);
+ g_object_unref (comp);
+}
+
+static void
+action_task_purge_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ GtkWidget *dialog;
+ GtkWidget *widget;
+ gboolean active;
+ gint response;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ if (!calendar_config_get_confirm_purge ())
+ goto purge;
+
+ /* XXX This needs reworked. The dialog looks like ass. */
+
+ dialog = gtk_message_dialog_new (
+ GTK_WINDOW (shell_window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_YES_NO,
+ "%s", _("This operation will permanently erase all tasks "
+ "marked as completed. If you continue, you will not be able "
+ "to recover these tasks.\n\nReally erase these tasks?"));
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_NO);
+
+ widget = gtk_check_button_new_with_label (_("Do not ask me again"));
+ gtk_box_pack_start (
+ GTK_BOX (GTK_DIALOG (dialog)->vbox), widget, TRUE, TRUE, 6);
+ gtk_widget_show (widget);
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ gtk_widget_destroy (dialog);
+
+ if (response != GTK_RESPONSE_YES)
+ return;
+
+ if (active)
+ calendar_config_set_confirm_purge (FALSE);
+
+purge:
+
+ /* FIXME */
+ ;
+}
+
+static void
+action_task_save_as_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModelComponent *comp_data;
+ GSList *list;
+ gchar *filename;
+ gchar *string;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ list = e_calendar_table_get_selected (task_table);
+ g_return_if_fail (list != NULL);
+ comp_data = list->data;
+ g_slist_free (list);
+
+ filename = e_file_dialog_save (_("Save as..."), NULL);
+ if (filename == NULL)
+ return;
+
+ string = e_cal_get_component_as_string (
+ comp_data->client, comp_data->icalcomp);
+ if (string == NULL) {
+ g_warning ("Could not convert task to a string");
+ return;
+ }
+
+ e_write_file_uri (filename, string);
+
+ g_free (filename);
+ g_free (string);
+}
+
+static void
+action_task_search_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ ETaskShellView *task_shell_view)
+{
+ EShellView *shell_view;
+ EShellContent *shell_content;
+ const gchar *search_hint;
+
+ /* XXX Figure out a way to handle this in EShellContent
+ * instead of every shell view having to handle it.
+ * The problem is EShellContent does not know what
+ * the search option actions are for this view. It
+ * would have to dig up the popup menu and retrieve
+ * the action for each menu item. Seems messy. */
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+
+ search_hint = gtk_action_get_label (GTK_ACTION (current));
+ e_shell_content_set_search_hint (shell_content, search_hint);
+}
+
+static GtkActionEntry task_entries[] = {
+
+ { "task-assign",
+ NULL,
+ N_("_Assign Task"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_task_assign_cb) },
+
+ { "task-clipboard-copy",
+ GTK_STOCK_COPY,
+ NULL,
+ NULL,
+ N_("Copy selected tasks"),
+ G_CALLBACK (action_task_clipboard_copy_cb) },
+
+ { "task-clipboard-cut",
+ GTK_STOCK_CUT,
+ NULL,
+ NULL,
+ N_("Cut selected tasks"),
+ G_CALLBACK (action_task_clipboard_cut_cb) },
+
+ { "task-clipboard-paste",
+ GTK_STOCK_PASTE,
+ NULL,
+ NULL,
+ N_("Paste tasks from the clipboard"),
+ G_CALLBACK (action_task_clipboard_paste_cb) },
+
+ { "task-delete",
+ GTK_STOCK_DELETE,
+ N_("_Delete Task"),
+ NULL,
+ N_("Delete selected tasks"),
+ G_CALLBACK (action_task_delete_cb) },
+
+ { "task-forward",
+ "mail-forward",
+ N_("_Forward as iCalendar..."),
+ "<Control>f",
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_task_forward_cb) },
+
+ { "task-list-copy",
+ GTK_STOCK_COPY,
+ N_("Copy..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_task_list_copy_cb) },
+
+ { "task-list-delete",
+ GTK_STOCK_DELETE,
+ N_("_Delete"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_task_list_delete_cb) },
+
+ { "task-list-new",
+ "stock_todo",
+ N_("_New Task List"),
+ NULL,
+ N_("Create a new task list"),
+ G_CALLBACK (action_task_list_new_cb) },
+
+ { "task-list-properties",
+ GTK_STOCK_PROPERTIES,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_task_list_properties_cb) },
+
+ { "task-list-rename",
+ NULL,
+ N_("_Rename..."),
+ "F2",
+ N_("Rename the selected task list"),
+ G_CALLBACK (action_task_list_rename_cb) },
+
+ { "task-list-select-one",
+ "stock_check-filled",
+ N_("Show _Only This Task List"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_task_list_select_one_cb) },
+
+ { "task-mark-complete",
+ NULL,
+ N_("_Mark as Complete"),
+ "<Control>k",
+ N_("Mark selected tasks as complete"),
+ G_CALLBACK (action_task_mark_complete_cb) },
+
+ { "task-mark-incomplete",
+ NULL,
+ N_("Mar_k as Incomplete"),
+ NULL,
+ N_("Mark selected tasks as incomplete"),
+ G_CALLBACK (action_task_mark_incomplete_cb) },
+
+ { "task-new",
+ "stock_task",
+ N_("New _Task"),
+ NULL,
+ N_("Create a new task"),
+ G_CALLBACK (action_task_new_cb) },
+
+ { "task-open",
+ GTK_STOCK_OPEN,
+ N_("_Open Task"),
+ "<Control>o",
+ N_("View the selected task"),
+ G_CALLBACK (action_task_open_cb) },
+
+ { "task-open-url",
+ "applications-internet",
+ N_("Open _Web Page"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_task_open_url_cb) },
+
+ { "task-purge",
+ NULL,
+ N_("Purg_e"),
+ "<Control>e",
+ N_("Delete completed tasks"),
+ G_CALLBACK (action_task_purge_cb) },
+
+ { "task-save-as",
+ GTK_STOCK_SAVE_AS,
+ N_("_Save as iCalendar..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_task_save_as_cb) },
+
+ /*** Menus ***/
+
+ { "task-actions-menu",
+ NULL,
+ N_("_Actions"),
+ NULL,
+ NULL,
+ NULL }
+};
+
+static EPopupActionEntry task_popup_entries[] = {
+
+ { "task-list-popup-copy",
+ NULL,
+ "task-list-copy" },
+
+ { "task-list-popup-delete",
+ NULL,
+ "task-list-delete" },
+
+ { "task-list-popup-properties",
+ NULL,
+ "task-list-properties" },
+
+ { "task-list-popup-rename",
+ NULL,
+ "task-list-rename" },
+
+ { "task-list-popup-select-one",
+ NULL,
+ "task-list-select-one" },
+
+ { "task-popup-assign",
+ NULL,
+ "task-assign" },
+
+ { "task-popup-clipboard-copy",
+ NULL,
+ "task-clipboard-copy" },
+
+ { "task-popup-clipboard-cut",
+ NULL,
+ "task-clipboard-cut" },
+
+ { "task-popup-clipboard-paste",
+ NULL,
+ "task-clipboard-paste" },
+
+ { "task-popup-delete",
+ NULL,
+ "task-delete" },
+
+ { "task-popup-forward",
+ NULL,
+ "task-forward" },
+
+ { "task-popup-mark-complete",
+ NULL,
+ "task-mark-complete" },
+
+ { "task-popup-mark-incomplete",
+ NULL,
+ "task-mark-incomplete" },
+
+ { "task-popup-open",
+ NULL,
+ "task-open" },
+
+ { "task-popup-open-url",
+ NULL,
+ "task-open-url" },
+
+ { "task-popup-save-as",
+ NULL,
+ "task-save-as" },
+};
+
+static GtkToggleActionEntry task_toggle_entries[] = {
+
+ { "task-preview",
+ NULL,
+ N_("Task _Preview"),
+ "<Control>m",
+ N_("Show task preview pane"),
+ G_CALLBACK (action_task_preview_cb),
+ TRUE }
+};
+
+static GtkRadioActionEntry task_filter_entries[] = {
+
+ { "task-filter-active-tasks",
+ NULL,
+ N_("Active Tasks"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_FILTER_ACTIVE_TASKS },
+
+ { "task-filter-any-category",
+ NULL,
+ N_("Any Category"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_FILTER_ANY_CATEGORY },
+
+ { "task-filter-completed-tasks",
+ NULL,
+ N_("Completed Tasks"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_FILTER_COMPLETED_TASKS },
+
+ { "task-filter-next-7-days-tasks",
+ NULL,
+ N_("Next 7 Days' Tasks"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_FILTER_NEXT_7_DAYS_TASKS },
+
+ { "task-filter-overdue-tasks",
+ NULL,
+ N_("Overdue Tasks"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_FILTER_OVERDUE_TASKS },
+
+ { "task-filter-tasks-with-attachments",
+ NULL,
+ N_("Tasks with Attachments"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_FILTER_TASKS_WITH_ATTACHMENTS },
+
+ { "task-filter-unmatched",
+ NULL,
+ N_("Unmatched"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_FILTER_UNMATCHED }
+};
+
+static GtkRadioActionEntry task_search_entries[] = {
+
+ { "task-search-any-field-contains",
+ NULL,
+ N_("Any field contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_SEARCH_ANY_FIELD_CONTAINS },
+
+ { "task-search-description-contains",
+ NULL,
+ N_("Description contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_SEARCH_DESCRIPTION_CONTAINS },
+
+ { "task-search-summary-contains",
+ NULL,
+ N_("Summary contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ TASK_SEARCH_SUMMARY_CONTAINS }
+};
+
+static GtkActionEntry lockdown_printing_entries[] = {
+
+ { "task-list-print",
+ GTK_STOCK_PRINT,
+ NULL,
+ "<Control>p",
+ N_("Print the list of tasks"),
+ G_CALLBACK (action_task_list_print_cb) },
+
+ { "task-list-print-preview",
+ GTK_STOCK_PRINT_PREVIEW,
+ NULL,
+ NULL,
+ N_("Preview the list of tasks to be printed"),
+ G_CALLBACK (action_task_list_print_preview_cb) },
+
+ { "task-print",
+ GTK_STOCK_PRINT,
+ NULL,
+ NULL,
+ N_("Print the selected task"),
+ G_CALLBACK (action_task_print_cb) }
+};
+
+static EPopupActionEntry lockdown_printing_popup_entries[] = {
+
+ { "task-popup-print",
+ NULL,
+ "task-print" }
+};
+
+void
+e_task_shell_view_actions_init (ETaskShellView *task_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ GtkActionGroup *action_group;
+ GConfBridge *bridge;
+ GtkAction *action;
+ GObject *object;
+ const gchar *key;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ /* Task Actions */
+ action_group = ACTION_GROUP (TASKS);
+ gtk_action_group_add_actions (
+ action_group, task_entries,
+ G_N_ELEMENTS (task_entries), task_shell_view);
+ e_action_group_add_popup_actions (
+ action_group, task_popup_entries,
+ G_N_ELEMENTS (task_popup_entries));
+ gtk_action_group_add_toggle_actions (
+ action_group, task_toggle_entries,
+ G_N_ELEMENTS (task_toggle_entries), task_shell_view);
+ gtk_action_group_add_radio_actions (
+ action_group, task_search_entries,
+ G_N_ELEMENTS (task_search_entries),
+ TASK_SEARCH_SUMMARY_CONTAINS,
+ G_CALLBACK (action_task_search_cb), task_shell_view);
+
+ /* Lockdown Printing Actions */
+ action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
+ gtk_action_group_add_actions (
+ action_group, lockdown_printing_entries,
+ G_N_ELEMENTS (lockdown_printing_entries), task_shell_view);
+ e_action_group_add_popup_actions (
+ action_group, lockdown_printing_popup_entries,
+ G_N_ELEMENTS (lockdown_printing_popup_entries));
+
+ /* Bind GObject properties to GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (ACTION (TASK_PREVIEW));
+ key = "/apps/evolution/calendar/display/show_task_preview";
+ gconf_bridge_bind_property (bridge, key, object, "active");
+
+ /* Fine tuning. */
+
+ action = ACTION (TASK_DELETE);
+ g_object_set (action, "short-label", _("Delete"), NULL);
+
+ g_signal_connect (
+ ACTION (GAL_SAVE_CUSTOM_VIEW), "activate",
+ G_CALLBACK (action_gal_save_custom_view_cb), task_shell_view);
+
+ g_signal_connect (
+ ACTION (SEARCH_EXECUTE), "activate",
+ G_CALLBACK (action_search_execute_cb), task_shell_view);
+}
+
+void
+e_task_shell_view_update_search_filter (ETaskShellView *task_shell_view)
+{
+ EShellContent *shell_content;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ GtkActionGroup *action_group;
+ GtkRadioAction *radio_action;
+ GList *list, *iter;
+ GSList *group;
+ gint ii;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ action_group = ACTION_GROUP (TASKS_FILTER);
+ e_action_group_remove_all_actions (action_group);
+
+ /* Add the standard filter actions. */
+ gtk_action_group_add_radio_actions (
+ action_group, task_filter_entries,
+ G_N_ELEMENTS (task_filter_entries),
+ TASK_FILTER_ANY_CATEGORY,
+ G_CALLBACK (action_search_filter_cb),
+ task_shell_view);
+
+ /* Retrieve the radio group from an action we just added. */
+ list = gtk_action_group_list_actions (action_group);
+ radio_action = GTK_RADIO_ACTION (list->data);
+ group = gtk_radio_action_get_group (radio_action);
+ g_list_free (list);
+
+ /* Build the category actions. */
+
+ list = e_categories_get_list ();
+ for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) {
+ const gchar *category_name = iter->data;
+ const gchar *filename;
+ GtkAction *action;
+ gchar *action_name;
+
+ action_name = g_strdup_printf (
+ "task-filter-category-%d", ii);
+ radio_action = gtk_radio_action_new (
+ action_name, category_name, NULL, NULL, ii);
+ g_free (action_name);
+
+ /* Convert the category icon file to a themed icon name. */
+ filename = e_categories_get_icon_file_for (category_name);
+ if (filename != NULL && *filename != '\0') {
+ gchar *basename;
+ gchar *cp;
+
+ basename = g_path_get_basename (filename);
+
+ /* Lose the file extension. */
+ if ((cp = strrchr (basename, '.')) != NULL)
+ *cp = '\0';
+
+ g_object_set (
+ radio_action, "icon-name", basename, NULL);
+
+ g_free (basename);
+ }
+
+ gtk_radio_action_set_group (radio_action, group);
+ group = gtk_radio_action_get_group (radio_action);
+
+ /* The action group takes ownership of the action. */
+ action = GTK_ACTION (radio_action);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (radio_action);
+ }
+ g_list_free (list);
+
+ /* Use any action in the group; doesn't matter which. */
+ e_shell_content_set_filter_action (shell_content, radio_action);
+
+ ii = TASK_FILTER_UNMATCHED;
+ e_shell_content_add_filter_separator_after (shell_content, ii);
+
+ ii = TASK_FILTER_TASKS_WITH_ATTACHMENTS;
+ e_shell_content_add_filter_separator_after (shell_content, ii);
+}
diff --git a/modules/calendar/e-task-shell-view-actions.h b/modules/calendar/e-task-shell-view-actions.h
new file mode 100644
index 0000000000..d7db39bcc9
--- /dev/null
+++ b/modules/calendar/e-task-shell-view-actions.h
@@ -0,0 +1,105 @@
+/*
+ * e-task-shell-view-actions.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_TASK_SHELL_VIEW_ACTIONS_H
+#define E_TASK_SHELL_VIEW_ACTIONS_H
+
+#include <shell/e-shell-window-actions.h>
+
+/* Task Actions */
+#define E_SHELL_WINDOW_ACTION_TASK_ASSIGN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-assign")
+#define E_SHELL_WINDOW_ACTION_TASK_CLIPBOARD_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-clipboard-copy")
+#define E_SHELL_WINDOW_ACTION_TASK_CLIPBOARD_CUT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-clipboard-cut")
+#define E_SHELL_WINDOW_ACTION_TASK_CLIPBOARD_PASTE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-clipboard-paste")
+#define E_SHELL_WINDOW_ACTION_TASK_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-delete")
+#define E_SHELL_WINDOW_ACTION_TASK_FORWARD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-forward")
+#define E_SHELL_WINDOW_ACTION_TASK_MARK_COMPLETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-mark-complete")
+#define E_SHELL_WINDOW_ACTION_TASK_MARK_INCOMPLETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-mark-incomplete")
+#define E_SHELL_WINDOW_ACTION_TASK_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-new")
+#define E_SHELL_WINDOW_ACTION_TASK_OPEN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-open")
+#define E_SHELL_WINDOW_ACTION_TASK_OPEN_URL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-open-url")
+#define E_SHELL_WINDOW_ACTION_TASK_PREVIEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-preview")
+#define E_SHELL_WINDOW_ACTION_TASK_PRINT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-print")
+#define E_SHELL_WINDOW_ACTION_TASK_PURGE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-purge")
+#define E_SHELL_WINDOW_ACTION_TASK_SAVE_AS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-save-as")
+
+/* Task List Actions */
+#define E_SHELL_WINDOW_ACTION_TASK_LIST_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-list-copy")
+#define E_SHELL_WINDOW_ACTION_TASK_LIST_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-list-delete")
+#define E_SHELL_WINDOW_ACTION_TASK_LIST_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-list-new")
+#define E_SHELL_WINDOW_ACTION_TASK_LIST_PRINT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-list-print")
+#define E_SHELL_WINDOW_ACTION_TASK_LIST_PRINT_PREVIEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-list-print-preview")
+#define E_SHELL_WINDOW_ACTION_TASK_LIST_PROPERTIES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-list-properties")
+#define E_SHELL_WINDOW_ACTION_TASK_LIST_RENAME(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-list-rename")
+#define E_SHELL_WINDOW_ACTION_TASK_LIST_SELECT_ONE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-list-select-one")
+
+/* Task Query Actions */
+#define E_SHELL_WINDOW_ACTION_TASK_FILTER_ACTIVE_TASKS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-filter-active-tasks")
+#define E_SHELL_WINDOW_ACTION_TASK_FILTER_ANY_CATEGORY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-filter-any-category")
+#define E_SHELL_WINDOW_ACTION_TASK_FILTER_COMPLETED_TASKS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-filter-completed-tasks")
+#define E_SHELL_WINDOW_ACTION_TASK_FILTER_NEXT_7_DAYS_TASKS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-filter-next-7-days-tasks")
+#define E_SHELL_WINDOW_ACTION_TASK_FILTER_OVERDUE_TASKS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-filter-overdue-tasks")
+#define E_SHELL_WINDOW_ACTION_TASK_FILTER_TASKS_WITH_ATTACHMENTS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-filter-tasks-with-attachments")
+#define E_SHELL_WINDOW_ACTION_TASK_FILTER_UNMATCHED(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-filter-unmatched")
+#define E_SHELL_WINDOW_ACTION_TASK_SEARCH_ANY_FIELD_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-search-any-field-contains")
+#define E_SHELL_WINDOW_ACTION_TASK_SEARCH_DESCRIPTION_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-search-description-contains")
+#define E_SHELL_WINDOW_ACTION_TASK_SEARCH_SUMMARY_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "task-search-summary-contains")
+
+/* Action Groups */
+#define E_SHELL_WINDOW_ACTION_GROUP_TASKS(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "tasks")
+#define E_SHELL_WINDOW_ACTION_GROUP_TASKS_FILTER(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "tasks-filter")
+
+#endif /* E_TASK_SHELL_VIEW_ACTIONS_H */
diff --git a/modules/calendar/e-task-shell-view-private.c b/modules/calendar/e-task-shell-view-private.c
new file mode 100644
index 0000000000..d6fd8e35aa
--- /dev/null
+++ b/modules/calendar/e-task-shell-view-private.c
@@ -0,0 +1,744 @@
+/*
+ * e-task-shell-view-private.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-task-shell-view-private.h"
+
+#include "widgets/menus/gal-view-factory-etable.h"
+
+static void
+task_shell_view_config_hide_completed_tasks_changed_cb (GConfClient *client,
+ guint id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ ETaskShellView *task_shell_view = user_data;
+ ETaskShellContent *task_shell_content;
+ ETaskShellSidebar *task_shell_sidebar;
+ ECalendarTable *task_table;
+ GList *clients;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
+ clients = e_task_shell_sidebar_get_clients (task_shell_sidebar);
+
+ e_calendar_table_process_completed_tasks (task_table, clients, TRUE);
+
+ /* Search query takes whether to show completed tasks into account,
+ * so if the preference has changed we need to update the query. */
+ e_task_shell_view_execute_search (task_shell_view);
+
+ g_list_free (clients);
+}
+
+static void
+task_shell_view_config_timezone_changed_cb (GConfClient *client,
+ guint id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ ETaskShellView *task_shell_view = user_data;
+
+ e_task_shell_view_update_timezone (task_shell_view);
+}
+
+static void
+task_shell_view_table_popup_event_cb (EShellView *shell_view,
+ GdkEventButton *event)
+{
+ const gchar *widget_path;
+
+ widget_path = "/task-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+}
+
+static void
+task_shell_view_table_user_created_cb (ETaskShellView *task_shell_view,
+ ECalendarTable *task_table)
+{
+ ETaskShellSidebar *task_shell_sidebar;
+ ECalModel *model;
+ ECal *client;
+ ESource *source;
+
+ /* This is the "Click to Add" handler. */
+
+ model = e_calendar_table_get_model (task_table);
+ client = e_cal_model_get_default_client (model);
+ source = e_cal_get_source (client);
+
+ task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
+ e_task_shell_sidebar_add_source (task_shell_sidebar, source);
+
+ e_cal_model_add_client (model, client);
+}
+
+static void
+task_shell_view_selector_client_added_cb (ETaskShellView *task_shell_view,
+ ECal *client)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModel *model;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ model = e_calendar_table_get_model (task_table);
+
+ e_cal_model_add_client (model, client);
+ e_task_shell_view_update_timezone (task_shell_view);
+}
+
+static void
+task_shell_view_selector_client_removed_cb (ETaskShellView *task_shell_view,
+ ECal *client)
+{
+ ETaskShellContent *task_shell_content;
+ ECalendarTable *task_table;
+ ECalModel *model;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ model = e_calendar_table_get_model (task_table);
+
+ e_cal_model_remove_client (model, client);
+}
+
+static gboolean
+task_shell_view_selector_popup_event_cb (EShellView *shell_view,
+ ESource *primary_source,
+ GdkEventButton *event)
+{
+ const gchar *widget_path;
+
+ widget_path = "/task-list-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+
+ return TRUE;
+}
+
+static gboolean
+task_shell_view_update_timeout_cb (ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ETaskShellSidebar *task_shell_sidebar;
+ ECalendarTable *task_table;
+ ECalModel *model;
+ GList *clients;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ model = e_calendar_table_get_model (task_table);
+
+ task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
+ clients = e_task_shell_sidebar_get_clients (task_shell_sidebar);
+
+ e_calendar_table_process_completed_tasks (task_table, clients, FALSE);
+ e_cal_model_tasks_update_due_tasks (E_CAL_MODEL_TASKS (model));
+
+ g_list_free (clients);
+
+ return TRUE;
+}
+
+static void
+task_shell_view_load_view_collection (EShellViewClass *shell_view_class)
+{
+ GalViewCollection *collection;
+ GalViewFactory *factory;
+ ETableSpecification *spec;
+ const gchar *base_dir;
+ gchar *filename;
+
+ collection = shell_view_class->view_collection;
+
+ base_dir = EVOLUTION_ETSPECDIR;
+ spec = e_table_specification_new ();
+ filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL);
+ if (!e_table_specification_load_from_file (spec, filename))
+ g_critical ("Unable to load ETable specification file "
+ "for tasks");
+ g_free (filename);
+
+ factory = gal_view_factory_etable_new (spec);
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+ g_object_unref (spec);
+
+ gal_view_collection_load (collection);
+}
+
+static void
+task_shell_view_notify_view_id_cb (ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ GalViewInstance *view_instance;
+ const gchar *view_id;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ view_instance =
+ e_task_shell_content_get_view_instance (task_shell_content);
+ view_id = e_shell_view_get_view_id (E_SHELL_VIEW (task_shell_view));
+
+ /* A NULL view ID implies we're in a custom view. But you can
+ * only get to a custom view via the "Define Views" dialog, which
+ * would have already modified the view instance appropriately.
+ * Furthermore, there's no way to refer to a custom view by ID
+ * anyway, since custom views have no IDs. */
+ if (view_id == NULL)
+ return;
+
+ gal_view_instance_set_current_view_id (view_instance, view_id);
+}
+
+void
+e_task_shell_view_private_init (ETaskShellView *task_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ if (!gal_view_collection_loaded (shell_view_class->view_collection))
+ task_shell_view_load_view_collection (shell_view_class);
+
+ g_signal_connect (
+ task_shell_view, "notify::view-id",
+ G_CALLBACK (task_shell_view_notify_view_id_cb), NULL);
+}
+
+void
+e_task_shell_view_private_constructed (ETaskShellView *task_shell_view)
+{
+ ETaskShellViewPrivate *priv = task_shell_view->priv;
+ ETaskShellContent *task_shell_content;
+ ETaskShellSidebar *task_shell_sidebar;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+ EShellContent *shell_content;
+ EShellSidebar *shell_sidebar;
+ EShellWindow *shell_window;
+ ECalendarTable *task_table;
+ ECalModel *model;
+ ETable *table;
+ ESourceSelector *selector;
+ guint id;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ e_shell_window_add_action_group (shell_window, "tasks");
+ e_shell_window_add_action_group (shell_window, "tasks-filter");
+
+ /* Cache these to avoid lots of awkward casting. */
+ priv->task_shell_backend = g_object_ref (shell_backend);
+ priv->task_shell_content = g_object_ref (shell_content);
+ priv->task_shell_sidebar = g_object_ref (shell_sidebar);
+
+ task_shell_content = E_TASK_SHELL_CONTENT (shell_content);
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ model = e_calendar_table_get_model (task_table);
+ table = e_calendar_table_get_table (task_table);
+
+ task_shell_sidebar = E_TASK_SHELL_SIDEBAR (shell_sidebar);
+ selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
+
+ g_signal_connect_swapped (
+ task_table, "open-component",
+ G_CALLBACK (e_task_shell_view_open_task),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ task_table, "popup-event",
+ G_CALLBACK (task_shell_view_table_popup_event_cb),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ task_table, "status-message",
+ G_CALLBACK (e_task_shell_view_set_status_message),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ task_table, "user-created",
+ G_CALLBACK (task_shell_view_table_user_created_cb),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ model, "model-changed",
+ G_CALLBACK (e_task_shell_view_update_sidebar),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ model, "model-rows-deleted",
+ G_CALLBACK (e_task_shell_view_update_sidebar),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ model, "model-rows-inserted",
+ G_CALLBACK (e_task_shell_view_update_sidebar),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ table, "selection-change",
+ G_CALLBACK (e_task_shell_view_update_sidebar),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ task_shell_sidebar, "client-added",
+ G_CALLBACK (task_shell_view_selector_client_added_cb),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ task_shell_sidebar, "client-removed",
+ G_CALLBACK (task_shell_view_selector_client_removed_cb),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ task_shell_sidebar, "status-message",
+ G_CALLBACK (e_task_shell_view_set_status_message),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ selector, "popup-event",
+ G_CALLBACK (task_shell_view_selector_popup_event_cb),
+ task_shell_view);
+
+ g_signal_connect_swapped (
+ selector, "primary-selection-changed",
+ G_CALLBACK (e_shell_view_update_actions),
+ task_shell_view);
+
+ e_categories_register_change_listener (
+ G_CALLBACK (e_task_shell_view_update_search_filter),
+ task_shell_view);
+
+ task_shell_view_update_timeout_cb (task_shell_view);
+ priv->update_timeout = g_timeout_add_full (
+ G_PRIORITY_LOW, 60000, (GSourceFunc)
+ task_shell_view_update_timeout_cb,
+ task_shell_view, NULL);
+
+ /* Listen for configuration changes. */
+
+ /* Timezone */
+ id = calendar_config_add_notification_timezone (
+ task_shell_view_config_timezone_changed_cb, task_shell_view);
+ priv->notifications = g_list_prepend (
+ priv->notifications, GUINT_TO_POINTER (id));
+
+ /* Hide Completed Tasks (enable/units/value) */
+ id = calendar_config_add_notification_hide_completed_tasks (
+ task_shell_view_config_hide_completed_tasks_changed_cb,
+ task_shell_view);
+ priv->notifications = g_list_prepend (
+ priv->notifications, GUINT_TO_POINTER (id));
+ id = calendar_config_add_notification_hide_completed_tasks_units (
+ task_shell_view_config_hide_completed_tasks_changed_cb,
+ task_shell_view);
+ priv->notifications = g_list_prepend (
+ priv->notifications, GUINT_TO_POINTER (id));
+ id = calendar_config_add_notification_hide_completed_tasks_value (
+ task_shell_view_config_hide_completed_tasks_changed_cb,
+ task_shell_view);
+ priv->notifications = g_list_prepend (
+ priv->notifications, GUINT_TO_POINTER (id));
+
+ e_task_shell_view_actions_init (task_shell_view);
+ e_task_shell_view_update_sidebar (task_shell_view);
+ e_task_shell_view_update_search_filter (task_shell_view);
+ e_task_shell_view_update_timezone (task_shell_view);
+
+ e_task_shell_view_execute_search (task_shell_view);
+}
+
+void
+e_task_shell_view_private_dispose (ETaskShellView *task_shell_view)
+{
+ ETaskShellViewPrivate *priv = task_shell_view->priv;
+ GList *iter;
+
+ DISPOSE (priv->task_shell_backend);
+ DISPOSE (priv->task_shell_content);
+ DISPOSE (priv->task_shell_sidebar);
+
+ if (task_shell_view->priv->activity != NULL) {
+ /* XXX Activity is no cancellable. */
+ e_activity_complete (task_shell_view->priv->activity);
+ g_object_unref (task_shell_view->priv->activity);
+ task_shell_view->priv->activity = NULL;
+ }
+
+ if (priv->update_timeout > 0) {
+ g_source_remove (priv->update_timeout);
+ priv->update_timeout = 0;
+ }
+
+ for (iter = priv->notifications; iter != NULL; iter = iter->next) {
+ guint notification_id = GPOINTER_TO_UINT (iter->data);
+ calendar_config_remove_notification (notification_id);
+ }
+ g_list_free (priv->notifications);
+ priv->notifications = NULL;
+}
+
+void
+e_task_shell_view_private_finalize (ETaskShellView *task_shell_view)
+{
+ /* XXX Nothing to do? */
+}
+
+void
+e_task_shell_view_execute_search (ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellContent *shell_content;
+ GtkAction *action;
+ GString *string;
+ ECalComponentPreview *task_preview;
+ ECalendarTable *task_table;
+ ECalModel *model;
+ FilterRule *rule;
+ const gchar *format;
+ const gchar *text;
+ time_t start_range;
+ time_t end_range;
+ gchar *start, *end;
+ gchar *query;
+ gchar *temp;
+ gint value;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ text = e_shell_content_get_search_text (shell_content);
+
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ action = ACTION (TASK_SEARCH_ANY_FIELD_CONTAINS);
+ value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
+
+ if (text == NULL || *text == '\0') {
+ text = "";
+ value = TASK_SEARCH_SUMMARY_CONTAINS;
+ }
+
+ switch (value) {
+ default:
+ text = "";
+ /* fall through */
+
+ case TASK_SEARCH_SUMMARY_CONTAINS:
+ format = "(contains? \"summary\" %s)";
+ break;
+
+ case TASK_SEARCH_DESCRIPTION_CONTAINS:
+ format = "(contains? \"description\" %s)";
+ break;
+
+ case TASK_SEARCH_ANY_FIELD_CONTAINS:
+ format = "(contains? \"any\" %s)";
+ break;
+ }
+
+ /* Build the query. */
+ string = g_string_new ("");
+ e_sexp_encode_string (string, text);
+ query = g_strdup_printf (format, string->str);
+ g_string_free (string, TRUE);
+
+ /* Apply selected filter. */
+ value = e_shell_content_get_filter_value (shell_content);
+ switch (value) {
+ case TASK_FILTER_ANY_CATEGORY:
+ break;
+
+ case TASK_FILTER_UNMATCHED:
+ temp = g_strdup_printf (
+ "(and (has-categories? #f) %s)", query);
+ g_free (query);
+ query = temp;
+ break;
+
+ case TASK_FILTER_NEXT_7_DAYS_TASKS:
+ start_range = time (NULL);
+ end_range = time_add_day (start_range, 7);
+ start = isodate_from_time_t (start_range);
+ end = isodate_from_time_t (end_range);
+
+ temp = g_strdup_printf (
+ "(and %s (due-in-time-range? "
+ "(make-time \"%s\") (make-time \"%s\")))",
+ query, start, end);
+ g_free (query);
+ query = temp;
+ break;
+
+ case TASK_FILTER_ACTIVE_TASKS:
+ start_range = time (NULL);
+ end_range = time_add_day (start_range, 365);
+ start = isodate_from_time_t (start_range);
+ end = isodate_from_time_t (end_range);
+
+ temp = g_strdup_printf (
+ "(and %s (due-in-time-range? "
+ "(make-time \"%s\") (make-time \"%s\")) "
+ "(not (is-completed?)))",
+ query, start, end);
+ g_free (query);
+ query = temp;
+ break;
+
+ case TASK_FILTER_OVERDUE_TASKS:
+ start_range = 0;
+ end_range = time (NULL);
+ start = isodate_from_time_t (start_range);
+ end = isodate_from_time_t (end_range);
+
+ temp = g_strdup_printf (
+ "(and %s (due-in-time-range? "
+ "(make-time \"%s\") (make-time \"%s\")) "
+ "(not (is-completed?)))",
+ query, start, end);
+ g_free (query);
+ query = temp;
+ break;
+
+ case TASK_FILTER_COMPLETED_TASKS:
+ temp = g_strdup_printf (
+ "(and (is-completed?) %s)", query);
+ g_free (query);
+ query = temp;
+ break;
+
+ case TASK_FILTER_TASKS_WITH_ATTACHMENTS:
+ temp = g_strdup_printf (
+ "(and (has-attachments?) %s)", query);
+ g_free (query);
+ query = temp;
+ break;
+
+ default:
+ {
+ GList *categories;
+ const gchar *category_name;
+
+ categories = e_categories_get_list ();
+ category_name = g_list_nth_data (categories, value);
+ g_list_free (categories);
+
+ temp = g_strdup_printf (
+ "(and (has-categories? \"%s\") %s)",
+ category_name, query);
+ g_free (query);
+ query = temp;
+ break;
+ }
+ }
+
+ /* Honor the user's preference to hide completed tasks. */
+ temp = calendar_config_get_hide_completed_tasks_sexp (FALSE);
+ if (temp != NULL) {
+ gchar *temp2;
+
+ temp2 = g_strdup_printf ("(and %s %s)", temp, query);
+ g_free (query);
+ g_free (temp);
+ query = temp2;
+ }
+
+ /* XXX This is wrong. We need to programmatically construct a
+ * FilterRule, tell it to build code, and pass the resulting
+ * expression string to ECalModel. */
+ rule = filter_rule_new ();
+ e_shell_content_set_search_rule (shell_content, rule);
+ g_object_unref (rule);
+
+ /* Submit the query. */
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+ model = e_calendar_table_get_model (task_table);
+ e_cal_model_set_search_query (model, query);
+ g_free (query);
+
+ task_preview =
+ e_task_shell_content_get_task_preview (task_shell_content);
+ e_cal_component_preview_clear (task_preview);
+}
+
+void
+e_task_shell_view_open_task (ETaskShellView *task_shell_view,
+ ECalModelComponent *comp_data)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ CompEditor *editor;
+ CompEditorFlags flags = 0;
+ ECalComponent *comp;
+ icalcomponent *clone;
+ icalproperty *prop;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view));
+ g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data));
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ uid = icalcomponent_get_uid (comp_data->icalcomp);
+ editor = comp_editor_find_instance (uid);
+
+ if (editor != NULL)
+ goto exit;
+
+ comp = e_cal_component_new ();
+ clone = icalcomponent_new_clone (comp_data->icalcomp);
+ e_cal_component_set_icalcomponent (comp, clone);
+
+ prop = icalcomponent_get_first_property (
+ comp_data->icalcomp, ICAL_ATTENDEE_PROPERTY);
+ if (prop != NULL)
+ flags |= COMP_EDITOR_IS_ASSIGNED;
+
+ if (itip_organizer_is_user (comp, comp_data->client))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ if (!e_cal_component_has_attendees (comp))
+ flags |= COMP_EDITOR_USER_ORG;
+
+ editor = task_editor_new (comp_data->client, shell, flags);
+ comp_editor_edit_comp (editor, comp);
+
+ g_object_ref (comp);
+
+ if (flags & COMP_EDITOR_IS_ASSIGNED)
+ task_editor_show_assignment (TASK_EDITOR (editor));
+
+exit:
+ gtk_window_present (GTK_WINDOW (editor));
+}
+
+void
+e_task_shell_view_set_status_message (ETaskShellView *task_shell_view,
+ const gchar *status_message,
+ gdouble percent)
+{
+ EActivity *activity;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+
+ g_return_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view));
+
+ activity = task_shell_view->priv->activity;
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+
+ if (status_message == NULL || *status_message == '\0') {
+ if (activity != NULL) {
+ e_activity_complete (activity);
+ g_object_unref (activity);
+ activity = NULL;
+ }
+
+ } else if (activity == NULL) {
+ activity = e_activity_new (status_message);
+ e_activity_set_percent (activity, percent);
+ e_shell_backend_add_activity (shell_backend, activity);
+
+ } else {
+ e_activity_set_percent (activity, percent);
+ e_activity_set_primary_text (activity, status_message);
+ }
+
+ task_shell_view->priv->activity = activity;
+}
+
+void
+e_task_shell_view_update_sidebar (ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ EShellView *shell_view;
+ EShellSidebar *shell_sidebar;
+ ECalendarTable *task_table;
+ ECalModel *model;
+ ETable *table;
+ GString *string;
+ const gchar *format;
+ gint n_rows;
+ gint n_selected;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_table = e_task_shell_content_get_task_table (task_shell_content);
+
+ model = e_calendar_table_get_model (task_table);
+ table = e_calendar_table_get_table (task_table);
+
+ n_rows = e_table_model_row_count (E_TABLE_MODEL (model));
+ n_selected = e_table_selected_count (table);
+
+ string = g_string_sized_new (64);
+
+ format = ngettext ("%d task", "%d tasks", n_rows);
+ g_string_append_printf (string, format, n_rows);
+
+ if (n_selected > 0) {
+ format = _("%d selected");
+ g_string_append_len (string, ", ", 2);
+ g_string_append_printf (string, format, n_selected);
+ }
+
+ e_shell_sidebar_set_secondary_text (shell_sidebar, string->str);
+
+ g_string_free (string, TRUE);
+}
+
+void
+e_task_shell_view_update_timezone (ETaskShellView *task_shell_view)
+{
+ ETaskShellContent *task_shell_content;
+ ETaskShellSidebar *task_shell_sidebar;
+ ECalComponentPreview *task_preview;
+ icaltimezone *timezone;
+ GList *clients, *iter;
+
+ task_shell_content = task_shell_view->priv->task_shell_content;
+ task_preview = e_task_shell_content_get_task_preview (task_shell_content);
+
+ task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
+ clients = e_task_shell_sidebar_get_clients (task_shell_sidebar);
+
+ timezone = calendar_config_get_icaltimezone ();
+
+ for (iter = clients; iter != NULL; iter = iter->next) {
+ ECal *client = iter->data;
+
+ if (e_cal_get_load_state (client) == E_CAL_LOAD_LOADED)
+ e_cal_set_default_timezone (client, timezone, NULL);
+ }
+
+ e_cal_component_preview_set_default_timezone (task_preview, timezone);
+
+ g_list_free (clients);
+}
diff --git a/modules/calendar/e-task-shell-view-private.h b/modules/calendar/e-task-shell-view-private.h
new file mode 100644
index 0000000000..d3bb3cf086
--- /dev/null
+++ b/modules/calendar/e-task-shell-view-private.h
@@ -0,0 +1,141 @@
+/*
+ * e-task-shell-view-private.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_TASK_SHELL_VIEW_PRIVATE_H
+#define E_TASK_SHELL_VIEW_PRIVATE_H
+
+#include "e-task-shell-view.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libecal/e-cal-time-util.h>
+#include <libedataserver/e-categories.h>
+#include <libedataserver/e-sexp.h>
+
+#include "e-util/e-dialog-utils.h"
+#include "e-util/e-error.h"
+#include "e-util/e-util.h"
+#include "e-util/gconf-bridge.h"
+#include "widgets/misc/e-popup-action.h"
+
+#include "calendar/common/authentication.h"
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/comp-util.h"
+#include "calendar/gui/e-cal-component-preview.h"
+#include "calendar/gui/e-cal-model-tasks.h"
+#include "calendar/gui/e-calendar-selector.h"
+#include "calendar/gui/print.h"
+#include "calendar/gui/dialogs/calendar-setup.h"
+#include "calendar/gui/dialogs/copy-source-dialog.h"
+#include "calendar/gui/dialogs/task-editor.h"
+
+#include "e-task-shell-backend.h"
+#include "e-task-shell-content.h"
+#include "e-task-shell-sidebar.h"
+#include "e-task-shell-view-actions.h"
+
+#define E_TASK_SHELL_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_TASK_SHELL_VIEW, ETaskShellViewPrivate))
+
+/* Shorthand, requires a variable named "shell_window". */
+#define ACTION(name) \
+ (E_SHELL_WINDOW_ACTION_##name (shell_window))
+#define ACTION_GROUP(name) \
+ (E_SHELL_WINDOW_ACTION_GROUP_##name (shell_window))
+
+/* For use in dispose() methods. */
+#define DISPOSE(obj) \
+ G_STMT_START { \
+ if ((obj) != NULL) { g_object_unref (obj); (obj) = NULL; } \
+ } G_STMT_END
+
+/* ETable Specifications */
+#define ETSPEC_FILENAME "e-calendar-table.etspec"
+
+G_BEGIN_DECLS
+
+/* Filter items are displayed in ascending order.
+ * Non-negative values are reserved for categories. */
+enum {
+ TASK_FILTER_ANY_CATEGORY = -7,
+ TASK_FILTER_UNMATCHED = -6,
+ TASK_FILTER_NEXT_7_DAYS_TASKS = -5,
+ TASK_FILTER_ACTIVE_TASKS = -4,
+ TASK_FILTER_OVERDUE_TASKS = -3,
+ TASK_FILTER_COMPLETED_TASKS = -2,
+ TASK_FILTER_TASKS_WITH_ATTACHMENTS = -1
+};
+
+/* Search items are displayed in ascending order. */
+enum {
+ TASK_SEARCH_SUMMARY_CONTAINS,
+ TASK_SEARCH_DESCRIPTION_CONTAINS,
+ TASK_SEARCH_ANY_FIELD_CONTAINS
+};
+
+struct _ETaskShellViewPrivate {
+
+ /* These are just for convenience. */
+ ETaskShellBackend *task_shell_backend;
+ ETaskShellContent *task_shell_content;
+ ETaskShellSidebar *task_shell_sidebar;
+
+ EActivity *activity;
+ guint update_timeout;
+
+ /* GConf notification IDs */
+ GList *notifications;
+};
+
+void e_task_shell_view_private_init
+ (ETaskShellView *task_shell_view,
+ EShellViewClass *shell_view_class);
+void e_task_shell_view_private_constructed
+ (ETaskShellView *task_shell_view);
+void e_task_shell_view_private_dispose
+ (ETaskShellView *task_shell_view);
+void e_task_shell_view_private_finalize
+ (ETaskShellView *task_shell_view);
+
+/* Private Utilities */
+
+void e_task_shell_view_actions_init
+ (ETaskShellView *task_shell_view);
+void e_task_shell_view_execute_search
+ (ETaskShellView *task_shell_view);
+void e_task_shell_view_open_task
+ (ETaskShellView *task_shell_view,
+ ECalModelComponent *comp_data);
+void e_task_shell_view_set_status_message
+ (ETaskShellView *task_shell_view,
+ const gchar *status_message,
+ gdouble percent);
+void e_task_shell_view_update_sidebar
+ (ETaskShellView *task_shell_view);
+void e_task_shell_view_update_search_filter
+ (ETaskShellView *task_shell_view);
+void e_task_shell_view_update_timezone
+ (ETaskShellView *task_shell_view);
+
+G_END_DECLS
+
+#endif /* E_TASK_SHELL_VIEW_PRIVATE_H */
diff --git a/modules/calendar/e-task-shell-view.c b/modules/calendar/e-task-shell-view.c
new file mode 100644
index 0000000000..ce1b53a43c
--- /dev/null
+++ b/modules/calendar/e-task-shell-view.c
@@ -0,0 +1,255 @@
+/*
+ * e-task-shell-view.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-task-shell-view-private.h"
+
+static gpointer parent_class;
+static GType task_shell_view_type;
+
+static void
+task_shell_view_dispose (GObject *object)
+{
+ e_task_shell_view_private_dispose (E_TASK_SHELL_VIEW (object));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+task_shell_view_finalize (GObject *object)
+{
+ e_task_shell_view_private_finalize (E_TASK_SHELL_VIEW (object));
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+task_shell_view_constructed (GObject *object)
+{
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ e_task_shell_view_private_constructed (E_TASK_SHELL_VIEW (object));
+}
+
+static void
+task_shell_view_update_actions (EShellView *shell_view)
+{
+ ETaskShellViewPrivate *priv;
+ EShellContent *shell_content;
+ EShellSidebar *shell_sidebar;
+ EShellWindow *shell_window;
+ GtkAction *action;
+ const gchar *label;
+ gboolean sensitive;
+ guint32 state;
+
+ /* Be descriptive. */
+ gboolean any_tasks_selected;
+ gboolean has_primary_source;
+ gboolean multiple_tasks_selected;
+ gboolean primary_source_is_system;
+ gboolean selection_has_url;
+ gboolean selection_is_assignable;
+ gboolean single_task_selected;
+ gboolean some_tasks_complete;
+ gboolean some_tasks_incomplete;
+ gboolean sources_are_editable;
+
+ priv = E_TASK_SHELL_VIEW_GET_PRIVATE (shell_view);
+
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ state = e_shell_content_check_state (shell_content);
+
+ single_task_selected =
+ (state & E_TASK_SHELL_CONTENT_SELECTION_SINGLE);
+ multiple_tasks_selected =
+ (state & E_TASK_SHELL_CONTENT_SELECTION_MULTIPLE);
+ selection_is_assignable =
+ (state & E_TASK_SHELL_CONTENT_SELECTION_CAN_ASSIGN);
+ sources_are_editable =
+ (state & E_TASK_SHELL_CONTENT_SELECTION_CAN_EDIT);
+ some_tasks_complete =
+ (state & E_TASK_SHELL_CONTENT_SELECTION_HAS_COMPLETE);
+ some_tasks_incomplete =
+ (state & E_TASK_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE);
+ selection_has_url =
+ (state & E_TASK_SHELL_CONTENT_SELECTION_HAS_URL);
+
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ state = e_shell_sidebar_check_state (shell_sidebar);
+
+ has_primary_source =
+ (state & E_TASK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE);
+ primary_source_is_system =
+ (state & E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM);
+
+ any_tasks_selected =
+ (single_task_selected || multiple_tasks_selected);
+
+ action = ACTION (TASK_ASSIGN);
+ sensitive =
+ single_task_selected && sources_are_editable &&
+ selection_is_assignable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_CLIPBOARD_COPY);
+ sensitive = any_tasks_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_CLIPBOARD_CUT);
+ sensitive = any_tasks_selected && sources_are_editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_CLIPBOARD_PASTE);
+ sensitive = sources_are_editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_DELETE);
+ sensitive = any_tasks_selected && sources_are_editable;
+ gtk_action_set_sensitive (action, sensitive);
+ if (multiple_tasks_selected)
+ label = _("Delete Tasks");
+ else
+ label = _("Delete Task");
+ g_object_set (action, "label", label, NULL);
+
+ action = ACTION (TASK_FORWARD);
+ sensitive = single_task_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_LIST_COPY);
+ sensitive = has_primary_source;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_LIST_DELETE);
+ sensitive = has_primary_source && !primary_source_is_system;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_LIST_PROPERTIES);
+ sensitive = has_primary_source;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_LIST_RENAME);
+ sensitive = has_primary_source;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_MARK_COMPLETE);
+ sensitive =
+ any_tasks_selected &&
+ sources_are_editable &&
+ some_tasks_incomplete;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_MARK_INCOMPLETE);
+ sensitive =
+ any_tasks_selected &&
+ sources_are_editable &&
+ some_tasks_complete;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_OPEN);
+ sensitive = single_task_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_OPEN_URL);
+ sensitive = single_task_selected && selection_has_url;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_PRINT);
+ sensitive = single_task_selected;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_PURGE);
+ sensitive = sources_are_editable;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (TASK_SAVE_AS);
+ sensitive = single_task_selected;
+ gtk_action_set_sensitive (action, sensitive);
+}
+
+static void
+task_shell_view_class_init (ETaskShellViewClass *class,
+ GTypeModule *type_module)
+{
+ GObjectClass *object_class;
+ EShellViewClass *shell_view_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ETaskShellViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = task_shell_view_dispose;
+ object_class->finalize = task_shell_view_finalize;
+ object_class->constructed = task_shell_view_constructed;
+
+ shell_view_class = E_SHELL_VIEW_CLASS (class);
+ shell_view_class->label = _("Tasks");
+ shell_view_class->icon_name = "evolution-tasks";
+ shell_view_class->ui_definition = "evolution-tasks.ui";
+ shell_view_class->ui_manager_id = "org.gnome.evolution.tasks";
+ shell_view_class->search_options = "/task-search-options";
+ shell_view_class->search_rules = "tasktypes.xml";
+ shell_view_class->new_shell_content = e_task_shell_content_new;
+ shell_view_class->new_shell_sidebar = e_task_shell_sidebar_new;
+ shell_view_class->update_actions = task_shell_view_update_actions;
+}
+
+static void
+task_shell_view_init (ETaskShellView *task_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ task_shell_view->priv =
+ E_TASK_SHELL_VIEW_GET_PRIVATE (task_shell_view);
+
+ e_task_shell_view_private_init (task_shell_view, shell_view_class);
+}
+
+GType
+e_task_shell_view_get_type (void)
+{
+ return task_shell_view_type;
+}
+
+void
+e_task_shell_view_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (ETaskShellViewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) task_shell_view_class_init,
+ (GClassFinalizeFunc) NULL,
+ type_module,
+ sizeof (ETaskShellView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) task_shell_view_init,
+ NULL /* value_table */
+ };
+
+ task_shell_view_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_VIEW,
+ "ETaskShellView", &type_info, 0);
+}
diff --git a/modules/calendar/e-task-shell-view.h b/modules/calendar/e-task-shell-view.h
new file mode 100644
index 0000000000..8478e53cc2
--- /dev/null
+++ b/modules/calendar/e-task-shell-view.h
@@ -0,0 +1,67 @@
+/*
+ * e-task-shell-view.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_TASK_SHELL_VIEW_H
+#define E_TASK_SHELL_VIEW_H
+
+#include <shell/e-shell-view.h>
+#include <libedataserver/e-source-list.h>
+
+/* Standard GObject macros */
+#define E_TYPE_TASK_SHELL_VIEW \
+ (e_task_shell_view_get_type ())
+#define E_TASK_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_TASK_SHELL_VIEW, ETaskShellView))
+#define E_TASK_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_TASK_SHELL_VIEW, ETaskShellViewClass))
+#define E_IS_TASK_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_TASK_SHELL_VIEW))
+#define E_IS_TASK_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_TASK_SHELL_VIEW))
+#define E_TASK_SHELL_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_TASK_SHELL_VIEW, ETaskShellViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ETaskShellView ETaskShellView;
+typedef struct _ETaskShellViewClass ETaskShellViewClass;
+typedef struct _ETaskShellViewPrivate ETaskShellViewPrivate;
+
+struct _ETaskShellView {
+ EShellView parent;
+ ETaskShellViewPrivate *priv;
+};
+
+struct _ETaskShellViewClass {
+ EShellViewClass parent_class;
+};
+
+GType e_task_shell_view_get_type (void);
+void e_task_shell_view_register_type (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_TASK_SHELL_VIEW_H */
diff --git a/modules/calendar/evolution-module-calendar.c b/modules/calendar/evolution-module-calendar.c
new file mode 100644
index 0000000000..63bf98ead9
--- /dev/null
+++ b/modules/calendar/evolution-module-calendar.c
@@ -0,0 +1,65 @@
+/*
+ * evolution-module-calendar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-cal-shell-backend.h"
+#include "e-cal-shell-content.h"
+#include "e-cal-shell-sidebar.h"
+#include "e-cal-shell-view.h"
+
+#include "e-memo-shell-backend.h"
+#include "e-memo-shell-content.h"
+#include "e-memo-shell-sidebar.h"
+#include "e-memo-shell-view.h"
+
+#include "e-task-shell-backend.h"
+#include "e-task-shell-content.h"
+#include "e-task-shell-sidebar.h"
+#include "e-task-shell-view.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ /* Register dynamically loaded types. */
+
+ e_cal_shell_backend_register_type (type_module);
+ e_cal_shell_content_register_type (type_module);
+ e_cal_shell_sidebar_register_type (type_module);
+ e_cal_shell_view_register_type (type_module);
+
+ e_memo_shell_backend_register_type (type_module);
+ e_memo_shell_content_register_type (type_module);
+ e_memo_shell_sidebar_register_type (type_module);
+ e_memo_shell_view_register_type (type_module);
+
+ e_task_shell_backend_register_type (type_module);
+ e_task_shell_content_register_type (type_module);
+ e_task_shell_sidebar_register_type (type_module);
+ e_task_shell_view_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/modules/mail/Makefile.am b/modules/mail/Makefile.am
new file mode 100644
index 0000000000..ab73a5d542
--- /dev/null
+++ b/modules/mail/Makefile.am
@@ -0,0 +1,56 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/mail \
+ -I$(top_srcdir)/widgets \
+ $(EVOLUTION_MAIL_CFLAGS) \
+ -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \
+ -DEVOLUTION_GLADEDIR=\""$(gladedir)"\" \
+ -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
+ -DG_LOG_DOMAIN=\"evolution-module-mail\"
+
+module_LTLIBRARIES = libevolution-module-mail.la
+
+libevolution_module_mail_la_SOURCES = \
+ evolution-module-mail.c \
+ e-attachment-handler-mail.c \
+ e-attachment-handler-mail.h \
+ e-mail-shell-backend.c \
+ e-mail-shell-backend.h \
+ e-mail-shell-content.c \
+ e-mail-shell-content.h \
+ e-mail-shell-migrate.c \
+ e-mail-shell-migrate.h \
+ e-mail-shell-settings.c \
+ e-mail-shell-settings.h \
+ e-mail-shell-sidebar.c \
+ e-mail-shell-sidebar.h \
+ e-mail-shell-view.c \
+ e-mail-shell-view.h \
+ e-mail-shell-view-actions.c \
+ e-mail-shell-view-actions.h \
+ e-mail-shell-view-private.c \
+ e-mail-shell-view-private.h \
+ em-account-editor.c \
+ em-account-editor.h \
+ em-account-prefs.c \
+ em-account-prefs.h \
+ em-composer-prefs.c \
+ em-composer-prefs.h \
+ em-mailer-prefs.c \
+ em-mailer-prefs.h \
+ em-network-prefs.c \
+ em-network-prefs.h
+
+libevolution_module_mail_la_LIBADD = \
+ $(top_builddir)/e-util/libeutil.la \
+ $(top_builddir)/shell/libeshell.la \
+ $(top_builddir)/composer/libcomposer.la \
+ $(top_builddir)/widgets/table/libetable.la \
+ $(top_builddir)/widgets/text/libetext.la \
+ $(top_builddir)/widgets/misc/libemiscwidgets.la \
+ $(top_builddir)/mail/importers/libevolution-mail-importers.la
+
+libevolution_module_mail_la_LDFLAGS = \
+ -avoid-version -module $(NO_UNDEFINED)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/mail/e-attachment-handler-mail.c b/modules/mail/e-attachment-handler-mail.c
new file mode 100644
index 0000000000..c17c97d8ca
--- /dev/null
+++ b/modules/mail/e-attachment-handler-mail.c
@@ -0,0 +1,524 @@
+/*
+ * e-attachment-handler-mail.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-attachment-handler-mail.h"
+
+#include <glib/gi18n.h>
+#include <camel/camel-folder.h>
+#include <camel/camel-stream-mem.h>
+
+#include "e-util/e-error.h"
+#include "mail/em-composer-utils.h"
+#include "mail/mail-tools.h"
+
+#define E_ATTACHMENT_HANDLER_MAIL_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailPrivate))
+
+struct _EAttachmentHandlerMailPrivate {
+ gint placeholder;
+};
+
+static gpointer parent_class;
+
+static const gchar *ui =
+"<ui>"
+" <popup name='context'>"
+" <placeholder name='custom-actions'>"
+" <menuitem action='mail-reply-sender'/>"
+" <menuitem action='mail-reply-all'/>"
+" <menuitem action='mail-forward'/>"
+" </placeholder>"
+" </popup>"
+"</ui>";
+
+/* Note: Do not use the info field. */
+static GtkTargetEntry target_table[] = {
+ { (gchar *) "message/rfc822", 0, 0 },
+ { (gchar *) "x-uid-list", 0, 0 }
+};
+
+static void
+attachment_handler_mail_forward (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachment *attachment;
+ CamelMimePart *mime_part;
+ CamelDataWrapper *wrapper;
+ GList *selected;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+
+ attachment = E_ATTACHMENT (selected->data);
+ mime_part = e_attachment_get_mime_part (attachment);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+ em_utils_forward_message (CAMEL_MIME_MESSAGE (wrapper), NULL);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+attachment_handler_mail_reply_all (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachment *attachment;
+ CamelMimePart *mime_part;
+ CamelDataWrapper *wrapper;
+ GList *selected;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+
+ attachment = E_ATTACHMENT (selected->data);
+ mime_part = e_attachment_get_mime_part (attachment);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+ em_utils_reply_to_message (
+ NULL, NULL, CAMEL_MIME_MESSAGE (wrapper),
+ REPLY_MODE_ALL, NULL);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+attachment_handler_mail_reply_sender (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachment *attachment;
+ CamelMimePart *mime_part;
+ CamelDataWrapper *wrapper;
+ GList *selected;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+
+ attachment = E_ATTACHMENT (selected->data);
+ mime_part = e_attachment_get_mime_part (attachment);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+ em_utils_reply_to_message (
+ NULL, NULL, CAMEL_MIME_MESSAGE (wrapper),
+ REPLY_MODE_SENDER, NULL);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+ { "mail-forward",
+ "mail-forward",
+ N_("_Forward"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (attachment_handler_mail_forward) },
+
+ { "mail-reply-all",
+ "mail-reply-all",
+ N_("Reply to _All"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (attachment_handler_mail_reply_all) },
+
+ { "mail-reply-sender",
+ "mail-reply-sender",
+ N_("_Reply to Sender"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (attachment_handler_mail_reply_sender) }
+};
+
+static void
+attachment_handler_mail_message_rfc822 (EAttachmentView *view,
+ GdkDragContext *drag_context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ static GdkAtom atom = GDK_NONE;
+ EAttachmentStore *store;
+ EAttachment *attachment;
+ CamelMimeMessage *message;
+ CamelDataWrapper *wrapper;
+ CamelStream *stream;
+ const gchar *data;
+ gboolean success = FALSE;
+ gpointer parent;
+ gint length;
+
+ if (G_UNLIKELY (atom == GDK_NONE))
+ atom = gdk_atom_intern_static_string ("message/rfc822");
+
+ if (gtk_selection_data_get_target (selection_data) != atom)
+ return;
+
+ g_signal_stop_emission_by_name (view, "drag-data-received");
+
+ data = (const gchar *) gtk_selection_data_get_data (selection_data);
+ length = gtk_selection_data_get_length (selection_data);
+
+ stream = camel_stream_mem_new ();
+ camel_stream_write (stream, data, length);
+ camel_stream_reset (stream);
+
+ message = camel_mime_message_new ();
+ wrapper = CAMEL_DATA_WRAPPER (message);
+
+ if (camel_data_wrapper_construct_from_stream (wrapper, stream) == -1)
+ goto exit;
+
+ store = e_attachment_view_get_store (view);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ attachment = e_attachment_new_for_message (message);
+ e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
+ g_object_unref (attachment);
+
+ success = TRUE;
+
+exit:
+ camel_object_unref (message);
+ camel_object_unref (stream);
+
+ gtk_drag_finish (drag_context, success, FALSE, time);
+}
+
+static void
+attachment_handler_mail_x_uid_list (EAttachmentView *view,
+ GdkDragContext *drag_context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ static GdkAtom atom = GDK_NONE;
+ CamelException ex = CAMEL_EXCEPTION_INITIALISER;
+ CamelDataWrapper *wrapper;
+ CamelMimeMessage *message;
+ CamelMultipart *multipart;
+ CamelMimePart *mime_part;
+ CamelFolder *folder = NULL;
+ EAttachment *attachment;
+ EAttachmentStore *store;
+ GPtrArray *uids;
+ const gchar *data;
+ const gchar *cp, *end;
+ gchar *description;
+ gpointer parent;
+ gint length;
+ guint ii;
+
+ if (G_UNLIKELY (atom == GDK_NONE))
+ atom = gdk_atom_intern_static_string ("x-uid-list");
+
+ if (gtk_selection_data_get_target (selection_data) != atom)
+ return;
+
+ store = e_attachment_view_get_store (view);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ uids = g_ptr_array_new ();
+
+ data = (const gchar *) gtk_selection_data_get_data (selection_data);
+ length = gtk_selection_data_get_length (selection_data);
+
+ /* The UID list is delimited by NUL characters.
+ * Brilliant. So we can't use g_strsplit(). */
+
+ cp = data;
+ end = data + length;
+
+ while (cp < end) {
+ const gchar *start = cp;
+
+ while (cp < end && *cp != '\0')
+ cp++;
+
+ /* Skip the first string. */
+ if (start > data)
+ g_ptr_array_add (uids, g_strndup (start, cp - start));
+
+ cp++;
+ }
+
+ if (uids->len == 0)
+ goto exit;
+
+ /* The first string is the folder URI. */
+ folder = mail_tool_uri_to_folder (data, 0, &ex);
+ if (folder == NULL)
+ goto exit;
+
+ /* Handle one message. */
+ if (uids->len == 1) {
+ message = camel_folder_get_message (
+ folder, uids->pdata[0], &ex);
+ if (message == NULL)
+ goto exit;
+
+ attachment = e_attachment_new_for_message (message);
+ e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
+ g_object_unref (attachment);
+
+ camel_object_unref (message);
+ goto exit;
+ }
+
+ /* Build a multipart/digest message out of the UIDs. */
+
+ multipart = camel_multipart_new ();
+ wrapper = CAMEL_DATA_WRAPPER (multipart);
+ camel_data_wrapper_set_mime_type (wrapper, "multipart/digest");
+ camel_multipart_set_boundary (multipart, NULL);
+
+ for (ii = 0; ii < uids->len; ii++) {
+ message = camel_folder_get_message (
+ folder, uids->pdata[ii], &ex);
+ if (message == NULL) {
+ camel_object_unref (multipart);
+ goto exit;
+ }
+
+ mime_part = camel_mime_part_new ();
+ wrapper = CAMEL_DATA_WRAPPER (message);
+ camel_mime_part_set_disposition (mime_part, "inline");
+ camel_medium_set_content_object (
+ CAMEL_MEDIUM (mime_part), wrapper);
+ camel_mime_part_set_content_type (mime_part, "message/rfc822");
+ camel_multipart_add_part (multipart, mime_part);
+ camel_object_unref (mime_part);
+
+ camel_object_unref (message);
+ }
+
+ mime_part = camel_mime_part_new ();
+ wrapper = CAMEL_DATA_WRAPPER (multipart);
+ camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
+
+ /* Translators: This is only for multiple messages. */
+ description = g_strdup_printf (_("%d attached messages"), uids->len);
+ camel_mime_part_set_description (mime_part, description);
+ g_free (description);
+
+ attachment = e_attachment_new ();
+ e_attachment_set_mime_part (attachment, mime_part);
+ e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
+ g_object_unref (attachment);
+
+ camel_object_unref (mime_part);
+ camel_object_unref (multipart);
+
+exit:
+ if (camel_exception_is_set (&ex)) {
+ gchar *folder_name;
+
+ if (folder != NULL)
+ camel_object_get (
+ folder, NULL, CAMEL_FOLDER_NAME,
+ &folder_name, NULL);
+ else
+ folder_name = g_strdup (data);
+
+ e_error_run (
+ parent, "mail-composer:attach-nomessages",
+ folder_name, camel_exception_get_description (&ex),
+ NULL);
+
+ if (folder != NULL)
+ camel_object_free (
+ folder, CAMEL_FOLDER_NAME, folder_name);
+ else
+ g_free (folder_name);
+
+ camel_exception_clear (&ex);
+ }
+
+ if (folder != NULL)
+ camel_object_unref (folder);
+
+ g_ptr_array_free (uids, TRUE);
+
+ g_signal_stop_emission_by_name (view, "drag-data-received");
+}
+
+static void
+attachment_handler_mail_update_actions (EAttachmentView *view)
+{
+ EAttachment *attachment;
+ CamelMimePart *mime_part;
+ CamelDataWrapper *wrapper;
+ GtkActionGroup *action_group;
+ GList *selected;
+ gboolean visible = FALSE;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+
+ if (g_list_length (selected) != 1)
+ goto exit;
+
+ attachment = E_ATTACHMENT (selected->data);
+ mime_part = e_attachment_get_mime_part (attachment);
+
+ if (!CAMEL_IS_MIME_PART (mime_part))
+ goto exit;
+
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+ visible = CAMEL_IS_MIME_MESSAGE (wrapper);
+
+exit:
+ action_group = e_attachment_view_get_action_group (view, "mail");
+ gtk_action_group_set_visible (action_group, visible);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+attachment_handler_mail_constructed (GObject *object)
+{
+ EAttachmentHandler *handler;
+ EAttachmentView *view;
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+ GError *error = NULL;
+
+ handler = E_ATTACHMENT_HANDLER (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ view = e_attachment_handler_get_view (handler);
+
+ action_group = e_attachment_view_add_action_group (view, "mail");
+ gtk_action_group_add_actions (
+ action_group, standard_entries,
+ G_N_ELEMENTS (standard_entries), view);
+
+ ui_manager = e_attachment_view_get_ui_manager (view);
+ gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ g_signal_connect (
+ view, "update-actions",
+ G_CALLBACK (attachment_handler_mail_update_actions),
+ NULL);
+
+ g_signal_connect (
+ view, "drag-data-received",
+ G_CALLBACK (attachment_handler_mail_message_rfc822),
+ NULL);
+
+ g_signal_connect (
+ view, "drag-data-received",
+ G_CALLBACK (attachment_handler_mail_x_uid_list),
+ NULL);
+}
+
+static GdkDragAction
+attachment_handler_mail_get_drag_actions (EAttachmentHandler *handler)
+{
+ return GDK_ACTION_COPY;
+}
+
+static const GtkTargetEntry *
+attachment_handler_mail_get_target_table (EAttachmentHandler *handler,
+ guint *n_targets)
+{
+ if (n_targets != NULL)
+ *n_targets = G_N_ELEMENTS (target_table);
+
+ return target_table;
+}
+
+static void
+attachment_handler_mail_class_init (EAttachmentHandlerMailClass *class)
+{
+ GObjectClass *object_class;
+ EAttachmentHandlerClass *handler_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentHandlerMailPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = attachment_handler_mail_constructed;
+
+ handler_class = E_ATTACHMENT_HANDLER_CLASS (class);
+ handler_class->get_drag_actions = attachment_handler_mail_get_drag_actions;
+ handler_class->get_target_table = attachment_handler_mail_get_target_table;
+}
+
+static void
+attachment_handler_mail_init (EAttachmentHandlerMail *handler)
+{
+ handler->priv = E_ATTACHMENT_HANDLER_MAIL_GET_PRIVATE (handler);
+}
+
+GType
+e_attachment_handler_mail_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentHandlerMailClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_handler_mail_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EAttachmentHandlerMail),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_handler_mail_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_ATTACHMENT_HANDLER,
+ "EAttachmentHandlerMail", &type_info, 0);
+ }
+
+ return type;
+}
diff --git a/modules/mail/e-attachment-handler-mail.h b/modules/mail/e-attachment-handler-mail.h
new file mode 100644
index 0000000000..c62ea99cab
--- /dev/null
+++ b/modules/mail/e-attachment-handler-mail.h
@@ -0,0 +1,65 @@
+/*
+ * e-attachment-handler-mail.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_ATTACHMENT_HANDLER_MAIL_H
+#define E_ATTACHMENT_HANDLER_MAIL_H
+
+#include <widgets/misc/e-attachment-handler.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER_MAIL \
+ (e_attachment_handler_mail_get_type ())
+#define E_ATTACHMENT_HANDLER_MAIL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMail))
+#define E_ATTACHMENT_HANDLER_MAIL_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailClass))
+#define E_IS_ATTACHMENT_HANDLER_MAIL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL))
+#define E_IS_ATTACHMENT_HANDLER_MAIL_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_MAIL))
+#define E_ATTACHMENT_HANDLER_MAIL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandlerMail EAttachmentHandlerMail;
+typedef struct _EAttachmentHandlerMailClass EAttachmentHandlerMailClass;
+typedef struct _EAttachmentHandlerMailPrivate EAttachmentHandlerMailPrivate;
+
+struct _EAttachmentHandlerMail {
+ EAttachmentHandler parent;
+ EAttachmentHandlerMailPrivate *priv;
+};
+
+struct _EAttachmentHandlerMailClass {
+ EAttachmentHandlerClass parent_class;
+};
+
+GType e_attachment_handler_mail_get_type (void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_MAIL_H */
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
new file mode 100644
index 0000000000..3dcb7b1391
--- /dev/null
+++ b/modules/mail/e-mail-shell-backend.c
@@ -0,0 +1,787 @@
+/*
+ * e-mail-shell-backend.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-mail-shell-backend.h"
+
+#include <glib/gi18n.h>
+#include <camel/camel-disco-store.h>
+#include <camel/camel-offline-store.h>
+#include <camel/camel-session.h>
+#include <camel/camel-url.h>
+
+#include "e-util/e-account-utils.h"
+#include "e-util/e-binding.h"
+#include "e-util/e-import.h"
+#include "e-util/e-util.h"
+#include "shell/e-shell.h"
+#include "shell/e-shell-window.h"
+#include "composer/e-msg-composer.h"
+#include "widgets/misc/e-preferences-window.h"
+
+#include "e-mail-shell-migrate.h"
+#include "e-mail-shell-settings.h"
+#include "e-mail-shell-sidebar.h"
+#include "e-mail-shell-view.h"
+
+#include "e-attachment-handler-mail.h"
+#include "e-mail-browser.h"
+#include "e-mail-reader.h"
+#include "e-mail-store.h"
+#include "em-account-editor.h"
+#include "em-account-prefs.h"
+#include "em-composer-prefs.h"
+#include "em-composer-utils.h"
+#include "em-config.h"
+#include "em-event.h"
+#include "em-folder-utils.h"
+#include "em-format-hook.h"
+#include "em-format-html-display.h"
+#include "em-junk-hook.h"
+#include "em-mailer-prefs.h"
+#include "em-network-prefs.h"
+#include "em-utils.h"
+#include "mail-config.h"
+#include "mail-ops.h"
+#include "mail-send-recv.h"
+#include "mail-session.h"
+#include "mail-vfolder.h"
+#include "importers/mail-importer.h"
+
+#define E_MAIL_SHELL_BACKEND_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MAIL_SHELL_BACKEND, EMailShellBackendPrivate))
+
+#define BACKEND_NAME "mail"
+
+struct _EMailShellBackendPrivate {
+ gint mail_sync_in_progress;
+ guint mail_sync_timeout_source_id;
+};
+
+static gpointer parent_class;
+static GType mail_shell_backend_type;
+
+/* XXX So many things need the shell backend that it's
+ * just easier for now to make it globally available.
+ * We should fix this, though. */
+EMailShellBackend *global_mail_shell_backend = NULL;
+
+extern gint camel_application_is_exiting;
+
+static gboolean
+mail_shell_backend_run_account_druid (GtkWindow *parent)
+{
+ EAccountList *account_list;
+ EMAccountEditor *account_editor;
+
+ account_editor = em_account_editor_new (
+ NULL, EMAE_DRUID,
+ "org.gnome.evolution.mail.config.accountDruid");
+ if (GTK_IS_WINDOW (parent))
+ gtk_window_set_transient_for (
+ GTK_WINDOW (account_editor->editor), parent);
+ g_object_weak_ref (
+ G_OBJECT (account_editor->editor),
+ (GWeakNotify) gtk_main_quit, NULL);
+ gtk_widget_show (account_editor->editor);
+ gtk_grab_add (account_editor->editor);
+ gtk_main ();
+
+ account_list = e_get_account_list ();
+
+ return (e_list_length ((EList *) account_list) > 0);
+}
+
+static void
+mail_shell_backend_init_hooks (void)
+{
+ e_plugin_hook_register_type (em_config_hook_get_type ());
+ e_plugin_hook_register_type (em_event_hook_get_type ());
+ e_plugin_hook_register_type (em_junk_hook_get_type ());
+
+ /* EMFormat classes must be registered before EMFormatHook. */
+ em_format_hook_register_type (em_format_get_type ());
+ em_format_hook_register_type (em_format_html_get_type ());
+ em_format_hook_register_type (em_format_html_display_get_type ());
+ e_plugin_hook_register_type (em_format_hook_get_type ());
+
+ em_junk_hook_register_type (emj_get_type ());
+}
+
+static void
+mail_shell_backend_init_importers (void)
+{
+ EImportClass *import_class;
+ EImportImporter *importer;
+
+ import_class = g_type_class_ref (e_import_get_type ());
+
+ importer = mbox_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+
+ importer = elm_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+
+ importer = pine_importer_peek ();
+ e_import_class_add_importer (import_class, importer, NULL, NULL);
+}
+
+static void
+mail_shell_backend_mail_icon_cb (EShellWindow *shell_window,
+ const gchar *icon_name)
+{
+ GtkAction *action;
+
+ action = e_shell_window_get_shell_view_action (
+ shell_window, BACKEND_NAME);
+ g_object_set (action, "icon-name", icon_name, NULL);
+}
+
+static void
+action_mail_folder_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ EMFolderTree *folder_tree = NULL;
+ EMailShellSidebar *mail_shell_sidebar;
+ EShellSidebar *shell_sidebar;
+ EShellView *shell_view;
+ const gchar *view_name;
+
+ /* Take care not to unnecessarily load the mail shell view. */
+ view_name = e_shell_window_get_active_view (shell_window);
+ if (g_strcmp0 (view_name, BACKEND_NAME) != 0)
+ goto exit;
+
+ shell_view = e_shell_window_get_shell_view (shell_window, view_name);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+
+ mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar);
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+
+exit:
+ em_folder_utils_create_folder (
+ NULL, folder_tree, GTK_WINDOW (shell_window));
+}
+
+static void
+action_mail_message_new_cb (GtkAction *action,
+ EShellWindow *shell_window)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ EShellSidebar *shell_sidebar;
+ EShellView *shell_view;
+ EMFolderTree *folder_tree;
+ const gchar *view_name;
+ gchar *uri = NULL;
+
+ if (!em_utils_check_user_can_send_mail ())
+ return;
+
+ /* Take care not to unnecessarily load the mail shell view. */
+ view_name = e_shell_window_get_active_view (shell_window);
+ if (g_strcmp0 (view_name, BACKEND_NAME) != 0)
+ goto exit;
+
+ shell_view = e_shell_window_get_shell_view (shell_window, view_name);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+
+ mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar);
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+ uri = em_folder_tree_get_selected_uri (folder_tree);
+
+exit:
+ em_utils_compose_new_message (uri);
+
+ g_free (uri);
+}
+
+static GtkActionEntry item_entries[] = {
+
+ { "mail-message-new",
+ "mail-message-new",
+ NC_("New", "_Mail Message"),
+ "<Shift><Control>m",
+ N_("Compose a new mail message"),
+ G_CALLBACK (action_mail_message_new_cb) }
+};
+
+static GtkActionEntry source_entries[] = {
+
+ { "mail-folder-new",
+ "folder-new",
+ NC_("New", "Mail _Folder"),
+ NULL,
+ N_("Create a new mail folder"),
+ G_CALLBACK (action_mail_folder_new_cb) }
+};
+
+static void
+mail_shell_backend_init_preferences (EShell *shell)
+{
+ EAccountList *account_list;
+ GtkWidget *preferences_window;
+
+ account_list = e_get_account_list ();
+ preferences_window = e_shell_get_preferences_window (shell);
+
+ e_preferences_window_add_page (
+ E_PREFERENCES_WINDOW (preferences_window),
+ "mail-accounts",
+ "preferences-mail-accounts",
+ _("Mail Accounts"),
+ em_account_prefs_new (account_list),
+ 100);
+
+ e_preferences_window_add_page (
+ E_PREFERENCES_WINDOW (preferences_window),
+ "mail",
+ "preferences-mail",
+ _("Mail Preferences"),
+ em_mailer_prefs_new (shell),
+ 300);
+
+ e_preferences_window_add_page (
+ E_PREFERENCES_WINDOW (preferences_window),
+ "composer",
+ "preferences-composer",
+ _("Composer Preferences"),
+ em_composer_prefs_new (shell),
+ 400);
+
+ e_preferences_window_add_page (
+ E_PREFERENCES_WINDOW (preferences_window),
+ "system-network-proxy",
+ "preferences-system-network-proxy",
+ _("Network Preferences"),
+ em_network_prefs_new (),
+ 500);
+}
+
+static void
+mail_shell_backend_sync_store_done_cb (CamelStore *store,
+ gpointer user_data)
+{
+ EMailShellBackend *mail_shell_backend = user_data;
+
+ mail_shell_backend->priv->mail_sync_in_progress--;
+}
+
+static void
+mail_shell_backend_sync_store_cb (CamelStore *store,
+ EMailShellBackend *mail_shell_backend)
+{
+ if (!camel_application_is_exiting) {
+ mail_shell_backend->priv->mail_sync_in_progress++;
+ mail_sync_store (
+ store, FALSE,
+ mail_shell_backend_sync_store_done_cb,
+ mail_shell_backend);
+ }
+}
+
+static gboolean
+mail_shell_backend_mail_sync (EMailShellBackend *mail_shell_backend)
+{
+ if (camel_application_is_exiting)
+ return FALSE;
+
+ if (mail_shell_backend->priv->mail_sync_in_progress)
+ goto exit;
+
+ if (session == NULL || !camel_session_is_online (session))
+ goto exit;
+
+ e_mail_store_foreach (
+ (GHFunc) mail_shell_backend_sync_store_cb,
+ mail_shell_backend);
+
+exit:
+ return !camel_application_is_exiting;
+}
+
+static void
+mail_shell_backend_notify_online_cb (EShell *shell,
+ GParamSpec *pspec,
+ EShellBackend *shell_backend)
+{
+ gboolean online;
+
+ online = e_shell_get_online (shell);
+ camel_session_set_online (session, online);
+}
+
+static void
+mail_shell_backend_handle_email_uri_cb (gchar *folder_uri,
+ CamelFolder *folder,
+ gpointer user_data)
+{
+ EShellBackend *shell_backend = user_data;
+ CamelURL *url = user_data;
+ const gchar *forward;
+ const gchar *reply;
+ const gchar *uid;
+
+ if (folder == NULL) {
+ g_warning ("Could not open folder '%s'", folder_uri);
+ goto exit;
+ }
+
+ forward = camel_url_get_param (url, "forward");
+ reply = camel_url_get_param (url, "reply");
+ uid = camel_url_get_param (url, "uid");
+
+ if (reply != NULL) {
+ gint mode;
+
+ if (g_strcmp0 (reply, "all") == 0)
+ mode = REPLY_MODE_ALL;
+ else if (g_strcmp0 (reply, "list") == 0)
+ mode = REPLY_MODE_LIST;
+ else
+ mode = REPLY_MODE_SENDER;
+
+ em_utils_reply_to_message (folder, uid, NULL, mode, NULL);
+
+ } else if (forward != NULL) {
+ GPtrArray *uids;
+
+ uids = g_ptr_array_new ();
+ g_ptr_array_add (uids, g_strdup (uid));
+
+ if (g_strcmp0 (forward, "attached") == 0)
+ em_utils_forward_attached (folder, uids, folder_uri);
+ else if (g_strcmp0 (forward, "inline") == 0)
+ em_utils_forward_inline (folder, uids, folder_uri);
+ else if (g_strcmp0 (forward, "quoted") == 0)
+ em_utils_forward_quoted (folder, uids, folder_uri);
+ else
+ em_utils_forward_messages (folder, uids, folder_uri);
+
+ } else {
+ GtkWidget *browser;
+
+ /* FIXME Should pass in the shell module. */
+ browser = e_mail_browser_new (shell_backend);
+ e_mail_reader_set_folder (
+ E_MAIL_READER (browser), folder, folder_uri);
+ e_mail_reader_set_message (
+ E_MAIL_READER (browser), uid, FALSE);
+ gtk_widget_show (browser);
+ }
+
+exit:
+ camel_url_free (url);
+}
+
+static gboolean
+mail_shell_backend_handle_uri_cb (EShell *shell,
+ const gchar *uri,
+ EMailShellBackend *mail_shell_backend)
+{
+ gboolean handled = TRUE;
+
+ if (g_str_has_prefix (uri, "mailto:")) {
+ if (em_utils_check_user_can_send_mail ())
+ em_utils_compose_new_message_with_mailto (uri, NULL);
+
+ } else if (g_str_has_prefix (uri, "email:")) {
+ CamelURL *url;
+
+ url = camel_url_new (uri, NULL);
+ if (camel_url_get_param (url, "uid") != NULL) {
+ gchar *curi = em_uri_to_camel (uri);
+
+ mail_get_folder (
+ curi, 0,
+ mail_shell_backend_handle_email_uri_cb,
+ mail_shell_backend, mail_msg_unordered_push);
+ g_free (curi);
+
+ } else {
+ g_warning ("Email URI's must include a uid parameter");
+ camel_url_free (url);
+ }
+ } else
+ handled = FALSE;
+
+ return TRUE;
+}
+
+/* Helper for mail_shell_backend_prepare_for_[off|on]line_cb() */
+static void
+mail_shell_store_line_transition_done_cb (CamelStore *store,
+ gpointer user_data)
+{
+ EActivity *activity = user_data;
+
+ g_object_unref (activity);
+}
+
+/* Helper for mail_shell_backend_prepare_for_offline_cb() */
+static void
+mail_shell_store_prepare_for_offline_cb (CamelService *service,
+ gpointer unused,
+ EActivity *activity)
+{
+ if (CAMEL_IS_DISCO_STORE (service) || CAMEL_IS_OFFLINE_STORE (service))
+ mail_store_set_offline (
+ CAMEL_STORE (service), TRUE,
+ mail_shell_store_line_transition_done_cb,
+ g_object_ref (activity));
+}
+
+static void
+mail_shell_backend_prepare_for_offline_cb (EShell *shell,
+ EActivity *activity,
+ EMailShellBackend *mail_shell_backend)
+{
+ GList *watched_windows;
+ GtkWidget *parent = NULL;
+ gboolean synchronize = FALSE;
+
+ watched_windows = e_shell_get_watched_windows (shell);
+ if (watched_windows != NULL)
+ parent = GTK_WIDGET (watched_windows->data);
+
+ if (e_shell_get_network_available (shell))
+ synchronize = em_utils_prompt_user (
+ GTK_WINDOW (parent),
+ "/apps/evolution/mail/prompts/quick_offline",
+ "mail:ask-quick-offline", NULL);
+
+ if (!synchronize) {
+ mail_cancel_all ();
+ camel_session_set_network_state (session, FALSE);
+ }
+
+ e_mail_store_foreach (
+ (GHFunc) mail_shell_store_prepare_for_offline_cb, activity);
+}
+
+/* Helper for mail_shell_backend_prepare_for_online_cb() */
+static void
+mail_shell_store_prepare_for_online_cb (CamelService *service,
+ gpointer unused,
+ EActivity *activity)
+{
+ if (CAMEL_IS_DISCO_STORE (service) || CAMEL_IS_OFFLINE_STORE (service))
+ mail_store_set_offline (
+ CAMEL_STORE (service), FALSE,
+ mail_shell_store_line_transition_done_cb,
+ g_object_ref (activity));
+}
+
+static void
+mail_shell_backend_prepare_for_online_cb (EShell *shell,
+ EActivity *activity,
+ EMailShellBackend *mail_shell_backend)
+{
+ camel_session_set_online (session, TRUE);
+
+ e_mail_store_foreach (
+ (GHFunc) mail_shell_store_prepare_for_online_cb, activity);
+}
+
+static void
+mail_shell_backend_send_receive_cb (EShell *shell,
+ GtkWindow *parent,
+ EShellBackend *shell_backend)
+{
+ em_utils_clear_get_password_canceled_accounts_flag ();
+ mail_send_receive (parent);
+}
+
+static void
+mail_shell_backend_window_weak_notify_cb (EShell *shell,
+ GObject *where_the_object_was)
+{
+ g_signal_handlers_disconnect_by_func (
+ shell, mail_shell_backend_mail_icon_cb,
+ where_the_object_was);
+}
+
+static void
+mail_shell_backend_window_created_cb (EShell *shell,
+ GtkWindow *window,
+ EShellBackend *shell_backend)
+{
+ EShellSettings *shell_settings;
+ static gboolean first_time = TRUE;
+ const gchar *backend_name;
+
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ /* This applies to both the composer and signature editor. */
+ if (GTKHTML_IS_EDITOR (window)) {
+ GList *spell_languages;
+
+ e_binding_new (
+ G_OBJECT (shell_settings), "composer-inline-spelling",
+ G_OBJECT (window), "inline-spelling");
+
+ e_binding_new (
+ G_OBJECT (shell_settings), "composer-magic-links",
+ G_OBJECT (window), "magic-links");
+
+ e_binding_new (
+ G_OBJECT (shell_settings), "composer-magic-smileys",
+ G_OBJECT (window), "magic-smileys");
+
+ spell_languages = e_load_spell_languages ();
+ gtkhtml_editor_set_spell_languages (
+ GTKHTML_EDITOR (window), spell_languages);
+ g_list_free (spell_languages);
+ }
+
+ if (E_IS_MSG_COMPOSER (window)) {
+ /* Integrate the new composer into the mail module. */
+ em_configure_new_composer (E_MSG_COMPOSER (window));
+ return;
+ }
+
+ if (!E_IS_SHELL_WINDOW (window))
+ return;
+
+ backend_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name;
+
+ e_shell_window_register_new_item_actions (
+ E_SHELL_WINDOW (window), backend_name,
+ item_entries, G_N_ELEMENTS (item_entries));
+
+ e_shell_window_register_new_source_actions (
+ E_SHELL_WINDOW (window), backend_name,
+ source_entries, G_N_ELEMENTS (source_entries));
+
+ g_signal_connect_swapped (
+ shell, "event::mail-icon",
+ G_CALLBACK (mail_shell_backend_mail_icon_cb), window);
+
+ g_object_weak_ref (
+ G_OBJECT (window), (GWeakNotify)
+ mail_shell_backend_window_weak_notify_cb, shell);
+
+ if (first_time) {
+ g_signal_connect (
+ window, "map-event",
+ G_CALLBACK (e_msg_composer_check_autosave), NULL);
+ first_time = FALSE;
+ }
+}
+
+static void
+mail_shell_backend_constructed (GObject *object)
+{
+ EMailShellBackendPrivate *priv;
+ EShell *shell;
+ EShellBackend *shell_backend;
+ const gchar *data_dir;
+
+ priv = E_MAIL_SHELL_BACKEND_GET_PRIVATE (object);
+
+ shell_backend = E_SHELL_BACKEND (object);
+ shell = e_shell_backend_get_shell (shell_backend);
+
+ /* This also initializes Camel, so it needs to happen early. */
+ mail_session_init (shell_backend);
+
+ mail_shell_backend_init_hooks ();
+ mail_shell_backend_init_importers ();
+
+ e_attachment_handler_mail_get_type ();
+
+ /* XXX This never gets unreffed. */
+ global_mail_shell_backend = g_object_ref (shell_backend);
+
+ g_signal_connect (
+ shell, "notify::online",
+ G_CALLBACK (mail_shell_backend_notify_online_cb),
+ shell_backend);
+
+ g_signal_connect (
+ shell, "handle-uri",
+ G_CALLBACK (mail_shell_backend_handle_uri_cb),
+ shell_backend);
+
+ g_signal_connect (
+ shell, "prepare-for-offline",
+ G_CALLBACK (mail_shell_backend_prepare_for_offline_cb),
+ shell_backend);
+
+ g_signal_connect (
+ shell, "prepare-for-online",
+ G_CALLBACK (mail_shell_backend_prepare_for_online_cb),
+ shell_backend);
+
+ g_signal_connect (
+ shell, "send-receive",
+ G_CALLBACK (mail_shell_backend_send_receive_cb),
+ shell_backend);
+
+ g_signal_connect (
+ shell, "window-created",
+ G_CALLBACK (mail_shell_backend_window_created_cb),
+ shell_backend);
+
+ mail_config_init ();
+ mail_msg_init ();
+
+ data_dir = e_shell_backend_get_data_dir (shell_backend);
+ e_mail_store_init (data_dir);
+
+ /* Initialize settings before initializing preferences,
+ * since the preferences bind to the shell settings. */
+ e_mail_shell_settings_init (shell);
+ mail_shell_backend_init_preferences (shell);
+}
+
+static void
+mail_shell_backend_start (EShellBackend *shell_backend)
+{
+ EMailShellBackendPrivate *priv;
+ EShell *shell;
+ EShellSettings *shell_settings;
+ gboolean enable_search_folders;
+
+ priv = E_MAIL_SHELL_BACKEND_GET_PRIVATE (shell_backend);
+
+ shell = e_shell_backend_get_shell (shell_backend);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ /* XXX Do we really still need this flag? */
+ mail_session_set_interactive (TRUE);
+
+ enable_search_folders = e_shell_settings_get_boolean (
+ shell_settings, "mail-enable-search-folders");
+ if (enable_search_folders)
+ vfolder_load_storage ();
+
+ mail_autoreceive_init (shell_backend, session);
+
+ if (g_getenv ("CAMEL_FLUSH_CHANGES") != NULL)
+ priv->mail_sync_timeout_source_id = g_timeout_add_seconds (
+ mail_config_get_sync_timeout (),
+ (GSourceFunc) mail_shell_backend_mail_sync,
+ shell_backend);
+}
+
+static void
+mail_shell_backend_class_init (EMailShellBackendClass *class)
+{
+ GObjectClass *object_class;
+ EShellBackendClass *shell_backend_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMailShellBackendPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = mail_shell_backend_constructed;
+
+ shell_backend_class = E_SHELL_BACKEND_CLASS (class);
+ shell_backend_class->shell_view_type = E_TYPE_MAIL_SHELL_VIEW;
+ shell_backend_class->name = BACKEND_NAME;
+ shell_backend_class->aliases = "";
+ shell_backend_class->schemes = "mailto:email";
+ shell_backend_class->sort_order = 200;
+ shell_backend_class->start = mail_shell_backend_start;
+ shell_backend_class->is_busy = NULL;
+ shell_backend_class->shutdown = NULL;
+ shell_backend_class->migrate = e_mail_shell_migrate;
+}
+
+static void
+mail_shell_backend_init (EMailShellBackend *mail_shell_backend)
+{
+ mail_shell_backend->priv =
+ E_MAIL_SHELL_BACKEND_GET_PRIVATE (mail_shell_backend);
+}
+
+GType
+e_mail_shell_backend_get_type (void)
+{
+ return mail_shell_backend_type;
+}
+
+void
+e_mail_shell_backend_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (EMailShellBackendClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) mail_shell_backend_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMailShellBackend),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) mail_shell_backend_init,
+ NULL /* value_table */
+ };
+
+ mail_shell_backend_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_BACKEND,
+ "EMailShellBackend", &type_info, 0);
+}
+
+/******************* Code below here belongs elsewhere. *******************/
+
+#include "filter/filter-option.h"
+#include "shell/e-shell-settings.h"
+#include "mail/e-mail-label-list-store.h"
+
+GSList *
+e_mail_labels_get_filter_options (void)
+{
+ EShell *shell;
+ EShellSettings *shell_settings;
+ EMailLabelListStore *list_store;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GSList *list = NULL;
+ gboolean valid;
+
+ shell = e_shell_get_default ();
+ shell_settings = e_shell_get_shell_settings (shell);
+ list_store = e_shell_settings_get_object (
+ shell_settings, "mail-label-list-store");
+
+ model = GTK_TREE_MODEL (list_store);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (valid) {
+ struct _filter_option *option;
+ gchar *name, *tag;
+
+ name = e_mail_label_list_store_get_name (list_store, &iter);
+ tag = e_mail_label_list_store_get_tag (list_store, &iter);
+
+ option = g_new0 (struct _filter_option, 1);
+ option->title = e_str_without_underscores (name);
+ option->value = tag; /* takes ownership */
+
+ g_free (name);
+
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ g_object_unref (list_store);
+
+ return list;
+}
diff --git a/modules/mail/e-mail-shell-backend.h b/modules/mail/e-mail-shell-backend.h
new file mode 100644
index 0000000000..4bc1a36706
--- /dev/null
+++ b/modules/mail/e-mail-shell-backend.h
@@ -0,0 +1,82 @@
+/*
+ * e-mail-shell-backend.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SHELL_BACKEND_H
+#define E_MAIL_SHELL_BACKEND_H
+
+#include <shell/e-shell-backend.h>
+
+#include <camel/camel-folder.h>
+#include <camel/camel-store.h>
+#include <e-util/e-signature-list.h>
+#include <libedataserver/e-account-list.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_SHELL_BACKEND \
+ (e_mail_shell_backend_get_type ())
+#define E_MAIL_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MAIL_SHELL_BACKEND, EMailShellBackend))
+#define E_MAIL_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MAIL_SHELL_BACKEND, EMailShellBackendClass))
+#define E_IS_MAIL_SHELL_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MAIL_SHELL_BACKEND))
+#define E_IS_MAIL_SHELL_BACKEND_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MAIL_SHELL_BACKEND))
+#define E_MAIL_SHELL_BACKEND_GET_CLASS(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MAIL_SHELL_BACKEND, EMailShellBackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailShellBackend EMailShellBackend;
+typedef struct _EMailShellBackendClass EMailShellBackendClass;
+typedef struct _EMailShellBackendPrivate EMailShellBackendPrivate;
+
+struct _EMailShellBackend {
+ EShellBackend parent;
+ EMailShellBackendPrivate *priv;
+};
+
+struct _EMailShellBackendClass {
+ EShellBackendClass parent_class;
+};
+
+/* Globally available shell backend.
+ *
+ * XXX I don't like having this globally available but passing it around
+ * to all the various utilities that need to access the backend's data
+ * directory is too much of a pain for now. */
+extern EMailShellBackend *global_mail_shell_backend;
+
+GType e_mail_shell_backend_get_type (void);
+void e_mail_shell_backend_register_type
+ (GTypeModule *type_module);
+
+/* XXX Find a better place for this function. */
+GSList * e_mail_labels_get_filter_options(void);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SHELL_BACKEND_H */
diff --git a/modules/mail/e-mail-shell-content.c b/modules/mail/e-mail-shell-content.c
new file mode 100644
index 0000000000..b801093116
--- /dev/null
+++ b/modules/mail/e-mail-shell-content.c
@@ -0,0 +1,1053 @@
+/*
+ * e-mail-shell-content.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-mail-shell-content.h"
+
+#include <glib/gi18n.h>
+#include <camel/camel-store.h>
+#include <libedataserver/e-data-server-util.h>
+
+#include "e-util/gconf-bridge.h"
+#include "widgets/menus/gal-view-etable.h"
+#include "widgets/menus/gal-view-instance.h"
+
+#include "em-search-context.h"
+#include "em-utils.h"
+#include "mail-config.h"
+#include "mail-ops.h"
+
+#include "e-mail-reader.h"
+#include "e-mail-search-bar.h"
+#include "e-mail-shell-backend.h"
+#include "e-mail-shell-view-actions.h"
+
+#define E_MAIL_SHELL_CONTENT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContentPrivate))
+
+#define STATE_KEY_SCROLLBAR_POSITION "ScrollbarPosition"
+#define STATE_KEY_SELECTED_MESSAGE "SelectedMessage"
+
+struct _EMailShellContentPrivate {
+ GtkWidget *paned;
+ GtkWidget *message_list;
+ GtkWidget *search_bar;
+
+ EMFormatHTMLDisplay *html_display;
+ GalViewInstance *view_instance;
+
+ /* ETable scrolling hack */
+ gdouble default_scrollbar_position;
+
+ guint paned_binding_id;
+ guint scroll_timeout_id;
+
+ /* Signal handler IDs */
+ guint message_list_built_id;
+ guint message_list_scrolled_id;
+
+ guint preview_visible : 1;
+ guint suppress_message_selection : 1;
+ guint vertical_view : 1;
+ guint show_deleted : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_PREVIEW_VISIBLE,
+ PROP_SHOW_DELETED,
+ PROP_VERTICAL_VIEW
+};
+
+static gpointer parent_class;
+static GType mail_shell_content_type;
+
+static void
+mail_shell_content_etree_unfreeze (MessageList *message_list,
+ GdkEvent *event)
+{
+ ETableItem *item;
+ GObject *object;
+
+ item = e_tree_get_item (message_list->tree);
+ object = G_OBJECT (((GnomeCanvasItem *) item)->canvas);
+
+ g_object_set_data (object, "freeze-cursor", 0);
+}
+
+static void
+mail_shell_content_message_list_scrolled_cb (EMailShellContent *mail_shell_content,
+ MessageList *message_list)
+{
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ GKeyFile *key_file;
+ const gchar *folder_uri;
+ const gchar *key;
+ gchar *group_name;
+ gdouble position;
+
+ /* Save the scrollbar position for the current folder. */
+
+ folder_uri = message_list->folder_uri;
+
+ if (folder_uri == NULL)
+ return;
+
+ shell_content = E_SHELL_CONTENT (mail_shell_content);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ key = STATE_KEY_SCROLLBAR_POSITION;
+ group_name = g_strdup_printf ("Folder %s", folder_uri);
+ position = message_list_get_scrollbar_position (message_list);
+
+ g_key_file_set_double (key_file, group_name, key, position);
+ e_shell_view_set_state_dirty (shell_view);
+
+ g_free (group_name);
+}
+
+static gboolean
+mail_shell_content_scroll_timeout_cb (EMailShellContent *mail_shell_content)
+{
+ EMailShellContentPrivate *priv = mail_shell_content->priv;
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ MessageList *message_list;
+ EMailReader *reader;
+ GKeyFile *key_file;
+ const gchar *folder_uri;
+ const gchar *key;
+ gchar *group_name;
+
+ /* Initialize the scrollbar position for the current folder
+ * and setup a callback to handle scrollbar position changes. */
+
+ shell_content = E_SHELL_CONTENT (mail_shell_content);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ reader = E_MAIL_READER (mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder_uri = message_list->folder_uri;
+
+ if (folder_uri == NULL)
+ goto skip;
+
+ /* Restore the message list scrollbar position. */
+
+ key = STATE_KEY_SCROLLBAR_POSITION;
+ group_name = g_strdup_printf ("Folder %s", folder_uri);
+
+ if (g_key_file_has_key (key_file, group_name, key, NULL)) {
+ gdouble position;
+
+ position = g_key_file_get_double (
+ key_file, group_name, key, NULL);
+ message_list_set_scrollbar_position (message_list, position);
+ }
+
+ g_free (group_name);
+
+skip:
+ priv->message_list_scrolled_id = g_signal_connect_swapped (
+ message_list, "message-list-scrolled",
+ G_CALLBACK (mail_shell_content_message_list_scrolled_cb),
+ mail_shell_content);
+
+ priv->scroll_timeout_id = 0;
+
+ return FALSE;
+}
+
+static void
+mail_shell_content_message_list_built_cb (EMailShellContent *mail_shell_content,
+ MessageList *message_list)
+{
+ EMailShellContentPrivate *priv = mail_shell_content->priv;
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ GtkScrolledWindow *scrolled_window;
+ GtkWidget *vscrollbar;
+ GKeyFile *key_file;
+ gchar *uid;
+
+ g_signal_handler_disconnect (
+ message_list, priv->message_list_built_id);
+ priv->message_list_built_id = 0;
+
+ shell_content = E_SHELL_CONTENT (mail_shell_content);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ if (message_list->cursor_uid != NULL)
+ uid = NULL;
+
+ else if (message_list->folder_uri == NULL)
+ uid = NULL;
+
+ else if (mail_shell_content->priv->suppress_message_selection)
+ uid = NULL;
+
+ else {
+ const gchar *folder_uri;
+ const gchar *key;
+ gchar *group_name;
+
+ key = STATE_KEY_SELECTED_MESSAGE;
+ folder_uri = message_list->folder_uri;
+ group_name = g_strdup_printf ("Folder %s", folder_uri);
+ uid = g_key_file_get_string (key_file, group_name, key, NULL);
+ g_free (group_name);
+ }
+
+ if (uid != NULL) {
+ CamelFolder *folder;
+ CamelMessageInfo *info;
+
+ folder = message_list->folder;
+ info = camel_folder_get_message_info (folder, uid);
+ if (info != NULL) {
+ EMailReader *reader;
+
+ reader = E_MAIL_READER (mail_shell_content);
+ e_mail_reader_set_message (reader, uid, TRUE);
+ camel_folder_free_message_info (folder, info);
+ }
+
+ g_free (uid);
+ }
+
+ /* FIXME This is a gross workaround for an ETable bug that I can't
+ * fix (Ximian bug #55303).
+ *
+ * Since e_canvas_item_region_show_relay() uses a timeout,
+ * we have to use a timeout of the same interval but a lower
+ * priority. */
+ priv->scroll_timeout_id = g_timeout_add_full (
+ G_PRIORITY_LOW, 250, (GSourceFunc)
+ mail_shell_content_scroll_timeout_cb,
+ mail_shell_content, NULL);
+
+ /* FIXME This is another ugly hack to hide a side-effect of the
+ * previous workaround. */
+ scrolled_window = GTK_SCROLLED_WINDOW (message_list);
+ vscrollbar = gtk_scrolled_window_get_vscrollbar (scrolled_window);
+ g_signal_connect_swapped (
+ vscrollbar, "button-press-event",
+ G_CALLBACK (mail_shell_content_etree_unfreeze),
+ message_list);
+}
+
+static void
+mail_shell_content_display_view_cb (EMailShellContent *mail_shell_content,
+ GalView *gal_view)
+{
+ EMailReader *reader;
+ MessageList *message_list;
+
+ reader = E_MAIL_READER (mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ if (GAL_IS_VIEW_ETABLE (gal_view))
+ gal_view_etable_attach_tree (
+ GAL_VIEW_ETABLE (gal_view), message_list->tree);
+}
+
+static void
+mail_shell_content_message_selected_cb (EMailShellContent *mail_shell_content,
+ const gchar *message_uid,
+ MessageList *message_list)
+{
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ GKeyFile *key_file;
+ const gchar *folder_uri;
+ const gchar *key;
+ gchar *group_name;
+
+ folder_uri = message_list->folder_uri;
+
+ /* This also gets triggered when selecting a store name on
+ * the sidebar such as "On This Computer", in which case
+ * 'folder_uri' will be NULL. */
+ if (folder_uri == NULL)
+ return;
+
+ shell_content = E_SHELL_CONTENT (mail_shell_content);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ key = STATE_KEY_SELECTED_MESSAGE;
+ group_name = g_strdup_printf ("Folder %s", folder_uri);
+
+ if (message_uid != NULL)
+ g_key_file_set_string (key_file, group_name, key, message_uid);
+ else
+ g_key_file_remove_key (key_file, group_name, key, NULL);
+ e_shell_view_set_state_dirty (shell_view);
+
+ g_free (group_name);
+}
+
+static void
+mail_shell_content_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_PREVIEW_VISIBLE:
+ e_mail_shell_content_set_preview_visible (
+ E_MAIL_SHELL_CONTENT (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_SHOW_DELETED:
+ e_mail_shell_content_set_show_deleted (
+ E_MAIL_SHELL_CONTENT (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_VERTICAL_VIEW:
+ e_mail_shell_content_set_vertical_view (
+ E_MAIL_SHELL_CONTENT (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_shell_content_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_PREVIEW_VISIBLE:
+ g_value_set_boolean (
+ value,
+ e_mail_shell_content_get_preview_visible (
+ E_MAIL_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_SHOW_DELETED:
+ g_value_set_boolean (
+ value,
+ e_mail_shell_content_get_show_deleted (
+ E_MAIL_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_VERTICAL_VIEW:
+ g_value_set_boolean (
+ value,
+ e_mail_shell_content_get_vertical_view (
+ E_MAIL_SHELL_CONTENT (object)));
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_shell_content_dispose (GObject *object)
+{
+ EMailShellContentPrivate *priv;
+
+ priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (object);
+
+ if (priv->paned != NULL) {
+ g_object_unref (priv->paned);
+ priv->paned = NULL;
+ }
+
+ if (priv->message_list != NULL) {
+ g_object_unref (priv->message_list);
+ priv->message_list = NULL;
+ }
+
+ if (priv->search_bar != NULL) {
+ g_object_unref (priv->search_bar);
+ priv->search_bar = NULL;
+ }
+
+ if (priv->html_display != NULL) {
+ g_object_unref (priv->html_display);
+ priv->html_display = NULL;
+ }
+
+ if (priv->view_instance != NULL) {
+ g_object_unref (priv->view_instance);
+ priv->view_instance = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+mail_shell_content_constructed (GObject *object)
+{
+ EMailShellContentPrivate *priv;
+ EShellContent *shell_content;
+ EShellBackend *shell_backend;
+ EShellView *shell_view;
+ EShellViewClass *shell_view_class;
+ EMailReader *reader;
+ MessageList *message_list;
+ GConfBridge *bridge;
+ GtkWidget *container;
+ GtkWidget *widget;
+ GtkHTML *html;
+ GalViewCollection *view_collection;
+ const gchar *key;
+
+ priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (object);
+ priv->html_display = em_format_html_display_new ();
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ shell_content = E_SHELL_CONTENT (object);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ view_collection = shell_view_class->view_collection;
+
+ html = EM_FORMAT_HTML (priv->html_display)->html;
+
+ /* Build content widgets. */
+
+ container = GTK_WIDGET (object);
+
+ widget = gtk_vpaned_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ priv->paned = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = message_list_new (shell_backend);
+ gtk_paned_add1 (GTK_PANED (container), widget);
+ priv->message_list = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_vbox_new (FALSE, 1);
+ gtk_paned_add2 (GTK_PANED (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (html));
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (GTK_WIDGET (html));
+ gtk_widget_show (widget);
+
+ widget = e_mail_search_bar_new (html);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ priv->search_bar = g_object_ref (widget);
+ gtk_widget_hide (widget);
+
+ g_signal_connect_swapped (
+ widget, "changed",
+ G_CALLBACK (em_format_redraw), priv->html_display);
+
+ /* Load the view instance. */
+
+ e_mail_shell_content_update_view_instance (
+ E_MAIL_SHELL_CONTENT (shell_content));
+
+ /* Bind GObject properties to GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (priv->paned);
+ key = "/apps/evolution/mail/display/paned_size";
+ gconf_bridge_bind_property_delayed (bridge, key, object, "position");
+
+ object = G_OBJECT (shell_content);
+ key = "/apps/evolution/mail/display/show_deleted";
+ gconf_bridge_bind_property (bridge, key, object, "show-deleted");
+
+ /* Message list customizations. */
+
+ reader = E_MAIL_READER (shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ g_signal_connect_swapped (
+ message_list, "message-selected",
+ G_CALLBACK (mail_shell_content_message_selected_cb),
+ shell_content);
+}
+
+static guint32
+mail_shell_content_check_state (EShellContent *shell_content)
+{
+ return e_mail_reader_check_state (E_MAIL_READER (shell_content));
+}
+
+static GtkActionGroup *
+mail_shell_content_get_action_group (EMailReader *reader)
+{
+ EShellContent *shell_content;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+
+ shell_content = E_SHELL_CONTENT (reader);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ return E_SHELL_WINDOW_ACTION_GROUP_MAIL (shell_window);
+}
+
+static gboolean
+mail_shell_content_get_hide_deleted (EMailReader *reader)
+{
+ EMailShellContent *mail_shell_content;
+
+ mail_shell_content = E_MAIL_SHELL_CONTENT (reader);
+
+ return !e_mail_shell_content_get_show_deleted (mail_shell_content);
+}
+
+static EMFormatHTMLDisplay *
+mail_shell_content_get_html_display (EMailReader *reader)
+{
+ EMailShellContentPrivate *priv;
+
+ priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader);
+
+ return priv->html_display;
+}
+
+static MessageList *
+mail_shell_content_get_message_list (EMailReader *reader)
+{
+ EMailShellContentPrivate *priv;
+
+ priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader);
+
+ return MESSAGE_LIST (priv->message_list);
+}
+
+static EShellBackend *
+mail_shell_content_get_shell_backend (EMailReader *reader)
+{
+ EShellContent *shell_content;
+ EShellView *shell_view;
+
+ shell_content = E_SHELL_CONTENT (reader);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+
+ return e_shell_view_get_shell_backend (shell_view);
+}
+
+static GtkWindow *
+mail_shell_content_get_window (EMailReader *reader)
+{
+ EShellContent *shell_content;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+
+ shell_content = E_SHELL_CONTENT (reader);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ return GTK_WINDOW (shell_window);
+}
+
+static void
+mail_shell_content_set_folder (EMailReader *reader,
+ CamelFolder *folder,
+ const gchar *folder_uri)
+{
+ EMailShellContentPrivate *priv;
+ EMailReaderIface *default_iface;
+ MessageList *message_list;
+ gboolean different_folder;
+
+ priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader);
+
+ message_list = e_mail_reader_get_message_list (reader);
+
+ message_list_freeze (message_list);
+
+ different_folder =
+ message_list->folder != NULL &&
+ folder != message_list->folder;
+
+ /* Chain up to interface's default set_folder() method. */
+ default_iface = g_type_default_interface_peek (E_TYPE_MAIL_READER);
+ default_iface->set_folder (reader, folder, folder_uri);
+
+ if (folder == NULL)
+ goto exit;
+
+ mail_refresh_folder (folder, NULL, NULL);
+
+ /* This function gets triggered several times at startup,
+ * so we don't want to reset the message suppression state
+ * unless we're actually switching to a different folder. */
+ if (different_folder)
+ priv->suppress_message_selection = FALSE;
+
+ /* This is a one-time-only callback. */
+ if (message_list->cursor_uid == NULL && priv->message_list_built_id == 0)
+ priv->message_list_built_id = g_signal_connect_swapped (
+ message_list, "message-list-built",
+ G_CALLBACK (mail_shell_content_message_list_built_cb),
+ reader);
+
+exit:
+ message_list_thaw (message_list);
+}
+
+static void
+mail_shell_content_show_search_bar (EMailReader *reader)
+{
+ EMailShellContentPrivate *priv;
+
+ priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader);
+
+ gtk_widget_show (priv->search_bar);
+}
+
+static void
+mail_shell_content_class_init (EMailShellContentClass *class)
+{
+ GObjectClass *object_class;
+ EShellContentClass *shell_content_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMailShellContentPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = mail_shell_content_set_property;
+ object_class->get_property = mail_shell_content_get_property;
+ object_class->dispose = mail_shell_content_dispose;
+ object_class->constructed = mail_shell_content_constructed;
+
+ shell_content_class = E_SHELL_CONTENT_CLASS (class);
+ shell_content_class->new_search_context = em_search_context_new;
+ shell_content_class->check_state = mail_shell_content_check_state;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PREVIEW_VISIBLE,
+ g_param_spec_boolean (
+ "preview-visible",
+ _("Preview is Visible"),
+ _("Whether the preview pane is visible"),
+ TRUE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SHOW_DELETED,
+ g_param_spec_boolean (
+ "show-deleted",
+ "Show Deleted",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_VERTICAL_VIEW,
+ g_param_spec_boolean (
+ "vertical-view",
+ _("Vertical View"),
+ _("Whether vertical view is enabled"),
+ FALSE,
+ G_PARAM_READWRITE));
+}
+
+static void
+mail_shell_content_iface_init (EMailReaderIface *iface)
+{
+ iface->get_action_group = mail_shell_content_get_action_group;
+ iface->get_hide_deleted = mail_shell_content_get_hide_deleted;
+ iface->get_html_display = mail_shell_content_get_html_display;
+ iface->get_message_list = mail_shell_content_get_message_list;
+ iface->get_shell_backend = mail_shell_content_get_shell_backend;
+ iface->get_window = mail_shell_content_get_window;
+ iface->set_folder = mail_shell_content_set_folder;
+ iface->show_search_bar = mail_shell_content_show_search_bar;
+}
+
+static void
+mail_shell_content_init (EMailShellContent *mail_shell_content)
+{
+ mail_shell_content->priv =
+ E_MAIL_SHELL_CONTENT_GET_PRIVATE (mail_shell_content);
+
+ mail_shell_content->priv->preview_visible = TRUE;
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_mail_shell_content_get_type (void)
+{
+ return mail_shell_content_type;
+}
+
+void
+e_mail_shell_content_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (EMailShellContentClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) mail_shell_content_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMailShellContent),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) mail_shell_content_init,
+ NULL /* value_table */
+ };
+
+ static const GInterfaceInfo iface_info = {
+ (GInterfaceInitFunc) mail_shell_content_iface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL /* interface_data */
+ };
+
+ mail_shell_content_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_CONTENT,
+ "EMailShellContent", &type_info, 0);
+
+ g_type_module_add_interface (
+ type_module, mail_shell_content_type,
+ E_TYPE_MAIL_READER, &iface_info);
+}
+
+GtkWidget *
+e_mail_shell_content_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_MAIL_SHELL_CONTENT,
+ "shell-view", shell_view, NULL);
+}
+
+gboolean
+e_mail_shell_content_get_preview_visible (EMailShellContent *mail_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_MAIL_SHELL_CONTENT (mail_shell_content), FALSE);
+
+ return mail_shell_content->priv->preview_visible;
+}
+
+void
+e_mail_shell_content_set_preview_visible (EMailShellContent *mail_shell_content,
+ gboolean preview_visible)
+{
+ GtkPaned *paned;
+ GtkWidget *child;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content));
+
+ if (preview_visible == mail_shell_content->priv->preview_visible)
+ return;
+
+ paned = GTK_PANED (mail_shell_content->priv->paned);
+ child = gtk_paned_get_child2 (paned);
+
+ if (preview_visible)
+ gtk_widget_show (child);
+ else
+ gtk_widget_hide (child);
+
+ mail_shell_content->priv->preview_visible = preview_visible;
+
+ g_object_notify (G_OBJECT (mail_shell_content), "preview-visible");
+}
+
+gboolean
+e_mail_shell_content_get_show_deleted (EMailShellContent *mail_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_MAIL_SHELL_CONTENT (mail_shell_content), FALSE);
+
+ return mail_shell_content->priv->show_deleted;
+}
+
+void
+e_mail_shell_content_set_show_deleted (EMailShellContent *mail_shell_content,
+ gboolean show_deleted)
+{
+ g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content));
+
+ mail_shell_content->priv->show_deleted = show_deleted;
+
+ g_object_notify (G_OBJECT (mail_shell_content), "show-deleted");
+}
+
+gboolean
+e_mail_shell_content_get_vertical_view (EMailShellContent *mail_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_MAIL_SHELL_CONTENT (mail_shell_content), FALSE);
+
+ return mail_shell_content->priv->vertical_view;
+}
+
+void
+e_mail_shell_content_set_vertical_view (EMailShellContent *mail_shell_content,
+ gboolean vertical_view)
+{
+ GConfBridge *bridge;
+ GtkWidget *old_paned;
+ GtkWidget *new_paned;
+ GtkWidget *child1;
+ GtkWidget *child2;
+ guint binding_id;
+ const gchar *key;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content));
+
+ if (vertical_view == mail_shell_content->priv->vertical_view)
+ return;
+
+ bridge = gconf_bridge_get ();
+ old_paned = mail_shell_content->priv->paned;
+ binding_id = mail_shell_content->priv->paned_binding_id;
+
+ child1 = gtk_paned_get_child1 (GTK_PANED (old_paned));
+ child2 = gtk_paned_get_child2 (GTK_PANED (old_paned));
+
+ if (binding_id > 0)
+ gconf_bridge_unbind (bridge, binding_id);
+
+ if (vertical_view) {
+ new_paned = gtk_hpaned_new ();
+ key = "/apps/evolution/mail/display/hpaned_size";
+ } else {
+ new_paned = gtk_vpaned_new ();
+ key = "/apps/evolution/mail/display/paned_size";
+ }
+
+ gtk_widget_reparent (child1, new_paned);
+ gtk_widget_reparent (child2, new_paned);
+ gtk_widget_show (new_paned);
+
+ gtk_widget_destroy (old_paned);
+ gtk_container_add (GTK_CONTAINER (mail_shell_content), new_paned);
+
+ binding_id = gconf_bridge_bind_property_delayed (
+ bridge, key, G_OBJECT (new_paned), "position");
+
+ mail_shell_content->priv->vertical_view = vertical_view;
+ mail_shell_content->priv->paned_binding_id = binding_id;
+ mail_shell_content->priv->paned = g_object_ref (new_paned);
+
+ e_mail_shell_content_update_view_instance (mail_shell_content);
+
+ g_object_notify (G_OBJECT (mail_shell_content), "vertical-view");
+}
+
+GalViewInstance *
+e_mail_shell_content_get_view_instance (EMailShellContent *mail_shell_content)
+{
+ g_return_val_if_fail (
+ E_IS_MAIL_SHELL_CONTENT (mail_shell_content), NULL);
+
+ return mail_shell_content->priv->view_instance;
+}
+
+void
+e_mail_shell_content_set_search_strings (EMailShellContent *mail_shell_content,
+ GSList *search_strings)
+{
+ EMailSearchBar *search_bar;
+ ESearchingTokenizer *tokenizer;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content));
+
+ search_bar = E_MAIL_SEARCH_BAR (mail_shell_content->priv->search_bar);
+ tokenizer = e_mail_search_bar_get_tokenizer (search_bar);
+
+ e_searching_tokenizer_set_secondary_case_sensitivity (tokenizer, FALSE);
+ e_searching_tokenizer_set_secondary_search_string (tokenizer, NULL);
+
+ while (search_strings != NULL) {
+ e_searching_tokenizer_add_secondary_search_string (
+ tokenizer, search_strings->data);
+ search_strings = g_slist_next (search_strings);
+ }
+
+ e_mail_search_bar_changed (search_bar);
+}
+
+void
+e_mail_shell_content_update_view_instance (EMailShellContent *mail_shell_content)
+{
+ EMailReader *reader;
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ EShellViewClass *shell_view_class;
+ GalViewCollection *view_collection;
+ GalViewInstance *view_instance;
+ MessageList *message_list;
+ gboolean outgoing_folder;
+ gboolean show_vertical_view;
+ gchar *view_id;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content));
+
+ shell_content = E_SHELL_CONTENT (mail_shell_content);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view);
+ view_collection = shell_view_class->view_collection;
+
+ reader = E_MAIL_READER (mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ /* If no folder is selected, return silently. */
+ if (message_list->folder == NULL)
+ return;
+
+ /* If we have a folder, we should also have a URI. */
+ g_return_if_fail (message_list->folder_uri != NULL);
+
+ if (mail_shell_content->priv->view_instance != NULL) {
+ g_object_unref (mail_shell_content->priv->view_instance);
+ mail_shell_content->priv->view_instance = NULL;
+ }
+
+ view_id = mail_config_folder_to_safe_url (message_list->folder);
+ view_instance = e_shell_view_new_view_instance (shell_view, view_id);
+ mail_shell_content->priv->view_instance = view_instance;
+
+ show_vertical_view =
+ e_mail_shell_content_get_vertical_view (mail_shell_content);
+
+ if (show_vertical_view) {
+ gchar *filename;
+ gchar *safe_view_id;
+
+ /* Force the view instance into vertical view. */
+
+ g_free (view_instance->custom_filename);
+ g_free (view_instance->current_view_filename);
+
+ safe_view_id = g_strdup (view_id);
+ e_filename_make_safe (safe_view_id);
+
+ filename = g_strdup_printf (
+ "custom_wide_view-%s.xml", safe_view_id);
+ view_instance->custom_filename = g_build_filename (
+ view_collection->local_dir, filename, NULL);
+ g_free (filename);
+
+ filename = g_strdup_printf (
+ "current_wide_view-%s.xml", safe_view_id);
+ view_instance->current_view_filename = g_build_filename (
+ view_collection->local_dir, filename, NULL);
+ g_free (filename);
+
+ g_free (safe_view_id);
+ }
+
+ g_free (view_id);
+
+ outgoing_folder =
+ em_utils_folder_is_drafts (
+ message_list->folder, message_list->folder_uri) ||
+ em_utils_folder_is_outbox (
+ message_list->folder, message_list->folder_uri) ||
+ em_utils_folder_is_sent (
+ message_list->folder, message_list->folder_uri);
+
+ if (outgoing_folder) {
+ if (show_vertical_view)
+ gal_view_instance_set_default_view (
+ view_instance, "Wide_View_Sent");
+ else
+ gal_view_instance_set_default_view (
+ view_instance, "As_Sent_Folder");
+ } else if (show_vertical_view) {
+ gal_view_instance_set_default_view (
+ view_instance, "Wide_View_Normal");
+ }
+
+ gal_view_instance_load (view_instance);
+
+ if (!gal_view_instance_exists (view_instance)) {
+ gchar *state_filename;
+
+ state_filename = mail_config_folder_to_cachename (
+ message_list->folder, "et-header-");
+
+ if (g_file_test (state_filename, G_FILE_TEST_IS_REGULAR)) {
+ ETableSpecification *spec;
+ ETableState *state;
+ GalView *view;
+ gchar *spec_filename;
+
+ spec = e_table_specification_new ();
+ spec_filename = g_build_filename (
+ EVOLUTION_ETSPECDIR,
+ "message-list.etspec",
+ NULL);
+ e_table_specification_load_from_file (
+ spec, spec_filename);
+ g_free (spec_filename);
+
+ state = e_table_state_new ();
+ view = gal_view_etable_new (spec, "");
+
+ e_table_state_load_from_file (
+ state, state_filename);
+ gal_view_etable_set_state (
+ GAL_VIEW_ETABLE (view), state);
+ gal_view_instance_set_custom_view (
+ view_instance, view);
+
+ g_object_unref (state);
+ g_object_unref (view);
+ g_object_unref (spec);
+ }
+
+ g_free (state_filename);
+ }
+
+ g_signal_connect (
+ view_instance, "display-view",
+ G_CALLBACK (mail_shell_content_display_view_cb),
+ mail_shell_content);
+
+ mail_shell_content_display_view_cb (
+ mail_shell_content,
+ gal_view_instance_get_current_view (view_instance));
+}
diff --git a/modules/mail/e-mail-shell-content.h b/modules/mail/e-mail-shell-content.h
new file mode 100644
index 0000000000..57d2438705
--- /dev/null
+++ b/modules/mail/e-mail-shell-content.h
@@ -0,0 +1,94 @@
+/*
+ * e-mail-shell-content.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SHELL_CONTENT_H
+#define E_MAIL_SHELL_CONTENT_H
+
+#include <shell/e-shell-content.h>
+#include <shell/e-shell-view.h>
+
+#include <mail/em-format-html-display.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_SHELL_CONTENT \
+ (e_mail_shell_content_get_type ())
+#define E_MAIL_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContent))
+#define E_MAIL_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContentClass))
+#define E_IS_MAIL_SHELL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MAIL_SHELL_CONTENT))
+#define E_IS_MAIL_SHELL_CONTENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MAIL_SHELL_CONTENT))
+#define E_MAIL_SHELL_CONTENT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContentClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailShellContent EMailShellContent;
+typedef struct _EMailShellContentClass EMailShellContentClass;
+typedef struct _EMailShellContentPrivate EMailShellContentPrivate;
+
+struct _EMailShellContent {
+ EShellContent parent;
+ EMailShellContentPrivate *priv;
+};
+
+struct _EMailShellContentClass {
+ EShellContentClass parent_class;
+};
+
+GType e_mail_shell_content_get_type (void);
+void e_mail_shell_content_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_mail_shell_content_new(EShellView *shell_view);
+gboolean e_mail_shell_content_get_preview_visible
+ (EMailShellContent *mail_shell_content);
+void e_mail_shell_content_set_preview_visible
+ (EMailShellContent *mail_shell_content,
+ gboolean preview_visible);
+gboolean e_mail_shell_content_get_show_deleted
+ (EMailShellContent *mail_shell_content);
+void e_mail_shell_content_set_show_deleted
+ (EMailShellContent *mail_shell_content,
+ gboolean show_deleted);
+gboolean e_mail_shell_content_get_vertical_view
+ (EMailShellContent *mail_shell_content);
+void e_mail_shell_content_set_vertical_view
+ (EMailShellContent *mail_shell_content,
+ gboolean vertical_view);
+GalViewInstance *
+ e_mail_shell_content_get_view_instance
+ (EMailShellContent *mail_shell_content);
+void e_mail_shell_content_set_search_strings
+ (EMailShellContent *mail_shell_content,
+ GSList *search_strings);
+void e_mail_shell_content_update_view_instance
+ (EMailShellContent *mail_shell_content);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SHELL_CONTENT_H */
diff --git a/modules/mail/e-mail-shell-migrate.c b/modules/mail/e-mail-shell-migrate.c
new file mode 100644
index 0000000000..2158580bf9
--- /dev/null
+++ b/modules/mail/e-mail-shell-migrate.c
@@ -0,0 +1,3104 @@
+/*
+ * e-mail-shell-migrate.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-mail-shell-migrate.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <utime.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <regex.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include <gconf/gconf-client.h>
+
+#include <camel/camel.h>
+#include <camel/camel-store.h>
+#include <camel/camel-session.h>
+#include <camel/camel-file-utils.h>
+#include <camel/camel-disco-folder.h>
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include <e-util/e-util.h>
+#include <libedataserver/e-xml-utils.h>
+#include <libedataserver/e-data-server-util.h>
+#include <e-util/e-xml-utils.h>
+
+#include "e-util/e-account-utils.h"
+#include "e-util/e-bconf-map.h"
+#include "e-util/e-error.h"
+#include "e-util/e-util-private.h"
+#include "e-util/e-plugin.h"
+#include "e-util/e-signature-utils.h"
+
+#include "e-mail-shell-backend.h"
+#include "shell/e-shell-migrate.h"
+
+#include "e-mail-store.h"
+#include "mail-config.h"
+#include "em-utils.h"
+
+#define d(x) x
+
+#ifndef G_OS_WIN32
+/* No versions previous to 2.8 or thereabouts have been available on
+ * Windows, so don't bother with upgrade support from earlier versions
+ * on Win32. Do try to support upgrades from 2.12 and later to the
+ * current version.
+ */
+
+/* upgrade helper functions */
+static xmlDocPtr
+emm_load_xml (const gchar *dirname, const gchar *filename)
+{
+ xmlDocPtr doc;
+ struct stat st;
+ gchar *path;
+
+ path = g_strdup_printf ("%s/%s", dirname, filename);
+ if (stat (path, &st) == -1 || !(doc = xmlParseFile (path))) {
+ g_free (path);
+ return NULL;
+ }
+
+ g_free (path);
+
+ return doc;
+}
+
+static gint
+emm_save_xml (xmlDocPtr doc, const gchar *dirname, const gchar *filename)
+{
+ gchar *path;
+ gint retval;
+
+ path = g_strdup_printf ("%s/%s", dirname, filename);
+ retval = e_xml_save_file (path, doc);
+ g_free (path);
+
+ return retval;
+}
+
+static xmlNodePtr
+xml_find_node (xmlNodePtr parent, const gchar *name)
+{
+ xmlNodePtr node;
+
+ node = parent->children;
+ while (node != NULL) {
+ if (node->name && !strcmp ((gchar *)node->name, name))
+ return node;
+
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+static void
+upgrade_xml_uris (xmlDocPtr doc, gchar * (* upgrade_uri) (const gchar *uri))
+{
+ xmlNodePtr root, node;
+ gchar *uri, *new;
+
+ if (!doc || !(root = xmlDocGetRootElement (doc)))
+ return;
+
+ if (!root->name || strcmp ((gchar *)root->name, "filteroptions") != 0) {
+ /* root node is not <filteroptions>, nothing to upgrade */
+ return;
+ }
+
+ if (!(node = xml_find_node (root, "ruleset"))) {
+ /* no ruleset node, nothing to upgrade */
+ return;
+ }
+
+ node = node->children;
+ while (node != NULL) {
+ if (node->name && !strcmp ((gchar *)node->name, "rule")) {
+ xmlNodePtr actionset, part, val, n;
+
+ if ((actionset = xml_find_node (node, "actionset"))) {
+ /* filters.xml */
+ part = actionset->children;
+ while (part != NULL) {
+ if (part->name && !strcmp ((gchar *)part->name, "part")) {
+ val = part->children;
+ while (val != NULL) {
+ if (val->name && !strcmp ((gchar *)val->name, "value")) {
+ gchar *type;
+
+ type = (gchar *)xmlGetProp (val, (const guchar *)"type");
+ if (type && !strcmp ((gchar *)type, "folder")) {
+ if ((n = xml_find_node (val, "folder"))) {
+ uri = (gchar *)xmlGetProp (n, (const guchar *)"uri");
+ new = upgrade_uri (uri);
+ xmlFree (uri);
+
+ xmlSetProp (n, (const guchar *)"uri", (guchar *)new);
+ g_free (new);
+ }
+ }
+
+ xmlFree (type);
+ }
+
+ val = val->next;
+ }
+ }
+
+ part = part->next;
+ }
+ } else if ((actionset = xml_find_node (node, "sources"))) {
+ /* vfolders.xml */
+ n = actionset->children;
+ while (n != NULL) {
+ if (n->name && !strcmp ((gchar *)n->name, "folder")) {
+ uri = (gchar *)xmlGetProp (n, (const guchar *)"uri");
+ new = upgrade_uri (uri);
+ xmlFree (uri);
+
+ xmlSetProp (n, (const guchar *)"uri", (guchar *)new);
+ g_free (new);
+ }
+
+ n = n->next;
+ }
+ }
+ }
+
+ node = node->next;
+ }
+}
+
+/* 1.0 upgrade functions & data */
+
+/* as much info as we have on a given account */
+struct _account_info_1_0 {
+ gchar *name;
+ gchar *uri;
+ gchar *base_uri;
+ union {
+ struct {
+ /* for imap */
+ gchar *namespace;
+ gchar *namespace_full;
+ guint32 capabilities;
+ GHashTable *folders;
+ gchar dir_sep;
+ } imap;
+ } u;
+};
+
+struct _imap_folder_info_1_0 {
+ gchar *folder;
+ /* encoded? decoded? canonicalised? */
+ gchar dir_sep;
+};
+
+static GHashTable *accounts_1_0 = NULL;
+static GHashTable *accounts_name_1_0 = NULL;
+
+static void
+imap_folder_info_1_0_free (struct _imap_folder_info_1_0 *fi)
+{
+ g_free(fi->folder);
+ g_free(fi);
+}
+
+static void
+account_info_1_0_free (struct _account_info_1_0 *ai)
+{
+ g_free(ai->name);
+ g_free(ai->uri);
+ g_free(ai->base_uri);
+ g_free(ai->u.imap.namespace);
+ g_free(ai->u.imap.namespace_full);
+ g_hash_table_destroy(ai->u.imap.folders);
+ g_free(ai);
+}
+
+static gchar *
+get_base_uri(const gchar *val)
+{
+ const gchar *tmp;
+
+ tmp = strchr(val, ':');
+ if (tmp) {
+ tmp++;
+ if (strncmp(tmp, "//", 2) == 0)
+ tmp += 2;
+ tmp = strchr(tmp, '/');
+ }
+
+ if (tmp)
+ return g_strndup(val, tmp-val);
+ else
+ return g_strdup(val);
+}
+
+static gchar *
+upgrade_xml_uris_1_0 (const gchar *uri)
+{
+ gchar *out = NULL;
+
+ /* upgrades camel uri's */
+ if (strncmp (uri, "imap:", 5) == 0) {
+ gchar *base_uri, dir_sep, *folder, *p;
+ struct _account_info_1_0 *ai;
+
+ /* add namespace, canonicalise dir_sep to / */
+ base_uri = get_base_uri (uri);
+ ai = g_hash_table_lookup (accounts_1_0, base_uri);
+
+ if (ai == NULL) {
+ g_free (base_uri);
+ return NULL;
+ }
+
+ dir_sep = ai->u.imap.dir_sep;
+ if (dir_sep == 0) {
+ /* no dir_sep listed, try get it from the namespace, if set */
+ if (ai->u.imap.namespace != NULL) {
+ p = ai->u.imap.namespace;
+ while ((dir_sep = *p++)) {
+ if (dir_sep < '0'
+ || (dir_sep > '9' && dir_sep < 'A')
+ || (dir_sep > 'Z' && dir_sep < 'a')
+ || (dir_sep > 'z')) {
+ break;
+ }
+ p++;
+ }
+ }
+
+ /* give up ... */
+ if (dir_sep == 0) {
+ g_free (base_uri);
+ return NULL;
+ }
+ }
+
+ folder = g_strdup (uri + strlen (base_uri) + 1);
+
+ /* Add the namespace before the mailbox name, unless the mailbox is INBOX */
+ if (ai->u.imap.namespace && strcmp ((gchar *)folder, "INBOX") != 0)
+ out = g_strdup_printf ("%s/%s/%s", base_uri, ai->u.imap.namespace, folder);
+ else
+ out = g_strdup_printf ("%s/%s", base_uri, folder);
+
+ p = out;
+ while (*p) {
+ if (*p == dir_sep)
+ *p = '/';
+ p++;
+ }
+
+ g_free (folder);
+ g_free (base_uri);
+ } else if (strncmp (uri, "exchange:", 9) == 0) {
+ gchar *base_uri, *folder, *p;
+
+ /* exchange://user@host/exchange/ * -> exchange://user@host/personal/ * */
+ /* Any url encoding (%xx) in the folder name is also removed */
+ base_uri = get_base_uri (uri);
+ uri += strlen (base_uri) + 1;
+ if (strncmp (uri, "exchange/", 9) == 0) {
+ folder = e_bconf_url_decode (uri + 9);
+ p = strchr (folder, '/');
+ out = g_strdup_printf ("%s/personal%s", base_uri, p ? p : "/");
+ g_free (folder);
+ }
+ } else if (strncmp (uri, "exchanget:", 10) == 0) {
+ /* these should be converted in the accounts table when it is loaded */
+ g_warning ("exchanget: uri not converted: '%s'", uri);
+ }
+
+ return out;
+}
+
+static gchar *
+parse_lsub (const gchar *lsub, gchar *dir_sep)
+{
+ static gint comp;
+ static regex_t pat;
+ regmatch_t match[3];
+ const gchar *m = "^\\* LSUB \\([^)]*\\) \"?([^\" ]+)\"? \"?(.*)\"?$";
+
+ if (!comp) {
+ if (regcomp (&pat, m, REG_EXTENDED|REG_ICASE) == -1) {
+ g_warning ("reg comp '%s' failed: %s", m, g_strerror (errno));
+ return NULL;
+ }
+ comp = 1;
+ }
+
+ if (regexec (&pat, lsub, 3, match, 0) == 0) {
+ if (match[1].rm_so != -1 && match[2].rm_so != -1) {
+ if (dir_sep)
+ *dir_sep = (match[1].rm_eo - match[1].rm_so == 1) ? lsub[match[1].rm_so] : 0;
+ return g_strndup (lsub + match[2].rm_so, match[2].rm_eo - match[2].rm_so);
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+read_imap_storeinfo (struct _account_info_1_0 *si)
+{
+ FILE *storeinfo;
+ guint32 tmp;
+ gchar *buf, *folder, dir_sep, *path, *name, *p;
+ struct _imap_folder_info_1_0 *fi;
+
+ si->u.imap.folders = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) NULL,
+ (GDestroyNotify) imap_folder_info_1_0_free);
+
+ /* get details from uri first */
+ name = strstr (si->uri, ";override_namespace");
+ if (name) {
+ name = strstr (si->uri, ";namespace=");
+ if (name) {
+ gchar *end;
+
+ name += strlen (";namespace=");
+ if (*name == '\"') {
+ name++;
+ end = strchr (name, '\"');
+ } else {
+ end = strchr (name, ';');
+ }
+
+ if (end) {
+ /* try get the dir_sep from the namespace */
+ si->u.imap.namespace = g_strndup (name, end-name);
+
+ p = si->u.imap.namespace;
+ while ((dir_sep = *p++)) {
+ if (dir_sep < '0'
+ || (dir_sep > '9' && dir_sep < 'A')
+ || (dir_sep > 'Z' && dir_sep < 'a')
+ || (dir_sep > 'z')) {
+ si->u.imap.dir_sep = dir_sep;
+ break;
+ }
+ p++;
+ }
+ }
+ }
+ }
+
+ /* now load storeinfo if it exists */
+ path = g_build_filename (g_get_home_dir (), "evolution", "mail", "imap", si->base_uri + 7, "storeinfo", NULL);
+ storeinfo = fopen (path, "r");
+ g_free (path);
+ if (storeinfo == NULL) {
+ g_warning ("could not find imap store info '%s'", path);
+ return FALSE;
+ }
+
+ /* ignore version */
+ camel_file_util_decode_uint32 (storeinfo, &tmp);
+ camel_file_util_decode_uint32 (storeinfo, &si->u.imap.capabilities);
+ g_free (si->u.imap.namespace);
+ camel_file_util_decode_string (storeinfo, &si->u.imap.namespace);
+ camel_file_util_decode_uint32 (storeinfo, &tmp);
+ si->u.imap.dir_sep = tmp;
+ /* strip trailing dir_sep or / */
+ if (si->u.imap.namespace
+ && (si->u.imap.namespace[strlen (si->u.imap.namespace) - 1] == si->u.imap.dir_sep
+ || si->u.imap.namespace[strlen (si->u.imap.namespace) - 1] == '/')) {
+ si->u.imap.namespace[strlen (si->u.imap.namespace) - 1] = 0;
+ }
+
+ d(printf ("namespace '%s' dir_sep '%c'\n", si->u.imap.namespace, si->u.imap.dir_sep ? si->u.imap.dir_sep : '?'));
+
+ while (camel_file_util_decode_string (storeinfo, &buf) == 0) {
+ folder = parse_lsub (buf, &dir_sep);
+ if (folder) {
+ fi = g_new0 (struct _imap_folder_info_1_0, 1);
+ fi->folder = folder;
+ fi->dir_sep = dir_sep;
+#if d(!)0
+ printf (" add folder '%s' ", folder);
+ if (dir_sep)
+ printf ("'%c'\n", dir_sep);
+ else
+ printf ("NIL\n");
+#endif
+ g_hash_table_insert (si->u.imap.folders, fi->folder, fi);
+ } else {
+ g_warning ("Could not parse LIST result '%s'\n", buf);
+ }
+ }
+
+ fclose (storeinfo);
+
+ return TRUE;
+}
+
+static gboolean
+load_accounts_1_0 (xmlDocPtr doc)
+{
+ xmlNodePtr source;
+ gchar *val, *tmp;
+ gint count = 0, i;
+ gchar key[32];
+
+ if (!(source = e_bconf_get_path (doc, "/Mail/Accounts")))
+ return TRUE;
+
+ if ((val = e_bconf_get_value (source, "num"))) {
+ count = atoi (val);
+ xmlFree (val);
+ }
+
+ /* load account upgrade info for each account */
+ for (i = 0; i < count; i++) {
+ struct _account_info_1_0 *ai;
+ gchar *rawuri;
+
+ sprintf (key, "source_url_%d", i);
+ if (!(rawuri = e_bconf_get_value (source, key)))
+ continue;
+
+ ai = g_malloc0 (sizeof (struct _account_info_1_0));
+ ai->uri = e_bconf_hex_decode (rawuri);
+ ai->base_uri = get_base_uri (ai->uri);
+ sprintf (key, "account_name_%d", i);
+ ai->name = e_bconf_get_string (source, key);
+
+ d(printf("load account '%s'\n", ai->uri));
+
+ if (!strncmp (ai->uri, "imap:", 5)) {
+ read_imap_storeinfo (ai);
+ } else if (!strncmp (ai->uri, "exchange:", 9)) {
+ xmlNodePtr node;
+
+ d(printf (" upgrade exchange account\n"));
+ /* small hack, poke the source_url into the transport_url for exchanget: transports
+ - this will be picked up later in the conversion */
+ sprintf (key, "transport_url_%d", i);
+ node = e_bconf_get_entry (source, key);
+ if (node && (val = (gchar *)xmlGetProp (node, (const guchar *)"value"))) {
+ tmp = e_bconf_hex_decode (val);
+ xmlFree (val);
+ if (strncmp (tmp, "exchanget:", 10) == 0)
+ xmlSetProp (node, (const guchar *)"value", (guchar *)rawuri);
+ g_free (tmp);
+ } else {
+ d(printf (" couldn't find transport uri?\n"));
+ }
+ }
+ xmlFree (rawuri);
+
+ g_hash_table_insert (accounts_1_0, ai->base_uri, ai);
+ if (ai->name)
+ g_hash_table_insert (accounts_name_1_0, ai->name, ai);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+em_migrate_1_0 (const gchar *data_dir, xmlDocPtr config_xmldb, xmlDocPtr filters, xmlDocPtr vfolders, GError **error)
+{
+ accounts_1_0 = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) NULL,
+ (GDestroyNotify) account_info_1_0_free);
+ accounts_name_1_0 = g_hash_table_new (g_str_hash, g_str_equal);
+ load_accounts_1_0 (config_xmldb);
+
+ upgrade_xml_uris(filters, upgrade_xml_uris_1_0);
+ upgrade_xml_uris(vfolders, upgrade_xml_uris_1_0);
+
+ g_hash_table_destroy (accounts_1_0);
+ g_hash_table_destroy (accounts_name_1_0);
+
+ return TRUE;
+}
+
+/* 1.2 upgrade functions */
+static gboolean
+is_xml1encoded (const gchar *txt)
+{
+ const guchar *p;
+ gint isxml1 = FALSE;
+ gint is8bit = FALSE;
+
+ p = (const guchar *)txt;
+ while (*p) {
+ if (p[0] == '\\' && p[1] == 'U' && p[2] == '+'
+ && isxdigit (p[3]) && isxdigit (p[4]) && isxdigit (p[5]) && isxdigit (p[6])
+ && p[7] == '\\') {
+ isxml1 = TRUE;
+ p+=7;
+ } else if (p[0] >= 0x80)
+ is8bit = TRUE;
+ p++;
+ }
+
+ /* check for invalid utf8 that needs cleaning */
+ if (is8bit && !isxml1)
+ isxml1 = !g_utf8_validate (txt, -1, NULL);
+
+ return isxml1;
+}
+
+static gchar *
+decode_xml1 (const gchar *txt)
+{
+ GString *out = g_string_new ("");
+ const guchar *p;
+ gchar *res;
+
+ /* convert:
+ \U+XXXX\ -> utf8
+ 8 bit characters -> utf8 (iso-8859-1) */
+
+ p = (const guchar *) txt;
+ while (*p) {
+ if (p[0] > 0x80
+ || (p[0] == '\\' && p[1] == 'U' && p[2] == '+'
+ && isxdigit (p[3]) && isxdigit (p[4]) && isxdigit (p[5]) && isxdigit (p[6])
+ && p[7] == '\\')) {
+ gchar utf8[8];
+ gunichar u;
+
+ if (p[0] == '\\') {
+ memcpy (utf8, p + 3, 4);
+ utf8[4] = 0;
+ u = strtoul (utf8, NULL, 16);
+ p+=7;
+ } else
+ u = p[0];
+ utf8[g_unichar_to_utf8 (u, utf8)] = 0;
+ g_string_append (out, utf8);
+ } else {
+ g_string_append_c (out, *p);
+ }
+ p++;
+ }
+
+ res = out->str;
+ g_string_free (out, FALSE);
+
+ return res;
+}
+
+static gchar *
+utf8_reencode (const gchar *txt)
+{
+ GString *out = g_string_new ("");
+ gchar *p;
+ gchar *res;
+
+ /* convert:
+ libxml1 8 bit utf8 converted to xml entities byte-by-byte chars -> utf8 */
+
+ p = (gchar *)txt;
+
+ while (*p) {
+ g_string_append_c (out, (gchar)g_utf8_get_char ((const gchar *)p));
+ p = (gchar *)g_utf8_next_char (p);
+ }
+
+ res = out->str;
+ if (g_utf8_validate (res, -1, NULL)) {
+ g_string_free (out, FALSE);
+ return res;
+ } else {
+ g_string_free (out, TRUE);
+ return g_strdup (txt);
+ }
+}
+
+static gboolean
+upgrade_xml_1_2_rec (xmlNodePtr node)
+{
+ const gchar *value_tags[] = { "string", "address", "regex", "file", "command", NULL };
+ const gchar *rule_tags[] = { "title", NULL };
+ const gchar *item_props[] = { "name", NULL };
+ struct {
+ const gchar *name;
+ const gchar **tags;
+ const gchar **props;
+ } tags[] = {
+ { "value", value_tags, NULL },
+ { "rule", rule_tags, NULL },
+ { "item", NULL, item_props },
+ { 0 },
+ };
+ xmlNodePtr work;
+ gint i,j;
+ gchar *txt, *tmp;
+
+ /* upgrades the content of a node, if the node has a specific parent/node name */
+
+ for (i = 0; tags[i].name; i++) {
+ if (!strcmp ((gchar *)node->name, tags[i].name)) {
+ if (tags[i].tags != NULL) {
+ work = node->children;
+ while (work) {
+ for (j = 0; tags[i].tags[j]; j++) {
+ if (!strcmp ((gchar *)work->name, tags[i].tags[j])) {
+ txt = (gchar *)xmlNodeGetContent (work);
+ if (is_xml1encoded (txt)) {
+ tmp = decode_xml1 (txt);
+ d(printf ("upgrading xml node %s/%s '%s' -> '%s'\n",
+ tags[i].name, tags[i].tags[j], txt, tmp));
+ xmlNodeSetContent (work, (guchar *)tmp);
+ g_free (tmp);
+ }
+ xmlFree (txt);
+ }
+ }
+ work = work->next;
+ }
+ break;
+ }
+
+ if (tags[i].props != NULL) {
+ for (j = 0; tags[i].props[j]; j++) {
+ txt = (gchar *)xmlGetProp (node, (guchar *)tags[i].props[j]);
+ tmp = utf8_reencode (txt);
+ d(printf ("upgrading xml property %s on node %s '%s' -> '%s'\n",
+ tags[i].props[j], tags[i].name, txt, tmp));
+ xmlSetProp (node, (const guchar *)tags[i].props[j], (guchar *)tmp);
+ g_free (tmp);
+ xmlFree (txt);
+ }
+ }
+ }
+ }
+
+ node = node->children;
+ while (node) {
+ upgrade_xml_1_2_rec (node);
+ node = node->next;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+em_upgrade_xml_1_2 (xmlDocPtr doc)
+{
+ xmlNodePtr root;
+
+ if (!doc || !(root = xmlDocGetRootElement (doc)))
+ return TRUE;
+
+ return upgrade_xml_1_2_rec (root);
+}
+
+/* ********************************************************************** */
+/* Tables for converting flat bonobo conf -> gconf xml blob */
+/* ********************************************************************** */
+
+/* Mail/Accounts/ * */
+static e_bconf_map_t cc_map[] = {
+ { "account_always_cc_%i", "always", E_BCONF_MAP_BOOL },
+ { "account_always_cc_addrs_%i", "recipients", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { NULL },
+};
+
+static e_bconf_map_t bcc_map[] = {
+ { "account_always_cc_%i", "always", E_BCONF_MAP_BOOL },
+ { "account_always_bcc_addrs_%i", "recipients", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { NULL },
+};
+
+static e_bconf_map_t pgp_map[] = {
+ { "account_pgp_encrypt_to_self_%i", "encrypt-to-self", E_BCONF_MAP_BOOL },
+ { "account_pgp_always_trust_%i", "always-trust", E_BCONF_MAP_BOOL },
+ { "account_pgp_always_sign_%i", "always-sign", E_BCONF_MAP_BOOL },
+ { "account_pgp_no_imip_sign_%i", "no-imip-sign", E_BCONF_MAP_BOOL },
+ { "account_pgp_key_%i", "key-id", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { NULL },
+};
+
+static e_bconf_map_t smime_map[] = {
+ { "account_smime_encrypt_to_self_%i", "encrypt-to-self", E_BCONF_MAP_BOOL },
+ { "account_smime_always_sign_%i", "always-sign", E_BCONF_MAP_BOOL },
+ { "account_smime_key_%i", "key-id", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { NULL },
+};
+
+static e_bconf_map_t identity_sig_map[] = {
+ { "identity_autogenerated_signature_%i", "auto", E_BCONF_MAP_BOOL },
+ { "identity_def_signature_%i", "default", E_BCONF_MAP_LONG },
+ { NULL },
+};
+
+static e_bconf_map_t identity_map[] = {
+ { "identity_name_%i", "name", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { "identity_address_%i", "addr-spec", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { "identity_reply_to_%i", "reply-to", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { "identity_organization_%i", "organization", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { NULL, "signature", E_BCONF_MAP_CHILD, identity_sig_map },
+ { NULL },
+};
+
+static e_bconf_map_t source_map[] = {
+ { "source_save_passwd_%i", "save-passwd", E_BCONF_MAP_BOOL },
+ { "source_keep_on_server_%i", "keep-on-server", E_BCONF_MAP_BOOL },
+ { "source_auto_check_%i", "auto-check", E_BCONF_MAP_BOOL },
+ { "source_auto_check_time_%i", "auto-check-timeout", E_BCONF_MAP_LONG },
+ { "source_url_%i", "url", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { NULL },
+};
+
+static e_bconf_map_t transport_map[] = {
+ { "transport_save_passwd_%i", "save-passwd", E_BCONF_MAP_BOOL },
+ { "transport_url_%i", "url", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { NULL },
+};
+
+static e_bconf_map_t account_map[] = {
+ { "account_name_%i", "name", E_BCONF_MAP_STRING },
+ { "source_enabled_%i", "enabled", E_BCONF_MAP_BOOL },
+ { NULL, "identity", E_BCONF_MAP_CHILD, identity_map },
+ { NULL, "source", E_BCONF_MAP_CHILD, source_map },
+ { NULL, "transport", E_BCONF_MAP_CHILD, transport_map },
+ { "account_drafts_folder_uri_%i", "drafts-folder", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { "account_sent_folder_uri_%i", "sent-folder", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { NULL, "auto-cc", E_BCONF_MAP_CHILD, cc_map },
+ { NULL, "auto-bcc", E_BCONF_MAP_CHILD, bcc_map },
+ { NULL, "pgp", E_BCONF_MAP_CHILD, pgp_map },
+ { NULL, "smime", E_BCONF_MAP_CHILD, smime_map },
+ { NULL },
+};
+
+/* /Mail/Signatures/ * */
+static e_bconf_map_t signature_format_map[] = {
+ { "text/plain", },
+ { "text/html", },
+ { NULL }
+};
+
+static e_bconf_map_t signature_map[] = {
+ { "name_%i", "name", E_BCONF_MAP_STRING },
+ { "html_%i", "format", E_BCONF_MAP_ENUM, signature_format_map },
+ { "filename_%i", "filename", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { "script_%i", "script", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT },
+ { NULL },
+};
+
+/* ********************************************************************** */
+/* Tables for bonobo conf -> gconf conversion */
+/* ********************************************************************** */
+
+static e_gconf_map_t mail_accounts_map[] = {
+ /* /Mail/Accounts - most entries are processed via the xml blob routine */
+ /* This also works because the initial uid mapping is 1:1 with the list order */
+ { "default_account", "mail/default_account", E_GCONF_MAP_SIMPLESTRING },
+ { 0 },
+};
+
+static e_gconf_map_t mail_display_map[] = {
+ /* /Mail/Display */
+ { "side_bar_search", "mail/display/side_bar_search", E_GCONF_MAP_BOOL },
+ { "thread_list", "mail/display/thread_list", E_GCONF_MAP_BOOL },
+ { "thread_subject", "mail/display/thread_subject", E_GCONF_MAP_BOOL },
+ { "hide_deleted", "mail/display/show_deleted", E_GCONF_MAP_BOOLNOT },
+ { "preview_pane", "mail/display/show_preview", E_GCONF_MAP_BOOL },
+ { "paned_size", "mail/display/paned_size", E_GCONF_MAP_INT },
+ { "seen_timeout", "mail/display/mark_seen_timeout", E_GCONF_MAP_INT },
+ { "do_seen_timeout", "mail/display/mark_seen", E_GCONF_MAP_BOOL },
+ { "http_images", "mail/display/load_http_images", E_GCONF_MAP_INT },
+ { "citation_highlight", "mail/display/mark_citations", E_GCONF_MAP_BOOL },
+ { "citation_color", "mail/display/citation_colour", E_GCONF_MAP_COLOUR },
+ { 0 },
+};
+
+static e_gconf_map_t mail_format_map[] = {
+ /* /Mail/Format */
+ { "message_display_style", "mail/display/message_style", E_GCONF_MAP_INT },
+ { "send_html", "mail/composer/send_html", E_GCONF_MAP_BOOL },
+ { "default_reply_style", "mail/format/reply_style", E_GCONF_MAP_INT },
+ { "default_forward_style", "mail/format/forward_style", E_GCONF_MAP_INT },
+ { "default_charset", "mail/composer/charset", E_GCONF_MAP_STRING },
+ { "confirm_unwanted_html", "mail/prompts/unwanted_html", E_GCONF_MAP_BOOL },
+ { 0 },
+};
+
+static e_gconf_map_t mail_trash_map[] = {
+ /* /Mail/Trash */
+ { "empty_on_exit", "mail/trash/empty_on_exit", E_GCONF_MAP_BOOL },
+ { 0 },
+};
+
+static e_gconf_map_t mail_prompts_map[] = {
+ /* /Mail/Prompts */
+ { "confirm_expunge", "mail/prompts/expunge", E_GCONF_MAP_BOOL },
+ { "empty_subject", "mail/prompts/empty_subject", E_GCONF_MAP_BOOL },
+ { "only_bcc", "mail/prompts/only_bcc", E_GCONF_MAP_BOOL },
+ { 0 }
+};
+
+static e_gconf_map_t mail_filters_map[] = {
+ /* /Mail/Filters */
+ { "log", "mail/filters/log", E_GCONF_MAP_BOOL },
+ { "log_path", "mail/filters/logfile", E_GCONF_MAP_STRING },
+ { 0 }
+};
+
+static e_gconf_map_t mail_notify_map[] = {
+ /* /Mail/Notify */
+ { "new_mail_notification", "mail/notify/type", E_GCONF_MAP_INT },
+ { "new_mail_notification_sound_file", "mail/notify/sound", E_GCONF_MAP_STRING },
+ { 0 }
+};
+
+static e_gconf_map_t mail_filesel_map[] = {
+ /* /Mail/Filesel */
+ { "last_filesel_dir", "mail/save_dir", E_GCONF_MAP_STRING },
+ { 0 }
+};
+
+static e_gconf_map_t mail_composer_map[] = {
+ /* /Mail/Composer */
+ { "ViewFrom", "mail/composer/view/From", E_GCONF_MAP_BOOL },
+ { "ViewReplyTo", "mail/composer/view/ReplyTo", E_GCONF_MAP_BOOL },
+ { "ViewCC", "mail/composer/view/Cc", E_GCONF_MAP_BOOL },
+ { "ViewBCC", "mail/composer/view/Bcc", E_GCONF_MAP_BOOL },
+ { "ViewSubject", "mail/composer/view/Subject", E_GCONF_MAP_BOOL },
+ { 0 },
+};
+
+/* ********************************************************************** */
+
+static e_gconf_map_t importer_elm_map[] = {
+ /* /Importer/Elm */
+ { "mail", "importer/elm/mail", E_GCONF_MAP_BOOL },
+ { "mail-imported", "importer/elm/mail-imported", E_GCONF_MAP_BOOL },
+ { 0 },
+};
+
+static e_gconf_map_t importer_pine_map[] = {
+ /* /Importer/Pine */
+ { "mail", "importer/elm/mail", E_GCONF_MAP_BOOL },
+ { "address", "importer/elm/address", E_GCONF_MAP_BOOL },
+ { 0 },
+};
+
+static e_gconf_map_t importer_netscape_map[] = {
+ /* /Importer/Netscape */
+ { "mail", "importer/netscape/mail", E_GCONF_MAP_BOOL },
+ { "settings", "importer/netscape/settings", E_GCONF_MAP_BOOL },
+ { "filters", "importer/netscape/filters", E_GCONF_MAP_BOOL },
+ { 0 },
+};
+
+/* ********************************************************************** */
+
+static e_gconf_map_list_t gconf_remap_list[] = {
+ { "/Mail/Accounts", mail_accounts_map },
+ { "/Mail/Display", mail_display_map },
+ { "/Mail/Format", mail_format_map },
+ { "/Mail/Trash", mail_trash_map },
+ { "/Mail/Prompts", mail_prompts_map },
+ { "/Mail/Filters", mail_filters_map },
+ { "/Mail/Notify", mail_notify_map },
+ { "/Mail/Filesel", mail_filesel_map },
+ { "/Mail/Composer", mail_composer_map },
+
+ { "/Importer/Elm", importer_elm_map },
+ { "/Importer/Pine", importer_pine_map },
+ { "/Importer/Netscape", importer_netscape_map },
+
+ { 0 },
+};
+
+static struct {
+ const gchar *label;
+ const gchar *colour;
+} label_default[5] = {
+ { N_("Important"), "#EF2929" }, /* red */
+ { N_("Work"), "#F57900" }, /* orange */
+ { N_("Personal"), "#4E9A06" }, /* green */
+ { N_("To Do"), "#3465A4" }, /* blue */
+ { N_("Later"), "#75507B" } /* purple */
+};
+
+/* remaps mail config from bconf to gconf */
+static gboolean
+bconf_import(GConfClient *gconf, xmlDocPtr config_xmldb)
+{
+ xmlNodePtr source;
+ gchar labx[16], colx[16];
+ gchar *val, *lab, *col;
+ GSList *list, *l;
+ gint i;
+
+ e_bconf_import(gconf, config_xmldb, gconf_remap_list);
+
+ /* Labels:
+ label string + label colour as integer
+ -> label string:# colour as hex */
+ source = e_bconf_get_path(config_xmldb, "/Mail/Labels");
+ if (source) {
+ list = NULL;
+ for (i = 0; i < 5; i++) {
+ sprintf(labx, "label_%d", i);
+ sprintf(colx, "color_%d", i);
+ lab = e_bconf_get_string(source, labx);
+ if ((col = e_bconf_get_value(source, colx))) {
+ sprintf(colx, "#%06x", atoi(col) & 0xffffff);
+ g_free(col);
+ } else
+ strcpy(colx, label_default[i].colour);
+
+ val = g_strdup_printf("%s:%s", lab ? lab : label_default[i].label, colx);
+ list = g_slist_append(list, val);
+ g_free(lab);
+ }
+
+ gconf_client_set_list(gconf, "/apps/evolution/mail/labels", GCONF_VALUE_STRING, list, NULL);
+ while (list) {
+ l = list->next;
+ g_free(list->data);
+ g_slist_free_1(list);
+ list = l;
+ }
+ } else {
+ g_warning("could not find /Mail/Labels in old config database, skipping");
+ }
+
+ /* Accounts: The flat bonobo-config structure is remapped to a list of xml blobs. Upgrades as necessary */
+ e_bconf_import_xml_blob(gconf, config_xmldb, account_map, "/Mail/Accounts",
+ "/apps/evolution/mail/accounts", "account", "uid");
+
+ /* Same for signatures */
+ e_bconf_import_xml_blob(gconf, config_xmldb, signature_map, "/Mail/Signatures",
+ "/apps/evolution/mail/signatures", "signature", NULL);
+
+ return TRUE;
+}
+
+static gboolean
+em_migrate_1_2(const gchar *data_dir, xmlDocPtr config_xmldb, xmlDocPtr filters, xmlDocPtr vfolders, GError **error)
+{
+ GConfClient *gconf;
+
+ gconf = gconf_client_get_default();
+ bconf_import(gconf, config_xmldb);
+ g_object_unref(gconf);
+
+ em_upgrade_xml_1_2(filters);
+ em_upgrade_xml_1_2(vfolders);
+
+ return TRUE;
+}
+
+/* 1.4 upgrade functions */
+
+#define EM_MIGRATE_SESSION_TYPE (em_migrate_session_get_type ())
+#define EM_MIGRATE_SESSION(obj) (CAMEL_CHECK_CAST((obj), EM_MIGRATE_SESSION_TYPE, EMMigrateSession))
+#define EM_MIGRATE_SESSION_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_MIGRATE_SESSION_TYPE, EMMigrateSessionClass))
+#define EM_MIGRATE_IS_SESSION(o) (CAMEL_CHECK_TYPE((o), EM_MIGRATE_SESSION_TYPE))
+
+typedef struct _EMMigrateSession {
+ CamelSession parent_object;
+
+ CamelStore *store; /* new folder tree store */
+ gchar *srcdir; /* old folder tree path */
+} EMMigrateSession;
+
+typedef struct _EMMigrateSessionClass {
+ CamelSessionClass parent_class;
+
+} EMMigrateSessionClass;
+
+static CamelType em_migrate_session_get_type (void);
+static CamelSession *em_migrate_session_new (const gchar *path);
+
+static void
+class_init (EMMigrateSessionClass *klass)
+{
+ ;
+}
+
+static CamelType
+em_migrate_session_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register (
+ camel_session_get_type (),
+ "EMMigrateSession",
+ sizeof (EMMigrateSession),
+ sizeof (EMMigrateSessionClass),
+ (CamelObjectClassInitFunc) class_init,
+ NULL,
+ NULL,
+ NULL);
+ }
+
+ return type;
+}
+
+static CamelSession *
+em_migrate_session_new (const gchar *path)
+{
+ CamelSession *session;
+
+ session = CAMEL_SESSION (camel_object_new (EM_MIGRATE_SESSION_TYPE));
+
+ camel_session_construct (session, path);
+
+ return session;
+}
+
+
+#endif /* !G_OS_WIN32 */
+
+static GtkWidget *window;
+static GtkLabel *label;
+static GtkProgressBar *progress;
+
+static void
+em_migrate_setup_progress_dialog (const gchar *title, const gchar *desc)
+{
+ GtkWidget *vbox, *hbox, *w;
+ gchar *markup;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title ((GtkWindow *) window, _("Migrating..."));
+ gtk_window_set_modal ((GtkWindow *) window, TRUE);
+ gtk_container_set_border_width ((GtkContainer *) window, 6);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (vbox);
+ gtk_container_add ((GtkContainer *) window, vbox);
+
+ w = gtk_label_new (desc);
+
+ gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
+ gtk_widget_show (w);
+ gtk_box_pack_start_defaults ((GtkBox *) vbox, w);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_widget_show (hbox);
+ gtk_box_pack_start_defaults ((GtkBox *) vbox, hbox);
+
+ label = (GtkLabel *) gtk_label_new ("");
+ gtk_widget_show ((GtkWidget *) label);
+ gtk_box_pack_start_defaults ((GtkBox *) hbox, (GtkWidget *) label);
+
+ progress = (GtkProgressBar *) gtk_progress_bar_new ();
+ gtk_widget_show ((GtkWidget *) progress);
+ gtk_box_pack_start_defaults ((GtkBox *) hbox, (GtkWidget *) progress);
+
+ /* Prepare the message */
+ vbox = gtk_vbox_new (FALSE, 12);
+ gtk_widget_show (vbox);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+
+ w = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0);
+ markup = g_strconcat ("<big><b>", title ? title : _("Migration"), "</b></big>", NULL);
+ gtk_label_set_markup (GTK_LABEL (w), markup);
+ gtk_box_pack_start (GTK_BOX (vbox), w, TRUE, TRUE, 0);
+ g_free (markup);
+
+ w = gtk_label_new (desc);
+ gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), w, TRUE, TRUE, 0);
+
+ /* Progress bar */
+ w = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), w, TRUE, TRUE, 0);
+
+ label = GTK_LABEL (gtk_label_new (""));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_label_set_line_wrap (label, TRUE);
+ gtk_widget_show (GTK_WIDGET (label));
+ gtk_box_pack_start (GTK_BOX (w), GTK_WIDGET (label), TRUE, TRUE, 0);
+
+ progress = GTK_PROGRESS_BAR (gtk_progress_bar_new ());
+ gtk_widget_show (GTK_WIDGET (progress));
+ gtk_box_pack_start (GTK_BOX (w), GTK_WIDGET (progress), TRUE, TRUE, 0);
+
+ gtk_container_add (GTK_CONTAINER (window), hbox);
+ gtk_widget_show_all (hbox);
+ gtk_widget_show (window);
+}
+
+static void
+em_migrate_close_progress_dialog (void)
+{
+ gtk_widget_destroy ((GtkWidget *) window);
+}
+
+static void
+em_migrate_set_folder_name (const gchar *folder_name)
+{
+ gchar *text;
+
+ text = g_strdup_printf (_("Migrating '%s':"), folder_name);
+ gtk_label_set_text (label, text);
+ g_free (text);
+
+ gtk_progress_bar_set_fraction (progress, 0.0);
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static void
+em_migrate_set_progress (double percent)
+{
+ gchar text[5];
+
+ snprintf (text, sizeof (text), "%d%%", (gint) (percent * 100.0f));
+
+ gtk_progress_bar_set_fraction (progress, percent);
+ gtk_progress_bar_set_text (progress, text);
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+#ifndef G_OS_WIN32
+
+static gboolean
+is_mail_folder (const gchar *metadata)
+{
+ xmlNodePtr node;
+ xmlDocPtr doc;
+ gchar *type;
+
+ if (!(doc = xmlParseFile (metadata))) {
+ g_warning ("Cannot parse `%s'", metadata);
+ return FALSE;
+ }
+
+ if (!(node = xmlDocGetRootElement (doc))) {
+ g_warning ("`%s' corrupt: document contains no root node", metadata);
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ if (!node->name || strcmp ((gchar *)node->name, "efolder") != 0) {
+ g_warning ("`%s' corrupt: root node is not 'efolder'", metadata);
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ node = node->children;
+ while (node != NULL) {
+ if (node->name && !strcmp ((gchar *)node->name, "type")) {
+ type = (gchar *)xmlNodeGetContent (node);
+ if (!strcmp ((gchar *)type, "mail")) {
+ xmlFreeDoc (doc);
+ xmlFree (type);
+
+ return TRUE;
+ }
+
+ xmlFree (type);
+
+ break;
+ }
+
+ node = node->next;
+ }
+
+ xmlFreeDoc (doc);
+
+ return FALSE;
+}
+
+static gboolean
+get_local_et_expanded (const gchar *dirname)
+{
+ xmlNodePtr node;
+ xmlDocPtr doc;
+ struct stat st;
+ gchar *buf, *p;
+ gint thread_list;
+
+ buf = g_strdup_printf ("%s/evolution/config/file:%s", g_get_home_dir (), dirname);
+ p = buf + strlen (g_get_home_dir ()) + strlen ("/evolution/config/file:");
+ e_filename_make_safe (p);
+
+ if (stat (buf, &st) == -1) {
+ g_free (buf);
+ return FALSE;
+ }
+
+ if (!(doc = xmlParseFile (buf))) {
+ g_free (buf);
+ return FALSE;
+ }
+
+ g_free (buf);
+
+ if (!(node = xmlDocGetRootElement (doc)) || strcmp ((gchar *)node->name, "expanded_state") != 0) {
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ if (!(buf = (gchar *)xmlGetProp (node, (const guchar *)"default"))) {
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ thread_list = strcmp (buf, "0") == 0 ? 0 : 1;
+ xmlFree (buf);
+
+ xmlFreeDoc (doc);
+
+ return thread_list;
+}
+
+static gchar *
+get_local_store_uri (const gchar *dirname, gchar **namep, gint *indexp)
+{
+ gchar *name, *protocol, *metadata, *tmp;
+ gint index;
+ struct stat st;
+ xmlNodePtr node;
+ xmlDocPtr doc;
+
+ metadata = g_build_filename(dirname, "local-metadata.xml", NULL);
+
+ /* in 1.4, any errors are treated as defaults, this function cannot fail */
+
+ /* defaults */
+ name = (gchar *) "mbox";
+ protocol = (gchar *) "mbox";
+ index = TRUE;
+
+ if (stat (metadata, &st) == -1 || !S_ISREG (st.st_mode))
+ goto nofile;
+
+ doc = xmlParseFile(metadata);
+ if (doc == NULL)
+ goto nofile;
+
+ node = doc->children;
+ if (strcmp((gchar *)node->name, "folderinfo"))
+ goto dodefault;
+
+ for (node = node->children; node; node = node->next) {
+ if (node->name && !strcmp ((gchar *)node->name, "folder")) {
+ tmp = (gchar *)xmlGetProp (node, (const guchar *)"type");
+ if (tmp) {
+ protocol = alloca(strlen(tmp)+1);
+ strcpy(protocol, tmp);
+ xmlFree(tmp);
+ }
+ tmp = (gchar *)xmlGetProp (node, (const guchar *)"name");
+ if (tmp) {
+ name = alloca(strlen(tmp)+1);
+ strcpy(name, tmp);
+ xmlFree(tmp);
+ }
+ tmp = (gchar *)xmlGetProp (node, (const guchar *)"index");
+ if (tmp) {
+ index = atoi(tmp);
+ xmlFree(tmp);
+ }
+ }
+ }
+dodefault:
+ xmlFreeDoc (doc);
+nofile:
+ g_free(metadata);
+
+ *namep = g_strdup(name);
+ *indexp = index;
+
+ return g_strdup_printf("%s:%s", protocol, dirname);
+}
+
+#endif /* !G_OS_WIN32 */
+
+enum {
+ CP_UNIQUE = 0,
+ CP_OVERWRITE,
+ CP_APPEND
+};
+
+static gint open_flags[3] = {
+ O_WRONLY | O_CREAT | O_TRUNC,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ O_WRONLY | O_CREAT | O_APPEND,
+};
+
+static gboolean
+cp (const gchar *src, const gchar *dest, gboolean show_progress, gint mode)
+{
+ guchar readbuf[65536];
+ gssize nread, nwritten;
+ gint errnosav, readfd, writefd;
+ gsize total = 0;
+ struct stat st;
+ struct utimbuf ut;
+
+ /* if the dest file exists and has content, abort - we don't
+ * want to corrupt their existing data */
+ if (g_stat (dest, &st) == 0 && st.st_size > 0 && mode == CP_UNIQUE) {
+ errno = EEXIST;
+ return FALSE;
+ }
+
+ if (g_stat (src, &st) == -1
+ || (readfd = g_open (src, O_RDONLY | O_BINARY, 0)) == -1)
+ return FALSE;
+
+ if ((writefd = g_open (dest, open_flags[mode] | O_BINARY, 0666)) == -1) {
+ errnosav = errno;
+ close (readfd);
+ errno = errnosav;
+ return FALSE;
+ }
+
+ do {
+ do {
+ nread = read (readfd, readbuf, sizeof (readbuf));
+ } while (nread == -1 && errno == EINTR);
+
+ if (nread == 0)
+ break;
+ else if (nread < 0)
+ goto exception;
+
+ do {
+ nwritten = write (writefd, readbuf, nread);
+ } while (nwritten == -1 && errno == EINTR);
+
+ if (nwritten < nread)
+ goto exception;
+
+ total += nwritten;
+ if (show_progress)
+ em_migrate_set_progress (((double) total) / ((double) st.st_size));
+ } while (total < st.st_size);
+
+ if (fsync (writefd) == -1)
+ goto exception;
+
+ close (readfd);
+ if (close (writefd) == -1)
+ goto failclose;
+
+ ut.actime = st.st_atime;
+ ut.modtime = st.st_mtime;
+ utime (dest, &ut);
+ chmod (dest, st.st_mode);
+
+ return TRUE;
+
+ exception:
+
+ errnosav = errno;
+ close (readfd);
+ close (writefd);
+ errno = errnosav;
+
+ failclose:
+
+ errnosav = errno;
+ unlink (dest);
+ errno = errnosav;
+
+ return FALSE;
+}
+
+#ifndef G_OS_WIN32
+
+static gboolean
+cp_r (const gchar *src, const gchar *dest, const gchar *pattern, gint mode)
+{
+ GString *srcpath, *destpath;
+ struct dirent *dent;
+ gsize slen, dlen;
+ struct stat st;
+ DIR *dir;
+
+ if (g_mkdir_with_parents (dest, 0777) == -1)
+ return FALSE;
+
+ if (!(dir = opendir (src)))
+ return FALSE;
+
+ srcpath = g_string_new (src);
+ g_string_append_c (srcpath, '/');
+ slen = srcpath->len;
+
+ destpath = g_string_new (dest);
+ g_string_append_c (destpath, '/');
+ dlen = destpath->len;
+
+ while ((dent = readdir (dir))) {
+ if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, ".."))
+ continue;
+
+ g_string_truncate (srcpath, slen);
+ g_string_truncate (destpath, dlen);
+
+ g_string_append (srcpath, dent->d_name);
+ g_string_append (destpath, dent->d_name);
+
+ if (stat (srcpath->str, &st) == -1)
+ continue;
+
+ if (S_ISDIR (st.st_mode)) {
+ cp_r (srcpath->str, destpath->str, pattern, mode);
+ } else if (!pattern || !strcmp (dent->d_name, pattern)) {
+ cp (srcpath->str, destpath->str, FALSE, mode);
+ }
+ }
+
+ closedir (dir);
+
+ g_string_free (destpath, TRUE);
+ g_string_free (srcpath, TRUE);
+
+ return TRUE;
+}
+
+static void
+mbox_build_filename (GString *path, const gchar *toplevel_dir, const gchar *full_name)
+{
+ const gchar *start, *inptr = full_name;
+ gint subdirs = 0;
+
+ while (*inptr != '\0') {
+ if (*inptr == '/')
+ subdirs++;
+ inptr++;
+ }
+
+ g_string_assign(path, toplevel_dir);
+ g_string_append_c (path, '/');
+
+ inptr = full_name;
+ while (*inptr != '\0') {
+ start = inptr;
+ while (*inptr != '/' && *inptr != '\0')
+ inptr++;
+
+ g_string_append_len (path, start, inptr - start);
+
+ if (*inptr == '/') {
+ g_string_append (path, ".sbd/");
+ inptr++;
+
+ /* strip extranaeous '/'s */
+ while (*inptr == '/')
+ inptr++;
+ }
+ }
+}
+
+static gboolean
+em_migrate_folder(EMMigrateSession *session, const gchar *dirname, const gchar *full_name, GError **error)
+{
+ CamelFolder *old_folder = NULL, *new_folder = NULL;
+ CamelStore *local_store = NULL;
+ CamelException ex;
+ gchar *name, *uri;
+ GPtrArray *uids;
+ struct stat st;
+ gboolean thread_list;
+ gint index, i;
+ GString *src, *dest;
+ gboolean success = FALSE;
+
+ camel_exception_init (&ex);
+
+ src = g_string_new("");
+
+ g_string_printf(src, "%s/folder-metadata.xml", dirname);
+ if (stat (src->str, &st) == -1
+ || !S_ISREG (st.st_mode)
+ || !is_mail_folder(src->str)) {
+ /* Not an evolution mail folder */
+ g_string_free(src, TRUE);
+ return TRUE;
+ }
+
+ dest = g_string_new("");
+ uri = get_local_store_uri(dirname, &name, &index);
+ em_migrate_set_folder_name (full_name);
+ thread_list = get_local_et_expanded (dirname);
+
+ /* Manually copy local mbox files, its much faster */
+ if (!strncmp (uri, "mbox:", 5)) {
+ static const gchar *meta_ext[] = { ".summary", ".ibex.index", ".ibex.index.data" };
+ gsize slen, dlen;
+ FILE *fp;
+ gchar *p;
+ gint mode;
+
+ g_string_printf (src, "%s/%s", uri + 5, name);
+ mbox_build_filename (dest, ((CamelService *)session->store)->url->path, full_name);
+ p = strrchr (dest->str, '/');
+ *p = '\0';
+
+ slen = src->len;
+ dlen = dest->len;
+
+ if (g_mkdir_with_parents (dest->str, 0777) == -1 && errno != EEXIST) {
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Unable to create new folder `%s': %s"),
+ dest->str, g_strerror (errno));
+ goto fatal;
+ }
+
+ *p = '/';
+ mode = CP_UNIQUE;
+ retry_copy:
+ if (!cp (src->str, dest->str, TRUE, mode)) {
+ if (errno == EEXIST) {
+ gint save = errno;
+
+ switch (e_error_run(NULL, "mail:ask-migrate-existing", src->str, dest->str, NULL)) {
+ case GTK_RESPONSE_ACCEPT:
+ mode = CP_OVERWRITE;
+ goto retry_copy;
+ case GTK_RESPONSE_OK:
+ mode = CP_APPEND;
+ goto retry_copy;
+ case GTK_RESPONSE_REJECT:
+ goto ignore;
+ }
+
+ errno = save;
+ }
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Unable to copy folder `%s' to `%s': %s"),
+ src->str, dest->str, g_strerror (errno));
+ goto fatal;
+ }
+ ignore:
+
+ /* create a .cmeta file specifying to index and/or thread the folder */
+ g_string_truncate (dest, dlen);
+ g_string_append (dest, ".cmeta");
+ if ((fp = fopen (dest->str, "w")) != NULL) {
+ gint fd = fileno (fp);
+
+ /* write the magic string */
+ if (fwrite ("CLMD", 4, 1, fp) != 1)
+ goto cmeta_err;
+
+ /* write the version (1) */
+ if (camel_file_util_encode_uint32 (fp, 1) == -1)
+ goto cmeta_err;
+
+ /* write the meta count */
+ if (camel_file_util_encode_uint32 (fp, thread_list ? 1 : 0) == -1)
+ goto cmeta_err;
+
+ if (!thread_list) {
+ if (camel_file_util_encode_string (fp, "evolution:thread_list") == -1)
+ goto cmeta_err;
+
+ if (camel_file_util_encode_string (fp, !thread_list ? "1" : "0") == -1)
+ goto cmeta_err;
+ }
+
+ /* write the prop count (only prop is the index prop) */
+ if (camel_file_util_encode_uint32 (fp, 1) == -1)
+ goto cmeta_err;
+
+ /* write the index prop tag (== CAMEL_FOLDER_ARG_LAST|CAMEL_ARG_BOO) */
+ if (camel_file_util_encode_uint32 (fp, CAMEL_FOLDER_ARG_LAST|CAMEL_ARG_BOO) == -1)
+ goto cmeta_err;
+
+ /* write the index prop value */
+ if (camel_file_util_encode_uint32 (fp, 1) == -1)
+ goto cmeta_err;
+
+ fflush (fp);
+
+ if (fsync (fd) == -1) {
+ cmeta_err:
+ fclose (fp);
+ unlink (dest->str);
+ } else {
+ fclose (fp);
+ }
+ }
+
+ /* copy over the metadata files */
+ for (i = 0; i < sizeof(meta_ext)/sizeof(meta_ext[0]); i++) {
+ g_string_truncate (src, slen);
+ g_string_truncate (dest, dlen);
+
+ g_string_append (src, meta_ext[i]);
+ g_string_append (dest, meta_ext[i]);
+ cp (src->str, dest->str, FALSE, CP_OVERWRITE);
+ }
+ } else {
+ guint32 flags = CAMEL_STORE_FOLDER_CREATE;
+
+ if (!(local_store = camel_session_get_store ((CamelSession *) session, uri, &ex))
+ || !(old_folder = camel_store_get_folder (local_store, name, 0, &ex)))
+ goto fatal;
+
+ flags |= (index ? CAMEL_STORE_FOLDER_BODY_INDEX : 0);
+ if (!(new_folder = camel_store_get_folder (session->store, full_name, flags, &ex)))
+ goto fatal;
+
+ if (!thread_list) {
+ camel_object_meta_set (new_folder, "evolution:thread_list", !thread_list ? "1" : "0");
+ camel_object_state_write (new_folder);
+ }
+
+ uids = camel_folder_get_uids (old_folder);
+ for (i = 0; i < uids->len; i++) {
+ CamelMimeMessage *message;
+ CamelMessageInfo *info;
+
+ if (!(info = camel_folder_get_message_info (old_folder, uids->pdata[i])))
+ continue;
+
+ if (!(message = camel_folder_get_message (old_folder, uids->pdata[i], &ex))) {
+ camel_folder_free_message_info (old_folder, info);
+ camel_folder_free_uids (old_folder, uids);
+ goto fatal;
+ }
+
+ camel_folder_append_message (new_folder, message, info, NULL, &ex);
+ camel_folder_free_message_info (old_folder, info);
+ camel_object_unref (message);
+
+ if (camel_exception_is_set (&ex))
+ break;
+
+ em_migrate_set_progress (((double) i + 1) / ((double) uids->len));
+ }
+
+ camel_folder_free_uids (old_folder, uids);
+
+ if (camel_exception_is_set (&ex))
+ goto fatal;
+ }
+ success = TRUE;
+fatal:
+ g_free (uri);
+ g_free (name);
+ g_string_free(src, TRUE);
+ g_string_free(dest, TRUE);
+ if (local_store)
+ camel_object_unref(local_store);
+ if (old_folder)
+ camel_object_unref(old_folder);
+ if (new_folder)
+ camel_object_unref(new_folder);
+
+ if (camel_exception_is_set (&ex)) {
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ "%s", camel_exception_get_description (&ex));
+ camel_exception_clear (&ex);
+ }
+
+ return success;
+}
+
+static gboolean
+em_migrate_dir (EMMigrateSession *session, const gchar *dirname, const gchar *full_name, GError **error)
+{
+ gchar *path;
+ DIR *dir;
+ struct stat st;
+ struct dirent *dent;
+ gboolean success = TRUE;
+
+ if (!em_migrate_folder(session, dirname, full_name, error))
+ return FALSE;
+
+ /* no subfolders, not readable, don't care */
+ path = g_strdup_printf ("%s/subfolders", dirname);
+ if (stat (path, &st) == -1 || !S_ISDIR (st.st_mode)) {
+ g_free (path);
+ return TRUE;
+ }
+
+ if (!(dir = opendir (path))) {
+ g_free (path);
+ return TRUE;
+ }
+
+ while (success && (dent = readdir (dir))) {
+ gchar *full_path;
+ gchar *name;
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ full_path = g_strdup_printf ("%s/%s", path, dent->d_name);
+ if (stat (full_path, &st) == -1 || !S_ISDIR (st.st_mode)) {
+ g_free (full_path);
+ continue;
+ }
+
+ name = g_strdup_printf ("%s/%s", full_name, dent->d_name);
+ success = em_migrate_dir (session, full_path, name, error);
+ g_free (full_path);
+ g_free (name);
+ }
+
+ closedir (dir);
+
+ g_free (path);
+
+ return success;
+}
+
+static gboolean
+em_migrate_local_folders_1_4 (EMMigrateSession *session, GError **error)
+{
+ struct dirent *dent;
+ struct stat st;
+ DIR *dir;
+ gboolean success = TRUE;
+
+ if (!(dir = opendir (session->srcdir))) {
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Unable to scan for existing mailboxes at "
+ "`%s': %s"), session->srcdir, g_strerror (errno));
+ return FALSE;
+ }
+
+ em_migrate_setup_progress_dialog (
+ _("Migrating Folders"),
+ _("The location and hierarchy of the Evolution mailbox "
+ "folders has changed since Evolution 1.x.\n\nPlease be "
+ "patient while Evolution migrates your folders..."));
+
+ while (success && (dent = readdir (dir))) {
+ gchar *full_path;
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ full_path = g_strdup_printf ("%s/%s", session->srcdir, dent->d_name);
+ if (stat (full_path, &st) == -1 || !S_ISDIR (st.st_mode)) {
+ g_free (full_path);
+ continue;
+ }
+
+ success = em_migrate_dir (session, full_path, dent->d_name, error);
+ g_free (full_path);
+ }
+
+ closedir (dir);
+
+ em_migrate_close_progress_dialog ();
+
+ return success;
+}
+
+static gchar *
+upgrade_xml_uris_1_4 (const gchar *uri)
+{
+ gchar *path, *prefix, *p;
+ CamelURL *url;
+
+ if (!strncmp (uri, "file:", 5)) {
+ url = camel_url_new (uri, NULL);
+ camel_url_set_protocol (url, "email");
+ camel_url_set_user (url, "local");
+ camel_url_set_host (url, "local");
+
+ prefix = g_build_filename (g_get_home_dir (), "evolution", "local", NULL);
+ if (strncmp (url->path, prefix, strlen (prefix)) != 0) {
+ /* uri is busticated - user probably copied from another user's home directory */
+ camel_url_free (url);
+ g_free (prefix);
+
+ return g_strdup (uri);
+ }
+ path = g_strdup (url->path + strlen (prefix));
+ g_free (prefix);
+
+ /* modify the path in-place */
+ p = path + strlen (path) - 12;
+ while (p > path) {
+ if (!strncmp (p, "/subfolders/", 12))
+ memmove (p, p + 11, strlen (p + 11) + 1);
+
+ p--;
+ }
+
+ camel_url_set_path (url, path);
+ g_free (path);
+
+ path = camel_url_to_string (url, 0);
+ camel_url_free (url);
+
+ return path;
+ } else {
+ return em_uri_from_camel (uri);
+ }
+}
+
+static void
+upgrade_vfolder_sources_1_4 (xmlDocPtr doc)
+{
+ xmlNodePtr root, node;
+
+ if (!doc || !(root = xmlDocGetRootElement (doc)))
+ return;
+
+ if (!root->name || strcmp ((gchar *)root->name, "filteroptions") != 0) {
+ /* root node is not <filteroptions>, nothing to upgrade */
+ return;
+ }
+
+ if (!(node = xml_find_node (root, "ruleset"))) {
+ /* no ruleset node, nothing to upgrade */
+ return;
+ }
+
+ node = node->children;
+ while (node != NULL) {
+ if (node->name && !strcmp ((gchar *)node->name, "rule")) {
+ xmlNodePtr sources;
+ gchar *src;
+
+ if (!(src = (gchar *)xmlGetProp (node, (const guchar *)"source")))
+ src = (gchar *)xmlStrdup ((const guchar *)"local"); /* default to all local folders? */
+
+ xmlSetProp (node, (const guchar *)"source", (const guchar *)"incoming");
+
+ if (!(sources = xml_find_node (node, "sources")))
+ sources = xmlNewChild (node, NULL, (const guchar *)"sources", NULL);
+
+ xmlSetProp (sources, (const guchar *)"with", (guchar *)src);
+ xmlFree (src);
+ }
+
+ node = node->next;
+ }
+}
+
+static gchar *
+get_nth_sig (gint id)
+{
+ ESignatureList *list;
+ ESignature *sig;
+ EIterator *iter;
+ gchar *uid = NULL;
+ gint i = 0;
+
+ list = e_get_signature_list ();
+ iter = e_list_get_iterator ((EList *) list);
+
+ while (e_iterator_is_valid (iter) && i < id) {
+ e_iterator_next (iter);
+ i++;
+ }
+
+ if (i == id && e_iterator_is_valid (iter)) {
+ sig = (ESignature *) e_iterator_get (iter);
+ uid = g_strdup (sig->uid);
+ }
+
+ g_object_unref (iter);
+
+ return uid;
+}
+
+static void
+em_upgrade_accounts_1_4 (void)
+{
+ EAccountList *accounts;
+ EIterator *iter;
+
+ if (!(accounts = e_get_account_list ()))
+ return;
+
+ iter = e_list_get_iterator ((EList *) accounts);
+ while (e_iterator_is_valid (iter)) {
+ EAccount *account = (EAccount *) e_iterator_get (iter);
+ gchar *url;
+
+ if (account->drafts_folder_uri) {
+ url = upgrade_xml_uris_1_4 (account->drafts_folder_uri);
+ g_free (account->drafts_folder_uri);
+ account->drafts_folder_uri = url;
+ }
+
+ if (account->sent_folder_uri) {
+ url = upgrade_xml_uris_1_4 (account->sent_folder_uri);
+ g_free (account->sent_folder_uri);
+ account->sent_folder_uri = url;
+ }
+
+ if (account->id->sig_uid && !strncmp (account->id->sig_uid, "::", 2)) {
+ gint sig_id;
+
+ sig_id = strtol (account->id->sig_uid + 2, NULL, 10);
+ g_free (account->id->sig_uid);
+ account->id->sig_uid = get_nth_sig (sig_id);
+ }
+
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+
+ e_account_list_save (accounts);
+}
+
+static gboolean
+em_migrate_pop_uid_caches_1_4 (const gchar *data_dir, GError **error)
+{
+ GString *oldpath, *newpath;
+ struct dirent *dent;
+ gsize olen, nlen;
+ gchar *cache_dir;
+ DIR *dir;
+ gboolean success = TRUE;
+
+ /* Sigh, too many unique strings to translate, for cases which shouldn't ever happen */
+
+ /* open the old cache dir */
+ cache_dir = g_build_filename (g_get_home_dir (), "evolution", "mail", "pop3", NULL);
+ if (!(dir = opendir (cache_dir))) {
+ if (errno == ENOENT) {
+ g_free(cache_dir);
+ return TRUE;
+ }
+
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Unable to open old POP keep-on-server data "
+ "`%s': %s"), cache_dir, g_strerror (errno));
+ g_free (cache_dir);
+ return FALSE;
+ }
+
+ oldpath = g_string_new (cache_dir);
+ g_string_append_c (oldpath, '/');
+ olen = oldpath->len;
+ g_free (cache_dir);
+
+ cache_dir = g_build_filename (data_dir, "pop", NULL);
+ if (g_mkdir_with_parents (cache_dir, 0777) == -1) {
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Unable to create POP3 keep-on-server data "
+ "directory `%s': %s"), cache_dir,
+ g_strerror (errno));
+ g_string_free (oldpath, TRUE);
+ g_free (cache_dir);
+ closedir (dir);
+ return FALSE;
+ }
+
+ newpath = g_string_new (cache_dir);
+ g_string_append_c (newpath, '/');
+ nlen = newpath->len;
+ g_free (cache_dir);
+
+ while (success && (dent = readdir (dir))) {
+ if (strncmp (dent->d_name, "cache-pop:__", 12) != 0)
+ continue;
+
+ g_string_truncate (oldpath, olen);
+ g_string_truncate (newpath, nlen);
+
+ g_string_append (oldpath, dent->d_name);
+ g_string_append (newpath, dent->d_name + 12);
+
+ /* strip the trailing '_' */
+ g_string_truncate (newpath, newpath->len - 1);
+
+ if (g_mkdir_with_parents (newpath->str, 0777) == -1
+ || !cp(oldpath->str, (g_string_append(newpath, "/uid-cache"))->str, FALSE, CP_UNIQUE)) {
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Unable to copy POP3 keep-on-server data "
+ "`%s': %s"), oldpath->str,
+ g_strerror (errno));
+ success = FALSE;
+ }
+
+ }
+
+ g_string_free (oldpath, TRUE);
+ g_string_free (newpath, TRUE);
+
+ closedir (dir);
+
+ return success;
+}
+
+static gboolean
+em_migrate_imap_caches_1_4 (const gchar *data_dir, GError **error)
+{
+ gchar *src, *dest;
+ struct stat st;
+
+ src = g_build_filename (g_get_home_dir (), "evolution", "mail", "imap", NULL);
+ if (stat (src, &st) == -1 || !S_ISDIR (st.st_mode)) {
+ g_free (src);
+ return TRUE;
+ }
+
+ dest = g_build_filename (data_dir, "imap", NULL);
+
+ /* we don't care if this fails, it's only a cache... */
+ cp_r (src, dest, "summary", CP_OVERWRITE);
+
+ g_free (dest);
+ g_free (src);
+
+ return TRUE;
+}
+
+static gboolean
+em_migrate_folder_expand_state_1_4 (const gchar *data_dir, GError **error)
+{
+ GString *srcpath, *destpath;
+ gsize slen, dlen, rlen;
+ gchar *evo14_mbox_root;
+ struct dirent *dent;
+ struct stat st;
+ DIR *dir;
+
+ srcpath = g_string_new (g_get_home_dir ());
+ g_string_append (srcpath, "/evolution/config");
+ if (stat (srcpath->str, &st) == -1 || !S_ISDIR (st.st_mode)) {
+ g_string_free (srcpath, TRUE);
+ return TRUE;
+ }
+
+ destpath = g_string_new (data_dir);
+ g_string_append (destpath, "/config");
+ if (g_mkdir_with_parents (destpath->str, 0777) == -1 || !(dir = opendir (srcpath->str))) {
+ g_string_free (destpath, TRUE);
+ g_string_free (srcpath, TRUE);
+ return TRUE;
+ }
+
+ g_string_append (srcpath, "/et-expanded-");
+ slen = srcpath->len;
+ g_string_append (destpath, "/et-expanded-");
+ dlen = destpath->len;
+
+ evo14_mbox_root = g_build_filename (g_get_home_dir (), "evolution", "local", NULL);
+ e_filename_make_safe (evo14_mbox_root);
+ rlen = strlen (evo14_mbox_root);
+ evo14_mbox_root = g_realloc (evo14_mbox_root, rlen + 2);
+ evo14_mbox_root[rlen++] = '_';
+ evo14_mbox_root[rlen] = '\0';
+
+ while ((dent = readdir (dir))) {
+ gchar *full_name, *inptr, *buf = NULL;
+ const gchar *filename;
+ GString *new;
+
+ if (strncmp (dent->d_name, "et-expanded-", 12) != 0)
+ continue;
+
+ if (!strncmp (dent->d_name + 12, "file:", 5)) {
+ /* need to munge the filename */
+ inptr = dent->d_name + 17;
+
+ if (!strncmp (inptr, evo14_mbox_root, rlen)) {
+ /* this should always be the case afaik... */
+ inptr += rlen;
+ new = g_string_new ("mbox:");
+ g_string_append_printf (new, "%s/local#", data_dir);
+
+ full_name = g_strdup (inptr);
+ inptr = full_name + strlen (full_name) - 12;
+ while (inptr > full_name) {
+ if (!strncmp (inptr, "_subfolders_", 12))
+ memmove (inptr, inptr + 11, strlen (inptr + 11) + 1);
+
+ inptr--;
+ }
+
+ g_string_append (new, full_name);
+ g_free (full_name);
+
+ filename = buf = new->str;
+ g_string_free (new, FALSE);
+ e_filename_make_safe (buf);
+ } else {
+ /* but just in case... */
+ filename = dent->d_name + 12;
+ }
+ } else {
+ /* no munging needed */
+ filename = dent->d_name + 12;
+ }
+
+ g_string_append (srcpath, dent->d_name + 12);
+ g_string_append (destpath, filename);
+ g_free (buf);
+
+ cp (srcpath->str, destpath->str, FALSE, CP_UNIQUE);
+
+ g_string_truncate (srcpath, slen);
+ g_string_truncate (destpath, dlen);
+ }
+
+ closedir (dir);
+
+ g_free (evo14_mbox_root);
+ g_string_free (destpath, TRUE);
+ g_string_free (srcpath, TRUE);
+
+ return TRUE;
+}
+
+static gboolean
+em_migrate_folder_view_settings_1_4 (const gchar *data_dir, GError **error)
+{
+ GString *srcpath, *destpath;
+ gsize slen, dlen, rlen;
+ gchar *evo14_mbox_root;
+ struct dirent *dent;
+ struct stat st;
+ DIR *dir;
+
+ srcpath = g_string_new (g_get_home_dir ());
+ g_string_append (srcpath, "/evolution/views/mail");
+ if (stat (srcpath->str, &st) == -1 || !S_ISDIR (st.st_mode)) {
+ g_string_free (srcpath, TRUE);
+ return TRUE;
+ }
+
+ destpath = g_string_new (data_dir);
+ g_string_append (destpath, "/views");
+ if (g_mkdir_with_parents (destpath->str, 0777) == -1 || !(dir = opendir (srcpath->str))) {
+ g_string_free (destpath, TRUE);
+ g_string_free (srcpath, TRUE);
+ return TRUE;
+ }
+
+ g_string_append_c (srcpath, '/');
+ slen = srcpath->len;
+ g_string_append_c (destpath, '/');
+ dlen = destpath->len;
+
+ evo14_mbox_root = g_build_filename (g_get_home_dir (), "evolution", "local", NULL);
+ e_filename_make_safe (evo14_mbox_root);
+ rlen = strlen (evo14_mbox_root);
+ evo14_mbox_root = g_realloc (evo14_mbox_root, rlen + 2);
+ evo14_mbox_root[rlen++] = '_';
+ evo14_mbox_root[rlen] = '\0';
+
+ while ((dent = readdir (dir))) {
+ gchar *full_name, *inptr, *buf = NULL;
+ const gchar *filename, *ext;
+ gsize prelen = 0;
+ GString *new;
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ if (!(ext = strrchr (dent->d_name, '.')))
+ continue;
+
+ if (!strcmp (ext, ".galview") || !strcmp ((gchar *)dent->d_name, "galview.xml")) {
+ /* just copy the file */
+ filename = dent->d_name;
+ goto copy;
+ } else if (strcmp (ext, ".xml") != 0) {
+ continue;
+ }
+
+ if (!strncmp ((const gchar *)dent->d_name, "current_view-", 13)) {
+ prelen = 13;
+ } else if (!strncmp ((const gchar *)dent->d_name, "custom_view-", 12)) {
+ prelen = 12;
+ } else {
+ /* huh? wtf is this file? */
+ continue;
+ }
+
+ if (!strncmp (dent->d_name + prelen, "file:", 5)) {
+ /* need to munge the filename */
+ inptr = dent->d_name + prelen + 5;
+
+ if (!strncmp (inptr, evo14_mbox_root, rlen)) {
+ /* this should always be the case afaik... */
+ inptr += rlen;
+ new = g_string_new ("mbox:");
+ g_string_append_printf (new, "%s/local#", data_dir);
+
+ full_name = g_strdup (inptr);
+ inptr = full_name + strlen (full_name) - 12;
+ while (inptr > full_name) {
+ if (!strncmp (inptr, "_subfolders_", 12))
+ memmove (inptr, inptr + 11, strlen (inptr + 11) + 1);
+
+ inptr--;
+ }
+
+ g_string_append (new, full_name);
+ g_free (full_name);
+
+ filename = buf = new->str;
+ g_string_free (new, FALSE);
+ e_filename_make_safe (buf);
+ } else {
+ /* but just in case... */
+ filename = dent->d_name + prelen;
+ }
+ } else {
+ /* no munging needed */
+ filename = dent->d_name + prelen;
+ }
+
+ copy:
+ g_string_append (srcpath, dent->d_name);
+ if (prelen > 0)
+ g_string_append_len (destpath, dent->d_name, prelen);
+ g_string_append (destpath, filename);
+ g_free (buf);
+
+ cp (srcpath->str, destpath->str, FALSE, CP_UNIQUE);
+
+ g_string_truncate (srcpath, slen);
+ g_string_truncate (destpath, dlen);
+ }
+
+ closedir (dir);
+
+ g_free (evo14_mbox_root);
+ g_string_free (destpath, TRUE);
+ g_string_free (srcpath, TRUE);
+
+ return TRUE;
+}
+
+#define SUBFOLDER_DIR_NAME "subfolders"
+#define SUBFOLDER_DIR_NAME_LEN 10
+
+static gchar *
+e_path_to_physical (const gchar *prefix, const gchar *vpath)
+{
+ const gchar *p, *newp;
+ gchar *dp;
+ gchar *ppath;
+ gint ppath_len;
+ gint prefix_len;
+
+ while (*vpath == '/')
+ vpath++;
+ if (!prefix)
+ prefix = "";
+
+ /* Calculate the length of the real path. */
+ ppath_len = strlen (vpath);
+ ppath_len++; /* For the ending zero. */
+
+ prefix_len = strlen (prefix);
+ ppath_len += prefix_len;
+ ppath_len++; /* For the separating slash. */
+
+ /* Take account of the fact that we need to translate every
+ * separator into `subfolders/'.
+ */
+ p = vpath;
+ while (1) {
+ newp = strchr (p, '/');
+ if (newp == NULL)
+ break;
+
+ ppath_len += SUBFOLDER_DIR_NAME_LEN;
+ ppath_len++; /* For the separating slash. */
+
+ /* Skip consecutive slashes. */
+ while (*newp == '/')
+ newp++;
+
+ p = newp;
+ };
+
+ ppath = g_malloc (ppath_len);
+ dp = ppath;
+
+ memcpy (dp, prefix, prefix_len);
+ dp += prefix_len;
+ *(dp++) = '/';
+
+ /* Copy the mangled path. */
+ p = vpath;
+ while (1) {
+ newp = strchr (p, '/');
+ if (newp == NULL) {
+ strcpy (dp, p);
+ break;
+ }
+
+ memcpy (dp, p, newp - p + 1); /* `+ 1' to copy the slash too. */
+ dp += newp - p + 1;
+
+ memcpy (dp, SUBFOLDER_DIR_NAME, SUBFOLDER_DIR_NAME_LEN);
+ dp += SUBFOLDER_DIR_NAME_LEN;
+
+ *(dp++) = '/';
+
+ /* Skip consecutive slashes. */
+ while (*newp == '/')
+ newp++;
+
+ p = newp;
+ }
+
+ return ppath;
+}
+
+static gboolean
+em_migrate_imap_cmeta_1_4(const gchar *data_dir, GError **error)
+{
+ GConfClient *gconf;
+ GSList *paths, *p;
+ EAccountList *accounts;
+ const EAccount *account;
+
+ if (!(accounts = e_get_account_list ()))
+ return TRUE;
+
+ gconf = gconf_client_get_default();
+ paths = gconf_client_get_list(gconf, "/apps/evolution/shell/offline/folder_paths", GCONF_VALUE_STRING, NULL);
+ for (p = paths;p;p = g_slist_next(p)) {
+ gchar *name, *path;
+
+ name = p->data;
+ if (*name)
+ name++;
+ path = strchr(name, '/');
+ if (path) {
+ *path++ = 0;
+ account = e_account_list_find(accounts, E_ACCOUNT_FIND_NAME, name);
+ if (account && !strncmp(account->source->url, "imap:", 5)) {
+ CamelURL *url = camel_url_new(account->source->url, NULL);
+
+ if (url) {
+ gchar *dir, *base;
+
+ base = g_strdup_printf("%s/imap/%s@%s/folders",
+ data_dir,
+ url->user?url->user:"",
+ url->host?url->host:"");
+
+ dir = e_path_to_physical(base, path);
+ if (g_mkdir_with_parents(dir, 0777) == 0) {
+ gchar *cmeta;
+ FILE *fp;
+
+ cmeta = g_build_filename(dir, "cmeta", NULL);
+ fp = fopen(cmeta, "w");
+ if (fp) {
+ /* header/version */
+ fwrite("CLMD", 4, 1, fp);
+ camel_file_util_encode_uint32(fp, 1);
+ /* meta count, do we have any metadata? */
+ camel_file_util_encode_uint32(fp, 0);
+ /* prop count */
+ camel_file_util_encode_uint32(fp, 1);
+ /* sync offline property */
+ camel_file_util_encode_uint32(fp, CAMEL_DISCO_FOLDER_OFFLINE_SYNC);
+ camel_file_util_encode_uint32(fp, 1);
+ fclose(fp);
+ } else {
+ g_warning("couldn't create imap folder cmeta file '%s'", cmeta);
+ }
+ g_free(cmeta);
+ } else {
+ g_warning("couldn't create imap folder directory '%s'", dir);
+ }
+ g_free(dir);
+ g_free(base);
+ camel_url_free(url);
+ }
+ } else
+ g_warning("can't find offline folder '%s' '%s'", name, path);
+ }
+ g_free(p->data);
+ }
+ g_slist_free(paths);
+ g_object_unref(gconf);
+
+ /* we couldn't care less if this doesn't work */
+
+ return TRUE;
+}
+
+static void
+remove_system_searches(xmlDocPtr searches)
+{
+ xmlNodePtr node;
+
+ /* in pre 2.0, system searches were stored in the user
+ * searches.xml file with the source set to 'demand'. In 2.0+
+ * the system searches are stored in the system
+ * searchtypes.xml file instead */
+
+ node = xmlDocGetRootElement(searches);
+ if (!node->name || strcmp((gchar *)node->name, "filteroptions"))
+ return;
+
+ if (!(node = xml_find_node(node, "ruleset")))
+ return;
+
+ node = node->children;
+ while (node != NULL) {
+ xmlNodePtr nnode = node->next;
+
+ if (node->name && !strcmp ((gchar *)node->name, "rule")) {
+ gchar *src;
+
+ src = (gchar *)xmlGetProp(node, (guchar *)"source");
+ if (src && !strcmp((gchar *)src, "demand")) {
+ xmlUnlinkNode(node);
+ xmlFreeNodeList(node);
+ }
+ xmlFree (src);
+ }
+
+ node = nnode;
+ }
+}
+
+static gboolean
+em_migrate_1_4 (const gchar *data_dir, xmlDocPtr filters, xmlDocPtr vfolders, GError **error)
+{
+ EMMigrateSession *session;
+ CamelException lex;
+ struct stat st;
+ gchar *path;
+ xmlDocPtr searches;
+
+ camel_init (data_dir, TRUE);
+ camel_provider_init();
+ session = (EMMigrateSession *) em_migrate_session_new (data_dir);
+
+ session->srcdir = g_build_filename (g_get_home_dir (), "evolution", "local", NULL);
+
+ path = g_strdup_printf ("mbox:%s/.evolution/mail/local", g_get_home_dir ());
+ if (stat (path + 5, &st) == -1) {
+ if (errno != ENOENT || g_mkdir_with_parents (path + 5, 0777) == -1) {
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Failed to create local mail storage "
+ "`%s': %s"), path + 5, g_strerror (errno));
+ g_free (session->srcdir);
+ camel_object_unref (session);
+ g_free (path);
+ return FALSE;
+ }
+ }
+
+ camel_exception_init (&lex);
+ if (!(session->store = camel_session_get_store ((CamelSession *) session, path, &lex))) {
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Failed to create local mail storage `%s': %s"),
+ path, lex.desc);
+ g_free (session->srcdir);
+ camel_object_unref (session);
+ camel_exception_clear (&lex);
+ g_free (path);
+ return FALSE;
+ }
+ g_free (path);
+
+ if (!em_migrate_local_folders_1_4 (session, error))
+ return FALSE;
+
+ camel_object_unref (session->store);
+ g_free (session->srcdir);
+
+ camel_object_unref (session);
+
+ em_upgrade_accounts_1_4();
+
+ upgrade_xml_uris(filters, upgrade_xml_uris_1_4);
+ upgrade_vfolder_sources_1_4(vfolders);
+ upgrade_xml_uris(vfolders, upgrade_xml_uris_1_4);
+
+ path = g_build_filename(g_get_home_dir(), "evolution", NULL);
+ searches = emm_load_xml(path, "searches.xml");
+ g_free(path);
+ if (searches) {
+ remove_system_searches(searches);
+ emm_save_xml(searches, data_dir, "searches.xml");
+ xmlFreeDoc(searches);
+ }
+
+ if (!em_migrate_pop_uid_caches_1_4 (data_dir, error))
+ return FALSE;
+
+ /* these are non-fatal */
+ em_migrate_imap_caches_1_4 (data_dir, error);
+ g_clear_error (error);
+ em_migrate_folder_expand_state_1_4 (data_dir, error);
+ g_clear_error (error);
+ em_migrate_folder_view_settings_1_4 (data_dir, error);
+ g_clear_error (error);
+ em_migrate_imap_cmeta_1_4 (data_dir, error);
+ g_clear_error (error);
+
+ return TRUE;
+}
+
+static void
+em_update_accounts_2_11 (void)
+{
+ EAccountList *accounts;
+ EIterator *iter;
+ gboolean changed = FALSE;
+
+ if (!(accounts = e_get_account_list ()))
+ return;
+
+ iter = e_list_get_iterator ((EList *) accounts);
+ while (e_iterator_is_valid (iter)) {
+ EAccount *account = (EAccount *) e_iterator_get (iter);
+
+ if (g_str_has_prefix (account->source->url, "spool://")) {
+ if (g_file_test (account->source->url + 8, G_FILE_TEST_IS_DIR)) {
+ gchar *str = g_strdup_printf ("spooldir://%s", account->source->url + 8);
+
+ g_free (account->source->url);
+ account->source->url = str;
+ changed = TRUE;
+ }
+ }
+
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+
+ if (changed)
+ e_account_list_save (accounts);
+}
+
+#endif /* !G_OS_WIN32 */
+
+static gboolean
+emm_setup_initial(const gchar *data_dir)
+{
+ GDir *dir;
+ const gchar *d;
+ gchar *local = NULL, *base;
+ const gchar * const *language_names;
+
+ /* special-case - this means brand new install of evolution */
+ /* FIXME: create default folders and stuff... */
+
+ d(printf("Setting up initial mail tree\n"));
+
+ base = g_build_filename(data_dir, "local", NULL);
+ if (g_mkdir_with_parents(base, 0777) == -1 && errno != EEXIST) {
+ g_free(base);
+ return FALSE;
+ }
+
+ /* e.g. try en-AU then en, etc */
+ language_names = g_get_language_names ();
+ while (*language_names != NULL) {
+ local = g_build_filename (
+ EVOLUTION_PRIVDATADIR, "default",
+ *language_names, "mail", "local", NULL);
+ if (g_file_test (local, G_FILE_TEST_EXISTS))
+ break;
+ g_free (local);
+ language_names++;
+ }
+
+ /* Make sure we found one. */
+ g_return_val_if_fail (*language_names != NULL, FALSE);
+
+ dir = g_dir_open(local, 0, NULL);
+ if (dir) {
+ while ((d = g_dir_read_name(dir))) {
+ gchar *src, *dest;
+
+ src = g_build_filename(local, d, NULL);
+ dest = g_build_filename(base, d, NULL);
+
+ cp(src, dest, FALSE, CP_UNIQUE);
+ g_free(dest);
+ g_free(src);
+ }
+ g_dir_close(dir);
+ }
+
+ g_free(local);
+ g_free(base);
+
+ return TRUE;
+}
+
+static gboolean
+is_in_plugs_list (GSList *list, const gchar *value)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ if (l->data && !strcmp (l->data, value))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * em_update_message_notify_settings_2_21
+ * DBus plugin and sound email notification was merged to mail-notification plugin,
+ * so move these options to new locations.
+ */
+static void
+em_update_message_notify_settings_2_21 (void)
+{
+ GConfClient *client;
+ GConfValue *is_key;
+ gboolean dbus, status;
+ GSList *list;
+ gchar *str;
+ gint val;
+
+ client = gconf_client_get_default ();
+
+ is_key = gconf_client_get (client, "/apps/evolution/eplugin/mail-notification/dbus-enabled", NULL);
+ if (is_key) {
+ /* already migrated, so do not migrate again */
+ gconf_value_free (is_key);
+ g_object_unref (client);
+
+ return;
+ }
+
+ gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/status-blink-icon",
+ gconf_client_get_bool (client, "/apps/evolution/mail/notification/blink-status-icon", NULL), NULL);
+ gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/status-notification",
+ gconf_client_get_bool (client, "/apps/evolution/mail/notification/notification", NULL), NULL);
+
+ list = gconf_client_get_list (client, "/apps/evolution/eplugin/disabled", GCONF_VALUE_STRING, NULL);
+ dbus = !is_in_plugs_list (list, "org.gnome.evolution.new_mail_notify");
+ status = !is_in_plugs_list (list, "org.gnome.evolution.mail_notification");
+
+ gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/dbus-enabled", dbus, NULL);
+ gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/status-enabled", status, NULL);
+
+ if (!status) {
+ /* enable this plugin, because it holds all those other things */
+ GSList *plugins, *l;
+
+ plugins = e_plugin_list_plugins ();
+
+ for (l = plugins; l; l = l->next) {
+ EPlugin *p = l->data;
+
+ if (p && p->id && !strcmp (p->id, "org.gnome.evolution.mail_notification")) {
+ e_plugin_enable (p, 1);
+ break;
+ }
+ }
+
+ g_slist_foreach (plugins, (GFunc)g_object_unref, NULL);
+ g_slist_free (plugins);
+ }
+
+ g_slist_foreach (list, (GFunc) g_free, NULL);
+ g_slist_free (list);
+
+ val = gconf_client_get_int (client, "/apps/evolution/mail/notify/type", NULL);
+ gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/sound-enabled", val == 1 || val == 2, NULL);
+ gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/sound-beep", val == 0 || val == 1, NULL);
+
+ str = gconf_client_get_string (client, "/apps/evolution/mail/notify/sound", NULL);
+ gconf_client_set_string (client, "/apps/evolution/eplugin/mail-notification/sound-file", str ? str : "", NULL);
+ g_free (str);
+
+ g_object_unref (client);
+}
+
+/* fixing typo in SpamAssassin name */
+static void
+em_update_sa_junk_setting_2_23 (void)
+{
+ GConfClient *client;
+ GConfValue *key;
+
+ client = gconf_client_get_default ();
+
+ key = gconf_client_get (client, "/apps/evolution/mail/junk/default_plugin", NULL);
+ if (key) {
+ const gchar *str = gconf_value_get_string (key);
+
+ if (str && strcmp (str, "Spamassasin") == 0)
+ gconf_client_set_string (client, "/apps/evolution/mail/junk/default_plugin", "SpamAssassin", NULL);
+
+ gconf_value_free (key);
+ g_object_unref (client);
+
+ return;
+ }
+
+ g_object_unref (client);
+}
+
+static gboolean
+update_progress_in_main_thread (double *progress)
+{
+ em_migrate_set_progress (*progress);
+ return FALSE;
+}
+
+static void
+migrate_folders(CamelStore *store, gboolean is_local, CamelFolderInfo *fi, const gchar *acc, CamelException *ex, gboolean *done, gint *nth_folder, gint total_folders)
+{
+ CamelFolder *folder;
+
+ while (fi) {
+ double progress;
+ gchar *tmp;
+
+ *nth_folder = *nth_folder + 1;
+
+ tmp = g_strdup_printf ("%s/%s", acc, fi->full_name);
+ em_migrate_set_folder_name (tmp);
+ g_free (tmp);
+
+ progress = (double) (*nth_folder) / total_folders;
+ g_idle_add ((GSourceFunc) update_progress_in_main_thread, &progress);
+
+ if (is_local)
+ folder = camel_store_get_folder (store, fi->full_name, CAMEL_STORE_IS_MIGRATING, ex);
+ else
+ folder = camel_store_get_folder (store, fi->full_name, 0, ex);
+
+ if (folder != NULL)
+ camel_folder_summary_migrate_infos (folder->summary);
+ migrate_folders(store, is_local, fi->child, acc, ex, done, nth_folder, total_folders);
+ fi = fi->next;
+ }
+
+ if ((*nth_folder) == (total_folders - 1))
+ *done = TRUE;
+}
+
+/* This could be in CamelStore.ch */
+static void
+count_folders (CamelFolderInfo *fi, gint *count)
+{
+ while (fi) {
+ *count = *count + 1;
+ count_folders (fi->child, count);
+ fi = fi->next;
+ }
+}
+
+static CamelStore *
+setup_local_store (EShellBackend *shell_backend,
+ EMMigrateSession *session)
+{
+ CamelURL *url;
+ const gchar *data_dir;
+ gchar *tmp;
+ CamelStore *store;
+
+ url = camel_url_new("mbox:", NULL);
+ data_dir = e_shell_backend_get_data_dir (shell_backend);
+ tmp = g_build_filename (data_dir, "local", NULL);
+ camel_url_set_path(url, tmp);
+ g_free(tmp);
+ tmp = camel_url_to_string(url, 0);
+ store = (CamelStore *)camel_session_get_service(CAMEL_SESSION (session), tmp, CAMEL_PROVIDER_STORE, NULL);
+ g_free(tmp);
+
+ return store;
+}
+
+struct migrate_folders_to_db_structure {
+ gchar *account_name;
+ CamelException ex;
+ CamelStore *store;
+ CamelFolderInfo *info;
+ gboolean done;
+ gboolean is_local_store;
+};
+
+static void
+migrate_folders_to_db_thread (struct migrate_folders_to_db_structure *migrate_dbs)
+{
+ gint num_of_folders = 0, nth_folder = 0;
+ count_folders (migrate_dbs->info, &num_of_folders);
+ migrate_folders (migrate_dbs->store, migrate_dbs->is_local_store, migrate_dbs->info,
+ migrate_dbs->account_name, &(migrate_dbs->ex), &(migrate_dbs->done),
+ &nth_folder, num_of_folders);
+}
+
+static void
+migrate_to_db (EShellBackend *shell_backend)
+{
+ EMMigrateSession *session;
+ EAccountList *accounts;
+ EIterator *iter;
+ gint i=0, len;
+ CamelStore *store = NULL;
+ CamelFolderInfo *info;
+ const gchar *data_dir;
+
+ if (!(accounts = e_get_account_list ()))
+ return;
+
+ iter = e_list_get_iterator ((EList *) accounts);
+ len = e_list_length ((EList *) accounts);
+
+ data_dir = e_shell_backend_get_data_dir (shell_backend);
+ session = (EMMigrateSession *) em_migrate_session_new (data_dir);
+ camel_session_set_online ((CamelSession *) session, FALSE);
+ em_migrate_setup_progress_dialog (
+ _("Migrating Folders"),
+ _("The summary format of the Evolution mailbox "
+ "folders has been moved to SQLite since Evolution 2.24.\n\nPlease be "
+ "patient while Evolution migrates your folders..."));
+
+ em_migrate_set_progress ( (double)i/(len+1));
+ store = setup_local_store (shell_backend, session);
+ info = camel_store_get_folder_info (store, NULL, CAMEL_STORE_FOLDER_INFO_RECURSIVE|CAMEL_STORE_FOLDER_INFO_FAST|CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, NULL);
+ if (info) {
+ GThread *thread;
+ struct migrate_folders_to_db_structure migrate_dbs;
+
+ if (g_str_has_suffix (((CamelService *)store)->url->path, ".evolution/mail/local"))
+ migrate_dbs.is_local_store = TRUE;
+ else
+ migrate_dbs.is_local_store = FALSE;
+ camel_exception_init (&migrate_dbs.ex);
+ migrate_dbs.account_name = _("On This Computer");
+ migrate_dbs.info = info;
+ migrate_dbs.store = store;
+ migrate_dbs.done = FALSE;
+
+ thread = g_thread_create ((GThreadFunc) migrate_folders_to_db_thread, &migrate_dbs, TRUE, NULL);
+ while (!migrate_dbs.done)
+ g_main_context_iteration (NULL, TRUE);
+ }
+ i++;
+ em_migrate_set_progress ( (double)i/(len+1));
+
+
+ while (e_iterator_is_valid (iter)) {
+ EAccount *account = (EAccount *) e_iterator_get (iter);
+ EAccountService *service;
+ const gchar *name;
+
+
+ service = account->source;
+ name = account->name;
+ em_migrate_set_progress ( (double)i/(len+1));
+ if (account->enabled
+ && service->url != NULL
+ && service->url[0]
+ && strncmp(service->url, "mbox:", 5) != 0) {
+
+ CamelException ex;
+
+ camel_exception_init (&ex);
+ e_mail_store_add_by_uri (service->url, name);
+
+ store = (CamelStore *) camel_session_get_service (CAMEL_SESSION (session), service->url, CAMEL_PROVIDER_STORE, &ex);
+ info = camel_store_get_folder_info (store, NULL, CAMEL_STORE_FOLDER_INFO_RECURSIVE|CAMEL_STORE_FOLDER_INFO_FAST|CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, &ex);
+ if (info) {
+ GThread *thread;
+ struct migrate_folders_to_db_structure migrate_dbs;
+
+ migrate_dbs.ex = ex;
+ migrate_dbs.account_name = account->name;
+ migrate_dbs.info = info;
+ migrate_dbs.store = store;
+ migrate_dbs.done = FALSE;
+
+ thread = g_thread_create ((GThreadFunc) migrate_folders_to_db_thread, &migrate_dbs, TRUE, NULL);
+ while (!migrate_dbs.done)
+ g_main_context_iteration (NULL, TRUE);
+ } else
+ printf("%s:%s: failed to get folder infos \n", G_STRLOC, G_STRFUNC);
+ camel_exception_clear(&ex);
+
+ }
+ i++;
+ e_iterator_next (iter);
+
+ }
+
+ //camel_session_set_online ((CamelSession *) session, TRUE);
+
+ g_object_unref (iter);
+ em_migrate_close_progress_dialog ();
+
+ g_object_unref (session);
+}
+
+gboolean
+e_mail_shell_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error)
+{
+ struct stat st;
+ const gchar *data_dir;
+ gchar *path;
+
+ /* make sure ~/.evolution/mail exists */
+ data_dir = e_shell_backend_get_data_dir (shell_backend);
+ if (g_stat (data_dir, &st) == -1) {
+ if (errno != ENOENT || g_mkdir_with_parents (data_dir, 0777) == -1) {
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Unable to create local mail folders at "
+ "`%s': %s"), data_dir, g_strerror (errno));
+ return FALSE;
+ }
+ }
+
+ if (major == 0)
+ return emm_setup_initial (data_dir);
+
+ if (major == 1 && minor < 5) {
+#ifndef G_OS_WIN32
+ xmlDocPtr config_xmldb = NULL, filters, vfolders;
+
+ path = g_build_filename (g_get_home_dir (), "evolution", NULL);
+ if (minor <= 2 && !(config_xmldb = emm_load_xml (path, "config.xmldb"))) {
+ g_set_error (
+ error, E_SHELL_MIGRATE_ERROR,
+ E_SHELL_MIGRATE_ERROR_FAILED,
+ _("Unable to read settings from previous "
+ "Evolution install, `evolution/config.xmldb' "
+ "does not exist or is corrupt."));
+ return FALSE;
+ }
+ filters = emm_load_xml (path, "filters.xml");
+ vfolders = emm_load_xml (path, "vfolders.xml");
+ g_free (path);
+
+ if (minor == 0) {
+ if (!em_migrate_1_0 (data_dir, config_xmldb, filters, vfolders, error)) {
+ xmlFreeDoc (config_xmldb);
+ xmlFreeDoc (filters);
+ xmlFreeDoc (vfolders);
+ return FALSE;
+ }
+ }
+
+ if (minor <= 2) {
+ if (!em_migrate_1_2 (data_dir, config_xmldb, filters, vfolders, error)) {
+ xmlFreeDoc (config_xmldb);
+ xmlFreeDoc (filters);
+ xmlFreeDoc (vfolders);
+ return FALSE;
+ }
+
+ xmlFreeDoc (config_xmldb);
+ }
+
+ if (minor <= 4) {
+ if (!em_migrate_1_4 (data_dir, filters, vfolders, error)) {
+ xmlFreeDoc (filters);
+ xmlFreeDoc (vfolders);
+ return FALSE;
+ }
+ }
+
+ if (filters) {
+ emm_save_xml (filters, path, "filters.xml");
+ xmlFreeDoc (filters);
+ }
+
+ if (vfolders) {
+ emm_save_xml (vfolders, path, "vfolders.xml");
+ xmlFreeDoc (vfolders);
+ }
+
+ g_free (path);
+#else
+ g_error ("Upgrading from ancient versions not supported on Windows");
+#endif
+ }
+
+ if (major < 2 || (major == 2 && minor < 12)) {
+#ifndef G_OS_WIN32
+ em_update_accounts_2_11 ();
+#else
+ g_error ("Upgrading from ancient versions not supported on Windows");
+#endif
+ }
+
+
+ if (major < 2 || (major == 2 && minor < 22))
+ em_update_message_notify_settings_2_21 ();
+
+ if (major < 2 || (major == 2 && minor < 24)) {
+ em_update_sa_junk_setting_2_23 ();
+ migrate_to_db (shell_backend);
+ }
+
+ return TRUE;
+}
diff --git a/modules/mail/e-mail-shell-migrate.h b/modules/mail/e-mail-shell-migrate.h
new file mode 100644
index 0000000000..8f3057ec0d
--- /dev/null
+++ b/modules/mail/e-mail-shell-migrate.h
@@ -0,0 +1,38 @@
+/*
+ * e-mail-shell-migrate.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SHELL_BACKEND_MIGRATE_H
+#define E_MAIL_SHELL_BACKEND_MIGRATE_H
+
+#include <glib.h>
+#include <shell/e-shell-backend.h>
+
+G_BEGIN_DECLS
+
+gboolean e_mail_shell_migrate (EShellBackend *shell_backend,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SHELL_BACKEND_MIGRATE_H */
diff --git a/modules/mail/e-mail-shell-settings.c b/modules/mail/e-mail-shell-settings.c
new file mode 100644
index 0000000000..8237924e3c
--- /dev/null
+++ b/modules/mail/e-mail-shell-settings.c
@@ -0,0 +1,521 @@
+/*
+ * e-mail-shell-settings.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-mail-shell-settings.h"
+
+#include <gconf/gconf-client.h>
+#include <libedataserver/e-account-list.h>
+
+#include "e-util/e-signature-list.h"
+#include "mail/e-mail-label-list-store.h"
+#include "mail/mail-session.h"
+
+void
+e_mail_shell_settings_init (EShell *shell)
+{
+ EShellSettings *shell_settings;
+ gpointer object;
+
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ /* XXX Default values should match the GConf schema.
+ * Yes it's redundant, but we're stuck with GConf. */
+
+ /*** Global Objects ***/
+
+ e_shell_settings_install_property (
+ g_param_spec_object (
+ "mail-label-list-store",
+ NULL,
+ NULL,
+ E_TYPE_MAIL_LABEL_LIST_STORE,
+ G_PARAM_READWRITE));
+
+ object = e_mail_label_list_store_new ();
+ e_shell_settings_set_object (
+ shell_settings, "mail-label-list-store", object);
+ g_object_unref (object);
+
+ e_shell_settings_install_property (
+ g_param_spec_pointer (
+ "mail-session",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ camel_object_ref (session);
+ e_shell_settings_set_pointer (
+ shell_settings, "mail-session", session);
+
+ /*** Mail Preferences ***/
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-address-compress",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-address-compress",
+ "/apps/evolution/mail/display/address_compress");
+
+ e_shell_settings_install_property (
+ g_param_spec_int (
+ "mail-address-count",
+ NULL,
+ NULL,
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-address-count",
+ "/apps/evolution/mail/display/address_count");
+
+ e_shell_settings_install_property (
+ g_param_spec_string (
+ "mail-charset-default",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-charset-default",
+ "/apps/evolution/mail/display/charset");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-check-for-junk",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-check-for-junk",
+ "/apps/evolution/mail/junk/check_incoming");
+
+ e_shell_settings_install_property (
+ g_param_spec_string (
+ "mail-citation-color",
+ NULL,
+ NULL,
+ "#737373",
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-citation-color",
+ "/apps/evolution/mail/display/citation_colour");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-confirm-expunge",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-confirm-expunge",
+ "/apps/evolution/mail/prompts/expunge");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-confirm-unwanted-html",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-confirm-unwanted-html",
+ "/apps/evolution/mail/prompts/unwanted_html");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-empty-trash-on-exit",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-empty-trash-on-exit",
+ "/apps/evolution/mail/trash/empty_on_exit");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-enable-search-folders",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-enable-search-folders",
+ "/apps/evolution/mail/display/enable_vfolders");
+
+ e_shell_settings_install_property (
+ g_param_spec_string (
+ "mail-font-monospace",
+ NULL,
+ NULL,
+ "",
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-font-monospace",
+ "/apps/evolution/mail/display/fonts/monospace");
+
+ e_shell_settings_install_property (
+ g_param_spec_string (
+ "mail-font-variable",
+ NULL,
+ NULL,
+ "",
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-font-variable",
+ "/apps/evolution/mail/display/fonts/variable");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-force-message-limit",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-force-message-limit",
+ "/apps/evolution/mail/display/force_message_limit");
+
+ /* This value corresponds to MailConfigForwardStyle enum. */
+ e_shell_settings_install_property (
+ g_param_spec_int (
+ "mail-forward-style",
+ NULL,
+ NULL,
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-forward-style",
+ "/apps/evolution/mail/format/forward_style");
+
+ /* This value corresponds to MailConfigHTTPMode enum. */
+ e_shell_settings_install_property (
+ g_param_spec_int (
+ "mail-image-loading-policy",
+ NULL,
+ NULL,
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-image-loading-policy",
+ "/apps/evolution/mail/display/load_http_images");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-magic-spacebar",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-magic-spacebar",
+ "/apps/evolution/mail/display/magic_spacebar");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-mark-citations",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-mark-citations",
+ "/apps/evolution/mail/display/mark_citations");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-mark-seen",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-mark-seen",
+ "/apps/evolution/mail/display/mark_seen");
+
+ e_shell_settings_install_property (
+ g_param_spec_int (
+ "mail-mark-seen-timeout",
+ NULL,
+ NULL,
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-mark-seen-timeout",
+ "/apps/evolution/mail/display/mark_seen_timeout");
+
+ e_shell_settings_install_property (
+ g_param_spec_int (
+ "mail-message-text-part-limit",
+ NULL,
+ NULL,
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-message-text-part-limit",
+ "/apps/evolution/mail/display/message_text_part_limit");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-only-local-photos",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-only-local-photos",
+ "/apps/evolution/mail/display/photo_local");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-prompt-delete-in-vfolder",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-prompt-delete-in-vfolder",
+ "/apps/evolution/mail/prompts/delete_in_vfolder");
+
+ /* This value corresponds to MailConfigReplyStyle enum,
+ * but the ordering of the combo box items in preferences
+ * has changed. We use transformation functions there. */
+ e_shell_settings_install_property (
+ g_param_spec_int (
+ "mail-reply-style",
+ NULL,
+ NULL,
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-reply-style",
+ "/apps/evolution/mail/format/reply_style");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-show-animated-images",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-show-animated-images",
+ "/apps/evolution/mail/display/animated_images");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-show-sender-photo",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-show-sender-photo",
+ "/apps/evolution/mail/display/sender_photo");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "mail-use-custom-fonts",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "mail-use-custom-fonts",
+ "/apps/evolution/mail/display/fonts/use_custom");
+
+
+ /*** Composer Preferences ***/
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-format-html",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-format-html",
+ "/apps/evolution/mail/composer/send_html");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-inline-spelling",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-inline-spelling",
+ "/apps/evolution/mail/composer/inline_spelling");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-magic-links",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-magic-links",
+ "/apps/evolution/mail/composer/magic_links");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-magic-smileys",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-magic-smileys",
+ "/apps/evolution/mail/composer/magic_smileys");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-outlook-filenames",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-outlook-filenames",
+ "/apps/evolution/mail/composer/outlook_filenames");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-prompt-only-bcc",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-prompt-only-bcc",
+ "/apps/evolution/mail/prompts/only_bcc");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-prompt-empty-subject",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-prompt-empty-subject",
+ "/apps/evolution/mail/prompts/empty_subject");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-reply-start-bottom",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-reply-start-bottom",
+ "/apps/evolution/mail/composer/reply_start_bottom");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-request-receipt",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-request-receipt",
+ "/apps/evolution/mail/composer/request_receipt");
+
+ e_shell_settings_install_property (
+ g_param_spec_string (
+ "composer-spell-color",
+ NULL,
+ NULL,
+ "#ff0000",
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-spell-color",
+ "/apps/evolution/mail/composer/spell_color");
+
+ e_shell_settings_install_property (
+ g_param_spec_boolean (
+ "composer-top-signature",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ e_shell_settings_bind_to_gconf (
+ shell_settings, "composer-top-signature",
+ "/apps/evolution/mail/composer/top_signature");
+}
diff --git a/modules/mail/e-mail-shell-settings.h b/modules/mail/e-mail-shell-settings.h
new file mode 100644
index 0000000000..4267fd8a60
--- /dev/null
+++ b/modules/mail/e-mail-shell-settings.h
@@ -0,0 +1,33 @@
+/*
+ * e-mail-shell-settings.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SHELL_SETTINGS_H
+#define E_MAIL_SHELL_SETTINGS_H
+
+#include <shell/e-shell.h>
+
+G_BEGIN_DECLS
+
+void e_mail_shell_settings_init (EShell *shell);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SHELL_SETTINGS_H */
diff --git a/modules/mail/e-mail-shell-sidebar.c b/modules/mail/e-mail-shell-sidebar.c
new file mode 100644
index 0000000000..2e5dffcb96
--- /dev/null
+++ b/modules/mail/e-mail-shell-sidebar.c
@@ -0,0 +1,644 @@
+/*
+ * e-mail-shell-sidebar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-mail-shell-sidebar.h"
+
+#include <string.h>
+#include <camel/camel.h>
+
+#include "em-utils.h"
+#include "em-folder-utils.h"
+
+#include "e-mail-local.h"
+#include "e-mail-store.h"
+
+#define E_MAIL_SHELL_SIDEBAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebarPrivate))
+
+#define STATE_KEY_EXPANDED "Expanded"
+
+struct _EMailShellSidebarPrivate {
+ GtkWidget *folder_tree;
+};
+
+enum {
+ PROP_0,
+ PROP_FOLDER_TREE
+};
+
+static gpointer parent_class;
+static GType mail_shell_sidebar_type;
+
+static void
+mail_shell_sidebar_restore_state (EMailShellSidebar *mail_shell_sidebar)
+{
+ EShellView *shell_view;
+ EShellSidebar *shell_sidebar;
+ EMFolderTree *folder_tree;
+ GtkTreeModel *tree_model;
+ GtkTreeView *tree_view;
+ GtkTreeIter iter;
+ GKeyFile *key_file;
+ gboolean valid;
+ gchar *selected;
+ gchar **groups;
+ gint ii;
+
+ shell_sidebar = E_SHELL_SIDEBAR (mail_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+
+ tree_view = GTK_TREE_VIEW (folder_tree);
+ tree_model = gtk_tree_view_get_model (tree_view);
+
+ /* Restore selected folder. */
+
+ selected = g_key_file_get_string (
+ key_file, "Folder Tree", "Selected", NULL);
+ if (selected != NULL) {
+ em_folder_tree_set_selected (folder_tree, selected, FALSE);
+ g_free (selected);
+ }
+
+ /* Set the initial folder tree expanded state in two stages:
+ *
+ * 1) Iterate over the "Store" and "Folder" state file groups
+ * and apply the "Expanded" keys where possible.
+ *
+ * 2) Iterate over the top-level nodes in the folder tree
+ * (these are all stores) and expand those that have no
+ * corresponding "Expanded" key in the state file. This
+ * ensures that new stores are expanded by default.
+ */
+
+ /* Stage 1 */
+
+ groups = g_key_file_get_groups (key_file, NULL);
+
+ for (ii = 0; groups[ii] != NULL; ii++) {
+ GtkTreeRowReference *reference;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ const gchar *group_name = groups[ii];
+ const gchar *key = STATE_KEY_EXPANDED;
+ const gchar *uri;
+ gboolean expanded;
+
+ if (g_str_has_prefix (group_name, "Store ")) {
+ uri = group_name + 6;
+ expanded = TRUE;
+ } else if (g_str_has_prefix (group_name, "Folder ")) {
+ uri = group_name + 7;
+ expanded = FALSE;
+ } else
+ continue;
+
+ if (g_key_file_has_key (key_file, group_name, key, NULL))
+ expanded = g_key_file_get_boolean (
+ key_file, group_name, key, NULL);
+
+ if (!expanded)
+ continue;
+
+ reference = em_folder_tree_model_lookup_uri (
+ EM_FOLDER_TREE_MODEL (tree_model), uri);
+ if (reference == NULL)
+ continue;
+
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (tree_model, &iter, path);
+ gtk_tree_view_expand_row (tree_view, path, FALSE);
+ gtk_tree_path_free (path);
+ }
+
+ g_strfreev (groups);
+
+ /* Stage 2 */
+
+ valid = gtk_tree_model_get_iter_first (tree_model, &iter);
+
+ while (valid) {
+ const gchar *key = STATE_KEY_EXPANDED;
+ gchar *group_name;
+ gchar *uri;
+
+ gtk_tree_model_get (
+ tree_model, &iter, COL_STRING_URI, &uri, -1);
+
+ if (uri == NULL)
+ goto next;
+
+ group_name = g_strdup_printf ("Store %s", uri);
+
+ if (!g_key_file_has_key (key_file, group_name, key, NULL)) {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (tree_model, &iter);
+ gtk_tree_view_expand_row (tree_view, path, FALSE);
+ gtk_tree_path_free (path);
+ }
+
+ g_free (group_name);
+ g_free (uri);
+
+ next:
+ valid = gtk_tree_model_iter_next (tree_model, &iter);
+ }
+}
+
+static void
+mail_shell_sidebar_row_collapsed_cb (EShellSidebar *shell_sidebar,
+ GtkTreeIter *iter,
+ GtkTreePath *path,
+ GtkTreeView *tree_view)
+{
+ EShellView *shell_view;
+ GtkTreeModel *model;
+ GKeyFile *key_file;
+ const gchar *key;
+ gboolean is_folder;
+ gboolean is_store;
+ gchar *group_name;
+ gchar *uri;
+
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ model = gtk_tree_view_get_model (tree_view);
+
+ gtk_tree_model_get (
+ model, iter,
+ COL_STRING_URI, &uri,
+ COL_BOOL_IS_STORE, &is_store,
+ COL_BOOL_IS_FOLDER, &is_folder, -1);
+
+ g_return_if_fail (is_store || is_folder);
+
+ key = STATE_KEY_EXPANDED;
+ if (is_store)
+ group_name = g_strdup_printf ("Store %s", uri);
+ else
+ group_name = g_strdup_printf ("Folder %s", uri);
+
+ g_key_file_set_boolean (key_file, group_name, key, FALSE);
+ e_shell_view_set_state_dirty (shell_view);
+
+ g_free (group_name);
+ g_free (uri);
+}
+
+static void
+mail_shell_sidebar_row_expanded_cb (EShellSidebar *shell_sidebar,
+ GtkTreeIter *unused,
+ GtkTreePath *path,
+ GtkTreeView *tree_view)
+{
+ EShellView *shell_view;
+ GtkTreeModel *model;
+ GKeyFile *key_file;
+ const gchar *key;
+ gboolean is_folder;
+ gboolean is_store;
+ gchar *group_name;
+ gchar *uri;
+
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ path = gtk_tree_path_copy (path);
+ model = gtk_tree_view_get_model (tree_view);
+
+ /* Expand the node and all ancestors. */
+ while (gtk_tree_path_get_depth (path) > 0) {
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_tree_model_get (
+ model, &iter,
+ COL_STRING_URI, &uri,
+ COL_BOOL_IS_STORE, &is_store,
+ COL_BOOL_IS_FOLDER, &is_folder, -1);
+
+ g_return_if_fail (is_store || is_folder);
+
+ key = STATE_KEY_EXPANDED;
+ if (is_store)
+ group_name = g_strdup_printf ("Store %s", uri);
+ else
+ group_name = g_strdup_printf ("Folder %s", uri);
+
+ g_key_file_set_boolean (key_file, group_name, key, TRUE);
+ e_shell_view_set_state_dirty (shell_view);
+
+ g_free (group_name);
+ g_free (uri);
+
+ gtk_tree_path_up (path);
+ }
+
+ gtk_tree_path_free (path);
+}
+
+static void
+mail_shell_sidebar_model_loaded_row_cb (EMailShellSidebar *mail_shell_sidebar,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ GtkTreeModel *model)
+{
+ EShellSidebar *shell_sidebar;
+ EShellView *shell_view;
+ GtkTreeView *tree_view;
+ GKeyFile *key_file;
+ gboolean is_folder;
+ gboolean is_store;
+ const gchar *key;
+ gchar *group_name;
+ gchar *uri;
+ gboolean expanded;
+
+ shell_sidebar = E_SHELL_SIDEBAR (mail_shell_sidebar);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ tree_view = GTK_TREE_VIEW (mail_shell_sidebar->priv->folder_tree);
+
+ gtk_tree_model_get (
+ model, iter,
+ COL_STRING_URI, &uri,
+ COL_BOOL_IS_STORE, &is_store,
+ COL_BOOL_IS_FOLDER, &is_folder, -1);
+
+ g_return_if_fail (is_store || is_folder);
+
+ key = STATE_KEY_EXPANDED;
+ if (is_store) {
+ group_name = g_strdup_printf ("Store %s", uri);
+ expanded = TRUE;
+ } else {
+ group_name = g_strdup_printf ("Folder %s", uri);
+ expanded = FALSE;
+ }
+
+ if (g_key_file_has_key (key_file, group_name, key, NULL))
+ expanded = g_key_file_get_boolean (
+ key_file, group_name, key, NULL);
+
+ if (expanded)
+ gtk_tree_view_expand_row (tree_view, path, FALSE);
+
+ g_free (group_name);
+ g_free (uri);
+}
+
+static void
+mail_shell_sidebar_selection_changed_cb (EShellSidebar *shell_sidebar,
+ GtkTreeSelection *selection)
+{
+ EShellView *shell_view;
+ EShellViewClass *shell_view_class;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GKeyFile *key_file;
+ const gchar *icon_name;
+ gchar *display_name = NULL;
+ gchar *uri = NULL;
+ gboolean is_folder = FALSE;
+ guint flags = 0;
+
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+ shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ gtk_tree_model_get (
+ model, &iter,
+ COL_STRING_DISPLAY_NAME, &display_name,
+ COL_STRING_URI, &uri,
+ COL_BOOL_IS_FOLDER, &is_folder,
+ COL_UINT_FLAGS, &flags, -1);
+
+ if (uri != NULL)
+ g_key_file_set_string (
+ key_file, "Folder Tree", "Selected", uri);
+ else
+ g_key_file_remove_key (
+ key_file, "Folder Tree", "Selected", NULL);
+
+ e_shell_view_set_state_dirty (shell_view);
+
+ if (is_folder)
+ icon_name = em_folder_utils_get_icon_name (flags);
+ else {
+ icon_name = shell_view_class->icon_name;
+ display_name = g_strdup (shell_view_class->label);
+ }
+
+ e_shell_sidebar_set_icon_name (shell_sidebar, icon_name);
+ e_shell_sidebar_set_primary_text (shell_sidebar, display_name);
+
+ g_free (display_name);
+ g_free (uri);
+}
+
+static void
+mail_shell_sidebar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_FOLDER_TREE:
+ g_value_set_object (
+ value, e_mail_shell_sidebar_get_folder_tree (
+ E_MAIL_SHELL_SIDEBAR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_shell_sidebar_dispose (GObject *object)
+{
+ EMailShellSidebarPrivate *priv;
+
+ priv = E_MAIL_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ if (priv->folder_tree != NULL) {
+ g_object_unref (priv->folder_tree);
+ priv->folder_tree = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+mail_shell_sidebar_finalize (GObject *object)
+{
+ EMailShellSidebarPrivate *priv;
+
+ priv = E_MAIL_SHELL_SIDEBAR_GET_PRIVATE (object);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+mail_shell_sidebar_constructed (GObject *object)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ EShellSidebar *shell_sidebar;
+ EShellView *shell_view;
+ GtkTreeSelection *selection;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkWidget *container;
+ GtkWidget *widget;
+
+ /* Chain up to parent's constructed method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ shell_sidebar = E_SHELL_SIDEBAR (object);
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+
+ mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (object);
+
+ /* Build sidebar widgets. */
+
+ container = GTK_WIDGET (object);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = em_folder_tree_new ();
+ em_folder_tree_set_excluded (EM_FOLDER_TREE (widget), 0);
+ em_folder_tree_enable_drag_and_drop (EM_FOLDER_TREE (widget));
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ mail_shell_sidebar->priv->folder_tree = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ tree_view = GTK_TREE_VIEW (mail_shell_sidebar->priv->folder_tree);
+ selection = gtk_tree_view_get_selection (tree_view);
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (em_folder_tree_model_get_selection (
+ EM_FOLDER_TREE_MODEL (model)) == NULL)
+ mail_shell_sidebar_restore_state (mail_shell_sidebar);
+
+ em_folder_tree_model_set_selection (
+ EM_FOLDER_TREE_MODEL (model), selection);
+
+ g_signal_connect_swapped (
+ tree_view, "row-collapsed",
+ G_CALLBACK (mail_shell_sidebar_row_collapsed_cb),
+ shell_sidebar);
+
+ g_signal_connect_swapped (
+ tree_view, "row-expanded",
+ G_CALLBACK (mail_shell_sidebar_row_expanded_cb),
+ shell_sidebar);
+
+ g_signal_connect_swapped (
+ model, "loaded-row",
+ G_CALLBACK (mail_shell_sidebar_model_loaded_row_cb),
+ shell_sidebar);
+
+ g_signal_connect_swapped (
+ selection, "changed",
+ G_CALLBACK (mail_shell_sidebar_selection_changed_cb),
+ shell_sidebar);
+}
+
+static guint32
+mail_shell_sidebar_check_state (EShellSidebar *shell_sidebar)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ EShellView *shell_view;
+ EMFolderTree *folder_tree;
+ GtkTreeSelection *selection;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ CamelFolder *folder;
+ CamelStore *local_store;
+ CamelStore *store;
+ gchar *full_name;
+ gchar *uri;
+ gboolean allows_children = TRUE;
+ gboolean can_delete = TRUE;
+ gboolean is_junk = FALSE;
+ gboolean is_outbox = FALSE;
+ gboolean is_store;
+ gboolean is_trash = FALSE;
+ guint32 folder_flags = 0;
+ guint32 state = 0;
+
+ shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+
+ local_store = e_mail_local_get_store ();
+
+ mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar);
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+ tree_view = GTK_TREE_VIEW (folder_tree);
+
+ selection = gtk_tree_view_get_selection (tree_view);
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return 0;
+
+ gtk_tree_model_get (
+ model, &iter,
+ COL_POINTER_CAMEL_STORE, &store,
+ COL_STRING_FULL_NAME, &full_name,
+ COL_BOOL_IS_STORE, &is_store,
+ COL_UINT_FLAGS, &folder_flags,
+ COL_STRING_URI, &uri, -1);
+
+ if (!is_store) {
+ is_junk = (strcmp (full_name, CAMEL_VJUNK_NAME) == 0);
+ is_trash = (strcmp (full_name, CAMEL_VTRASH_NAME) == 0);
+ allows_children = !(is_junk || is_trash);
+
+ /* Don't allow deletion of special local folders. */
+ if (store == local_store)
+ can_delete =
+ (strcmp (full_name, "Drafts") != 0) &&
+ (strcmp (full_name, "Inbox") != 0) &&
+ (strcmp (full_name, "Outbox") != 0) &&
+ (strcmp (full_name, "Sent") != 0) &&
+ (strcmp (full_name, "Templates") != 0);
+
+ folder = em_folder_tree_get_selected_folder (folder_tree);
+ is_outbox = em_utils_folder_is_outbox (folder, NULL);
+ can_delete &= !(folder_flags & CAMEL_FOLDER_SYSTEM);
+ }
+
+ if (allows_children)
+ state |= E_MAIL_SHELL_SIDEBAR_FOLDER_ALLOWS_CHILDREN;
+ if (can_delete)
+ state |= E_MAIL_SHELL_SIDEBAR_FOLDER_CAN_DELETE;
+ if (is_junk)
+ state |= E_MAIL_SHELL_SIDEBAR_FOLDER_IS_JUNK;
+ if (is_outbox)
+ state |= E_MAIL_SHELL_SIDEBAR_FOLDER_IS_OUTBOX;
+ if (is_store)
+ state |= E_MAIL_SHELL_SIDEBAR_FOLDER_IS_STORE;
+ if (is_trash)
+ state |= E_MAIL_SHELL_SIDEBAR_FOLDER_IS_TRASH;
+
+ return state;
+}
+
+static void
+mail_shell_sidebar_class_init (EMailShellSidebarClass *class)
+{
+ GObjectClass *object_class;
+ EShellSidebarClass *shell_sidebar_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMailShellSidebarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = mail_shell_sidebar_get_property;
+ object_class->dispose = mail_shell_sidebar_dispose;
+ object_class->finalize = mail_shell_sidebar_finalize;
+ object_class->constructed = mail_shell_sidebar_constructed;
+
+ shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class);
+ shell_sidebar_class->check_state = mail_shell_sidebar_check_state;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_FOLDER_TREE,
+ g_param_spec_object (
+ "folder-tree",
+ NULL,
+ NULL,
+ EM_TYPE_FOLDER_TREE,
+ G_PARAM_READABLE));
+}
+
+static void
+mail_shell_sidebar_init (EMailShellSidebar *mail_shell_sidebar)
+{
+ mail_shell_sidebar->priv =
+ E_MAIL_SHELL_SIDEBAR_GET_PRIVATE (mail_shell_sidebar);
+
+ /* Postpone widget construction until we have a shell view. */
+}
+
+GType
+e_mail_shell_sidebar_get_type (void)
+{
+ return mail_shell_sidebar_type;
+}
+
+void
+e_mail_shell_sidebar_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo type_info = {
+ sizeof (EMailShellSidebarClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) mail_shell_sidebar_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMailShellSidebar),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) mail_shell_sidebar_init,
+ NULL /* value_table */
+ };
+
+ mail_shell_sidebar_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_SIDEBAR,
+ "EMailShellSidebar", &type_info, 0);
+}
+
+GtkWidget *
+e_mail_shell_sidebar_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_MAIL_SHELL_SIDEBAR,
+ "shell-view", shell_view, NULL);
+}
+
+EMFolderTree *
+e_mail_shell_sidebar_get_folder_tree (EMailShellSidebar *mail_shell_sidebar)
+{
+ g_return_val_if_fail (
+ E_IS_MAIL_SHELL_SIDEBAR (mail_shell_sidebar), NULL);
+
+ return EM_FOLDER_TREE (mail_shell_sidebar->priv->folder_tree);
+}
diff --git a/modules/mail/e-mail-shell-sidebar.h b/modules/mail/e-mail-shell-sidebar.h
new file mode 100644
index 0000000000..10a2ff6a2a
--- /dev/null
+++ b/modules/mail/e-mail-shell-sidebar.h
@@ -0,0 +1,81 @@
+/*
+ * e-mail-shell-sidebar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SHELL_SIDEBAR_H
+#define E_MAIL_SHELL_SIDEBAR_H
+
+#include <shell/e-shell-sidebar.h>
+#include <shell/e-shell-view.h>
+#include <mail/em-folder-tree.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_SHELL_SIDEBAR \
+ (e_mail_shell_sidebar_get_type ())
+#define E_MAIL_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebar))
+#define E_MAIL_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebarClass))
+#define E_IS_MAIL_SHELL_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MAIL_SHELL_SIDEBAR))
+#define E_IS_MAIL_SHELL_SIDEBAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MAIL_SHELL_SIDEBAR))
+#define E_MAIL_SHELL_SIDEBAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailShellSidebar EMailShellSidebar;
+typedef struct _EMailShellSidebarClass EMailShellSidebarClass;
+typedef struct _EMailShellSidebarPrivate EMailShellSidebarPrivate;
+
+enum {
+ E_MAIL_SHELL_SIDEBAR_FOLDER_ALLOWS_CHILDREN = 1 << 0,
+ E_MAIL_SHELL_SIDEBAR_FOLDER_CAN_DELETE = 1 << 1,
+ E_MAIL_SHELL_SIDEBAR_FOLDER_IS_JUNK = 1 << 2,
+ E_MAIL_SHELL_SIDEBAR_FOLDER_IS_OUTBOX = 1 << 3,
+ E_MAIL_SHELL_SIDEBAR_FOLDER_IS_STORE = 1 << 4,
+ E_MAIL_SHELL_SIDEBAR_FOLDER_IS_TRASH = 1 << 5
+};
+
+struct _EMailShellSidebar {
+ EShellSidebar parent;
+ EMailShellSidebarPrivate *priv;
+};
+
+struct _EMailShellSidebarClass {
+ EShellSidebarClass parent_class;
+};
+
+GType e_mail_shell_sidebar_get_type (void);
+void e_mail_shell_sidebar_register_type
+ (GTypeModule *type_module);
+GtkWidget * e_mail_shell_sidebar_new(EShellView *shell_view);
+EMFolderTree * e_mail_shell_sidebar_get_folder_tree
+ (EMailShellSidebar *mail_shell_sidebar);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SHELL_SIDEBAR_H */
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
new file mode 100644
index 0000000000..25e7d80da3
--- /dev/null
+++ b/modules/mail/e-mail-shell-view-actions.c
@@ -0,0 +1,1806 @@
+/*
+ * e-mail-shell-view-actions.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-mail-shell-view-private.h"
+
+#define STATE_KEY_SEARCH_FILTER "SearchFilter"
+#define STATE_KEY_SEARCH_SCOPE "SearchScope"
+#define STATE_KEY_SEARCH_TEXT "SearchText"
+
+static void
+action_gal_save_custom_view_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellContent *mail_shell_content;
+ EShellView *shell_view;
+ GalViewInstance *view_instance;
+
+ /* All shell views repond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with saving the custom view. */
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ mail_shell_content = mail_shell_view->priv->mail_shell_content;
+ view_instance = e_mail_shell_content_get_view_instance (mail_shell_content);
+ gal_view_instance_save_as (view_instance);
+}
+
+static void
+action_mail_account_disable_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ EMFolderTree *folder_tree;
+ EAccountList *account_list;
+ EAccount *account;
+ gchar *folder_uri;
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+ folder_uri = em_folder_tree_get_selected_uri (folder_tree);
+ g_return_if_fail (folder_uri != NULL);
+
+ account_list = e_get_account_list ();
+ account = mail_config_get_account_by_source_url (folder_uri);
+ g_return_if_fail (account != NULL);
+
+ if (e_account_list_account_has_proxies (account_list, account))
+ e_account_list_remove_account_proxies (account_list, account);
+
+ account->enabled = !account->enabled;
+ e_account_list_change (account_list, account);
+ e_mail_store_remove_by_uri (folder_uri);
+
+ if (account->parent_uid != NULL)
+ e_account_list_remove (account_list, account);
+
+ e_account_list_save (account_list);
+
+ g_free (folder_uri);
+}
+
+static void
+action_mail_create_search_folder_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ /* FIXME */
+ g_print ("Action: %s\n", gtk_action_get_name (GTK_ACTION (action)));
+}
+
+static void
+action_mail_download_foreach_cb (CamelService *service)
+{
+ if (CAMEL_IS_DISCO_STORE (service) || CAMEL_IS_OFFLINE_STORE (service))
+ mail_store_prepare_offline (CAMEL_STORE (service));
+}
+
+static void
+action_mail_download_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ e_mail_store_foreach ((GHFunc) action_mail_download_foreach_cb, NULL);
+}
+
+static void
+action_mail_empty_trash_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ em_utils_empty_trash (GTK_WIDGET (shell_window));
+}
+
+static void
+action_mail_flush_outbox_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ mail_send ();
+}
+
+static void
+action_mail_folder_copy_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ CamelFolderInfo *folder_info;
+ EMFolderTree *folder_tree;
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+ folder_info = em_folder_tree_get_selected_folder_info (folder_tree);
+ g_return_if_fail (folder_info != NULL);
+
+ /* XXX Leaking folder_info? */
+ em_folder_utils_copy_folder (folder_info, FALSE);
+}
+
+static void
+action_mail_folder_delete_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ EMFolderTree *folder_tree;
+ CamelFolder *folder;
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+ folder = em_folder_tree_get_selected_folder (folder_tree);
+ g_return_if_fail (folder != NULL);
+
+ em_folder_utils_delete_folder (folder);
+}
+
+static void
+action_mail_folder_expunge_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailReader *reader;
+ MessageList *message_list;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ g_return_if_fail (message_list->folder != NULL);
+
+ em_utils_expunge_folder (
+ GTK_WIDGET (shell_window), message_list->folder);
+}
+
+static void
+action_mail_folder_mark_all_as_read_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailReader *reader;
+ MessageList *message_list;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ CamelFolder *folder;
+ GtkWindow *parent;
+ GPtrArray *uids;
+ const gchar *key;
+ const gchar *prompt;
+ guint ii;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ parent = GTK_WINDOW (shell_window);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder = message_list->folder;
+ g_return_if_fail (folder != NULL);
+
+ key = "/apps/evolution/mail/prompts/mark_all_read";
+ prompt = "mail:ask-mark-all-read";
+
+ if (!em_utils_prompt_user (parent, key, prompt, NULL))
+ return;
+
+ uids = message_list_get_uids (message_list);
+
+ camel_folder_freeze (folder);
+ for (ii = 0; ii < uids->len; ii++)
+ camel_folder_set_message_flags (
+ folder, uids->pdata[ii],
+ CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+ camel_folder_thaw (folder);
+
+ message_list_free_uids (message_list, uids);
+}
+
+static void
+action_mail_folder_move_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ CamelFolderInfo *folder_info;
+ EMFolderTree *folder_tree;
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+ folder_info = em_folder_tree_get_selected_folder_info (folder_tree);
+ g_return_if_fail (folder_info != NULL);
+
+ /* XXX Leaking folder_info? */
+ em_folder_utils_copy_folder (folder_info, TRUE);
+}
+
+static void
+action_mail_folder_new_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EMailShellSidebar *mail_shell_sidebar;
+ CamelFolderInfo *folder_info;
+ EMFolderTree *folder_tree;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+ folder_info = em_folder_tree_get_selected_folder_info (folder_tree);
+ g_return_if_fail (folder_info != NULL);
+
+ em_folder_utils_create_folder (
+ folder_info, folder_tree, GTK_WINDOW (shell_window));
+ camel_folder_info_free (folder_info);
+}
+
+static void
+action_mail_folder_properties_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ EMFolderTree *folder_tree;
+ EShellView *shell_view;
+ GtkTreeSelection *selection;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *uri;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+
+ tree_view = GTK_TREE_VIEW (folder_tree);
+ selection = gtk_tree_view_get_selection (tree_view);
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return;
+
+ gtk_tree_model_get (model, &iter, COL_STRING_URI, &uri, -1);
+ em_folder_properties_show (shell_view, NULL, uri);
+ g_free (uri);
+}
+
+static void
+action_mail_folder_refresh_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ EMFolderTree *folder_tree;
+ CamelFolder *folder;
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+ folder = em_folder_tree_get_selected_folder (folder_tree);
+ g_return_if_fail (folder != NULL);
+
+ mail_refresh_folder (folder, NULL, NULL);
+}
+
+static void
+action_mail_folder_rename_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellSidebar *mail_shell_sidebar;
+ EMFolderTree *folder_tree;
+ CamelFolder *folder;
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+ folder = em_folder_tree_get_selected_folder (folder_tree);
+ g_return_if_fail (folder != NULL);
+
+ em_folder_utils_rename_folder (folder);
+}
+
+/* Helper for action_mail_folder_select_all_cb() */
+static gboolean
+action_mail_folder_select_all_timeout_cb (MessageList *message_list)
+{
+ message_list_select_all (message_list);
+ gtk_widget_grab_focus (GTK_WIDGET (message_list));
+
+ return FALSE;
+}
+
+static void
+action_mail_folder_select_all_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailReader *reader;
+ MessageList *message_list;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ if (message_list->threaded) {
+ gtk_action_activate (ACTION (MAIL_THREADS_EXPAND_ALL));
+
+ /* XXX The timeout below is added so that the execution
+ * thread to expand all conversation threads would
+ * have completed. The timeout 505 is just to ensure
+ * that the value is a small delta more than the
+ * timeout value in mail_regen_list(). */
+ g_timeout_add (
+ 505, (GSourceFunc)
+ action_mail_folder_select_all_timeout_cb,
+ message_list);
+ } else
+ /* If there is no threading, just select all immediately. */
+ action_mail_folder_select_all_timeout_cb (message_list);
+}
+
+static void
+action_mail_folder_select_thread_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ MessageList *message_list;
+ EMailReader *reader;
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ message_list_select_thread (message_list);
+}
+
+static void
+action_mail_folder_select_subthread_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ MessageList *message_list;
+ EMailReader *reader;
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ message_list_select_subthread (message_list);
+}
+
+static void
+action_mail_hide_deleted_cb (GtkToggleAction *action,
+ EMailShellView *mail_shell_view)
+{
+ MessageList *message_list;
+ EMailReader *reader;
+ gboolean active;
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ active = gtk_toggle_action_get_active (action);
+ message_list_set_hidedeleted (message_list, active);
+}
+
+static void
+action_mail_hide_read_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ MessageList *message_list;
+ EMailReader *reader;
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ message_list_hide_add (
+ message_list,
+ "(match-all (system-flag \"seen\"))",
+ ML_HIDE_SAME, ML_HIDE_SAME);
+}
+
+static void
+action_mail_hide_selected_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ MessageList *message_list;
+ EMailReader *reader;
+ GPtrArray *uids;
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ uids = message_list_get_selected (message_list);
+ message_list_hide_uids (message_list, uids);
+ message_list_free_uids (message_list, uids);
+}
+
+static void
+action_mail_label_cb (GtkToggleAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailReader *reader;
+ MessageList *message_list;
+ CamelFolder *folder;
+ GPtrArray *uids;
+ const gchar *tag;
+ gint ii;
+
+ tag = g_object_get_data (G_OBJECT (action), "tag");
+ g_return_if_fail (tag != NULL);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder = message_list->folder;
+
+ uids = message_list_get_selected (message_list);
+
+ for (ii = 0; ii < uids->len; ii++) {
+ if (gtk_toggle_action_get_active (action))
+ camel_folder_set_message_user_flag (
+ folder, uids->pdata[ii], tag, TRUE);
+ else {
+ camel_folder_set_message_user_flag (
+ folder, uids->pdata[ii], tag, FALSE);
+ camel_folder_set_message_user_tag (
+ folder, uids->pdata[ii], "label", NULL);
+ }
+ }
+
+ message_list_free_uids (message_list, uids);
+}
+
+static void
+action_mail_label_new_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EShell *shell;
+ EShellSettings *shell_settings;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ EMailLabelDialog *label_dialog;
+ EMailLabelListStore *store;
+ EMailReader *reader;
+ MessageList *message_list;
+ CamelFolder *folder;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkWidget *dialog;
+ GPtrArray *uids;
+ GdkColor label_color;
+ const gchar *property_name;
+ const gchar *label_name;
+ gchar *label_tag;
+ gint n_children;
+ guint ii;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ dialog = e_mail_label_dialog_new (GTK_WINDOW (shell_window));
+
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Add Label"));
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
+ goto exit;
+
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ label_dialog = E_MAIL_LABEL_DIALOG (dialog);
+ label_name = e_mail_label_dialog_get_label_name (label_dialog);
+ e_mail_label_dialog_get_label_color (label_dialog, &label_color);
+
+ property_name = "mail-label-list-store";
+ store = e_shell_settings_get_object (shell_settings, property_name);
+ e_mail_label_list_store_set (store, NULL, label_name, &label_color);
+ g_object_unref (store);
+
+ /* XXX This is awkward. We've added a new label to the list store
+ * but we don't have the new label's tag nor an iterator to use
+ * to fetch it. We know the label was appended to the store,
+ * so we have to dig it out manually. EMailLabelListStore API
+ * probably needs some rethinking. */
+ model = GTK_TREE_MODEL (store);
+ n_children = gtk_tree_model_iter_n_children (model, NULL);
+ gtk_tree_model_iter_nth_child (model, &iter, NULL, n_children - 1);
+ label_tag = e_mail_label_list_store_get_tag (store, &iter);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder = message_list->folder;
+
+ uids = message_list_get_selected (message_list);
+
+ for (ii = 0; ii < uids->len; ii++)
+ camel_folder_set_message_user_flag (
+ folder, uids->pdata[ii], label_tag, TRUE);
+
+ message_list_free_uids (message_list, uids);
+
+ g_free (label_tag);
+
+exit:
+ gtk_widget_destroy (dialog);
+}
+
+static void
+action_mail_label_none_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellSettings *shell_settings;
+ EShellWindow *shell_window;
+ EMailReader *reader;
+ MessageList *message_list;
+ GtkTreeModel *tree_model;
+ CamelFolder *folder;
+ GtkTreeIter iter;
+ GPtrArray *uids;
+ gboolean valid;
+ guint ii;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ tree_model = e_shell_settings_get_object (
+ shell_settings, "mail-label-list-store");
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ uids = message_list_get_selected (message_list);
+ folder = message_list->folder;
+
+ valid = gtk_tree_model_get_iter_first (tree_model, &iter);
+
+ while (valid) {
+ gchar *tag;
+
+ tag = e_mail_label_list_store_get_tag (
+ E_MAIL_LABEL_LIST_STORE (tree_model), &iter);
+
+ for (ii = 0; ii < uids->len; ii++) {
+ camel_folder_set_message_user_flag (
+ folder, uids->pdata[ii], tag, FALSE);
+ camel_folder_set_message_user_tag (
+ folder, uids->pdata[ii], "label", NULL);
+ }
+
+ g_free (tag);
+
+ valid = gtk_tree_model_iter_next (tree_model, &iter);
+ }
+
+ message_list_free_uids (message_list, uids);
+}
+
+static void
+action_mail_search_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EMailShellView *mail_shell_view)
+{
+ EShellView *shell_view;
+ EShellContent *shell_content;
+ const gchar *search_hint;
+
+ /* XXX Figure out a way to handle this in EShellContent
+ * instead of every shell view having to handle it.
+ * The problem is EShellContent does not know what
+ * the search option actions are for this view. It
+ * would have to dig up the popup menu and retrieve
+ * the action for each menu item. Seems messy. */
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+
+ search_hint = gtk_action_get_label (GTK_ACTION (current));
+ e_shell_content_set_search_hint (shell_content, search_hint);
+}
+
+static void
+action_mail_show_hidden_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ MessageList *message_list;
+ EMailReader *reader;
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ message_list_hide_clear (message_list);
+}
+
+static void
+action_mail_smart_backward_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSettings *shell_settings;
+ EMailShellSidebar *mail_shell_sidebar;
+ EMFolderTree *folder_tree;
+ EMFormatHTMLDisplay *html_display;
+ EMailReader *reader;
+ MessageList *message_list;
+ GtkToggleAction *toggle_action;
+ GtkHTML *html;
+ gboolean caret_mode;
+ gboolean magic_spacebar;
+
+ /* This implements the so-called "Magic Backspace". */
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ html_display = e_mail_reader_get_html_display (reader);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ magic_spacebar = e_shell_settings_get_boolean (
+ shell_settings, "mail-magic-spacebar");
+
+ toggle_action = GTK_TOGGLE_ACTION (ACTION (MAIL_CARET_MODE));
+ caret_mode = gtk_toggle_action_get_active (toggle_action);
+
+ html = EM_FORMAT_HTML (html_display)->html;
+
+ if (gtk_html_command (html, "scroll-backward"))
+ return;
+
+ if (caret_mode || !magic_spacebar)
+ return;
+
+ /* XXX Are two separate calls really necessary? */
+
+ if (message_list_select (
+ message_list, MESSAGE_LIST_SELECT_PREVIOUS,
+ 0, CAMEL_MESSAGE_SEEN))
+ return;
+
+ if (message_list_select (
+ message_list, MESSAGE_LIST_SELECT_PREVIOUS |
+ MESSAGE_LIST_SELECT_WRAP, 0, CAMEL_MESSAGE_SEEN))
+ return;
+
+ em_folder_tree_select_prev_path (folder_tree, TRUE);
+
+ gtk_widget_grab_focus (GTK_WIDGET (message_list));
+}
+
+static void
+action_mail_smart_forward_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSettings *shell_settings;
+ EMailShellSidebar *mail_shell_sidebar;
+ EMFolderTree *folder_tree;
+ EMFormatHTMLDisplay *html_display;
+ EMailReader *reader;
+ MessageList *message_list;
+ GtkToggleAction *toggle_action;
+ GtkHTML *html;
+ gboolean caret_mode;
+ gboolean magic_spacebar;
+
+ /* This implements the so-called "Magic Spacebar". */
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ html_display = e_mail_reader_get_html_display (reader);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ magic_spacebar = e_shell_settings_get_boolean (
+ shell_settings, "mail-magic-spacebar");
+
+ toggle_action = GTK_TOGGLE_ACTION (ACTION (MAIL_CARET_MODE));
+ caret_mode = gtk_toggle_action_get_active (toggle_action);
+
+ html = EM_FORMAT_HTML (html_display)->html;
+
+ if (gtk_html_command (html, "scroll-forward"))
+ return;
+
+ if (caret_mode || !magic_spacebar)
+ return;
+
+ /* XXX Are two separate calls really necessary? */
+
+ if (message_list_select (
+ message_list, MESSAGE_LIST_SELECT_NEXT,
+ 0, CAMEL_MESSAGE_SEEN))
+ return;
+
+ if (message_list_select (
+ message_list, MESSAGE_LIST_SELECT_NEXT |
+ MESSAGE_LIST_SELECT_WRAP, 0, CAMEL_MESSAGE_SEEN))
+ return;
+
+ em_folder_tree_select_next_path (folder_tree, TRUE);
+
+ gtk_widget_grab_focus (GTK_WIDGET (message_list));
+}
+
+static void
+action_mail_stop_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ mail_cancel_all ();
+}
+
+static void
+action_mail_threads_collapse_all_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ MessageList *message_list;
+ EMailReader *reader;
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ message_list_set_threaded_collapse_all (message_list);
+}
+
+static void
+action_mail_threads_expand_all_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ MessageList *message_list;
+ EMailReader *reader;
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ message_list_set_threaded_expand_all (message_list);
+}
+
+static void
+action_mail_threads_group_by_cb (GtkToggleAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellContent *mail_shell_content;
+ MessageList *message_list;
+ EMailReader *reader;
+ gboolean active;
+
+ mail_shell_content = mail_shell_view->priv->mail_shell_content;
+ active = gtk_toggle_action_get_active (action);
+
+ reader = E_MAIL_READER (mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ message_list_set_threaded (message_list, active);
+}
+
+static void
+action_mail_tools_filters_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ em_utils_edit_filters (GTK_WIDGET (shell_window));
+}
+
+static void
+action_mail_tools_search_folders_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ vfolder_edit (E_SHELL_VIEW (mail_shell_view));
+}
+
+static void
+action_mail_tools_subscriptions_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ GtkWidget *dialog;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ dialog = em_subscribe_editor_new ();
+ gtk_window_set_transient_for (
+ GTK_WINDOW (dialog), GTK_WINDOW (shell_window));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ /* XXX Dialog destroys itself. */
+}
+
+static void
+action_mail_view_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EMailShellView *mail_shell_view)
+{
+ EMailShellContent *mail_shell_content;
+ gboolean vertical_view;
+
+ mail_shell_content = mail_shell_view->priv->mail_shell_content;
+ vertical_view = (gtk_radio_action_get_current_value (action) == 1);
+
+ e_mail_shell_content_set_vertical_view (
+ mail_shell_content, vertical_view);
+}
+
+static void
+action_search_execute_cb (GtkAction *action,
+ EMailShellView *mail_shell_view)
+{
+ EShellView *shell_view;
+ EShellContent *shell_content;
+ EMailReader *reader;
+ MessageList *message_list;
+ GKeyFile *key_file;
+ const gchar *folder_uri;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ /* All shell views respond to the activation of this action,
+ * which is defined by EShellWindow. But only the currently
+ * active shell view proceeds with executing the search. */
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder_uri = message_list->folder_uri;
+
+ if (folder_uri != NULL) {
+ const gchar *key;
+ const gchar *string;
+ gchar *group_name;
+
+ key = STATE_KEY_SEARCH_TEXT;
+ string = e_shell_content_get_search_text (shell_content);
+ group_name = g_strdup_printf ("Folder %s", folder_uri);
+
+ if (string != NULL && *string != '\0')
+ g_key_file_set_string (
+ key_file, group_name, key, string);
+ else
+ g_key_file_remove_key (
+ key_file, group_name, key, NULL);
+ e_shell_view_set_state_dirty (shell_view);
+
+ g_free (group_name);
+ }
+
+ e_mail_shell_view_execute_search (mail_shell_view);
+}
+
+static void
+action_search_filter_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EMailShellView *mail_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EMailReader *reader;
+ MessageList *message_list;
+ GKeyFile *key_file;
+ const gchar *folder_uri;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder_uri = message_list->folder_uri;
+
+ if (folder_uri != NULL) {
+ const gchar *key;
+ const gchar *string;
+ gchar *group_name;
+
+ key = STATE_KEY_SEARCH_FILTER;
+ string = gtk_action_get_name (GTK_ACTION (current));
+ group_name = g_strdup_printf ("Folder %s", folder_uri);
+
+ g_key_file_set_string (key_file, group_name, key, string);
+ e_shell_view_set_state_dirty (shell_view);
+
+ g_free (group_name);
+ }
+
+ gtk_action_activate (ACTION (SEARCH_EXECUTE));
+}
+
+static void
+action_search_scope_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EMailShellView *mail_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EMailReader *reader;
+ MessageList *message_list;
+ GKeyFile *key_file;
+ const gchar *folder_uri;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ key_file = e_shell_view_get_state_key_file (shell_view);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder_uri = message_list->folder_uri;
+
+ if (folder_uri != NULL) {
+ const gchar *key;
+ const gchar *string;
+ gchar *group_name;
+
+ key = STATE_KEY_SEARCH_SCOPE;
+ string = gtk_action_get_name (GTK_ACTION (current));
+ group_name = g_strdup_printf ("Folder %s", folder_uri);
+
+ g_key_file_set_string (key_file, group_name, key, string);
+ e_shell_view_set_state_dirty (shell_view);
+
+ g_free (group_name);
+ }
+
+ gtk_action_activate (ACTION (SEARCH_EXECUTE));
+}
+
+static GtkActionEntry mail_entries[] = {
+
+ { "mail-account-disable",
+ NULL,
+ N_("_Disable Account"),
+ NULL,
+ N_("Disable this account"),
+ G_CALLBACK (action_mail_account_disable_cb) },
+
+ { "mail-create-search-folder",
+ NULL,
+ N_("C_reate Search Folder From Search..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_mail_create_search_folder_cb) },
+
+ { "mail-download",
+ NULL,
+ N_("_Download Messages for Offline Usage"),
+ NULL,
+ N_("Download messages of accounts and folders marked for offline"),
+ G_CALLBACK (action_mail_download_cb) },
+
+ { "mail-empty-trash",
+ NULL,
+ N_("Empty _Trash"),
+ NULL,
+ N_("Permanently remove all the deleted messages from all folders"),
+ G_CALLBACK (action_mail_empty_trash_cb) },
+
+ { "mail-flush-outbox",
+ "mail-send",
+ N_("Fl_ush Outbox"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_mail_flush_outbox_cb) },
+
+ { "mail-folder-copy",
+ "folder-copy",
+ N_("_Copy Folder To..."),
+ NULL,
+ N_("Copy the selected folder into another folder"),
+ G_CALLBACK (action_mail_folder_copy_cb) },
+
+ { "mail-folder-delete",
+ GTK_STOCK_DELETE,
+ NULL,
+ NULL,
+ N_("Permanently remove this folder"),
+ G_CALLBACK (action_mail_folder_delete_cb) },
+
+ { "mail-folder-expunge",
+ NULL,
+ N_("E_xpunge"),
+ "<Control>e",
+ N_("Permanently remove all deleted messages from this folder"),
+ G_CALLBACK (action_mail_folder_expunge_cb) },
+
+ { "mail-folder-mark-all-as-read",
+ "mail-read",
+ N_("Mar_k All Messages as Read"),
+ NULL,
+ N_("Mark all messages in the folder as read"),
+ G_CALLBACK (action_mail_folder_mark_all_as_read_cb) },
+
+ { "mail-folder-move",
+ "folder-move",
+ N_("_Move Folder To..."),
+ NULL,
+ N_("Move the selected folder into another folder"),
+ G_CALLBACK (action_mail_folder_move_cb) },
+
+ { "mail-folder-new",
+ "folder-new",
+ N_("_New..."),
+ NULL,
+ N_("Create a new folder for storing mail"),
+ G_CALLBACK (action_mail_folder_new_cb) },
+
+ { "mail-folder-properties",
+ GTK_STOCK_PROPERTIES,
+ NULL,
+ NULL,
+ N_("Change the properties of this folder"),
+ G_CALLBACK (action_mail_folder_properties_cb) },
+
+ { "mail-folder-refresh",
+ GTK_STOCK_REFRESH,
+ NULL,
+ "F5",
+ N_("Refresh the folder"),
+ G_CALLBACK (action_mail_folder_refresh_cb) },
+
+ { "mail-folder-rename",
+ NULL,
+ N_("_Rename..."),
+ "F2",
+ N_("Change the name of this folder"),
+ G_CALLBACK (action_mail_folder_rename_cb) },
+
+ { "mail-folder-select-all",
+ NULL,
+ N_("Select _All Messages"),
+ "<Control>a",
+ N_("Select all visible messages"),
+ G_CALLBACK (action_mail_folder_select_all_cb) },
+
+ { "mail-folder-select-thread",
+ NULL,
+ N_("Select Message _Thread"),
+ "<Control>h",
+ N_("Select all messages in the same thread as the selected message"),
+ G_CALLBACK (action_mail_folder_select_thread_cb) },
+
+ { "mail-folder-select-subthread",
+ NULL,
+ N_("Select Message S_ubthread"),
+ "<Shift><Control>h",
+ N_("Select all replies to the currently selected message"),
+ G_CALLBACK (action_mail_folder_select_subthread_cb) },
+
+ { "mail-label-new",
+ NULL,
+ N_("_New Label"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_mail_label_new_cb) },
+
+ { "mail-label-none",
+ NULL,
+ N_("N_one"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_mail_label_none_cb) },
+
+ { "mail-hide-read",
+ NULL,
+ N_("Hide _Read Messages"),
+ NULL,
+ N_("Temporarily hide all messages that have already been read"),
+ G_CALLBACK (action_mail_hide_read_cb) },
+
+ { "mail-hide-selected",
+ NULL,
+ N_("Hide S_elected Messages"),
+ NULL,
+ N_("Temporarily hide the selected messages"),
+ G_CALLBACK (action_mail_hide_selected_cb) },
+
+ { "mail-show-hidden",
+ NULL,
+ N_("Show Hidde_n Messages"),
+ NULL,
+ N_("Show messages that have been temporarily hidden"),
+ G_CALLBACK (action_mail_show_hidden_cb) },
+
+ { "mail-smart-backward",
+ NULL,
+ NULL, /* No menu item; key press only */
+ NULL,
+ NULL,
+ G_CALLBACK (action_mail_smart_backward_cb) },
+
+ { "mail-smart-forward",
+ NULL,
+ NULL, /* No menu item; key press only */
+ NULL,
+ NULL,
+ G_CALLBACK (action_mail_smart_forward_cb) },
+
+ { "mail-stop",
+ GTK_STOCK_STOP,
+ N_("Cancel"),
+ NULL,
+ N_("Cancel the current mail operation"),
+ G_CALLBACK (action_mail_stop_cb) },
+
+ { "mail-threads-collapse-all",
+ NULL,
+ N_("Collapse All _Threads"),
+ "<Shift><Control>b",
+ N_("Collapse all message threads"),
+ G_CALLBACK (action_mail_threads_collapse_all_cb) },
+
+ { "mail-threads-expand-all",
+ NULL,
+ N_("E_xpand All Threads"),
+ NULL,
+ N_("Expand all message threads"),
+ G_CALLBACK (action_mail_threads_expand_all_cb) },
+
+ { "mail-tools-filters",
+ NULL,
+ N_("_Message Filters"),
+ NULL,
+ N_("Create or edit rules for filtering new mail"),
+ G_CALLBACK (action_mail_tools_filters_cb) },
+
+ { "mail-tools-search-folders",
+ NULL,
+ N_("Search F_olders"),
+ NULL,
+ N_("Create or edit search folder definitions"),
+ G_CALLBACK (action_mail_tools_search_folders_cb) },
+
+ { "mail-tools-subscriptions",
+ NULL,
+ N_("_Subscriptions..."),
+ NULL,
+ N_("Subscribe or unsubscribe to folders on remote servers"),
+ G_CALLBACK (action_mail_tools_subscriptions_cb) },
+
+ /*** Menus ***/
+
+ { "mail-folder-menu",
+ NULL,
+ N_("F_older"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "mail-label-menu",
+ NULL,
+ N_("_Label"),
+ NULL,
+ NULL,
+ NULL },
+
+ { "mail-preview-menu",
+ NULL,
+ N_("_Preview"),
+ NULL,
+ NULL,
+ NULL }
+};
+
+static EPopupActionEntry mail_popup_entries[] = {
+
+ { "mail-popup-account-disable",
+ NULL,
+ "mail-account-disable" },
+
+ { "mail-popup-empty-trash",
+ NULL,
+ "mail-empty-trash" },
+
+ { "mail-popup-flush-outbox",
+ NULL,
+ "mail-flush-outbox" },
+
+ { "mail-popup-folder-copy",
+ NULL,
+ "mail-folder-copy" },
+
+ { "mail-popup-folder-delete",
+ NULL,
+ "mail-folder-delete" },
+
+ { "mail-popup-folder-move",
+ NULL,
+ "mail-folder-move" },
+
+ { "mail-popup-folder-new",
+ N_("_New Folder..."),
+ "mail-folder-new" },
+
+ { "mail-popup-folder-properties",
+ NULL,
+ "mail-folder-properties" },
+
+ { "mail-popup-folder-refresh",
+ NULL,
+ "mail-folder-refresh" },
+
+ { "mail-popup-folder-rename",
+ NULL,
+ "mail-folder-rename" }
+};
+
+static GtkToggleActionEntry mail_toggle_entries[] = {
+
+ { "mail-hide-deleted",
+ NULL,
+ N_("Hide _Deleted Messages"),
+ NULL,
+ N_("Hide deleted messages rather than displaying "
+ "them with a line through them"),
+ G_CALLBACK (action_mail_hide_deleted_cb),
+ TRUE },
+
+ { "mail-preview",
+ NULL,
+ N_("Show Message _Preview"),
+ "<Control>m",
+ N_("Show message preview pane"),
+ NULL, /* Handled by property bindings */
+ TRUE },
+
+ { "mail-threads-group-by",
+ NULL,
+ N_("_Group By Threads"),
+ "<Control>t",
+ N_("Threaded message list"),
+ G_CALLBACK (action_mail_threads_group_by_cb),
+ FALSE }
+};
+
+static GtkRadioActionEntry mail_view_entries[] = {
+
+ /* This action represents the initial active mail view.
+ * It should not be visible in the UI, nor should it be
+ * possible to switch to it from another shell view. */
+ { "mail-view-internal",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ -1 },
+
+ { "mail-view-classic",
+ NULL,
+ N_("_Classic View"),
+ NULL,
+ N_("Show message preview below the message list"),
+ 0 },
+
+ { "mail-view-vertical",
+ NULL,
+ N_("_Vertical View"),
+ NULL,
+ N_("Show message preview alongside the message list"),
+ 1 }
+};
+
+static GtkRadioActionEntry mail_filter_entries[] = {
+
+ { "mail-filter-all-messages",
+ NULL,
+ N_("All Messages"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_FILTER_ALL_MESSAGES },
+
+ { "mail-filter-important-messages",
+ "emblem-important",
+ N_("Important Messages"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_FILTER_IMPORTANT_MESSAGES },
+
+ { "mail-filter-last-5-days-messages",
+ NULL,
+ N_("Last 5 Days' Messages"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_FILTER_LAST_5_DAYS_MESSAGES },
+
+ { "mail-filter-messages-not-junk",
+ "mail-mark-notjunk",
+ N_("Messages Not Junk"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_FILTER_MESSAGES_NOT_JUNK },
+
+ { "mail-filter-messages-with-attachments",
+ "mail-attachment",
+ N_("Messages with Attachments"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS },
+
+ { "mail-filter-no-label",
+ NULL,
+ N_("No Label"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_FILTER_NO_LABEL },
+
+ { "mail-filter-read-messages",
+ "mail-read",
+ N_("Read Messages"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_FILTER_READ_MESSAGES },
+
+ { "mail-filter-recent-messages",
+ NULL,
+ N_("Recent Messages"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_FILTER_RECENT_MESSAGES },
+
+ { "mail-filter-unread-messages",
+ "mail-unread",
+ N_("Unread Messages"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_FILTER_UNREAD_MESSAGES }
+};
+
+static GtkRadioActionEntry mail_search_entries[] = {
+
+ { "mail-search-body-contains",
+ NULL,
+ N_("Body contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SEARCH_BODY_CONTAINS },
+
+ { "mail-search-message-contains",
+ NULL,
+ N_("Message contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SEARCH_MESSAGE_CONTAINS },
+
+ { "mail-search-recipients-contain",
+ NULL,
+ N_("Recipients contain"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SEARCH_RECIPIENTS_CONTAIN },
+
+ { "mail-search-sender-contains",
+ NULL,
+ N_("Sender contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SEARCH_SENDER_CONTAINS },
+
+ { "mail-search-subject-contains",
+ NULL,
+ N_("Subject contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SEARCH_SUBJECT_CONTAINS },
+
+ { "mail-search-subject-or-recipients-contains",
+ NULL,
+ N_("Subject or Recipients contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SEARCH_SUBJECT_OR_RECIPIENTS_CONTAINS },
+
+ { "mail-search-subject-or-sender-contains",
+ NULL,
+ N_("Subject or Sender contains"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS }
+};
+
+static GtkRadioActionEntry mail_scope_entries[] = {
+
+ { "mail-scope-all-accounts",
+ NULL,
+ N_("All Accounts"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SCOPE_ALL_ACCOUNTS },
+
+ { "mail-scope-current-account",
+ NULL,
+ N_("Current Account"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SCOPE_CURRENT_ACCOUNT },
+
+ { "mail-scope-current-folder",
+ NULL,
+ N_("Current Folder"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SCOPE_CURRENT_FOLDER }
+};
+
+void
+e_mail_shell_view_actions_init (EMailShellView *mail_shell_view)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellContent *shell_content;
+ GtkActionGroup *action_group;
+ GtkRadioAction *radio_action;
+ GConfBridge *bridge;
+ GObject *object;
+ GObject *src_object;
+ GObject *dst_object;
+ const gchar *key;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+
+ /* Mail Actions */
+ action_group = ACTION_GROUP (MAIL);
+ gtk_action_group_add_actions (
+ action_group, mail_entries,
+ G_N_ELEMENTS (mail_entries), mail_shell_view);
+ e_action_group_add_popup_actions (
+ action_group, mail_popup_entries,
+ G_N_ELEMENTS (mail_popup_entries));
+ gtk_action_group_add_toggle_actions (
+ action_group, mail_toggle_entries,
+ G_N_ELEMENTS (mail_toggle_entries), mail_shell_view);
+ gtk_action_group_add_radio_actions (
+ action_group, mail_view_entries,
+ G_N_ELEMENTS (mail_view_entries), -1,
+ G_CALLBACK (action_mail_view_cb), mail_shell_view);
+ gtk_action_group_add_radio_actions (
+ action_group, mail_search_entries,
+ G_N_ELEMENTS (mail_search_entries),
+ MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS,
+ G_CALLBACK (action_mail_search_cb), mail_shell_view);
+ gtk_action_group_add_radio_actions (
+ action_group, mail_scope_entries,
+ G_N_ELEMENTS (mail_scope_entries),
+ MAIL_SCOPE_CURRENT_FOLDER,
+ G_CALLBACK (action_search_scope_cb), mail_shell_view);
+
+ radio_action = GTK_RADIO_ACTION (ACTION (MAIL_SCOPE_ALL_ACCOUNTS));
+ e_shell_content_set_scope_action (shell_content, radio_action);
+ e_shell_content_set_scope_visible (shell_content, TRUE);
+
+ /* Bind GObject properties for GConf keys. */
+
+ bridge = gconf_bridge_get ();
+
+ object = G_OBJECT (ACTION (MAIL_PREVIEW));
+ key = "/apps/evolution/mail/display/show_preview";
+ gconf_bridge_bind_property (bridge, key, object, "active");
+
+ object = G_OBJECT (ACTION (MAIL_THREADS_GROUP_BY));
+ key = "/apps/evolution/mail/display/thread_list";
+ gconf_bridge_bind_property (bridge, key, object, "active");
+
+ object = G_OBJECT (ACTION (MAIL_VIEW_VERTICAL));
+ key = "/apps/evolution/mail/display/layout";
+ gconf_bridge_bind_property (bridge, key, object, "current-value");
+
+ /* Fine tuning. */
+
+ src_object = G_OBJECT (ACTION (MAIL_THREADS_GROUP_BY));
+
+ dst_object = G_OBJECT (ACTION (MAIL_FOLDER_SELECT_THREAD));
+ e_binding_new (src_object, "active", dst_object, "sensitive");
+
+ dst_object = G_OBJECT (ACTION (MAIL_FOLDER_SELECT_SUBTHREAD));
+ e_binding_new (src_object, "active", dst_object, "sensitive");
+
+ dst_object = G_OBJECT (ACTION (MAIL_THREADS_COLLAPSE_ALL));
+ e_binding_new (src_object, "active", dst_object, "sensitive");
+
+ dst_object = G_OBJECT (ACTION (MAIL_THREADS_EXPAND_ALL));
+ e_binding_new (src_object, "active", dst_object, "sensitive");
+
+ e_mutual_binding_new (
+ G_OBJECT (ACTION (MAIL_PREVIEW)), "active",
+ G_OBJECT (shell_content), "preview-visible");
+
+ /* XXX The boolean sense of the GConf key is the inverse of
+ * the menu item, so we have to maintain two properties. */
+ e_mutual_binding_new_with_negation (
+ G_OBJECT (shell_content), "show-deleted",
+ G_OBJECT (ACTION (MAIL_HIDE_DELETED)), "active");
+
+ g_signal_connect (
+ ACTION (GAL_SAVE_CUSTOM_VIEW), "activate",
+ G_CALLBACK (action_gal_save_custom_view_cb), mail_shell_view);
+
+ g_signal_connect (
+ ACTION (SEARCH_EXECUTE), "activate",
+ G_CALLBACK (action_search_execute_cb), mail_shell_view);
+}
+
+/* Helper for e_mail_shell_view_update_popup_labels() */
+static void
+mail_shell_view_update_label_action (GtkToggleAction *action,
+ MessageList *message_list,
+ GPtrArray *uids,
+ const gchar *label_tag)
+{
+ CamelFolder *folder;
+ gboolean exists = FALSE;
+ gboolean not_exists = FALSE;
+ gboolean sensitive;
+ guint ii;
+
+ folder = message_list->folder;
+
+ /* Figure out the proper label action state for the selected
+ * messages. If all the selected messages have the given label,
+ * make the toggle action active. If all the selected message
+ * DO NOT have the given label, make the toggle action inactive.
+ * If some do and some don't, make the action insensitive. */
+
+ for (ii = 0; ii < uids->len && (!exists || !not_exists); ii++) {
+ const gchar *old_label;
+ gchar *new_label;
+
+ /* Check for new-style labels. */
+ if (camel_folder_get_message_user_flag (
+ folder, uids->pdata[ii], label_tag)) {
+ exists = TRUE;
+ continue;
+ }
+
+ /* Check for old-style labels. */
+ old_label = camel_folder_get_message_user_tag (
+ folder, uids->pdata[ii], "label");
+ if (old_label == NULL) {
+ not_exists = TRUE;
+ continue;
+ }
+
+ /* Convert old-style labels ("<name>") to "$Label<name>". */
+ new_label = g_alloca (strlen (old_label) + 10);
+ g_stpcpy (g_stpcpy (new_label, "$Label"), old_label);
+
+ if (strcmp (new_label, label_tag) == 0)
+ exists = TRUE;
+ else
+ not_exists = TRUE;
+ }
+
+ sensitive = !(exists && not_exists);
+ gtk_toggle_action_set_active (action, exists);
+ gtk_action_set_sensitive (GTK_ACTION (action), sensitive);
+}
+
+void
+e_mail_shell_view_update_popup_labels (EMailShellView *mail_shell_view)
+{
+ EShell *shell;
+ EShellSettings *shell_settings;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ EMailReader *reader;
+ MessageList *message_list;
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GtkTreeModel *tree_model;
+ GtkTreeIter iter;
+ GPtrArray *uids;
+ const gchar *path;
+ gboolean valid;
+ guint merge_id;
+ gint ii = 0;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ ui_manager = e_shell_window_get_ui_manager (shell_window);
+
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ tree_model = e_shell_settings_get_object (
+ shell_settings, "mail-label-list-store");
+
+ action_group = ACTION_GROUP (MAIL_LABEL);
+ merge_id = mail_shell_view->priv->label_merge_id;
+ path = "/mail-message-popup/mail-label-menu/mail-label-actions";
+
+ /* Unmerge the previous menu items. */
+ gtk_ui_manager_remove_ui (ui_manager, merge_id);
+ e_action_group_remove_all_actions (action_group);
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ uids = message_list_get_selected (message_list);
+
+ valid = gtk_tree_model_get_iter_first (tree_model, &iter);
+
+ while (valid) {
+ GtkToggleAction *toggle_action;
+ GtkAction *action;
+ gchar *action_name;
+ gchar *stock_id;
+ gchar *label;
+ gchar *tag;
+
+ label = e_mail_label_list_store_get_name (
+ E_MAIL_LABEL_LIST_STORE (tree_model), &iter);
+ stock_id = e_mail_label_list_store_get_stock_id (
+ E_MAIL_LABEL_LIST_STORE (tree_model), &iter);
+ tag = e_mail_label_list_store_get_tag (
+ E_MAIL_LABEL_LIST_STORE (tree_model), &iter);
+ action_name = g_strdup_printf ("mail-label-%d", ii);
+
+ /* XXX Add a tooltip! */
+ toggle_action = gtk_toggle_action_new (
+ action_name, label, NULL, stock_id);
+
+ g_object_set_data_full (
+ G_OBJECT (toggle_action), "tag",
+ tag, (GDestroyNotify) g_free);
+
+ /* Configure the action before we connect to signals. */
+ mail_shell_view_update_label_action (
+ toggle_action, message_list, uids, tag);
+
+ g_signal_connect (
+ toggle_action, "toggled",
+ G_CALLBACK (action_mail_label_cb), mail_shell_view);
+
+ /* The action group takes ownership of the action. */
+ action = GTK_ACTION (toggle_action);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (toggle_action);
+
+ gtk_ui_manager_add_ui (
+ ui_manager, merge_id, path, action_name,
+ action_name, GTK_UI_MANAGER_AUTO, FALSE);
+
+ g_free (label);
+ g_free (stock_id);
+ g_free (action_name);
+
+ valid = gtk_tree_model_iter_next (tree_model, &iter);
+ ii++;
+ }
+
+ message_list_free_uids (message_list, uids);
+
+ g_object_unref (tree_model);
+}
+
+void
+e_mail_shell_view_update_search_filter (EMailShellView *mail_shell_view)
+{
+ EShell *shell;
+ EShellContent *shell_content;
+ EShellSettings *shell_settings;
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ GtkActionGroup *action_group;
+ GtkRadioAction *radio_action;
+ GtkTreeModel *tree_model;
+ GtkTreeIter iter;
+ GList *list;
+ GSList *group;
+ gboolean valid;
+ gint ii = 0;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ tree_model = e_shell_settings_get_object (
+ shell_settings, "mail-label-list-store");
+
+ action_group = ACTION_GROUP (MAIL_FILTER);
+ e_action_group_remove_all_actions (action_group);
+
+ /* Add the standard filter actions. */
+ gtk_action_group_add_radio_actions (
+ action_group, mail_filter_entries,
+ G_N_ELEMENTS (mail_filter_entries),
+ MAIL_FILTER_ALL_MESSAGES,
+ G_CALLBACK (action_search_filter_cb),
+ mail_shell_view);
+
+ /* Retrieve the radio group from an action we just added. */
+ list = gtk_action_group_list_actions (action_group);
+ radio_action = GTK_RADIO_ACTION (list->data);
+ group = gtk_radio_action_get_group (radio_action);
+ g_list_free (list);
+
+ valid = gtk_tree_model_get_iter_first (tree_model, &iter);
+
+ while (valid) {
+ GtkAction *action;
+ gchar *action_name;
+ gchar *stock_id;
+ gchar *label;
+
+ label = e_mail_label_list_store_get_name (
+ E_MAIL_LABEL_LIST_STORE (tree_model), &iter);
+ stock_id = e_mail_label_list_store_get_stock_id (
+ E_MAIL_LABEL_LIST_STORE (tree_model), &iter);
+
+ action_name = g_strdup_printf ("mail-filter-label-%d", ii);
+ radio_action = gtk_radio_action_new (
+ action_name, label, NULL, stock_id, ii);
+ g_free (action_name);
+
+ gtk_radio_action_set_group (radio_action, group);
+ group = gtk_radio_action_get_group (radio_action);
+
+ /* The action group takes ownership of the action. */
+ action = GTK_ACTION (radio_action);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (radio_action);
+
+ g_free (label);
+ g_free (stock_id);
+
+ valid = gtk_tree_model_iter_next (tree_model, &iter);
+ ii++;
+ }
+
+ /* Use any action in the group; doesn't matter which. */
+ e_shell_content_set_filter_action (shell_content, radio_action);
+
+ ii = MAIL_FILTER_UNREAD_MESSAGES;
+ e_shell_content_add_filter_separator_after (shell_content, ii);
+
+ ii = MAIL_FILTER_READ_MESSAGES;
+ e_shell_content_add_filter_separator_before (shell_content, ii);
+
+ g_object_unref (tree_model);
+}
diff --git a/modules/mail/e-mail-shell-view-actions.h b/modules/mail/e-mail-shell-view-actions.h
new file mode 100644
index 0000000000..2a05582df8
--- /dev/null
+++ b/modules/mail/e-mail-shell-view-actions.h
@@ -0,0 +1,257 @@
+/*
+ * e-mail-shell-view-actions.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SHELL_VIEW_ACTIONS_H
+#define E_MAIL_SHELL_VIEW_ACTIONS_H
+
+#include <shell/e-shell-window-actions.h>
+
+/* Mail Actions */
+#define E_SHELL_WINDOW_ACTION_MAIL_ACCOUNT_DISABLE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-account-disable")
+#define E_SHELL_WINDOW_ACTION_MAIL_ADD_SENDER(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-add-sender")
+#define E_SHELL_WINDOW_ACTION_MAIL_CARET_MODE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-caret-mode")
+#define E_SHELL_WINDOW_ACTION_MAIL_CHECK_FOR_JUNK(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-check-for-junk")
+#define E_SHELL_WINDOW_ACTION_MAIL_CLIPBOARD_COPY(window) \
+ E_SHELL_WINDOw_ACTION ((window), "mail-clipboard-copy")
+#define E_SHELL_WINDOW_ACTION_MAIL_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-copy")
+#define E_SHELL_WINDOW_ACTION_MAIL_CREATE_SEARCH_FOLDER(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-create-search-folder")
+#define E_SHELL_WINDOW_ACTION_MAIL_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-delete")
+#define E_SHELL_WINDOW_ACTION_MAIL_DOWNLOAD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-download")
+#define E_SHELL_WINDOW_ACTION_MAIL_EMPTY_TRASH(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-empty-trash")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ON_MAILING_LIST(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-on-mailing-list")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ON_RECIPIENTS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-on-recipients")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ON_SENDER(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-on-sender")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ON_SUBJECT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-on-subject")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTERS_APPLY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filters-apply")
+#define E_SHELL_WINDOW_ACTION_MAIL_FIND(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-find")
+#define E_SHELL_WINDOW_ACTION_MAIL_FLAG_CLEAR(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-flag-clear")
+#define E_SHELL_WINDOW_ACTION_MAIL_FLAG_COMPLETED(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-flag-completed")
+#define E_SHELL_WINDOW_ACTION_MAIL_FLAG_FOR_FOLLOWUP(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-flag-for-followup")
+#define E_SHELL_WINDOW_ACTION_MAIL_FLUSH_OUTBOX(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-flush-outbox")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_COPY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-copy")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_DELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-delete")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_EXPUNGE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-expunge")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_MARK_ALL_READ(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-mark-all-read")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_MOVE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-move")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-new")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_PROPERTIES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-properties")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_REFRESH(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-refresh")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_RENAME(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-rename")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_ALL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-all")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_THREAD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-thread")
+#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_SUBTHREAD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-subthread")
+#define E_SHELL_WINDOW_ACTION_MAIL_FORWARD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-forward")
+#define E_SHELL_WINDOW_ACTION_MAIL_FORWARD_ATTACHED(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-forward-attached")
+#define E_SHELL_WINDOW_ACTION_MAIL_FORWARD_INLINE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-forward-inline")
+#define E_SHELL_WINDOW_ACTION_MAIL_FORWARD_QUOTED(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-forward-quoted")
+#define E_SHELL_WINDOW_ACTION_MAIL_HIDE_DELETED(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-hide-deleted")
+#define E_SHELL_WINDOW_ACTION_MAIL_HIDE_READ(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-hide-read")
+#define E_SHELL_WINDOW_ACTION_MAIL_HIDE_SELECTED(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-hide-selected")
+#define E_SHELL_WINDOW_ACTION_MAIL_LABEL_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-label-new")
+#define E_SHELL_WINDOW_ACTION_MAIL_LABEL_NONE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-label-none")
+#define E_SHELL_WINDOW_ACTION_MAIL_LOAD_IMAGES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-load-images")
+#define E_SHELL_WINDOW_ACTION_MAIL_MARK_IMPORTANT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-mark-important")
+#define E_SHELL_WINDOW_ACTION_MAIL_MARK_JUNK(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-mark-junk")
+#define E_SHELL_WINDOW_ACTION_MAIL_MARK_NOTJUNK(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-mark-notjunk")
+#define E_SHELL_WINDOW_ACTION_MAIL_MARK_READ(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-mark-read")
+#define E_SHELL_WINDOW_ACTION_MAIL_MARK_UNIMPORTANT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-mark-unimportant")
+#define E_SHELL_WINDOW_ACTION_MAIL_MARK_UNREAD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-mark-unread")
+#define E_SHELL_WINDOW_ACTION_MAIL_MESSAGE_EDIT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-message-edit")
+#define E_SHELL_WINDOW_ACTION_MAIL_MESSAGE_NEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-message-new")
+#define E_SHELL_WINDOW_ACTION_MAIL_MESSAGE_OPEN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-message-open")
+#define E_SHELL_WINDOW_ACTION_MAIL_MOVE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-move")
+#define E_SHELL_WINDOW_ACTION_MAIL_NEXT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-next")
+#define E_SHELL_WINDOW_ACTION_MAIL_NEXT_IMPORTANT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-next-important")
+#define E_SHELL_WINDOW_ACTION_MAIL_NEXT_THREAD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-next-thread")
+#define E_SHELL_WINDOW_ACTION_MAIL_NEXT_UNREAD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-next-unread")
+#define E_SHELL_WINDOW_ACTION_MAIL_PREVIEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-preview")
+#define E_SHELL_WINDOW_ACTION_MAIL_PREVIOUS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-previous")
+#define E_SHELL_WINDOW_ACTION_MAIL_PREVIOUS_IMPORTANT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-previous-important")
+#define E_SHELL_WINDOW_ACTION_MAIL_PREVIOUS_UNREAD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-previous-unread")
+#define E_SHELL_WINDOW_ACTION_MAIL_PRINT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-print")
+#define E_SHELL_WINDOW_ACTION_MAIL_PRINT_PREVIEW(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-print-preview")
+#define E_SHELL_WINDOW_ACTION_MAIL_REDIRECT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-redirect")
+#define E_SHELL_WINDOW_ACTION_MAIL_REPLY_ALL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-reply-all")
+#define E_SHELL_WINDOW_ACTION_MAIL_REPLY_LIST(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-reply-list")
+#define E_SHELL_WINDOW_ACTION_MAIL_REPLY_SENDER(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-reply-sender")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_FOLDER_FROM_MAILING_LIST(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-folder-from-mailing-list")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_FOLDER_FROM_RECIPIENTS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-folder-from-recipients")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_FOLDER_FROM_SENDER(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-folder-from-sender")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_FOLDER_FROM_SUBJECT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-folder-from-subject")
+#define E_SHELL_WINDOW_ACTION_MAIL_SELECT_ALL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-select-all")
+#define E_SHELL_WINDOW_ACTION_MAIL_SHOW_ALL_HEADERS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-show-all-headers")
+#define E_SHELL_WINDOW_ACTION_MAIL_SHOW_HIDDEN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-show-hidden")
+#define E_SHELL_WINDOW_ACTION_MAIL_SHOW_SOURCE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-show-source")
+#define E_SHELL_WINDOW_ACTION_MAIL_SMART_BACKWARD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-smart-backward")
+#define E_SHELL_WINDOW_ACTION_MAIL_SMART_FORWARD(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-smart-forward")
+#define E_SHELL_WINDOW_ACTION_MAIL_STOP(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-stop")
+#define E_SHELL_WINDOW_ACTION_MAIL_THREADS_COLLAPSE_ALL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-threads-collapse-all")
+#define E_SHELL_WINDOW_ACTION_MAIL_THREADS_EXPAND_ALL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-threads-expand-all")
+#define E_SHELL_WINDOW_ACTION_MAIL_THREADS_GROUP_BY(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-threads-group-by")
+#define E_SHELL_WINDOW_ACTION_MAIL_TOOLS_FILTERS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-tools-filters")
+#define E_SHELL_WINDOW_ACTION_MAIL_TOOLS_SEARCH_FOLDERS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-tools-search-folders")
+#define E_SHELL_WINDOW_ACTION_MAIL_TOOLS_SUBSCRIPTIONS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-tools-subscriptions")
+#define E_SHELL_WINDOW_ACTION_MAIL_UNDELETE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-undelete")
+#define E_SHELL_WINDOW_ACTION_MAIL_VIEW_CLASSIC(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-view-classic")
+#define E_SHELL_WINDOW_ACTION_MAIL_VIEW_VERTICAL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-view-vertical")
+#define E_SHELL_WINDOW_ACTION_MAIL_ZOOM_100(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-zoom-100")
+#define E_SHELL_WINDOW_ACTION_MAIL_ZOOM_IN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-zoom-in")
+#define E_SHELL_WINDOW_ACTION_MAIL_ZOOM_OUT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-zoom-out")
+
+/* Mail Query Actions */
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ALL_MESSAGES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-all-messages")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_IMPORTANT_MESSAGES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-important-messages")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_LAST_5_DAYS_MESSAGES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-last-5-days-messages")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_MESSAGES_NOT_JUNK(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-messages-not-junk")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-messages-with-attachments")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_NO_LABEL(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-no-label")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_READ_MESSAGES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-read-messages")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_RECENT_MESSAGES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-recent-messages")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_UNREAD_MESSAGES(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-filter-unread-messages")
+#define E_SHELL_WINDOW_ACTION_MAIL_SCOPE_ALL_ACCOUNTS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-scope-all-accounts")
+#define E_SHELL_WINDOW_ACTION_MAIL_SCOPE_CURRENT_ACCOUNT(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-scope-current-account")
+#define E_SHELL_WINDOW_ACTION_MAIL_SCOPE_CURRENT_FOLDER(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-scope-current-folder")
+#define E_SHELL_WINDOW_ACTION_MAIL_SCOPE_CURRENT_MESSAGE(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-scope-current-message")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_BODY_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-body-contains")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_MESSAGE_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-message-contains")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_RECIPIENTS_CONTAIN(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-recipients-contain")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_SENDER_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-sender-contains")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_SUBJECT_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-subject-contains")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_SUBJECT_OR_RECIPIENTS_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-subject-or-recipients-contains")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-subject-or-sender-contains")
+
+/* Action Groups */
+#define E_SHELL_WINDOW_ACTION_GROUP_MAIL(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "mail")
+#define E_SHELL_WINDOW_ACTION_GROUP_MAIL_FILTER(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "mail-filter")
+#define E_SHELL_WINDOW_ACTION_GROUP_MAIL_LABEL(window) \
+ E_SHELL_WINDOW_ACTION_GROUP ((window), "mail-label")
+
+#endif /* E_MAIL_SHELL_VIEW_ACTIONS_H */
diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c
new file mode 100644
index 0000000000..f3b3b5a513
--- /dev/null
+++ b/modules/mail/e-mail-shell-view-private.c
@@ -0,0 +1,905 @@
+/*
+ * e-mail-shell-view-private.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-mail-shell-view-private.h"
+
+#include "widgets/menus/gal-view-factory-etable.h"
+
+static void
+mail_shell_view_folder_tree_selected_cb (EMailShellView *mail_shell_view,
+ const gchar *full_name,
+ const gchar *uri,
+ guint32 flags,
+ EMFolderTree *folder_tree)
+{
+ EShellView *shell_view;
+ EMailReader *reader;
+ gboolean folder_selected;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+
+ folder_selected =
+ !(flags & CAMEL_FOLDER_NOSELECT) &&
+ full_name != NULL;
+
+ if (folder_selected)
+ e_mail_reader_set_folder_uri (reader, uri);
+ else
+ e_mail_reader_set_folder (reader, NULL, NULL);
+
+ e_shell_view_update_actions (shell_view);
+}
+
+static void
+mail_shell_view_folder_tree_popup_event_cb (EShellView *shell_view,
+ GdkEventButton *event)
+{
+ const gchar *widget_path;
+
+ widget_path = "/mail-folder-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+}
+
+static gboolean
+mail_shell_view_key_press_event_cb (EMailShellView *mail_shell_view,
+ GdkEventKey *event)
+{
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ GtkAction *action;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ if ((event->state & GDK_CONTROL_MASK) != 0)
+ return FALSE;
+
+ switch (event->keyval) {
+ case GDK_space:
+ action = ACTION (MAIL_SMART_FORWARD);
+ break;
+
+ case GDK_BackSpace:
+ action = ACTION (MAIL_SMART_BACKWARD);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ gtk_action_activate (action);
+
+ return TRUE;
+}
+
+static gint
+mail_shell_view_message_list_key_press_cb (EMailShellView *mail_shell_view,
+ gint row,
+ ETreePath path,
+ gint col,
+ GdkEvent *event)
+{
+ return mail_shell_view_key_press_event_cb (
+ mail_shell_view, &event->key);
+}
+
+static gboolean
+mail_shell_view_message_list_right_click_cb (EShellView *shell_view,
+ gint row,
+ ETreePath path,
+ gint col,
+ GdkEventButton *event)
+{
+ const gchar *widget_path;
+
+ widget_path = "/mail-message-popup";
+ e_shell_view_show_popup_menu (shell_view, widget_path, event);
+
+ return TRUE;
+}
+
+static void
+mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view,
+ EMailReader *reader)
+{
+ EMailShellContent *mail_shell_content;
+
+ mail_shell_content = mail_shell_view->priv->mail_shell_content;
+ e_mail_shell_content_update_view_instance (mail_shell_content);
+ e_mail_shell_view_update_sidebar (mail_shell_view);
+}
+
+static void
+mail_shell_view_reader_status_message_cb (EMailShellView *mail_shell_view,
+ const gchar *status_message)
+{
+ EShellView *shell_view;
+ EShellTaskbar *shell_taskbar;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
+
+ e_shell_taskbar_set_message (shell_taskbar, status_message);
+}
+
+static void
+mail_shell_view_load_view_collection (EShellViewClass *shell_view_class)
+{
+ GalViewCollection *collection;
+ GalViewFactory *factory;
+ ETableSpecification *spec;
+ const gchar *base_dir;
+ gchar *filename;
+
+ collection = shell_view_class->view_collection;
+
+ base_dir = EVOLUTION_ETSPECDIR;
+ spec = e_table_specification_new ();
+ filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL);
+ if (!e_table_specification_load_from_file (spec, filename))
+ g_critical ("Unable to load ETable specification file "
+ "for mail");
+ g_free (filename);
+
+ factory = gal_view_factory_etable_new (spec);
+ gal_view_collection_add_factory (collection, factory);
+ g_object_unref (factory);
+ g_object_unref (spec);
+
+ gal_view_collection_load (collection);
+}
+
+static void
+mail_shell_view_notify_view_id_cb (EMailShellView *mail_shell_view)
+{
+ EMailShellContent *mail_shell_content;
+ GalViewInstance *view_instance;
+ const gchar *view_id;
+
+ mail_shell_content = mail_shell_view->priv->mail_shell_content;
+ view_instance = NULL; /* FIXME */
+ view_id = e_shell_view_get_view_id (E_SHELL_VIEW (mail_shell_view));
+
+ /* A NULL view ID implies we're in a custom view. But you can
+ * only get to a custom view via the "Define Views" dialog, which
+ * would have already modified the view instance appropriately.
+ * Furthermore, there's no way to refer to a custom view by ID
+ * anyway, since custom views have no IDs. */
+ if (view_id == NULL)
+ return;
+
+ gal_view_instance_set_current_view_id (view_instance, view_id);
+}
+
+void
+e_mail_shell_view_private_init (EMailShellView *mail_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ if (!gal_view_collection_loaded (shell_view_class->view_collection))
+ mail_shell_view_load_view_collection (shell_view_class);
+
+ g_signal_connect (
+ mail_shell_view, "notify::view-id",
+ G_CALLBACK (mail_shell_view_notify_view_id_cb), NULL);
+}
+
+void
+e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
+{
+ EMailShellViewPrivate *priv = mail_shell_view->priv;
+ EMailShellSidebar *mail_shell_sidebar;
+ EShell *shell;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+ EShellContent *shell_content;
+ EShellSettings *shell_settings;
+ EShellSidebar *shell_sidebar;
+ EShellWindow *shell_window;
+ EMFormatHTMLDisplay *html_display;
+ EMFolderTree *folder_tree;
+ RuleContext *context;
+ FilterRule *rule = NULL;
+ GtkTreeModel *tree_model;
+ GtkUIManager *ui_manager;
+ MessageList *message_list;
+ EMailReader *reader;
+ GtkHTML *html;
+ const gchar *source;
+ guint merge_id;
+ gint ii = 0;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ ui_manager = e_shell_window_get_ui_manager (shell_window);
+
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ tree_model = e_shell_settings_get_object (
+ shell_settings, "mail-label-list-store");
+
+ e_shell_window_add_action_group (shell_window, "mail");
+ e_shell_window_add_action_group (shell_window, "mail-filter");
+ e_shell_window_add_action_group (shell_window, "mail-label");
+
+ merge_id = gtk_ui_manager_new_merge_id (ui_manager);
+ priv->label_merge_id = merge_id;
+
+ /* Cache these to avoid lots of awkward casting. */
+ priv->mail_shell_backend = g_object_ref (shell_backend);
+ priv->mail_shell_content = g_object_ref (shell_content);
+ priv->mail_shell_sidebar = g_object_ref (shell_sidebar);
+
+ reader = E_MAIL_READER (shell_content);
+ html_display = e_mail_reader_get_html_display (reader);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar);
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+
+ html = EM_FORMAT_HTML (html_display)->html;
+
+ g_signal_connect_swapped (
+ folder_tree, "folder-selected",
+ G_CALLBACK (mail_shell_view_folder_tree_selected_cb),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ folder_tree, "popup-event",
+ G_CALLBACK (mail_shell_view_folder_tree_popup_event_cb),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ message_list->tree, "key-press",
+ G_CALLBACK (mail_shell_view_message_list_key_press_cb),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ message_list->tree, "right-click",
+ G_CALLBACK (mail_shell_view_message_list_right_click_cb),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ reader, "changed",
+ G_CALLBACK (mail_shell_view_reader_changed_cb),
+ mail_shell_view);
+
+ /* Use the same callback as "changed". */
+ g_signal_connect_swapped (
+ reader, "folder-loaded",
+ G_CALLBACK (mail_shell_view_reader_changed_cb),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ reader, "folder-loaded",
+ G_CALLBACK (e_mail_shell_view_restore_state),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ tree_model, "row-changed",
+ G_CALLBACK (e_mail_shell_view_update_search_filter),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ tree_model, "row-deleted",
+ G_CALLBACK (e_mail_shell_view_update_search_filter),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ tree_model, "row-inserted",
+ G_CALLBACK (e_mail_shell_view_update_search_filter),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ html, "key-press-event",
+ G_CALLBACK (mail_shell_view_key_press_event_cb),
+ mail_shell_view);
+
+ g_signal_connect_swapped (
+ html, "status-message",
+ G_CALLBACK (mail_shell_view_reader_status_message_cb),
+ mail_shell_view);
+
+ e_mail_shell_view_actions_init (mail_shell_view);
+ e_mail_shell_view_update_search_filter (mail_shell_view);
+ e_mail_reader_init (reader);
+
+ /* Populate built-in rules for search entry popup menu.
+ * Keep the assertions, please. If the conditions aren't
+ * met we're going to crash anyway, just more mysteriously. */
+ context = e_shell_content_get_search_context (shell_content);
+ source = FILTER_SOURCE_DEMAND;
+ while ((rule = rule_context_next_rule (context, rule, source))) {
+ g_assert (ii < MAIL_NUM_SEARCH_RULES);
+ priv->search_rules[ii++] = g_object_ref (rule);
+ }
+ g_assert (ii == MAIL_NUM_SEARCH_RULES);
+}
+
+void
+e_mail_shell_view_private_dispose (EMailShellView *mail_shell_view)
+{
+ EMailShellViewPrivate *priv = mail_shell_view->priv;
+ gint ii;
+
+ DISPOSE (priv->mail_shell_backend);
+ DISPOSE (priv->mail_shell_content);
+ DISPOSE (priv->mail_shell_sidebar);
+
+ for (ii = 0; ii < MAIL_NUM_SEARCH_RULES; ii++)
+ DISPOSE (priv->search_rules[ii]);
+}
+
+void
+e_mail_shell_view_private_finalize (EMailShellView *mail_shell_view)
+{
+ /* XXX Nothing to do? */
+}
+
+void
+e_mail_shell_view_restore_state (EMailShellView *mail_shell_view)
+{
+ EShellView *shell_view;
+ EShellContent *shell_content;
+ EMailReader *reader;
+ MessageList *message_list;
+ const gchar *folder_uri;
+ gchar *group_name;
+
+ /* XXX Move this to EMailShellContent. */
+
+ g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+
+ reader = E_MAIL_READER (shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder_uri = message_list->folder_uri;
+ g_return_if_fail (folder_uri != NULL);
+
+ group_name = g_strdup_printf ("Folder %s", folder_uri);
+ e_shell_content_restore_state (shell_content, group_name);
+ g_free (group_name);
+}
+
+void
+e_mail_shell_view_execute_search (EMailShellView *mail_shell_view)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellContent *shell_content;
+ EShellSettings *shell_settings;
+ EMFormatHTMLDisplay *html_display;
+ EMailShellContent *mail_shell_content;
+ MessageList *message_list;
+ FilterRule *rule;
+ EMailReader *reader;
+ CamelFolder *folder;
+ GtkAction *action;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter tree_iter;
+ GString *string;
+ GList *iter;
+ GSList *search_strings = NULL;
+ const gchar *folder_uri;
+ const gchar *text;
+ gboolean valid;
+ gchar *query;
+ gchar *temp;
+ gchar *tag;
+ gint value;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell_content = e_shell_view_get_shell_content (shell_view);
+
+ shell = e_shell_window_get_shell (shell_window);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ mail_shell_content = mail_shell_view->priv->mail_shell_content;
+
+ reader = E_MAIL_READER (shell_content);
+ html_display = e_mail_reader_get_html_display (reader);
+ message_list = e_mail_reader_get_message_list (reader);
+
+ folder_uri = message_list->folder_uri;
+ folder = message_list->folder;
+
+ /* This returns a new object reference. */
+ model = e_shell_settings_get_object (
+ shell_settings, "mail-label-list-store");
+
+ text = e_shell_content_get_search_text (shell_content);
+ if (text == NULL || *text == '\0') {
+ query = g_strdup ("");
+ goto filter;
+ }
+
+ /* Replace variables in the selected rule with the
+ * current search text and extract a query string. */
+
+ action = ACTION (MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS);
+ value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
+ g_return_if_fail (value >= 0 && value < MAIL_NUM_SEARCH_RULES);
+ rule = mail_shell_view->priv->search_rules[value];
+
+ for (iter = rule->parts; iter != NULL; iter = iter->next) {
+ FilterPart *part = iter->data;
+ FilterElement *element = NULL;
+
+ if (strcmp (part->name, "subject") == 0)
+ element = filter_part_find_element (part, "subject");
+ else if (strcmp (part->name, "body") == 0)
+ element = filter_part_find_element (part, "word");
+ else if (strcmp (part->name, "sender") == 0)
+ element = filter_part_find_element (part, "sender");
+ else if (strcmp (part->name, "to") == 0)
+ element = filter_part_find_element (part, "recipient");
+
+ if (strcmp (part->name, "body") == 0) {
+ struct _camel_search_words *words;
+ gint ii;
+
+ words = camel_search_words_split ((guchar *) text);
+ for (ii = 0; ii < words->len; ii++)
+ search_strings = g_slist_prepend (
+ search_strings, g_strdup (
+ words->words[ii]->word));
+ camel_search_words_free (words);
+ }
+
+ if (element != NULL) {
+ FilterInput *input = FILTER_INPUT (element);
+ filter_input_set_value (input, text);
+ }
+ }
+
+ string = g_string_sized_new (1024);
+ filter_rule_build_code (rule, string);
+ query = g_string_free (string, FALSE);
+
+filter:
+
+ /* Apply selected filter. */
+
+ value = e_shell_content_get_filter_value (shell_content);
+ switch (value) {
+ case MAIL_FILTER_ALL_MESSAGES:
+ break;
+
+ case MAIL_FILTER_UNREAD_MESSAGES:
+ temp = g_strdup_printf (
+ "(and %s (match-all (not "
+ "(system-flag \"Seen\"))))", query);
+ g_free (query);
+ query = temp;
+ break;
+
+ case MAIL_FILTER_NO_LABEL:
+ string = g_string_sized_new (1024);
+ g_string_append_printf (
+ string, "(and %s (and ", query);
+ valid = gtk_tree_model_get_iter_first (
+ model, &tree_iter);
+ while (valid) {
+ tag = e_mail_label_list_store_get_tag (
+ E_MAIL_LABEL_LIST_STORE (model),
+ &tree_iter);
+ g_string_append_printf (
+ string, " (match-all (not (or "
+ "(= (user-tag \"label\") \"%s\") "
+ "(user-flag \"$Label%s\") "
+ "(user-flag \"%s\"))))",
+ tag, tag, tag);
+ g_free (tag);
+
+ valid = gtk_tree_model_iter_next (
+ model, &tree_iter);
+ }
+ g_string_append_len (string, "))", 2);
+ g_free (query);
+ query = g_string_free (string, FALSE);
+ break;
+
+ case MAIL_FILTER_READ_MESSAGES:
+ temp = g_strdup_printf (
+ "(and %s (match-all "
+ "(system-flag \"Seen\")))", query);
+ g_free (query);
+ query = temp;
+ break;
+
+ case MAIL_FILTER_RECENT_MESSAGES:
+ if (em_utils_folder_is_sent (folder, folder_uri))
+ temp = g_strdup_printf (
+ "(and %s (match-all "
+ "(> (get-sent-date) "
+ "(- (get-current-date) 86400))))",
+ query);
+ else
+ temp = g_strdup_printf (
+ "(and %s (match-all "
+ "(> (get-received-date) "
+ "(- (get-current-date) 86400))))",
+ query);
+ g_free (query);
+ query = temp;
+ break;
+
+ case MAIL_FILTER_LAST_5_DAYS_MESSAGES:
+ if (em_utils_folder_is_sent (folder, folder_uri))
+ temp = g_strdup_printf (
+ "(and %s (match-all "
+ "(> (get-sent-date) "
+ "(- (get-current-date) 432000))))",
+ query);
+ else
+ temp = g_strdup_printf (
+ "(and %s (match-all "
+ "(> (get-received-date) "
+ "(- (get-current-date) 432000))))",
+ query);
+ g_free (query);
+ query = temp;
+ break;
+
+ case MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS:
+ temp = g_strdup_printf (
+ "(and %s (match-all "
+ "(system-flag \"Attachments\")))", query);
+ g_free (query);
+ query = temp;
+ break;
+
+ case MAIL_FILTER_IMPORTANT_MESSAGES:
+ temp = g_strdup_printf (
+ "(and %s (match-all "
+ "(system-flag \"Flagged\")))", query);
+ g_free (query);
+ query = temp;
+ break;
+
+ case MAIL_FILTER_MESSAGES_NOT_JUNK:
+ temp = g_strdup_printf (
+ "(and %s (match-all (not "
+ "(system-flag \"junk\"))))", query);
+ g_free (query);
+ query = temp;
+ break;
+
+ default:
+ /* The action value also serves as a path for
+ * the label list store. That's why we number
+ * the label actions from zero. */
+ path = gtk_tree_path_new_from_indices (value, -1);
+ gtk_tree_model_get_iter (model, &tree_iter, path);
+ gtk_tree_path_free (path);
+
+ tag = e_mail_label_list_store_get_tag (
+ E_MAIL_LABEL_LIST_STORE (model), &tree_iter);
+ temp = g_strdup_printf (
+ "(and %s (match-all (or "
+ "(= (user-tag \"label\") \"%s\") "
+ "(user-flag \"$Label%s\") "
+ "(user-flag \"%s\"))))",
+ query, tag, tag, tag);
+ g_free (tag);
+
+ g_free (query);
+ query = temp;
+ break;
+ }
+
+ message_list_set_search (message_list, query);
+
+ e_mail_shell_content_set_search_strings (
+ mail_shell_content, search_strings);
+
+ g_slist_foreach (search_strings, (GFunc) g_free, NULL);
+ g_slist_free (search_strings);
+
+ g_object_unref (model);
+ g_free (query);
+}
+
+/* Helper for e_mail_shell_view_create_filter_from_selected() */
+static void
+mail_shell_view_create_filter_cb (CamelFolder *folder,
+ const gchar *uid,
+ CamelMimeMessage *message,
+ gpointer user_data)
+{
+ struct {
+ const gchar *source;
+ gint type;
+ } *filter_data = user_data;
+
+ if (message != NULL)
+ filter_gui_add_from_message (
+ message, filter_data->source, filter_data->type);
+
+ g_free (filter_data);
+}
+
+void
+e_mail_shell_view_create_filter_from_selected (EMailShellView *mail_shell_view,
+ gint filter_type)
+{
+ EMailReader *reader;
+ MessageList *message_list;
+ CamelFolder *folder;
+ const gchar *filter_source;
+ const gchar *folder_uri;
+ GPtrArray *uids;
+
+ struct {
+ const gchar *source;
+ gint type;
+ } *filter_data;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder_uri = message_list->folder_uri;
+ folder = message_list->folder;
+
+ if (em_utils_folder_is_sent (folder, folder_uri))
+ filter_source = FILTER_SOURCE_OUTGOING;
+ else if (em_utils_folder_is_outbox (folder, folder_uri))
+ filter_source = FILTER_SOURCE_OUTGOING;
+ else
+ filter_source = FILTER_SOURCE_INCOMING;
+
+ uids = message_list_get_selected (message_list);
+
+ if (uids->len == 1) {
+ filter_data = g_malloc (sizeof (*filter_data));
+ filter_data->source = filter_source;
+ filter_data->type = filter_type;
+
+ mail_get_message (
+ folder, uids->pdata[0],
+ mail_shell_view_create_filter_cb,
+ filter_data, mail_msg_unordered_push);
+ }
+
+ em_utils_uids_free (uids);
+}
+
+/* Helper for e_mail_shell_view_create_vfolder_from_selected() */
+static void
+mail_shell_view_create_vfolder_cb (CamelFolder *folder,
+ const gchar *uid,
+ CamelMimeMessage *message,
+ gpointer user_data)
+{
+ struct {
+ gchar *uri;
+ gint type;
+ } *vfolder_data = user_data;
+
+ if (message != NULL)
+ vfolder_gui_add_from_message (
+ message, vfolder_data->type, vfolder_data->uri);
+
+ g_free (vfolder_data->uri);
+ g_free (vfolder_data);
+}
+
+void
+e_mail_shell_view_create_vfolder_from_selected (EMailShellView *mail_shell_view,
+ gint vfolder_type)
+{
+ EMailReader *reader;
+ MessageList *message_list;
+ CamelFolder *folder;
+ const gchar *folder_uri;
+ GPtrArray *uids;
+
+ struct {
+ gchar *uri;
+ gint type;
+ } *vfolder_data;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
+
+ reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder_uri = message_list->folder_uri;
+ folder = message_list->folder;
+
+ uids = message_list_get_selected (message_list);
+
+ if (uids->len == 1) {
+ vfolder_data = g_malloc (sizeof (*vfolder_data));
+ vfolder_data->uri = g_strdup (folder_uri);
+ vfolder_data->type = vfolder_type;
+
+ mail_get_message (
+ folder, uids->pdata[0],
+ mail_shell_view_create_vfolder_cb,
+ vfolder_data, mail_msg_unordered_push);
+ }
+
+ em_utils_uids_free (uids);
+}
+
+void
+e_mail_shell_view_update_sidebar (EMailShellView *mail_shell_view)
+{
+ EMailShellContent *mail_shell_content;
+ EShellSidebar *shell_sidebar;
+ EShellView *shell_view;
+ EMailReader *reader;
+ MessageList *message_list;
+ CamelStore *local_store;
+ CamelFolder *folder;
+ GPtrArray *selected;
+ GString *buffer;
+ const gchar *display_name;
+ const gchar *folder_uri;
+ gchar *folder_name;
+ gchar *title;
+ guint32 num_deleted;
+ guint32 num_junked;
+ guint32 num_junked_not_deleted;
+ guint32 num_unread;
+ guint32 num_visible;
+
+ g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
+
+ mail_shell_content = mail_shell_view->priv->mail_shell_content;
+
+ shell_view = E_SHELL_VIEW (mail_shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+
+ reader = E_MAIL_READER (mail_shell_content);
+ message_list = e_mail_reader_get_message_list (reader);
+ folder_uri = message_list->folder_uri;
+ folder = message_list->folder;
+
+ local_store = e_mail_local_get_store ();
+
+ /* If no folder is selected, reset the sidebar banners
+ * to their default values and stop. */
+ if (folder == NULL) {
+ GtkAction *action;
+ gchar *label;
+
+ action = e_shell_view_get_action (shell_view);
+
+ g_object_get (action, "label", &label, NULL);
+ e_shell_sidebar_set_secondary_text (shell_sidebar, NULL);
+ e_shell_view_set_title (shell_view, label);
+ g_free (label);
+
+ return;
+ }
+
+ camel_object_get (
+ folder, NULL,
+ CAMEL_FOLDER_NAME, &folder_name,
+ CAMEL_FOLDER_DELETED, &num_deleted,
+ CAMEL_FOLDER_JUNKED, &num_junked,
+ CAMEL_FOLDER_JUNKED_NOT_DELETED, &num_junked_not_deleted,
+ CAMEL_FOLDER_UNREAD, &num_unread,
+ CAMEL_FOLDER_VISIBLE, &num_visible,
+ NULL);
+
+ buffer = g_string_sized_new (256);
+ selected = message_list_get_selected (message_list);
+
+ if (selected->len > 1)
+ g_string_append_printf (
+ buffer, ngettext ("%d selected, ", "%d selected, ",
+ selected->len), selected->len);
+
+ if (CAMEL_IS_VTRASH_FOLDER (folder)) {
+ CamelVTrashFolder *trash_folder;
+
+ trash_folder = (CamelVTrashFolder *) folder;
+
+ /* "Trash" folder */
+ if (trash_folder->type == CAMEL_VTRASH_FOLDER_TRASH)
+ g_string_append_printf (
+ buffer, ngettext ("%d deleted",
+ "%d deleted", num_deleted), num_deleted);
+
+ /* "Junk" folder (hide deleted messages) */
+ else if (e_mail_reader_get_hide_deleted (reader))
+ g_string_append_printf (
+ buffer, ngettext ("%d junk",
+ "%d junk", num_junked_not_deleted),
+ num_junked_not_deleted);
+
+ /* "Junk" folder (show deleted messages) */
+ else
+ g_string_append_printf (
+ buffer, ngettext ("%d junk", "%d junk",
+ num_junked), num_junked);
+
+ /* "Drafts" folder */
+ } else if (em_utils_folder_is_drafts (folder, folder_uri)) {
+ g_string_append_printf (
+ buffer, ngettext ("%d draft", "%d drafts",
+ num_visible), num_visible);
+
+ /* "Outbox" folder */
+ } else if (em_utils_folder_is_outbox (folder, folder_uri)) {
+ g_string_append_printf (
+ buffer, ngettext ("%d unsent", "%d unsent",
+ num_visible), num_visible);
+
+ /* "Sent" folder */
+ } else if (em_utils_folder_is_sent (folder, folder_uri)) {
+ g_string_append_printf (
+ buffer, ngettext ("%d sent", "%d sent",
+ num_visible), num_visible);
+
+ /* Normal folder */
+ } else {
+ if (!e_mail_reader_get_hide_deleted (reader))
+ num_visible +=
+ num_deleted - num_junked +
+ num_junked_not_deleted;
+
+ if (num_unread > 0 && selected->len <= 1)
+ g_string_append_printf (
+ buffer, ngettext ("%d unread, ",
+ "%d unread, ", num_unread), num_unread);
+ g_string_append_printf (
+ buffer, ngettext ("%d total", "%d total",
+ num_visible), num_visible);
+ }
+
+ message_list_free_uids (message_list, selected);
+
+ /* Choose a suitable folder name for displaying. */
+ if (folder->parent_store == local_store && (
+ strcmp (folder_name, "Drafts") == 0 ||
+ strcmp (folder_name, "Inbox") == 0 ||
+ strcmp (folder_name, "Outbox") == 0 ||
+ strcmp (folder_name, "Sent") == 0 ||
+ strcmp (folder_name, "Templates") == 0))
+ display_name = _(folder_name);
+ else if (strcmp (folder_name, "INBOX") == 0)
+ display_name = _("Inbox");
+ else
+ display_name = folder_name;
+
+ title = g_strdup_printf ("%s (%s)", display_name, buffer->str);
+ e_shell_sidebar_set_secondary_text (shell_sidebar, buffer->str);
+ e_shell_view_set_title (shell_view, title);
+ g_free (title);
+
+ camel_object_free (folder, CAMEL_FOLDER_NAME, folder_name);
+ g_string_free (buffer, TRUE);
+}
diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h
new file mode 100644
index 0000000000..988d494219
--- /dev/null
+++ b/modules/mail/e-mail-shell-view-private.h
@@ -0,0 +1,173 @@
+/*
+ * e-mail-shell-view-private.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SHELL_VIEW_PRIVATE_H
+#define E_MAIL_SHELL_VIEW_PRIVATE_H
+
+#include "e-mail-shell-view.h"
+
+#include <glib/gi18n.h>
+#include <gtkhtml/gtkhtml.h>
+#include <camel/camel-disco-store.h>
+#include <camel/camel-offline-store.h>
+#include <camel/camel-vtrash-folder.h>
+#include <camel/camel-search-private.h> /* for camel_search_word */
+
+#include "e-util/e-util.h"
+#include "e-util/e-binding.h"
+#include "e-util/gconf-bridge.h"
+#include "e-util/e-account-utils.h"
+#include "filter/filter-part.h"
+#include "widgets/misc/e-popup-action.h"
+#include "widgets/menus/gal-view-instance.h"
+
+#include "e-mail-label-dialog.h"
+#include "e-mail-label-list-store.h"
+#include "e-mail-local.h"
+#include "e-mail-reader.h"
+#include "e-mail-store.h"
+#include "em-composer-utils.h"
+#include "em-folder-properties.h"
+#include "em-folder-selector.h"
+#include "em-folder-utils.h"
+#include "em-subscribe-editor.h"
+#include "em-utils.h"
+#include "mail-autofilter.h"
+#include "mail-config.h"
+#include "mail-ops.h"
+#include "mail-send-recv.h"
+#include "mail-vfolder.h"
+
+#include "e-mail-shell-backend.h"
+#include "e-mail-shell-content.h"
+#include "e-mail-shell-sidebar.h"
+#include "e-mail-shell-view-actions.h"
+
+#define E_MAIL_SHELL_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MAIL_SHELL_VIEW, EMailShellViewPrivate))
+
+/* Shorthand, requires a variable named "shell_window". */
+#define ACTION(name) \
+ (E_SHELL_WINDOW_ACTION_##name (shell_window))
+#define ACTION_GROUP(name) \
+ (E_SHELL_WINDOW_ACTION_GROUP_##name (shell_window))
+
+/* For use in dispose() methods. */
+#define DISPOSE(obj) \
+ G_STMT_START { \
+ if ((obj) != NULL) { g_object_unref (obj); (obj) = NULL; } \
+ } G_STMT_END
+
+/* ETable Specifications */
+#define ETSPEC_FILENAME "message-list.etspec"
+
+G_BEGIN_DECLS
+
+/* Filter items are displayed in ascending order.
+ * Labels are numbered from zero, so subsequent items must have
+ * sufficiently large values. Unfortunately this introduces an
+ * arbitrary upper bound on labels. */
+enum {
+ MAIL_FILTER_ALL_MESSAGES = -3,
+ MAIL_FILTER_UNREAD_MESSAGES = -2,
+ MAIL_FILTER_NO_LABEL = -1,
+ /* Labels go here */
+ MAIL_FILTER_READ_MESSAGES = 5000,
+ MAIL_FILTER_RECENT_MESSAGES = 5001,
+ MAIL_FILTER_LAST_5_DAYS_MESSAGES = 5002,
+ MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS = 5003,
+ MAIL_FILTER_IMPORTANT_MESSAGES = 5004,
+ MAIL_FILTER_MESSAGES_NOT_JUNK = 5005
+};
+
+/* Search items are displayed in ascending order. */
+enum {
+ MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS,
+ MAIL_SEARCH_SUBJECT_OR_RECIPIENTS_CONTAINS,
+ MAIL_SEARCH_RECIPIENTS_CONTAIN,
+ MAIL_SEARCH_MESSAGE_CONTAINS,
+ MAIL_SEARCH_SUBJECT_CONTAINS,
+ MAIL_SEARCH_SENDER_CONTAINS,
+ MAIL_SEARCH_BODY_CONTAINS,
+ MAIL_NUM_SEARCH_RULES
+};
+
+/* Scope items are displayed in ascending order. */
+enum {
+ MAIL_SCOPE_CURRENT_FOLDER,
+ MAIL_SCOPE_CURRENT_ACCOUNT,
+ MAIL_SCOPE_ALL_ACCOUNTS
+};
+
+struct _EMailShellViewPrivate {
+
+ /*** Other Stuff ***/
+
+ /* These are just for convenience. */
+ EMailShellBackend *mail_shell_backend;
+ EMailShellContent *mail_shell_content;
+ EMailShellSidebar *mail_shell_sidebar;
+
+ /* For UI merging and unmerging. */
+ guint merge_id;
+ guint label_merge_id;
+
+ /* Filter rules correspond to the search entry menu. */
+ FilterRule *search_rules[MAIL_NUM_SEARCH_RULES];
+
+ guint show_deleted : 1;
+};
+
+void e_mail_shell_view_private_init
+ (EMailShellView *mail_shell_view,
+ EShellViewClass *shell_view_class);
+void e_mail_shell_view_private_constructed
+ (EMailShellView *mail_shell_view);
+void e_mail_shell_view_private_dispose
+ (EMailShellView *mail_shell_view);
+void e_mail_shell_view_private_finalize
+ (EMailShellView *mail_shell_view);
+
+/* Private Utilities */
+
+void e_mail_shell_view_actions_init
+ (EMailShellView *mail_shell_view);
+void e_mail_shell_view_restore_state
+ (EMailShellView *mail_shell_view);
+void e_mail_shell_view_execute_search
+ (EMailShellView *mail_shell_view);
+void e_mail_shell_view_create_filter_from_selected
+ (EMailShellView *mail_shell_view,
+ gint filter_type);
+void e_mail_shell_view_create_vfolder_from_selected
+ (EMailShellView *mail_shell_view,
+ gint vfolder_type);
+void e_mail_shell_view_update_popup_labels
+ (EMailShellView *mail_shell_view);
+void e_mail_shell_view_update_search_filter
+ (EMailShellView *mail_shell_view);
+void e_mail_shell_view_update_sidebar
+ (EMailShellView *mail_shell_view);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SHELL_VIEW_PRIVATE_H */
diff --git a/modules/mail/e-mail-shell-view.c b/modules/mail/e-mail-shell-view.c
new file mode 100644
index 0000000000..8d8b4aa2b3
--- /dev/null
+++ b/modules/mail/e-mail-shell-view.c
@@ -0,0 +1,260 @@
+/*
+ * e-mail-shell-view.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-mail-shell-view-private.h"
+
+static gpointer parent_class;
+static GType mail_shell_view_type;
+
+static void
+mail_shell_view_dispose (GObject *object)
+{
+ e_mail_shell_view_private_dispose (E_MAIL_SHELL_VIEW (object));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+mail_shell_view_finalize (GObject *object)
+{
+ e_mail_shell_view_private_finalize (E_MAIL_SHELL_VIEW (object));
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+mail_shell_view_constructed (GObject *object)
+{
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ e_mail_shell_view_private_constructed (E_MAIL_SHELL_VIEW (object));
+}
+
+static void
+mail_shell_view_toggled (EShellView *shell_view)
+{
+ EMailShellViewPrivate *priv;
+ EShellWindow *shell_window;
+ GtkUIManager *ui_manager;
+ const gchar *basename;
+ gboolean view_is_active;
+
+ /* Chain up to parent's toggled() method. */
+ E_SHELL_VIEW_CLASS (parent_class)->toggled (shell_view);
+
+ priv = E_MAIL_SHELL_VIEW_GET_PRIVATE (shell_view);
+
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ ui_manager = e_shell_window_get_ui_manager (shell_window);
+ view_is_active = e_shell_view_is_active (shell_view);
+ basename = E_MAIL_READER_UI_DEFINITION;
+
+ if (view_is_active && priv->merge_id == 0) {
+ priv->merge_id = e_load_ui_definition (ui_manager, basename);
+ e_mail_reader_create_charset_menu (
+ E_MAIL_READER (priv->mail_shell_content),
+ ui_manager, priv->merge_id);
+ } else if (!view_is_active && priv->merge_id != 0) {
+ gtk_ui_manager_remove_ui (ui_manager, priv->merge_id);
+ priv->merge_id = 0;
+ }
+
+ gtk_ui_manager_ensure_update (ui_manager);
+}
+
+static void
+mail_shell_view_update_actions (EShellView *shell_view)
+{
+ EMailShellView *mail_shell_view;
+ EMailShellSidebar *mail_shell_sidebar;
+ EShellContent *shell_content;
+ EShellSidebar *shell_sidebar;
+ EShellWindow *shell_window;
+ EMFolderTree *folder_tree;
+ EAccount *account = NULL;
+ GtkAction *action;
+ const gchar *label;
+ gchar *uri;
+ gboolean sensitive;
+ guint32 state;
+
+ /* Be descriptive. */
+ gboolean account_is_groupwise;
+ gboolean folder_allows_children;
+ gboolean folder_can_be_deleted;
+ gboolean folder_is_junk;
+ gboolean folder_is_outbox;
+ gboolean folder_is_store;
+ gboolean folder_is_trash;
+
+ mail_shell_view = E_MAIL_SHELL_VIEW (shell_view);
+
+ shell_window = e_shell_view_get_shell_window (shell_view);
+
+ shell_content = e_shell_view_get_shell_content (shell_view);
+ e_mail_reader_update_actions (E_MAIL_READER (shell_content));
+
+ mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
+ folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ state = e_shell_sidebar_check_state (shell_sidebar);
+
+ folder_allows_children =
+ (state & E_MAIL_SHELL_SIDEBAR_FOLDER_ALLOWS_CHILDREN);
+ folder_can_be_deleted =
+ (state & E_MAIL_SHELL_SIDEBAR_FOLDER_CAN_DELETE);
+ folder_is_junk =
+ (state & E_MAIL_SHELL_SIDEBAR_FOLDER_IS_JUNK);
+ folder_is_outbox =
+ (state & E_MAIL_SHELL_SIDEBAR_FOLDER_IS_OUTBOX);
+ folder_is_store =
+ (state & E_MAIL_SHELL_SIDEBAR_FOLDER_IS_STORE);
+ folder_is_trash =
+ (state & E_MAIL_SHELL_SIDEBAR_FOLDER_IS_TRASH);
+
+ uri = em_folder_tree_get_selected_uri (folder_tree);
+ if (uri != NULL) {
+ account = mail_config_get_account_by_source_url (uri);
+
+ /* FIXME This belongs in a GroupWise plugin. */
+ account_is_groupwise =
+ (g_strrstr (uri, "groupwise://") != NULL) &&
+ account != NULL && account->parent_uid != NULL;
+
+ g_free (uri);
+ }
+
+ action = ACTION (MAIL_ACCOUNT_DISABLE);
+ sensitive = (account != NULL) && folder_is_store;
+ if (account_is_groupwise)
+ label = _("Proxy _Logout");
+ else
+ label = _("_Disable Account");
+ gtk_action_set_sensitive (action, sensitive);
+ g_object_set (action, "label", label, NULL);
+
+ action = ACTION (MAIL_EMPTY_TRASH);
+ sensitive = folder_is_trash;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MAIL_FLUSH_OUTBOX);
+ sensitive = folder_is_outbox;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MAIL_FOLDER_COPY);
+ sensitive = !folder_is_store;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MAIL_FOLDER_DELETE);
+ sensitive = !folder_is_store && folder_can_be_deleted;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MAIL_FOLDER_MOVE);
+ sensitive = !folder_is_store && folder_can_be_deleted;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MAIL_FOLDER_NEW);
+ sensitive = folder_allows_children;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MAIL_FOLDER_PROPERTIES);
+ sensitive = !folder_is_store;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MAIL_FOLDER_REFRESH);
+ sensitive = !folder_is_store;
+ gtk_action_set_sensitive (action, sensitive);
+
+ action = ACTION (MAIL_FOLDER_RENAME);
+ sensitive = !folder_is_store && folder_can_be_deleted;
+ gtk_action_set_sensitive (action, sensitive);
+
+ e_mail_shell_view_update_popup_labels (mail_shell_view);
+}
+
+static void
+mail_shell_view_class_init (EMailShellViewClass *class,
+ GTypeModule *type_module)
+{
+ GObjectClass *object_class;
+ EShellViewClass *shell_view_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMailShellViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = mail_shell_view_dispose;
+ object_class->finalize = mail_shell_view_finalize;
+ object_class->constructed = mail_shell_view_constructed;
+
+ shell_view_class = E_SHELL_VIEW_CLASS (class);
+ shell_view_class->label = _("Mail");
+ shell_view_class->icon_name = "evolution-mail";
+ shell_view_class->ui_definition = "evolution-mail.ui";
+ shell_view_class->ui_manager_id = "org.gnome.evolution.mail";
+ shell_view_class->search_options = "/mail-search-options";
+ shell_view_class->search_rules = "searchtypes.xml";
+ shell_view_class->new_shell_content = e_mail_shell_content_new;
+ shell_view_class->new_shell_sidebar = e_mail_shell_sidebar_new;
+ shell_view_class->toggled = mail_shell_view_toggled;
+ shell_view_class->update_actions = mail_shell_view_update_actions;
+}
+
+static void
+mail_shell_view_init (EMailShellView *mail_shell_view,
+ EShellViewClass *shell_view_class)
+{
+ mail_shell_view->priv =
+ E_MAIL_SHELL_VIEW_GET_PRIVATE (mail_shell_view);
+
+ e_mail_shell_view_private_init (mail_shell_view, shell_view_class);
+}
+
+GType
+e_mail_shell_view_get_type (void)
+{
+ return mail_shell_view_type;
+}
+
+void
+e_mail_shell_view_register_type (GTypeModule *type_module)
+{
+ const GTypeInfo type_info = {
+ sizeof (EMailShellViewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) mail_shell_view_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMailShellView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) mail_shell_view_init,
+ NULL /* value_table */
+ };
+
+ mail_shell_view_type = g_type_module_register_type (
+ type_module, E_TYPE_SHELL_VIEW,
+ "EMailShellView", &type_info, 0);
+}
diff --git a/modules/mail/e-mail-shell-view.h b/modules/mail/e-mail-shell-view.h
new file mode 100644
index 0000000000..d20bde74a6
--- /dev/null
+++ b/modules/mail/e-mail-shell-view.h
@@ -0,0 +1,72 @@
+/*
+ * e-mail-shell-view.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SHELL_VIEW_H
+#define E_MAIL_SHELL_VIEW_H
+
+#include <shell/e-shell-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_SHELL_VIEW \
+ (e_mail_shell_view_get_type ())
+#define E_MAIL_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MAIL_SHELL_VIEW, EMailShellView))
+#define E_MAIL_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MAIL_SHELL_VIEW, EMailShellViewClass))
+#define E_IS_MAIL_SHELL_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MAIL_SHELL_VIEW))
+#define E_IS_MAIL_SHELL_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MAIL_SHELL_VIEW))
+#define E_MAIL_SHELL_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MAIL_SHELL_VIEW, EMailShellViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailShellView EMailShellView;
+typedef struct _EMailShellViewClass EMailShellViewClass;
+typedef struct _EMailShellViewPrivate EMailShellViewPrivate;
+
+struct _EMailShellView {
+ EShellView parent;
+ EMailShellViewPrivate *priv;
+};
+
+struct _EMailShellViewClass {
+ EShellViewClass parent_class;
+};
+
+GType e_mail_shell_view_get_type (void);
+void e_mail_shell_view_register_type
+ (GTypeModule *type_module);
+gboolean e_mail_shell_view_get_show_deleted
+ (EMailShellView *mail_shell_view);
+void e_mail_shell_view_set_show_deleted
+ (EMailShellView *mail_shell_view,
+ gboolean show_deleted);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SHELL_VIEW_H */
diff --git a/modules/mail/em-account-editor.c b/modules/mail/em-account-editor.c
new file mode 100644
index 0000000000..188a2d475f
--- /dev/null
+++ b/modules/mail/em-account-editor.c
@@ -0,0 +1,3113 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Dan Winship <danw@ximian.com>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/*
+ work before merge can occur:
+
+ verify behaviour.
+ work out what to do with the startup druid.
+
+ also need to work out:
+ how to remove unecessary items from a service url once
+ configured (removing settings from other types).
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <string.h>
+#include <stdarg.h>
+
+#include <gconf/gconf-client.h>
+
+#include <glade/glade.h>
+
+#include <libgnomeui/gnome-druid.h>
+#include <libgnomeui/gnome-druid-page-standard.h>
+
+#include "e-util/e-error.h"
+#include "e-util/e-account-utils.h"
+#include "e-util/e-signature-list.h"
+#include "e-util/e-signature-utils.h"
+#include "e-util/e-util-private.h"
+#include "misc/e-signature-editor.h"
+
+#include "e-mail-local.h"
+#include "em-config.h"
+#include "em-folder-selection-button.h"
+#include "em-account-editor.h"
+#include "mail-session.h"
+#include "mail-send-recv.h"
+#include "em-utils.h"
+#include "em-composer-prefs.h"
+#include "mail-config.h"
+#include "mail-ops.h"
+#include "mail-mt.h"
+
+#if defined (HAVE_NSS)
+#include "smime/gui/e-cert-selector.h"
+#endif
+
+#define d(x)
+
+/* econfig item for the extra config hings */
+struct _receive_options_item {
+ EMConfigItem item;
+
+ /* Only CAMEL_PROVIDER_CONF_ENTRYs GtkEntrys are stored here.
+ The auto-detect camel provider code will probably be removed */
+ GHashTable *extra_table;
+};
+
+typedef struct _EMAccountEditorService {
+ EMAccountEditor *emae; /* parent pointer, for callbacks */
+
+ /* NOTE: keep all widgets together, first frame last check_dialog */
+ GtkWidget *frame;
+ GtkWidget *container;
+
+ GtkComboBox *providers;
+
+ GtkLabel *description;
+ GtkLabel *hostlabel;
+ GtkEntry *hostname;
+ GtkLabel *userlabel;
+ GtkEntry *username;
+ GtkEntry *path;
+ GtkLabel *pathlabel;
+ GtkWidget *pathentry;
+
+ GtkWidget *ssl_frame;
+ GtkComboBox *use_ssl;
+ GtkWidget *ssl_hbox;
+ GtkWidget *no_ssl;
+
+ GtkWidget *auth_frame;
+ GtkComboBox *authtype;
+
+ GtkWidget *authitem;
+ GtkToggleButton *remember;
+ GtkButton *check_supported;
+ GtkToggleButton *needs_auth;
+
+ GtkWidget *check_dialog;
+ gint check_id;
+
+ GList *authtypes; /* if "Check supported" */
+ CamelProvider *provider;
+ CamelProviderType type;
+
+ gint auth_changed_id;
+} EMAccountEditorService;
+
+struct _EMAccountEditorPrivate {
+ struct _EMConfig *config;
+ GList *providers;
+
+ /* signatures */
+ GtkComboBox *signatures_dropdown;
+ guint sig_added_id;
+ guint sig_removed_id;
+ guint sig_changed_id;
+ const gchar *sig_uid;
+
+ /* incoming mail */
+ EMAccountEditorService source;
+
+ /* extra incoming config */
+ CamelProvider *extra_provider;
+ GSList *extra_items; /* this is freed by the econfig automatically */
+
+ /* outgoing mail */
+ EMAccountEditorService transport;
+
+ /* account management */
+ GtkEntry *identity_entries[5];
+ GtkToggleButton *default_account;
+ GtkWidget *management_frame;
+
+ /* special folders */
+ GtkButton *drafts_folder_button;
+ GtkButton *sent_folder_button;
+ GtkButton *restore_folders_button;
+
+ /* Security */
+ GtkEntry *pgp_key;
+ GtkToggleButton *pgp_encrypt_to_self;
+ GtkToggleButton *pgp_always_sign;
+ GtkToggleButton *pgp_no_imip_sign;
+ GtkToggleButton *pgp_always_trust;
+
+ GtkToggleButton *smime_sign_default;
+ GtkEntry *smime_sign_key;
+ GtkButton *smime_sign_key_select;
+ GtkButton *smime_sign_key_clear;
+ GtkButton *smime_sign_select;
+ GtkToggleButton *smime_encrypt_default;
+ GtkToggleButton *smime_encrypt_to_self;
+ GtkEntry *smime_encrypt_key;
+ GtkButton *smime_encrypt_key_select;
+ GtkButton *smime_encrypt_key_clear;
+
+ /* for e-config callbacks, each page sets up its widgets, then they are dealed out by the get_widget callback in order*/
+ GtkWidget *widgets[5];
+ const gchar *widgets_name[5];
+ gint widgets_index;
+
+ /* for druid page preparation */
+ guint identity_set:1;
+ guint receive_set:1;
+ guint management_set:1;
+};
+
+static void emae_refresh_authtype(EMAccountEditor *emae, EMAccountEditorService *service);
+static void em_account_editor_construct(EMAccountEditor *emae, EAccount *account, em_account_editor_t type, const gchar *id);
+static void emae_account_folder_changed(EMFolderSelectionButton *folder, EMAccountEditor *emae);
+static GtkVBoxClass *emae_parent;
+
+static void
+emae_init(GObject *o)
+{
+ EMAccountEditor *emae = (EMAccountEditor *)o;
+
+ emae->priv = g_malloc0(sizeof(*emae->priv));
+
+ emae->priv->source.emae = emae;
+ emae->priv->transport.emae = emae;
+}
+
+static void
+emae_finalise(GObject *o)
+{
+ EMAccountEditor *emae = (EMAccountEditor *)o;
+ EMAccountEditorPrivate *p = emae->priv;
+
+ if (p->sig_added_id) {
+ ESignatureList *signatures = e_get_signature_list ();
+
+ g_signal_handler_disconnect(signatures, p->sig_added_id);
+ g_signal_handler_disconnect(signatures, p->sig_removed_id);
+ g_signal_handler_disconnect(signatures, p->sig_changed_id);
+ }
+
+ g_list_free(p->source.authtypes);
+ g_list_free(p->transport.authtypes);
+
+ g_list_free(p->providers);
+ g_free(p);
+
+ g_object_unref(emae->account);
+ if (emae->original)
+ g_object_unref(emae->original);
+
+ ((GObjectClass *)emae_parent)->finalize(o);
+}
+
+static void
+emae_class_init(GObjectClass *klass)
+{
+ klass->finalize = emae_finalise;
+}
+
+GType
+em_account_editor_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMAccountEditorClass),
+ NULL, NULL,
+ (GClassInitFunc)emae_class_init,
+ NULL, NULL,
+ sizeof(EMAccountEditor), 0,
+ (GInstanceInitFunc)emae_init
+ };
+ emae_parent = g_type_class_ref(G_TYPE_OBJECT);
+ type = g_type_register_static(G_TYPE_OBJECT, "EMAccountEditor", &info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * em_account_editor_new:
+ * @account:
+ * @type:
+ *
+ * Create a new account editor. If @account is NULL then this is to
+ * create a new account, else @account is copied to a working
+ * structure and is for editing an existing account.
+ *
+ * Return value:
+ **/
+EMAccountEditor *em_account_editor_new(EAccount *account, em_account_editor_t type, const gchar *id)
+{
+ EMAccountEditor *emae = g_object_new(em_account_editor_get_type(), NULL);
+
+ em_account_editor_construct(emae, account, type, id);
+
+ return emae;
+}
+
+/**
+ * em_account_editor_new_for_pages:
+ * @account:
+ * @type:
+ *
+ * Create a new account editor. If @account is NULL then this is to
+ * create a new account, else @account is copied to a working
+ * structure and is for editing an existing account.
+ *
+ * Return value:
+ **/
+EMAccountEditor *em_account_editor_new_for_pages(EAccount *account, em_account_editor_t type, gchar *id, GtkWidget **pages)
+{
+ EMAccountEditor *emae = g_object_new(em_account_editor_get_type(), NULL);
+ emae->pages = pages;
+ em_account_editor_construct(emae, account, type, id);
+
+ return emae;
+}
+
+/* ********************************************************************** */
+
+static struct {
+ const gchar *label;
+ const gchar *value;
+} ssl_options[] = {
+ /* Translators: This string is a "Use secure connection" option for
+ the Mailer. It will not use an encrypted connection. */
+ { N_("No encryption"), "never" },
+ /* Translators: This string is a "Use secure connection" option for
+ the Mailer. TLS (Transport Layer Security) is commonly known by
+ this abbreviation. */
+ { N_("TLS encryption"), "when-possible" },
+ /* Translators: This string is a "Use secure connection" option for
+ the Mailer. SSL (Secure Sockets Layer) is commonly known by this
+ abbreviation. */
+ { N_("SSL encryption"), "always" }
+};
+
+#define num_ssl_options (sizeof (ssl_options) / sizeof (ssl_options[0]))
+
+static gboolean
+is_email (const gchar *address)
+{
+ /* This is supposed to check if the address's domain could be
+ an FQDN but alas, it's not worth the pain and suffering. */
+ const gchar *at;
+
+ at = strchr (address, '@');
+ /* make sure we have an '@' and that it's not the first or last gchar */
+ if (!at || at == address || *(at + 1) == '\0')
+ return FALSE;
+
+ return TRUE;
+}
+
+static CamelURL *
+emae_account_url(EMAccountEditor *emae, gint urlid)
+{
+ CamelURL *url = NULL;
+ const gchar *uri;
+
+ uri = e_account_get_string(emae->account, urlid);
+
+ if (uri && uri[0])
+ url = camel_url_new(uri, NULL);
+
+ if (url == NULL) {
+ url = camel_url_new("dummy:", NULL);
+ camel_url_set_protocol(url, NULL);
+ }
+
+ return url;
+}
+
+/* ********************************************************************** */
+static void
+emae_license_state(GtkToggleButton *button, GtkDialog *dialog)
+{
+ gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_ACCEPT,
+ gtk_toggle_button_get_active(button));
+}
+
+static gboolean
+emae_load_text(GtkTextView *view, const gchar *filename)
+{
+ FILE *fd;
+ gchar filebuf[1024];
+ GtkTextIter iter;
+ GtkTextBuffer *buffer;
+ gint count;
+
+ g_return_val_if_fail (filename != NULL , FALSE);
+
+ fd = g_fopen (filename, "r");
+ if (fd) {
+ buffer = gtk_text_buffer_new (NULL);
+ gtk_text_buffer_get_start_iter (buffer, &iter);
+ while (!feof (fd) && !ferror (fd)) {
+ count = fread (filebuf, 1, sizeof (filebuf), fd);
+ gtk_text_buffer_insert (buffer, &iter, filebuf, count);
+ }
+
+ gtk_text_view_set_buffer(GTK_TEXT_VIEW (view), GTK_TEXT_BUFFER(buffer));
+ fclose (fd);
+ }
+
+ return fd != NULL;
+}
+
+static gboolean
+emae_display_license(EMAccountEditor *emae, CamelProvider *prov)
+{
+ GladeXML *xml;
+ GtkWidget *w, *dialog;
+ gchar *tmp;
+ GtkResponseType response = GTK_RESPONSE_NONE;
+ gchar *gladefile;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-dialogs.glade",
+ NULL);
+ xml = glade_xml_new (gladefile, "license_dialog", NULL);
+ g_free (gladefile);
+
+ dialog = glade_xml_get_widget(xml, "license_dialog");
+ gtk_dialog_set_response_sensitive((GtkDialog *)dialog, GTK_RESPONSE_ACCEPT, FALSE);
+ tmp = g_strdup_printf(_("%s License Agreement"), prov->license);
+ gtk_window_set_title((GtkWindow *)dialog, tmp);
+ g_free(tmp);
+
+ g_signal_connect(glade_xml_get_widget(xml, "license_checkbutton"),
+ "toggled", G_CALLBACK(emae_license_state), dialog);
+
+ tmp = g_strdup_printf(_("\nPlease read carefully the license agreement\n"
+ "for %s displayed below\n"
+ "and tick the check box for accepting it\n"), prov->license);
+ gtk_label_set_text((GtkLabel *)glade_xml_get_widget(xml, "license_top_label"), tmp);
+ g_free(tmp);
+
+ w = glade_xml_get_widget(xml, "license_textview");
+ if (emae_load_text((GtkTextView *)w, prov->license_file)) {
+ gtk_text_view_set_editable((GtkTextView *)w, FALSE);
+ response = gtk_dialog_run((GtkDialog *)dialog);
+ } else {
+ e_error_run(emae->editor ? (GtkWindow *)gtk_widget_get_toplevel(emae->editor) : NULL,
+ "mail:no-load-license", prov->license_file, NULL);
+ }
+
+ gtk_widget_destroy(dialog);
+ g_object_unref(xml);
+
+ return (response == GTK_RESPONSE_ACCEPT);
+}
+
+static gboolean
+emae_check_license(EMAccountEditor *emae, CamelProvider *prov)
+{
+ gboolean accepted = TRUE;
+
+ if (prov->flags & CAMEL_PROVIDER_HAS_LICENSE) {
+ GConfClient *gconf = mail_config_get_gconf_client();
+ GSList *providers_list, *l;
+
+ providers_list = gconf_client_get_list (gconf, "/apps/evolution/mail/licenses", GCONF_VALUE_STRING, NULL);
+
+ for (l = providers_list, accepted = FALSE; l && !accepted; l = g_slist_next(l))
+ accepted = (strcmp((gchar *)l->data, prov->protocol) == 0);
+
+ if (!accepted
+ && (accepted = emae_display_license(emae, prov)) == TRUE) {
+ providers_list = g_slist_append(providers_list, g_strdup(prov->protocol));
+ gconf_client_set_list(gconf,
+ "/apps/evolution/mail/licenses",
+ GCONF_VALUE_STRING,
+ providers_list, NULL);
+ }
+
+ g_slist_foreach(providers_list, (GFunc)g_free, NULL);
+ g_slist_free(providers_list);
+ }
+
+ return accepted;
+}
+
+static void
+default_folders_clicked (GtkButton *button, gpointer user_data)
+{
+ EMAccountEditor *emae = user_data;
+ const gchar *uri;
+
+ uri = e_mail_local_get_folder_uri (E_MAIL_FOLDER_DRAFTS);
+ em_folder_selection_button_set_selection((EMFolderSelectionButton *)emae->priv->drafts_folder_button, uri);
+ emae_account_folder_changed((EMFolderSelectionButton *)emae->priv->drafts_folder_button, emae);
+
+ uri = e_mail_local_get_folder_uri (E_MAIL_FOLDER_SENT);
+ em_folder_selection_button_set_selection((EMFolderSelectionButton *)emae->priv->sent_folder_button, uri);
+ emae_account_folder_changed((EMFolderSelectionButton *)emae->priv->sent_folder_button, emae);
+}
+
+/* custom widget factories */
+GtkWidget *em_account_editor_folder_selector_button_new (gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2);
+
+GtkWidget *
+em_account_editor_folder_selector_button_new (gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2)
+{
+ return (GtkWidget *)em_folder_selection_button_new (
+ string1 ? string1 : _("Select Folder"), NULL);
+}
+
+GtkWidget *em_account_editor_dropdown_new(gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2);
+
+GtkWidget *
+em_account_editor_dropdown_new(gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2)
+{
+ return gtk_combo_box_new ();
+}
+
+GtkWidget *em_account_editor_ssl_selector_new(gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2);
+
+GtkWidget *
+em_account_editor_ssl_selector_new(gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2)
+{
+ GtkComboBox *dropdown = (GtkComboBox *)gtk_combo_box_new();
+ GtkCellRenderer *cell = gtk_cell_renderer_text_new();
+ GtkListStore *store;
+ gint i;
+ GtkTreeIter iter;
+
+ gtk_widget_show((GtkWidget *)dropdown);
+
+ store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+
+ for (i=0;i<num_ssl_options;i++) {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, _(ssl_options[i].label), 1, ssl_options[i].value, -1);
+ }
+
+ gtk_cell_layout_pack_start((GtkCellLayout *)dropdown, cell, TRUE);
+ gtk_cell_layout_set_attributes((GtkCellLayout *)dropdown, cell, "text", 0, NULL);
+
+ gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store);
+
+ return (GtkWidget *)dropdown;
+}
+
+/* The camel provider auto-detect interface should be deprecated.
+ But it still needs to be replaced with something of similar functionality.
+ Just using the normal econfig plugin mechanism should be adequate. */
+static void
+emae_auto_detect_free (gpointer key, gpointer value, gpointer user_data)
+{
+ g_free (key);
+ g_free (value);
+}
+
+static void
+emae_auto_detect(EMAccountEditor *emae)
+{
+ EMAccountEditorPrivate *gui = emae->priv;
+ EMAccountEditorService *service = &gui->source;
+ GHashTable *auto_detected;
+ GSList *l;
+ CamelProviderConfEntry *entries;
+ gchar *value;
+ gint i;
+ CamelURL *url;
+
+ if (service->provider == NULL
+ || (entries = service->provider->extra_conf) == NULL)
+ return;
+
+ d(printf("Running auto-detect\n"));
+
+ url = emae_account_url(emae, E_ACCOUNT_SOURCE_URL);
+ camel_provider_auto_detect(service->provider, url, &auto_detected, NULL);
+ camel_url_free(url);
+ if (auto_detected == NULL) {
+ d(printf(" no values detected\n"));
+ return;
+ }
+
+ for (i = 0; entries[i].type != CAMEL_PROVIDER_CONF_END; i++) {
+ struct _receive_options_item *item;
+ GtkWidget *w;
+
+ if (entries[i].name == NULL
+ || (value = g_hash_table_lookup (auto_detected, entries[i].name)) == NULL)
+ continue;
+
+ /* only 2 providers use this, and they only do it for 3 entries only */
+ g_return_if_fail (entries[i].type == CAMEL_PROVIDER_CONF_ENTRY);
+
+ w = NULL;
+ for (l = emae->priv->extra_items;l;l=g_slist_next(l)) {
+ item = l->data;
+ if (item->extra_table && (w = g_hash_table_lookup(item->extra_table, entries[i].name)))
+ break;
+ }
+
+ gtk_entry_set_text((GtkEntry *)w, value?value:"");
+ }
+
+ g_hash_table_foreach(auto_detected, emae_auto_detect_free, NULL);
+ g_hash_table_destroy(auto_detected);
+}
+
+static gint
+provider_compare (const CamelProvider *p1, const CamelProvider *p2)
+{
+ /* sort providers based on "location" (ie. local or remote) */
+ if (p1->flags & CAMEL_PROVIDER_IS_REMOTE) {
+ if (p2->flags & CAMEL_PROVIDER_IS_REMOTE)
+ return 0;
+ return -1;
+ } else {
+ if (p2->flags & CAMEL_PROVIDER_IS_REMOTE)
+ return 1;
+ return 0;
+ }
+}
+
+static void
+emae_signature_added(ESignatureList *signatures, ESignature *sig, EMAccountEditor *emae)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_combo_box_get_model(emae->priv->signatures_dropdown);
+
+ gtk_list_store_append((GtkListStore *)model, &iter);
+ gtk_list_store_set((GtkListStore *)model, &iter, 0, sig->autogen?_("Autogenerated"):sig->name, 1, sig->uid, -1);
+
+ gtk_combo_box_set_active(emae->priv->signatures_dropdown, gtk_tree_model_iter_n_children(model, NULL)-1);
+}
+
+static gint
+emae_signature_get_iter(EMAccountEditor *emae, ESignature *sig, GtkTreeModel **modelp, GtkTreeIter *iter)
+{
+ GtkTreeModel *model;
+ gint found = 0;
+
+ model = gtk_combo_box_get_model(emae->priv->signatures_dropdown);
+ *modelp = model;
+ if (!gtk_tree_model_get_iter_first(model, iter))
+ return FALSE;
+
+ do {
+ gchar *uid;
+
+ gtk_tree_model_get(model, iter, 1, &uid, -1);
+ if (uid && !strcmp(uid, sig->uid))
+ found = TRUE;
+ g_free(uid);
+ } while (!found && gtk_tree_model_iter_next(model, iter));
+
+ return found;
+}
+
+static void
+emae_signature_removed(ESignatureList *signatures, ESignature *sig, EMAccountEditor *emae)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (emae_signature_get_iter(emae, sig, &model, &iter))
+ gtk_list_store_remove((GtkListStore *)model, &iter);
+}
+
+static void
+emae_signature_changed(ESignatureList *signatures, ESignature *sig, EMAccountEditor *emae)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (emae_signature_get_iter(emae, sig, &model, &iter))
+ gtk_list_store_set((GtkListStore *)model, &iter, 0, sig->autogen?_("Autogenerated"):sig->name, -1);
+}
+
+static void
+emae_signaturetype_changed(GtkComboBox *dropdown, EMAccountEditor *emae)
+{
+ gint id = gtk_combo_box_get_active(dropdown);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *uid = NULL;
+
+ if (id != -1) {
+ model = gtk_combo_box_get_model(dropdown);
+ if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id))
+ gtk_tree_model_get(model, &iter, 1, &uid, -1);
+ }
+
+ e_account_set_string(emae->account, E_ACCOUNT_ID_SIGNATURE, uid);
+ g_free(uid);
+}
+
+static void
+emae_signature_new(GtkWidget *w, EMAccountEditor *emae)
+{
+ EShell *shell;
+ EShellSettings *shell_settings;
+ GtkWidget *parent;
+ gboolean html_mode;
+
+ shell = e_shell_get_default ();
+ shell_settings = e_shell_get_shell_settings (shell);
+ parent = gtk_widget_get_toplevel (w);
+
+ html_mode = e_shell_settings_get_boolean (
+ shell_settings, "composer-format-html");
+
+ em_composer_prefs_new_signature (GTK_WINDOW (parent), html_mode);
+}
+
+static GtkWidget *
+emae_setup_signatures(EMAccountEditor *emae, GladeXML *xml)
+{
+ EMAccountEditorPrivate *p = emae->priv;
+ GtkComboBox *dropdown = (GtkComboBox *)glade_xml_get_widget(xml, "signature_dropdown");
+ GtkCellRenderer *cell = gtk_cell_renderer_text_new();
+ GtkListStore *store;
+ gint i, active=0;
+ GtkTreeIter iter;
+ ESignatureList *signatures;
+ EIterator *it;
+ const gchar *current = e_account_get_string(emae->account, E_ACCOUNT_ID_SIGNATURE);
+ GtkWidget *button;
+
+ emae->priv->signatures_dropdown = dropdown;
+ gtk_widget_show((GtkWidget *)dropdown);
+
+ store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, _("None"), 1, NULL, -1);
+
+ signatures = e_get_signature_list ();
+
+ if (p->sig_added_id == 0) {
+ p->sig_added_id = g_signal_connect(signatures, "signature-added", G_CALLBACK(emae_signature_added), emae);
+ p->sig_removed_id = g_signal_connect(signatures, "signature-removed", G_CALLBACK(emae_signature_removed), emae);
+ p->sig_changed_id = g_signal_connect(signatures, "signature-changed", G_CALLBACK(emae_signature_changed), emae);
+ }
+
+ /* we need to count the 'none' entry before using the index */
+ i = 1;
+ it = e_list_get_iterator ((EList *) signatures);
+ while (e_iterator_is_valid (it)) {
+ ESignature *sig = (ESignature *)e_iterator_get(it);
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, sig->autogen?_("Autogenerated"):sig->name, 1, sig->uid, -1);
+
+ if (current && !strcmp(current, sig->uid))
+ active = i;
+
+ e_iterator_next(it);
+ i++;
+ }
+ g_object_unref (it);
+
+ gtk_cell_layout_pack_start((GtkCellLayout *)dropdown, cell, TRUE);
+ gtk_cell_layout_set_attributes((GtkCellLayout *)dropdown, cell, "text", 0, NULL);
+
+ gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store);
+ gtk_combo_box_set_active(dropdown, active);
+
+ g_signal_connect(dropdown, "changed", G_CALLBACK(emae_signaturetype_changed), emae);
+ gtk_widget_set_sensitive((GtkWidget *)dropdown, e_account_writable(emae->account, E_ACCOUNT_ID_SIGNATURE));
+
+ button = glade_xml_get_widget(xml, "sigAddNew");
+ g_signal_connect(button, "clicked", G_CALLBACK(emae_signature_new), emae);
+ gtk_widget_set_sensitive(button,
+ gconf_client_key_is_writable(mail_config_get_gconf_client(),
+ "/apps/evolution/mail/signatures", NULL));
+
+ return (GtkWidget *)dropdown;
+}
+
+static void
+emae_receipt_policy_changed(GtkComboBox *dropdown, EMAccountEditor *emae)
+{
+ gint id = gtk_combo_box_get_active(dropdown);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ EAccountReceiptPolicy policy;
+
+ if (id != -1) {
+ model = gtk_combo_box_get_model(dropdown);
+ if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) {
+ gtk_tree_model_get(model, &iter, 1, &policy, -1);
+ e_account_set_int (emae->account, E_ACCOUNT_RECEIPT_POLICY, policy);
+ }
+ }
+
+}
+
+static GtkWidget *
+emae_setup_receipt_policy (EMAccountEditor *emae, GladeXML *xml)
+{
+ GtkComboBox *dropdown = (GtkComboBox *)glade_xml_get_widget(xml, "receipt_policy_dropdown");
+ GtkListStore *store;
+ gint i = 0, active = 0;
+ GtkTreeIter iter;
+ EAccountReceiptPolicy current = emae->account->receipt_policy;
+ static struct {
+ EAccountReceiptPolicy policy;
+ const gchar *label;
+ } receipt_policies[] = {
+ { E_ACCOUNT_RECEIPT_NEVER, N_("Never") },
+ { E_ACCOUNT_RECEIPT_ALWAYS, N_("Always") },
+ { E_ACCOUNT_RECEIPT_ASK, N_("Ask for each message") }
+ };
+
+ gtk_widget_show((GtkWidget *)dropdown);
+
+ store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
+
+ for (i = 0; i < 3; ++i) {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ 0, _(receipt_policies[i].label),
+ 1, receipt_policies[i].policy,
+ -1);
+ if (current == receipt_policies[i].policy)
+ active = i;
+ }
+
+ gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store);
+ gtk_combo_box_set_active(dropdown, active);
+
+ g_signal_connect(dropdown, "changed", G_CALLBACK(emae_receipt_policy_changed), emae);
+ gtk_widget_set_sensitive((GtkWidget *)dropdown, e_account_writable(emae->account, E_ACCOUNT_RECEIPT_POLICY));
+
+ return (GtkWidget *)dropdown;
+}
+
+static void
+emae_account_entry_changed(GtkEntry *entry, EMAccountEditor *emae)
+{
+ gint item = GPOINTER_TO_INT(g_object_get_data((GObject *)entry, "account-item"));
+
+ e_account_set_string(emae->account, item, gtk_entry_get_text(entry));
+}
+
+static GtkEntry *
+emae_account_entry(EMAccountEditor *emae, const gchar *name, gint item, GladeXML *xml)
+{
+ GtkEntry *entry;
+ const gchar *text;
+
+ entry = (GtkEntry *)glade_xml_get_widget(xml, name);
+ text = e_account_get_string(emae->account, item);
+ if (text)
+ gtk_entry_set_text(entry, text);
+ g_object_set_data((GObject *)entry, "account-item", GINT_TO_POINTER(item));
+ g_signal_connect(entry, "changed", G_CALLBACK(emae_account_entry_changed), emae);
+ gtk_widget_set_sensitive((GtkWidget *)entry, e_account_writable(emae->account, item));
+
+ return entry;
+}
+
+static void
+emae_account_toggle_changed(GtkToggleButton *toggle, EMAccountEditor *emae)
+{
+ gint item = GPOINTER_TO_INT(g_object_get_data((GObject *)toggle, "account-item"));
+
+ e_account_set_bool(emae->account, item, gtk_toggle_button_get_active(toggle));
+}
+
+static void
+emae_account_toggle_widget(EMAccountEditor *emae, GtkToggleButton *toggle, gint item)
+{
+ gtk_toggle_button_set_active(toggle, e_account_get_bool(emae->account, item));
+ g_object_set_data((GObject *)toggle, "account-item", GINT_TO_POINTER(item));
+ g_signal_connect(toggle, "toggled", G_CALLBACK(emae_account_toggle_changed), emae);
+ gtk_widget_set_sensitive((GtkWidget *)toggle, e_account_writable(emae->account, item));
+}
+
+static GtkToggleButton *
+emae_account_toggle(EMAccountEditor *emae, const gchar *name, gint item, GladeXML *xml)
+{
+ GtkToggleButton *toggle;
+
+ toggle = (GtkToggleButton *)glade_xml_get_widget(xml, name);
+ emae_account_toggle_widget(emae, toggle, item);
+
+ return toggle;
+}
+
+static void
+emae_account_spinint_changed(GtkSpinButton *spin, EMAccountEditor *emae)
+{
+ gint item = GPOINTER_TO_INT(g_object_get_data((GObject *)spin, "account-item"));
+
+ e_account_set_int(emae->account, item, gtk_spin_button_get_value(spin));
+}
+
+static void
+emae_account_spinint_widget(EMAccountEditor *emae, GtkSpinButton *spin, gint item)
+{
+ gtk_spin_button_set_value(spin, e_account_get_int(emae->account, item));
+ g_object_set_data((GObject *)spin, "account-item", GINT_TO_POINTER(item));
+ g_signal_connect(spin, "value_changed", G_CALLBACK(emae_account_spinint_changed), emae);
+ gtk_widget_set_sensitive((GtkWidget *)spin, e_account_writable(emae->account, item));
+}
+
+#if 0
+static GtkSpinButton *
+emae_account_spinint(EMAccountEditor *emae, const gchar *name, gint item)
+{
+ GtkSpinButton *spin;
+
+ spin = (GtkSpinButton *)glade_xml_get_widget(emae->priv->xml, name);
+ emae_account_spinint_widget(emae, spin, item);
+
+ return spin;
+}
+#endif
+
+static void
+emae_account_folder_changed(EMFolderSelectionButton *folder, EMAccountEditor *emae)
+{
+ gint item = GPOINTER_TO_INT(g_object_get_data((GObject *)folder, "account-item"));
+
+ e_account_set_string(emae->account, item, em_folder_selection_button_get_selection(folder));
+}
+
+static EMFolderSelectionButton *
+emae_account_folder(EMAccountEditor *emae, const gchar *name, gint item, gint deffolder, GladeXML *xml)
+{
+ EMFolderSelectionButton *folder;
+ const gchar *uri;
+
+ folder = (EMFolderSelectionButton *)glade_xml_get_widget(xml, name);
+ uri = e_account_get_string(emae->account, item);
+ if (uri) {
+ gchar *tmp = em_uri_to_camel(uri);
+
+ em_folder_selection_button_set_selection(folder, tmp);
+ g_free(tmp);
+ } else {
+ const gchar *uri;
+
+ uri = e_mail_local_get_folder_uri (deffolder);
+ em_folder_selection_button_set_selection(folder, uri);
+ }
+
+ g_object_set_data((GObject *)folder, "account-item", GINT_TO_POINTER(item));
+ g_object_set_data((GObject *)folder, "folder-default", GINT_TO_POINTER(deffolder));
+ g_signal_connect(folder, "selected", G_CALLBACK(emae_account_folder_changed), emae);
+ gtk_widget_show((GtkWidget *)folder);
+
+ gtk_widget_set_sensitive((GtkWidget *)folder, e_account_writable(emae->account, item));
+
+ return folder;
+}
+
+#if defined (HAVE_NSS)
+static void
+smime_changed(EMAccountEditor *emae)
+{
+ EMAccountEditorPrivate *gui = emae->priv;
+ gint act;
+ const gchar *tmp;
+
+ tmp = gtk_entry_get_text(gui->smime_sign_key);
+ act = tmp && tmp[0];
+ gtk_widget_set_sensitive((GtkWidget *)gui->smime_sign_key_clear, act);
+ gtk_widget_set_sensitive((GtkWidget *)gui->smime_sign_default, act);
+ if (!act)
+ gtk_toggle_button_set_active(gui->smime_sign_default, FALSE);
+
+ tmp = gtk_entry_get_text(gui->smime_encrypt_key);
+ act = tmp && tmp[0];
+ gtk_widget_set_sensitive((GtkWidget *)gui->smime_encrypt_key_clear, act);
+ gtk_widget_set_sensitive((GtkWidget *)gui->smime_encrypt_default, act);
+ gtk_widget_set_sensitive((GtkWidget *)gui->smime_encrypt_to_self, act);
+ if (!act) {
+ gtk_toggle_button_set_active(gui->smime_encrypt_default, FALSE);
+ gtk_toggle_button_set_active(gui->smime_encrypt_to_self, FALSE);
+ }
+}
+
+static void
+smime_sign_key_selected(GtkWidget *dialog, const gchar *key, EMAccountEditor *emae)
+{
+ EMAccountEditorPrivate *gui = emae->priv;
+
+ if (key != NULL) {
+ gtk_entry_set_text(gui->smime_sign_key, key);
+ smime_changed(emae);
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static void
+smime_sign_key_select(GtkWidget *button, EMAccountEditor *emae)
+{
+ EMAccountEditorPrivate *gui = emae->priv;
+ GtkWidget *w;
+
+ w = e_cert_selector_new(E_CERT_SELECTOR_SIGNER, gtk_entry_get_text(gui->smime_sign_key));
+ gtk_window_set_modal((GtkWindow *)w, TRUE);
+ gtk_window_set_transient_for((GtkWindow *)w, (GtkWindow *)gtk_widget_get_toplevel(button));
+ g_signal_connect(w, "selected", G_CALLBACK(smime_sign_key_selected), emae);
+ gtk_widget_show(w);
+}
+
+static void
+smime_sign_key_clear(GtkWidget *w, EMAccountEditor *emae)
+{
+ EMAccountEditorPrivate *gui = emae->priv;
+
+ gtk_entry_set_text(gui->smime_sign_key, "");
+ smime_changed(emae);
+}
+
+static void
+smime_encrypt_key_selected(GtkWidget *dialog, const gchar *key, EMAccountEditor *emae)
+{
+ EMAccountEditorPrivate *gui = emae->priv;
+
+ if (key != NULL) {
+ gtk_entry_set_text(gui->smime_encrypt_key, key);
+ smime_changed(emae);
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static void
+smime_encrypt_key_select(GtkWidget *button, EMAccountEditor *emae)
+{
+ EMAccountEditorPrivate *gui = emae->priv;
+ GtkWidget *w;
+
+ w = e_cert_selector_new(E_CERT_SELECTOR_SIGNER, gtk_entry_get_text(gui->smime_encrypt_key));
+ gtk_window_set_modal((GtkWindow *)w, TRUE);
+ gtk_window_set_transient_for((GtkWindow *)w, (GtkWindow *)gtk_widget_get_toplevel(button));
+ g_signal_connect(w, "selected", G_CALLBACK(smime_encrypt_key_selected), emae);
+ gtk_widget_show(w);
+}
+
+static void
+smime_encrypt_key_clear(GtkWidget *w, EMAccountEditor *emae)
+{
+ EMAccountEditorPrivate *gui = emae->priv;
+
+ gtk_entry_set_text(gui->smime_encrypt_key, "");
+ smime_changed(emae);
+}
+#endif
+
+static void
+emae_url_set_hostport(CamelURL *url, const gchar *txt)
+{
+ const gchar *port;
+ gchar *host;
+
+ /* FIXME: what if this was a raw IPv6 address? */
+ if (txt && (port = strchr(txt, ':'))) {
+ camel_url_set_port(url, atoi(port+1));
+ host = g_strdup(txt);
+ host[port-txt] = 0;
+ } else {
+ /* "" is converted to NULL, but if we set NULL on the url,
+ camel_url_to_string strips lots of details */
+ host = g_strdup((txt?txt:""));
+ camel_url_set_port (url, 0);
+ }
+
+ g_strstrip(host);
+ if (txt && *txt)
+ camel_url_set_host(url, host);
+
+ g_free(host);
+}
+
+/* This is used to map a funciton which will set on the url a string value.
+ if widgets[0] is set, it is the entry which will be called against setval()
+ We need our own function for host:port decoding, as above */
+struct _provider_host_info {
+ guint32 flag;
+ void (*setval)(CamelURL *, const gchar *);
+ glong widgets[3];
+};
+
+static struct _provider_host_info emae_source_host_info[] = {
+ { CAMEL_URL_PART_HOST, emae_url_set_hostport, { G_STRUCT_OFFSET(EMAccountEditorService, hostname), G_STRUCT_OFFSET(EMAccountEditorService, hostlabel), }, },
+ { CAMEL_URL_PART_USER, camel_url_set_user, { G_STRUCT_OFFSET(EMAccountEditorService, username), G_STRUCT_OFFSET(EMAccountEditorService, userlabel), } },
+ { CAMEL_URL_PART_PATH, camel_url_set_path, { G_STRUCT_OFFSET(EMAccountEditorService, path), G_STRUCT_OFFSET(EMAccountEditorService, pathlabel), G_STRUCT_OFFSET(EMAccountEditorService, pathentry) }, },
+ { CAMEL_URL_PART_AUTH, NULL, { 0, G_STRUCT_OFFSET(EMAccountEditorService, auth_frame), }, },
+ { 0 },
+};
+
+static struct _provider_host_info emae_transport_host_info[] = {
+ { CAMEL_URL_PART_HOST, emae_url_set_hostport, { G_STRUCT_OFFSET(EMAccountEditorService, hostname), G_STRUCT_OFFSET(EMAccountEditorService, hostlabel), }, },
+ { CAMEL_URL_PART_USER, camel_url_set_user, { G_STRUCT_OFFSET(EMAccountEditorService, username), G_STRUCT_OFFSET(EMAccountEditorService, userlabel), } },
+ { CAMEL_URL_PART_AUTH, NULL, { 0, G_STRUCT_OFFSET(EMAccountEditorService, auth_frame), }, },
+ { 0 },
+};
+
+/* This is used to map each of the two services in a typical account to the widgets that represent each service.
+ i.e. the receiving (source) service, and the sending (transport) service.
+ It is used throughout the following code to drive each page */
+static struct _service_info {
+ gint account_uri_key;
+ gint save_passwd_key;
+
+ const gchar *frame;
+ const gchar *type_dropdown;
+
+ const gchar *container;
+ const gchar *description;
+ const gchar *hostname;
+ const gchar *hostlabel;
+ const gchar *username;
+ const gchar *userlabel;
+ const gchar *path;
+ const gchar *pathlabel;
+ const gchar *pathentry;
+
+ const gchar *security_frame;
+ const gchar *ssl_hbox;
+ const gchar *use_ssl;
+ const gchar *ssl_disabled;
+
+ const gchar *needs_auth;
+ const gchar *auth_frame;
+
+ const gchar *authtype;
+ const gchar *authtype_check;
+
+ const gchar *remember_password;
+
+ struct _provider_host_info *host_info;
+} emae_service_info[CAMEL_NUM_PROVIDER_TYPES] = {
+ { E_ACCOUNT_SOURCE_URL, E_ACCOUNT_SOURCE_SAVE_PASSWD,
+ "source_frame", "source_type_dropdown",
+ "source_vbox", "source_description", "source_host", "source_host_label", "source_user", "source_user_label", "source_path", "source_path_label", "source_path_entry",
+ "source_security_frame", "source_ssl_hbox", "source_use_ssl", "source_ssl_disabled",
+ NULL, "source_auth_frame",
+ "source_auth_dropdown", "source_check_supported",
+ "source_remember_password",
+ emae_source_host_info,
+ },
+ { E_ACCOUNT_TRANSPORT_URL, E_ACCOUNT_TRANSPORT_SAVE_PASSWD,
+ "transport_frame", "transport_type_dropdown",
+ "transport_vbox", "transport_description", "transport_host", "transport_host_label", "transport_user", "transport_user_label", NULL, NULL, NULL,
+ "transport_security_frame", "transport_ssl_hbox", "transport_use_ssl", "transport_ssl_disabled",
+ "transport_needs_auth", "transport_auth_frame",
+ "transport_auth_dropdown", "transport_check_supported",
+ "transport_remember_password",
+ emae_transport_host_info,
+ },
+};
+
+static void
+emae_uri_changed(EMAccountEditorService *service, CamelURL *url)
+{
+ gchar *uri;
+
+ uri = camel_url_to_string(url, 0);
+
+ e_account_set_string(service->emae->account, emae_service_info[service->type].account_uri_key, uri);
+
+ /* small hack for providers which are store and transport - copy settings across */
+ if (service->type == CAMEL_PROVIDER_STORE
+ && service->provider
+ && CAMEL_PROVIDER_IS_STORE_AND_TRANSPORT(service->provider))
+ e_account_set_string(service->emae->account, E_ACCOUNT_TRANSPORT_URL, uri);
+
+ g_free(uri);
+}
+
+static void
+emae_service_url_changed(EMAccountEditorService *service, void (*setval)(CamelURL *, const gchar *), GtkEntry *entry)
+{
+ GtkComboBox *dropdown;
+ gint id;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ CamelServiceAuthType *authtype;
+
+ CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+ const gchar *text = gtk_entry_get_text(entry);
+
+ setval(url, (text && text[0])?text:NULL);
+
+ if (text && text[0] && setval == camel_url_set_user) {
+ dropdown = service->authtype;
+ if(dropdown) {
+ id = gtk_combo_box_get_active (dropdown);
+ if (id != -1) {
+ model = gtk_combo_box_get_model (dropdown);
+ if (gtk_tree_model_iter_nth_child (model, &iter, NULL, id)) {
+ gtk_tree_model_get (model, &iter, 1, &authtype, -1);
+ if (authtype)
+ camel_url_set_authmech (url, authtype->authproto);
+ }
+ }
+ }
+ }
+
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+}
+
+static void
+emae_service_url_path_changed(EMAccountEditorService *service, void (*setval)(CamelURL *, const gchar *), GtkWidget *widget)
+{
+ GtkComboBox *dropdown;
+ gint id;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ CamelServiceAuthType *authtype;
+
+ CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+ const gchar *text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+
+ setval(url, (text && text[0])?text:NULL);
+
+ if (text && text[0] && setval == camel_url_set_user) {
+ dropdown = service->authtype;
+ if(dropdown) {
+ id = gtk_combo_box_get_active (dropdown);
+ if (id != -1) {
+ model = gtk_combo_box_get_model (dropdown);
+ if (gtk_tree_model_iter_nth_child (model, &iter, NULL, id)) {
+ gtk_tree_model_get (model, &iter, 1, &authtype, -1);
+ if (authtype)
+ camel_url_set_authmech (url, authtype->authproto);
+ }
+ }
+ }
+ }
+
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+}
+
+static void
+emae_hostname_changed(GtkEntry *entry, EMAccountEditorService *service)
+{
+ emae_service_url_changed(service, emae_url_set_hostport, entry);
+}
+
+static void
+emae_username_changed(GtkEntry *entry, EMAccountEditorService *service)
+{
+ emae_service_url_changed(service, camel_url_set_user, entry);
+}
+
+static void
+emae_path_changed(GtkWidget *widget, EMAccountEditorService *service)
+{
+ emae_service_url_path_changed(service, camel_url_set_path, widget);
+}
+
+static gint
+emae_ssl_update(EMAccountEditorService *service, CamelURL *url)
+{
+ gint id = gtk_combo_box_get_active(service->use_ssl);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *ssl;
+
+ if (id == -1)
+ return 0;
+
+ model = gtk_combo_box_get_model(service->use_ssl);
+ if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) {
+ gtk_tree_model_get(model, &iter, 1, &ssl, -1);
+ if (!strcmp(ssl, "none"))
+ ssl = NULL;
+ camel_url_set_param(url, "use_ssl", ssl);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+emae_ssl_changed(GtkComboBox *dropdown, EMAccountEditorService *service)
+{
+ CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+
+ if (emae_ssl_update(service, url))
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+}
+
+static void
+emae_service_provider_changed(EMAccountEditorService *service)
+{
+ gint i, j;
+ void (*show)(GtkWidget *);
+ CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+
+ if (service->provider) {
+ gint enable;
+ GtkWidget *dwidget = NULL;
+
+ camel_url_set_protocol(url, service->provider->protocol);
+ gtk_label_set_text(service->description, service->provider->description);
+ if (!emae_check_license(service->emae, service->provider))
+ gtk_widget_hide(service->frame);
+ else
+ gtk_widget_show(service->frame);
+
+ enable = e_account_writable_option(service->emae->account, service->provider->protocol, "auth");
+ gtk_widget_set_sensitive((GtkWidget *)service->authtype, enable);
+ gtk_widget_set_sensitive((GtkWidget *)service->check_supported, enable);
+
+ enable = e_account_writable_option(service->emae->account, service->provider->protocol, "use_ssl");
+ gtk_widget_set_sensitive((GtkWidget *)service->use_ssl, enable);
+
+ enable = e_account_writable(service->emae->account, emae_service_info[service->type].save_passwd_key);
+ gtk_widget_set_sensitive((GtkWidget *)service->remember, enable);
+
+ for (i=0;emae_service_info[service->type].host_info[i].flag;i++) {
+ GtkWidget *w;
+ gint hide;
+ struct _provider_host_info *info = &emae_service_info[service->type].host_info[i];
+
+ enable = CAMEL_PROVIDER_ALLOWS(service->provider, info->flag);
+ hide = CAMEL_PROVIDER_HIDDEN(service->provider, info->flag);
+ show = (enable && !hide)?gtk_widget_show:gtk_widget_hide;
+
+ for (j=0; j < sizeof(info->widgets)/sizeof(info->widgets[0]); j++) {
+ if (info->widgets[j] && (w = G_STRUCT_MEMBER(GtkWidget *, service, info->widgets[j]))) {
+ show(w);
+ if (j == 0) {
+ if (dwidget == NULL && enable)
+ dwidget = w;
+
+ if (info->setval && !hide)
+ info->setval(url, enable?gtk_entry_get_text((GtkEntry *)w):NULL);
+ }
+ }
+ }
+ }
+
+ if (dwidget)
+ gtk_widget_grab_focus(dwidget);
+
+ if (CAMEL_PROVIDER_ALLOWS(service->provider, CAMEL_URL_PART_AUTH)) {
+ GList *ll;
+
+ /* try to keep the authmech from the current url, or clear it */
+ if (url->authmech) {
+ if (service->provider->authtypes) {
+ for (ll = service->provider->authtypes;ll;ll = g_list_next(ll))
+ if (!strcmp(url->authmech, ((CamelServiceAuthType *)ll->data)->authproto))
+ break;
+ if (ll == NULL)
+ camel_url_set_authmech(url, NULL);
+ } else {
+ camel_url_set_authmech(url, NULL);
+ }
+ }
+
+ emae_refresh_authtype(service->emae, service);
+ if (service->needs_auth && !CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_AUTH))
+ gtk_widget_show((GtkWidget *)service->needs_auth);
+ } else {
+ if (service->needs_auth)
+ gtk_widget_hide((GtkWidget *)service->needs_auth);
+ }
+#ifdef HAVE_SSL
+ gtk_widget_hide(service->no_ssl);
+ if (service->provider->flags & CAMEL_PROVIDER_SUPPORTS_SSL) {
+ emae_ssl_update(service, url);
+ show = gtk_widget_show;
+ } else {
+ camel_url_set_param(url, "use_ssl", NULL);
+ show = gtk_widget_hide;
+ }
+ show(service->ssl_frame);
+ show(service->ssl_hbox);
+#else
+ gtk_widget_hide(service->ssl_hbox);
+ gtk_widget_show(service->no_ssl);
+ camel_url_set_param(url, "use_ssl", NULL);
+#endif
+ } else {
+ camel_url_set_protocol(url, NULL);
+ gtk_label_set_text(service->description, "");
+ gtk_widget_hide(service->frame);
+ gtk_widget_hide(service->auth_frame);
+ gtk_widget_hide(service->ssl_frame);
+ }
+
+ /* FIXME: linked services? */
+ /* FIXME: permissions setup */
+
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+}
+
+static void
+emae_provider_changed(GtkComboBox *dropdown, EMAccountEditorService *service)
+{
+ gint id = gtk_combo_box_get_active(dropdown);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (id == -1)
+ return;
+
+ model = gtk_combo_box_get_model(dropdown);
+ if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, id))
+ return;
+
+ gtk_tree_model_get(model, &iter, 1, &service->provider, -1);
+
+ g_list_free(service->authtypes);
+ service->authtypes = NULL;
+
+ emae_service_provider_changed(service);
+
+ e_config_target_changed((EConfig *)service->emae->priv->config, E_CONFIG_TARGET_CHANGED_REBUILD);
+}
+
+static void
+emae_refresh_providers(EMAccountEditor *emae, EMAccountEditorService *service)
+{
+ EAccount *account = emae->account;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GList *l;
+ GtkCellRenderer *cell = gtk_cell_renderer_text_new();
+ GtkComboBox *dropdown;
+ gint active = 0, i;
+ struct _service_info *info = &emae_service_info[service->type];
+ const gchar *uri = e_account_get_string(account, info->account_uri_key);
+ gchar *current = NULL;
+ const gchar *tmp;
+ CamelURL *url;
+
+ dropdown = service->providers;
+ gtk_widget_show((GtkWidget *)dropdown);
+
+ if (uri) {
+ const gchar *colon = strchr(uri, ':');
+ gint len;
+
+ if (colon) {
+ len = colon-uri;
+ current = g_alloca(len+1);
+ memcpy(current, uri, len);
+ current[len] = 0;
+ }
+ } else {
+ current = (gchar *) "imap";
+ }
+
+ store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+
+ i = 0;
+
+ /* We just special case each type here, its just easier */
+ if (service->type == CAMEL_PROVIDER_STORE) {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, _("None"), 1, NULL, -1);
+ i++;
+ }
+
+ for (l=emae->priv->providers; l; l=l->next) {
+ CamelProvider *provider = l->data;
+
+ if (!((strcmp(provider->domain, "mail") == 0
+ || strcmp (provider->domain, "news") == 0)
+ && provider->object_types[service->type]
+ && (service->type != CAMEL_PROVIDER_STORE || (provider->flags & CAMEL_PROVIDER_IS_SOURCE) != 0))
+ /* hardcode not showing providers who's transport is done in the store */
+ || (service->type == CAMEL_PROVIDER_TRANSPORT
+ && CAMEL_PROVIDER_IS_STORE_AND_TRANSPORT (provider)))
+ continue;
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, provider->name, 1, provider, -1);
+
+ /* find the displayed and set default */
+ if (i == 0 || (current && strcmp(provider->protocol, current) == 0)) {
+ service->provider = provider;
+ active = i;
+
+ /* we need to set this value on the uri too */
+ if (current == NULL) {
+ CamelURL *url = emae_account_url(emae, info->account_uri_key);
+
+ camel_url_set_protocol(url, provider->protocol);
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+ }
+ }
+ i++;
+ }
+
+
+ gtk_cell_layout_clear((GtkCellLayout *)dropdown);
+ gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store);
+ gtk_cell_layout_pack_start((GtkCellLayout *)dropdown, cell, TRUE);
+ gtk_cell_layout_set_attributes((GtkCellLayout *)dropdown, cell, "text", 0, NULL);
+
+ g_signal_handlers_disconnect_by_func(dropdown, emae_provider_changed, service);
+ gtk_combo_box_set_active(dropdown, -1); /* needed for gtkcombo bug(?) */
+ gtk_combo_box_set_active(dropdown, active);
+ g_signal_connect(dropdown, "changed", G_CALLBACK(emae_provider_changed), service);
+
+ if (!uri || (url = camel_url_new(uri, NULL)) == NULL) {
+ return;
+ }
+
+ tmp = camel_url_get_param(url, "use_ssl");
+ if (tmp == NULL)
+ tmp = "never";
+ for (i=0;i<num_ssl_options;i++) {
+ if (!strcmp(ssl_options[i].value, tmp)) {
+ gtk_combo_box_set_active(service->use_ssl, i);
+ break;
+ }
+ }
+}
+
+static void
+emae_authtype_changed(GtkComboBox *dropdown, EMAccountEditorService *service)
+{
+ gint id = gtk_combo_box_get_active(dropdown);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ CamelServiceAuthType *authtype;
+ CamelURL *url;
+
+ if (id == -1)
+ return;
+
+ url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+ model = gtk_combo_box_get_model(dropdown);
+ if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) {
+ gtk_tree_model_get(model, &iter, 1, &authtype, -1);
+ if (authtype)
+ camel_url_set_authmech(url, authtype->authproto);
+ else
+ camel_url_set_authmech(url, NULL);
+ emae_uri_changed(service, url);
+ }
+ camel_url_free(url);
+
+ gtk_widget_set_sensitive((GtkWidget *)service->remember,
+ authtype
+ ?(authtype->need_password && e_account_writable(service->emae->account, emae_service_info[service->type].save_passwd_key))
+ :FALSE);
+}
+
+static void
+emae_needs_auth(GtkToggleButton *toggle, EMAccountEditorService *service)
+{
+ gint need = gtk_toggle_button_get_active(toggle);
+
+ gtk_widget_set_sensitive(service->auth_frame, need);
+
+ if (need)
+ emae_authtype_changed(service->authtype, service);
+ else {
+ CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+
+ camel_url_set_authmech(url, NULL);
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+ }
+}
+
+static void emae_check_authtype(GtkWidget *w, EMAccountEditorService *service);
+
+static void
+emae_refresh_authtype (EMAccountEditor *emae, EMAccountEditorService *service)
+{
+ EAccount *account = emae->account;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkComboBox *dropdown;
+ gint active = 0;
+ gint i;
+ struct _service_info *info = &emae_service_info[service->type];
+ const gchar *uri = e_account_get_string(account, info->account_uri_key);
+ GList *l, *ll;
+ CamelURL *url = NULL;
+
+ dropdown = service->authtype;
+ gtk_widget_show((GtkWidget *)dropdown);
+
+ store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
+
+ if (uri)
+ url = camel_url_new(uri, NULL);
+
+ if (service->provider) {
+ for (i=0, l=service->provider->authtypes; l; l=l->next, i++) {
+ CamelServiceAuthType *authtype = l->data;
+ gint avail;
+
+ /* if we have some already shown */
+ if (service->authtypes) {
+ for (ll = service->authtypes;ll;ll = g_list_next(ll))
+ if (!strcmp(authtype->authproto, ((CamelServiceAuthType *)ll->data)->authproto))
+ break;
+ avail = ll != NULL;
+ } else {
+ avail = TRUE;
+ }
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, authtype->name, 1, authtype, 2, !avail, -1);
+
+ if (url && url->authmech && !strcmp(url->authmech, authtype->authproto))
+ active = i;
+ }
+ }
+
+ gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store);
+ gtk_combo_box_set_active(dropdown, -1);
+
+ if (service->auth_changed_id == 0) {
+ GtkCellRenderer *cell = gtk_cell_renderer_text_new();
+
+ gtk_cell_layout_pack_start((GtkCellLayout *)dropdown, cell, TRUE);
+ gtk_cell_layout_set_attributes((GtkCellLayout *)dropdown, cell, "text", 0, "strikethrough", 2, NULL);
+
+ service->auth_changed_id = g_signal_connect(dropdown, "changed", G_CALLBACK(emae_authtype_changed), service);
+ g_signal_connect(service->check_supported, "clicked", G_CALLBACK(emae_check_authtype), service);
+ }
+
+ gtk_combo_box_set_active(dropdown, active);
+
+ if (url)
+ camel_url_free(url);
+}
+
+static void emae_check_authtype_done(const gchar *uri, CamelProviderType type, GList *types, gpointer data)
+{
+ EMAccountEditorService *service = data;
+
+ if (service->check_dialog) {
+ if (service->authtypes)
+ g_list_free(service->authtypes);
+
+ service->authtypes = g_list_copy(types);
+ emae_refresh_authtype(service->emae, service);
+ gtk_widget_destroy(service->check_dialog);
+ }
+
+ if (service->emae->editor)
+ gtk_widget_set_sensitive(service->emae->editor, TRUE);
+
+ service->check_id = -1;
+ g_object_unref(service->emae);
+}
+
+static void emae_check_authtype_response(GtkWidget *d, gint button, EMAccountEditorService *service)
+{
+ mail_msg_cancel(service->check_id);
+ gtk_widget_destroy(service->check_dialog);
+ service->check_dialog = NULL;
+
+ if (service->emae->editor)
+ gtk_widget_set_sensitive(service->emae->editor, TRUE);
+}
+
+static void emae_check_authtype(GtkWidget *w, EMAccountEditorService *service)
+{
+ EMAccountEditor *emae = service->emae;
+ const gchar *uri;
+
+ /* TODO: do we need to remove the auth mechanism from the uri? */
+ uri = e_account_get_string(emae->account, emae_service_info[service->type].account_uri_key);
+ g_object_ref(emae);
+
+ service->check_dialog = e_error_new(emae->editor ? (GtkWindow *)gtk_widget_get_toplevel(emae->editor) : NULL,
+ "mail:checking-service", NULL);
+ g_signal_connect(service->check_dialog, "response", G_CALLBACK(emae_check_authtype_response), service);
+ gtk_widget_show(service->check_dialog);
+ if (emae->editor)
+ gtk_widget_set_sensitive(emae->editor, FALSE);
+ service->check_id = mail_check_service(uri, service->type, emae_check_authtype_done, service);
+}
+
+static void
+emae_setup_service(EMAccountEditor *emae, EMAccountEditorService *service, GladeXML *xml)
+{
+ struct _service_info *info = &emae_service_info[service->type];
+ CamelURL *url = emae_account_url(emae, info->account_uri_key);
+ const gchar *uri = e_account_get_string(emae->account, info->account_uri_key);
+ const gchar *tmp;
+ gint i;
+
+ service->provider = uri?camel_provider_get(uri, NULL):NULL;
+ service->frame = glade_xml_get_widget(xml, info->frame);
+ service->container = glade_xml_get_widget(xml, info->container);
+ service->description = GTK_LABEL (glade_xml_get_widget (xml, info->description));
+ service->hostname = GTK_ENTRY (glade_xml_get_widget (xml, info->hostname));
+ service->hostlabel = (GtkLabel *)glade_xml_get_widget (xml, info->hostlabel);
+ service->username = GTK_ENTRY (glade_xml_get_widget (xml, info->username));
+ service->userlabel = (GtkLabel *)glade_xml_get_widget (xml, info->userlabel);
+ if (info->pathentry) {
+ service->pathlabel = (GtkLabel *)glade_xml_get_widget(xml, info->pathlabel);
+ service->pathentry = glade_xml_get_widget(xml, info->pathentry);
+ }
+
+ service->ssl_frame = glade_xml_get_widget (xml, info->security_frame);
+ gtk_widget_hide (service->ssl_frame);
+ service->ssl_hbox = glade_xml_get_widget (xml, info->ssl_hbox);
+ service->use_ssl = (GtkComboBox *)glade_xml_get_widget (xml, info->use_ssl);
+ service->no_ssl = glade_xml_get_widget (xml, info->ssl_disabled);
+
+ /* configure ui for current settings */
+ if (url->host) {
+ if (url->port) {
+ gchar *host = g_strdup_printf("%s:%d", url->host, url->port);
+
+ gtk_entry_set_text(service->hostname, host);
+ g_free(host);
+ } else
+ gtk_entry_set_text(service->hostname, url->host);
+ }
+ if (url->user && *url->user) {
+ gtk_entry_set_text(service->username, url->user);
+ }
+ if (service->pathentry) {
+ GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
+
+ if (service->provider && (service->provider->url_flags & CAMEL_URL_NEED_PATH_DIR) == 0)
+ action = GTK_FILE_CHOOSER_ACTION_OPEN;
+
+ if (action != gtk_file_chooser_get_action (GTK_FILE_CHOOSER (service->pathentry)))
+ gtk_file_chooser_set_action (GTK_FILE_CHOOSER (service->pathentry), action);
+
+ if (url->path)
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (service->pathentry), url->path);
+ }
+
+ tmp = camel_url_get_param(url, "use_ssl");
+ if (tmp == NULL)
+ tmp = "never";
+
+ for (i=0;i<num_ssl_options;i++) {
+ if (!strcmp(ssl_options[i].value, tmp)) {
+ gtk_combo_box_set_active(service->use_ssl, i);
+ break;
+ }
+ }
+
+ g_signal_connect (service->hostname, "changed", G_CALLBACK (emae_hostname_changed), service);
+ g_signal_connect (service->username, "changed", G_CALLBACK (emae_username_changed), service);
+ if (service->pathentry)
+ g_signal_connect (GTK_FILE_CHOOSER (service->pathentry), "selection-changed", G_CALLBACK (emae_path_changed), service);
+
+ g_signal_connect(service->use_ssl, "changed", G_CALLBACK(emae_ssl_changed), service);
+
+ service->auth_frame = glade_xml_get_widget(xml, info->auth_frame);
+ service->remember = emae_account_toggle(emae, info->remember_password, info->save_passwd_key, xml);
+ service->check_supported = (GtkButton *)glade_xml_get_widget(xml, info->authtype_check);
+ service->authtype = (GtkComboBox *)glade_xml_get_widget(xml, info->authtype);
+ /* old authtype will be destroyed when we exit */
+ service->auth_changed_id = 0;
+ service->providers = (GtkComboBox *)glade_xml_get_widget(xml, info->type_dropdown);
+ emae_refresh_providers(emae, service);
+ emae_refresh_authtype(emae, service);
+
+ if (info->needs_auth) {
+ service->needs_auth = (GtkToggleButton *)glade_xml_get_widget (xml, info->needs_auth);
+ gtk_toggle_button_set_active(service->needs_auth, url->authmech != NULL);
+ g_signal_connect(service->needs_auth, "toggled", G_CALLBACK(emae_needs_auth), service);
+ emae_needs_auth(service->needs_auth, service);
+ } else {
+ service->needs_auth = NULL;
+ }
+
+ if (!e_account_writable (emae->account, info->account_uri_key))
+ gtk_widget_set_sensitive(service->container, FALSE);
+ else
+ gtk_widget_set_sensitive(service->container, TRUE);
+
+ emae_service_provider_changed(service);
+
+ camel_url_free(url);
+}
+
+/* do not re-order these, the order is used by various code to look up emae->priv->identity_entries[] */
+static struct {
+ const gchar *name;
+ gint item;
+} emae_identity_entries[] = {
+ { "management_name", E_ACCOUNT_NAME },
+ { "identity_full_name", E_ACCOUNT_ID_NAME },
+ { "identity_address", E_ACCOUNT_ID_ADDRESS },
+ { "identity_reply_to", E_ACCOUNT_ID_REPLY_TO },
+ { "identity_organization", E_ACCOUNT_ID_ORGANIZATION },
+};
+
+/* its a bit obtuse, but its simple */
+static void
+emae_queue_widgets(EMAccountEditor *emae, GladeXML *xml, const gchar *first, ...)
+{
+ gint i = 0;
+ va_list ap;
+
+ va_start(ap, first);
+ while (first) {
+ emae->priv->widgets_name[i] = first;
+ emae->priv->widgets[i++] = glade_xml_get_widget(xml, first);
+ first = va_arg(ap, const gchar *);
+ }
+ va_end(ap);
+
+ g_return_if_fail(i < sizeof(emae->priv->widgets)/sizeof(emae->priv->widgets[0]));
+
+ emae->priv->widgets[i] = NULL;
+ emae->priv->widgets_index = 0;
+}
+
+static GtkWidget *
+emae_identity_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ EMAccountEditorPrivate *gui = emae->priv;
+ EAccount *account = emae->account;
+ gint i;
+ GtkWidget *w;
+ GladeXML *xml;
+ gchar *gladefile;
+
+ /*if (old)
+ return old;*/
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ xml = glade_xml_new(gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ /* Management & Identity fields, in the druid the management frame is relocated to the last page later on */
+ for (i=0;i<sizeof(emae_identity_entries)/sizeof(emae_identity_entries[0]);i++)
+ gui->identity_entries[i] = emae_account_entry(emae, emae_identity_entries[i].name, emae_identity_entries[i].item, xml);
+
+ gui->management_frame = glade_xml_get_widget(xml, "management_frame");
+
+ gui->default_account = GTK_TOGGLE_BUTTON (glade_xml_get_widget (xml, "management_default"));
+ if (!e_get_default_account ()
+ || (account == e_get_default_account ())
+ || (GPOINTER_TO_INT(g_object_get_data (G_OBJECT (emae->account), "default_flagged"))) )
+ gtk_toggle_button_set_active (gui->default_account, TRUE);
+
+ if (emae->do_signature) {
+ emae_setup_signatures(emae, xml);
+ } else {
+ /* TODO: this could/should probably be neater */
+ gtk_widget_hide(glade_xml_get_widget(xml, "sigLabel"));
+#if 0
+ gtk_widget_hide(glade_xml_get_widget(xml, "sigOption"));
+#endif
+ gtk_widget_hide(glade_xml_get_widget(xml, "sigAddNew"));
+ }
+
+ w = glade_xml_get_widget(xml, item->label);
+ if (emae->type == EMAE_PAGES) {
+ gtk_box_pack_start ((GtkBox *)emae->pages[0], w, TRUE, TRUE, 0);
+ } else if (((EConfig *)gui->config)->type == E_CONFIG_DRUID) {
+ GladeXML *druidxml;
+ GtkWidget *page;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ druidxml = glade_xml_new(gladefile, "identity_page", NULL);
+ g_free (gladefile);
+
+ page = glade_xml_get_widget(druidxml, "identity_page");
+
+ gtk_box_pack_start((GtkBox*)((GnomeDruidPageStandard *)page)->vbox, w, TRUE, TRUE, 0);
+ w = page;
+ g_object_unref(druidxml);
+ gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)page);
+ } else {
+ gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Identity")));
+ }
+
+ emae_queue_widgets(emae, xml, "account_vbox", "identity_required_table", "identity_optional_table", NULL);
+
+ g_object_unref(xml);
+
+ return w;
+}
+
+static GtkWidget *
+emae_receive_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ EMAccountEditorPrivate *gui = emae->priv;
+ GtkWidget *w;
+ GladeXML *xml;
+ gchar *gladefile;
+
+ /*if (old)
+ return old;*/
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ xml = glade_xml_new(gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ gui->source.type = CAMEL_PROVIDER_STORE;
+ emae_setup_service(emae, &gui->source, xml);
+
+ w = glade_xml_get_widget(xml, item->label);
+ if (emae->type == EMAE_PAGES) {
+ gtk_box_pack_start ((GtkBox *)emae->pages[1], w, TRUE, TRUE, 0);
+ } else if (((EConfig *)gui->config)->type == E_CONFIG_DRUID) {
+ GladeXML *druidxml;
+ GtkWidget *page;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ druidxml = glade_xml_new(gladefile, "source_page", NULL);
+ g_free (gladefile);
+
+ page = glade_xml_get_widget(druidxml, "source_page");
+
+ gtk_box_pack_start((GtkBox*)((GnomeDruidPageStandard *)page)->vbox, w, TRUE, TRUE, 0);
+ w = page;
+ g_object_unref(druidxml);
+ gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)page);
+ } else {
+ gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Receiving Email")));
+ }
+
+ emae_queue_widgets(emae, xml, "source_type_table", "table4", "vbox181", "vbox179", NULL);
+
+ g_object_unref(xml);
+
+ return w;
+}
+
+static void
+emae_option_toggle_changed(GtkToggleButton *toggle, EMAccountEditorService *service)
+{
+ const gchar *name = g_object_get_data((GObject *)toggle, "option-name");
+ GSList *depl = g_object_get_data((GObject *)toggle, "dependent-list");
+ gint active = gtk_toggle_button_get_active(toggle);
+ CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+
+ for (;depl;depl = g_slist_next(depl))
+ gtk_widget_set_sensitive((GtkWidget *)depl->data, active);
+
+ camel_url_set_param(url, name, active?"":NULL);
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+}
+
+static GtkWidget *
+emae_option_toggle(EMAccountEditorService *service, CamelURL *url, const gchar *text, const gchar *name, gint def)
+{
+ GtkWidget *w;
+
+ /* FIXME: how do we get the default value ever? */
+ w = gtk_check_button_new_with_mnemonic(text);
+ g_object_set_data((GObject *)w, "option-name", (gpointer)name);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), camel_url_get_param (url, name) != NULL);
+ g_signal_connect(w, "toggled", G_CALLBACK(emae_option_toggle_changed), service);
+ gtk_widget_show(w);
+
+ return w;
+}
+
+static void
+emae_option_entry_changed(GtkEntry *entry, EMAccountEditorService *service)
+{
+ const gchar *name = g_object_get_data((GObject *)entry, "option-name");
+ const gchar *text = gtk_entry_get_text(entry);
+ CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+
+ camel_url_set_param(url, name, text && text[0]?text:NULL);
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+}
+
+static GtkWidget *
+emae_option_entry(EMAccountEditorService *service, CamelURL *url, const gchar *name, const gchar *def, GtkWidget *l)
+{
+ GtkWidget *w;
+ const gchar *val = camel_url_get_param(url, name);
+
+ if (val == NULL) {
+ if (def) {
+ val = def;
+ camel_url_set_param(url, name, val);
+ emae_uri_changed(service, url);
+ } else
+ val = "";
+ }
+
+ w = g_object_new(gtk_entry_get_type(),
+ "text", val,
+ NULL);
+ gtk_label_set_mnemonic_widget ((GtkLabel*)l, w);
+ g_object_set_data((GObject *)w, "option-name", (gpointer)name);
+ g_signal_connect(w, "changed", G_CALLBACK(emae_option_entry_changed), service);
+ gtk_widget_show(w);
+
+ return w;
+}
+
+static void
+emae_option_checkspin_changed(GtkSpinButton *spin, EMAccountEditorService *service)
+{
+ const gchar *name = g_object_get_data((GObject *)spin, "option-name");
+ gchar value[16];
+ CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+
+ sprintf(value, "%d", gtk_spin_button_get_value_as_int(spin));
+ camel_url_set_param(url, name, value);
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+}
+
+static void
+emae_option_checkspin_check_changed(GtkToggleButton *toggle, EMAccountEditorService *service)
+{
+ const gchar *name = g_object_get_data((GObject *)toggle, "option-name");
+ GtkSpinButton *spin = g_object_get_data((GObject *)toggle, "option-target");
+
+ if (gtk_toggle_button_get_active(toggle)) {
+ gtk_widget_set_sensitive((GtkWidget *)spin, TRUE);
+ emae_option_checkspin_changed(spin, service);
+ } else {
+ CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key);
+
+ camel_url_set_param(url, name, NULL);
+ gtk_widget_set_sensitive((GtkWidget *)spin, FALSE);
+ emae_uri_changed(service, url);
+ camel_url_free(url);
+ }
+}
+
+/* this is a fugly api */
+static GtkWidget *
+emae_option_checkspin(EMAccountEditorService *service, CamelURL *url, const gchar *name, const gchar *fmt, const gchar *info)
+{
+ GtkWidget *hbox, *check, *spin, *label = NULL;
+ double min, def, max;
+ gchar *pre, *post;
+ const gchar *val;
+ gchar on;
+ gint enable;
+
+ pre = g_alloca(strlen(fmt)+1);
+ strcpy(pre, fmt);
+ post = strstr(pre, "%s");
+ if (post) {
+ *post = 0;
+ post+=2;
+ }
+
+ if (sscanf(info, "%c:%lf:%lf:%lf", &on, &min, &def, &max) != 4) {
+ min = 0.0;
+ def = 0.0;
+ max = 1.0;
+ }
+
+ if ((enable = (val = camel_url_get_param(url, name)) != NULL) )
+ def = strtod(val, NULL);
+ else
+ enable = (on == 'y');
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ check = g_object_new(gtk_check_button_get_type(), "label", pre, "use_underline", TRUE, "active", enable, NULL);
+
+ spin = gtk_spin_button_new((GtkAdjustment *)gtk_adjustment_new(def, min, max, 1, 1, 0), 1, 0);
+ if (post)
+ label = gtk_label_new_with_mnemonic(post);
+ gtk_box_pack_start((GtkBox *)hbox, check, FALSE, TRUE, 0);
+ gtk_box_pack_start((GtkBox *)hbox, spin, FALSE, TRUE, 0);
+ if (label)
+ gtk_box_pack_start((GtkBox *)hbox, label, FALSE, TRUE, 4);
+
+ g_object_set_data((GObject *)spin, "option-name", (gpointer)name);
+ g_object_set_data((GObject *)check, "option-name", (gpointer)name);
+ g_object_set_data((GObject *)check, "option-target", (gpointer)spin);
+
+ g_signal_connect(spin, "value_changed", G_CALLBACK(emae_option_checkspin_changed), service);
+ g_signal_connect(check, "toggled", G_CALLBACK(emae_option_checkspin_check_changed), service);
+
+ gtk_widget_show_all(hbox);
+
+ return hbox;
+}
+
+static void
+emae_option_options_changed (GtkComboBox *options, EMAccountEditorService *service)
+{
+ const gchar *name = g_object_get_data (G_OBJECT (options), "option-name");
+ gchar *value = NULL;
+ CamelURL *url = emae_account_url (service->emae, emae_service_info[service->type].account_uri_key);
+ gint id = gtk_combo_box_get_active (options);
+
+ if (id != -1) {
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_combo_box_get_model (options);
+ if (gtk_tree_model_iter_nth_child (model, &iter, NULL, id)) {
+ gtk_tree_model_get (model, &iter, 0, &value, -1);
+ }
+ }
+
+ camel_url_set_param (url, name, value);
+ emae_uri_changed (service, url);
+ camel_url_free (url);
+ g_free (value);
+}
+
+/* 'values' is in format "value0:caption0:value2:caption2:...valueN:captionN" */
+static GtkWidget *
+emae_option_options (EMAccountEditorService *service, CamelURL *url, const gchar *name, const gchar *values, GtkWidget *l)
+{
+ GtkComboBox *w;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ const gchar *p, *value, *caption;
+ GtkCellRenderer *cell;
+ gint active = 0; /* the first item entered is always a default item */
+ gint i;
+ const gchar *val = camel_url_get_param (url, name);
+
+ w = GTK_COMBO_BOX (gtk_combo_box_new ());
+
+ /* value and caption */
+ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+ p = values;
+ for (p = values, i = 0; p; i++) {
+ gchar *vl, *cp;
+
+ value = p;
+ caption = strchr (p, ':');
+ if (caption) {
+ caption++;
+ } else {
+ g_warning (G_STRLOC ": expected ':' not found at '%s'", p);
+ break;
+ }
+ p = strchr (caption, ':');
+
+ vl = g_strndup (value, caption - value - 1);
+ if (p) {
+ p++;
+ cp = g_strndup (caption, p - caption - 1);
+ } else
+ cp = g_strdup (caption);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, vl, 1, dgettext (service->provider->translation_domain, cp), -1);
+
+ if (val && g_ascii_strcasecmp (val, vl) == 0)
+ active = i;
+
+ g_free (vl);
+ g_free (cp);
+ }
+
+ gtk_combo_box_set_model (w, (GtkTreeModel *)store);
+ gtk_combo_box_set_active (w, i > 0 ? active : -1);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), cell, "text", 1, NULL);
+
+ gtk_widget_show (GTK_WIDGET (w));
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (l), GTK_WIDGET (w));
+
+ g_object_set_data (G_OBJECT (w), "option-name", (gpointer)name);
+ g_signal_connect (w, "changed", G_CALLBACK (emae_option_options_changed), service);
+
+ return GTK_WIDGET (w);
+}
+
+static GtkWidget *
+emae_receive_options_item(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ GtkWidget *w, *box, *spin;
+ gint row;
+
+ if (emae->priv->source.provider == NULL
+ || emae->priv->source.provider->extra_conf == NULL)
+ return NULL;
+
+ if (old)
+ return old;
+
+ /* We have to add the automatic mail check item with the rest of the receive options */
+ row = ((GtkTable *)parent)->nrows;
+
+ box = gtk_hbox_new(FALSE, 4);
+ w = gtk_check_button_new_with_mnemonic (_("Check for _new messages every"));
+ emae_account_toggle_widget(emae, (GtkToggleButton *)w, E_ACCOUNT_SOURCE_AUTO_CHECK);
+ gtk_box_pack_start((GtkBox *)box, w, FALSE, FALSE, 0);
+
+ spin = gtk_spin_button_new_with_range(1.0, 1440.0, 1.0);
+ emae_account_spinint_widget(emae, (GtkSpinButton *)spin, E_ACCOUNT_SOURCE_AUTO_CHECK_TIME);
+ gtk_box_pack_start((GtkBox *)box, spin, FALSE, TRUE, 0);
+
+ w = gtk_label_new_with_mnemonic (_("minu_tes"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (w), spin);
+ gtk_box_pack_start((GtkBox *)box, w, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(box);
+
+ gtk_table_attach((GtkTable *)parent, box, 0, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
+
+ return box;
+}
+
+static GtkWidget *
+emae_receive_options_extra_item(EConfig *ec, EConfigItem *eitem, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ struct _receive_options_item *item = (struct _receive_options_item *)eitem;
+ GtkWidget *w, *l, *h;
+ CamelProviderConfEntry *entries;
+ GtkWidget *depw;
+ GSList *depl = NULL, *n;
+ EMAccountEditorService *service = &emae->priv->source;
+ gint row, i;
+ GHashTable *extra;
+ CamelURL *url;
+
+ if (emae->priv->source.provider == NULL
+ || emae->priv->source.provider->extra_conf == NULL)
+ return NULL;
+
+ entries = emae->priv->source.provider->extra_conf;
+ for (i=0;entries && entries[i].type != CAMEL_PROVIDER_CONF_END;i++)
+ if (entries[i].type == CAMEL_PROVIDER_CONF_SECTION_START
+ && entries[i].name
+ && strcmp(entries[i].name, eitem->user_data) == 0)
+ goto section;
+
+ return NULL;
+section:
+ d(printf("Building extra section '%s'\n", eitem->path));
+
+ url = emae_account_url(emae, emae_service_info[service->type].account_uri_key);
+ item->extra_table = g_hash_table_new(g_str_hash, g_str_equal);
+ extra = g_hash_table_new(g_str_hash, g_str_equal);
+ row = ((GtkTable *)parent)->nrows;
+
+ for (;entries[i].type != CAMEL_PROVIDER_CONF_END && entries[i].type != CAMEL_PROVIDER_CONF_SECTION_END;i++) {
+ if (entries[i].depname) {
+ depw = g_hash_table_lookup(extra, entries[i].depname);
+ if (depw)
+ depl = g_object_steal_data((GObject *)depw, "dependent-list");
+ } else
+ depw = NULL;
+
+ switch (entries[i].type) {
+ case CAMEL_PROVIDER_CONF_SECTION_START:
+ case CAMEL_PROVIDER_CONF_SECTION_END:
+ break;
+ case CAMEL_PROVIDER_CONF_LABEL:
+ /* FIXME: This is a hack for exchange connector, labels should be removed from confentry */
+ if (!strcmp(entries[i].name, "hostname"))
+ l = (GtkWidget *)emae->priv->source.hostlabel;
+ else if (!strcmp(entries[i].name, "username"))
+ l = (GtkWidget *)emae->priv->source.userlabel;
+ else
+ l = NULL;
+
+ if (l) {
+ gtk_label_set_text_with_mnemonic((GtkLabel *)l, entries[i].text);
+ if (depw)
+ depl = g_slist_prepend(depl, l);
+ }
+ break;
+ case CAMEL_PROVIDER_CONF_CHECKBOX:
+ w = emae_option_toggle(service, url, entries[i].text, entries[i].name, atoi(entries[i].value));
+ gtk_table_attach((GtkTable *)parent, w, 0, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
+ g_hash_table_insert(extra, (gpointer)entries[i].name, w);
+ if (depw)
+ depl = g_slist_prepend(depl, w);
+ row++;
+ /* HACK: keep_on_server is stored in the e-account, but is displayed as a properly on the uri,
+ make sure they track/match here */
+ if (!strcmp(entries[i].name, "keep_on_server"))
+ emae_account_toggle_widget(emae, (GtkToggleButton *)w, E_ACCOUNT_SOURCE_KEEP_ON_SERVER);
+ break;
+ case CAMEL_PROVIDER_CONF_ENTRY:
+ l = g_object_new(gtk_label_get_type(), "label", entries[i].text, "xalign", 0.0, "use_underline", TRUE, NULL);
+ gtk_widget_show(l);
+ w = emae_option_entry(service, url, entries[i].name, entries[i].value, l);
+ gtk_table_attach((GtkTable *)parent, l, 0, 1, row, row+1, GTK_FILL, 0, 0, 0);
+ gtk_table_attach((GtkTable *)parent, w, 1, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
+ if (depw) {
+ depl = g_slist_prepend(depl, w);
+ depl = g_slist_prepend(depl, l);
+ }
+ row++;
+ /* FIXME: this is another hack for exchange/groupwise connector */
+ g_hash_table_insert(item->extra_table, (gpointer)entries[i].name, w);
+ break;
+ case CAMEL_PROVIDER_CONF_CHECKSPIN:
+ w = emae_option_checkspin(service, url, entries[i].name, entries[i].text, entries[i].value);
+ gtk_table_attach((GtkTable *)parent, w, 0, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
+ if (depw)
+ depl = g_slist_prepend(depl, w);
+ row++;
+ break;
+ case CAMEL_PROVIDER_CONF_OPTIONS:
+ h = gtk_hbox_new (FALSE, 4);
+ gtk_widget_show (h);
+ l = g_object_new (gtk_label_get_type (), "label", entries[i].text, "xalign", 0.0, "use_underline", TRUE, NULL);
+ gtk_widget_show (l);
+ w = emae_option_options (service, url, entries[i].name, entries[i].value, l);
+ gtk_box_pack_start (GTK_BOX (h), l, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (h), w, FALSE, FALSE, 0);
+ gtk_table_attach ((GtkTable *)parent, h, 0, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
+ if (depw) {
+ depl = g_slist_prepend (depl, h);
+ }
+ row++;
+ break;
+ default:
+ break;
+ }
+
+ if (depw && depl) {
+ gint act = gtk_toggle_button_get_active((GtkToggleButton *)depw);
+
+ g_object_set_data_full((GObject *)depw, "dependent-list", depl, (GDestroyNotify)g_slist_free);
+ for (n=depl;n;n=g_slist_next(n))
+ gtk_widget_set_sensitive((GtkWidget *)n->data, act);
+ }
+ }
+
+ camel_url_free(url);
+
+ /* Since EConfig destroys the factory widget when it changes, we
+ * need to destroy our own ones as well, and add a dummy item
+ * so it knows this section isn't empty */
+
+ w = gtk_label_new("");
+ gtk_widget_hide(w);
+ gtk_table_attach((GtkTable *)parent, w, 0, 2, row, row+1, 0, 0, 0, 0);
+
+ return w;
+}
+
+static GtkWidget *
+emae_send_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ EMAccountEditorPrivate *gui = emae->priv;
+ GtkWidget *w;
+ GladeXML *xml;
+ gchar *gladefile;
+
+ /* no transport options page at all for these types of providers */
+ if (gui->source.provider && CAMEL_PROVIDER_IS_STORE_AND_TRANSPORT(gui->source.provider)) {
+ memset(&gui->transport.frame, 0, ((gchar *)&gui->transport.check_dialog)-((gchar *)&gui->transport.frame));
+ return NULL;
+ }
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ xml = glade_xml_new(gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ /* Transport */
+ gui->transport.type = CAMEL_PROVIDER_TRANSPORT;
+ emae_setup_service(emae, &gui->transport, xml);
+
+ w = glade_xml_get_widget(xml, item->label);
+ if (emae->type == EMAE_PAGES) {
+ gtk_box_pack_start ((GtkBox *)emae->pages[2], w, TRUE, TRUE, 0);
+ } else if (((EConfig *)gui->config)->type == E_CONFIG_DRUID) {
+ GladeXML *druidxml;
+ GtkWidget *page;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ druidxml = glade_xml_new(gladefile, "transport_page", NULL);
+ g_free (gladefile);
+
+ page = glade_xml_get_widget(druidxml, "transport_page");
+
+ gtk_box_pack_start((GtkBox*)((GnomeDruidPageStandard *)page)->vbox, w, TRUE, TRUE, 0);
+ w = page;
+ g_object_unref(druidxml);
+ gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)page);
+ } else {
+ gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Sending Email")));
+ }
+
+ emae_queue_widgets(emae, xml, "transport_type_table", "vbox12", "vbox183", "vbox61", NULL);
+
+ g_object_unref(xml);
+
+ return w;
+}
+
+static GtkWidget *
+emae_defaults_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ EMAccountEditorPrivate *gui = emae->priv;
+ GtkWidget *w;
+ GladeXML *xml;
+ gchar *gladefile;
+
+ /*if (old)
+ return old;*/
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ xml = glade_xml_new(gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ /* Special folders */
+ gui->drafts_folder_button = (GtkButton *)emae_account_folder(emae, "drafts_button", E_ACCOUNT_DRAFTS_FOLDER_URI, E_MAIL_FOLDER_DRAFTS, xml);
+ gui->sent_folder_button = (GtkButton *)emae_account_folder(emae, "sent_button", E_ACCOUNT_SENT_FOLDER_URI, E_MAIL_FOLDER_SENT, xml);
+
+ /* Special Folders "Reset Defaults" button */
+ gui->restore_folders_button = (GtkButton *)glade_xml_get_widget (xml, "default_folders_button");
+ g_signal_connect (gui->restore_folders_button, "clicked", G_CALLBACK (default_folders_clicked), emae);
+
+ /* Always Cc/Bcc */
+ emae_account_toggle(emae, "always_cc", E_ACCOUNT_CC_ALWAYS, xml);
+ emae_account_entry(emae, "cc_addrs", E_ACCOUNT_CC_ADDRS, xml);
+ emae_account_toggle(emae, "always_bcc", E_ACCOUNT_BCC_ALWAYS, xml);
+ emae_account_entry(emae, "bcc_addrs", E_ACCOUNT_BCC_ADDRS, xml);
+
+ gtk_widget_set_sensitive((GtkWidget *)gui->drafts_folder_button, e_account_writable(emae->account, E_ACCOUNT_DRAFTS_FOLDER_URI));
+
+ gtk_widget_set_sensitive( (GtkWidget *)gui->sent_folder_button,
+ e_account_writable(emae->account, E_ACCOUNT_SENT_FOLDER_URI)
+ &&
+ (emae->priv->source.provider ? !(emae->priv->source.provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER): TRUE)
+ );
+
+ gtk_widget_set_sensitive((GtkWidget *)gui->restore_folders_button,
+ (e_account_writable(emae->account, E_ACCOUNT_SENT_FOLDER_URI)
+ && ((emae->priv->source.provider && !( emae->priv->source.provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER))
+ || e_account_writable(emae->account, E_ACCOUNT_DRAFTS_FOLDER_URI))));
+
+ /* Receipt policy */
+ emae_setup_receipt_policy (emae, xml);
+
+ w = glade_xml_get_widget(xml, item->label);
+ gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Defaults")));
+
+ emae_queue_widgets(emae, xml, "vbox184", "table8", NULL);
+
+ g_object_unref(xml);
+
+ return w;
+}
+
+static GtkWidget *
+emae_security_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMAccountEditor *emae = data;
+#if defined (HAVE_NSS)
+ EMAccountEditorPrivate *gui = emae->priv;
+#endif
+ GtkWidget *w;
+ GladeXML *xml;
+ gchar *gladefile;
+
+ /*if (old)
+ return old;*/
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ xml = glade_xml_new(gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ /* Security */
+ emae_account_entry(emae, "pgp_key", E_ACCOUNT_PGP_KEY, xml);
+ emae_account_toggle(emae, "pgp_encrypt_to_self", E_ACCOUNT_PGP_ENCRYPT_TO_SELF, xml);
+ emae_account_toggle(emae, "pgp_always_sign", E_ACCOUNT_PGP_ALWAYS_SIGN, xml);
+ emae_account_toggle(emae, "pgp_no_imip_sign", E_ACCOUNT_PGP_NO_IMIP_SIGN, xml);
+ emae_account_toggle(emae, "pgp_always_trust", E_ACCOUNT_PGP_ALWAYS_TRUST, xml);
+
+#if defined (HAVE_NSS)
+ /* TODO: this should handle its entry separately? */
+ gui->smime_sign_key = emae_account_entry(emae, "smime_sign_key", E_ACCOUNT_SMIME_SIGN_KEY, xml);
+ gui->smime_sign_key_select = (GtkButton *)glade_xml_get_widget (xml, "smime_sign_key_select");
+ gui->smime_sign_key_clear = (GtkButton *)glade_xml_get_widget (xml, "smime_sign_key_clear");
+ g_signal_connect(gui->smime_sign_key_select, "clicked", G_CALLBACK(smime_sign_key_select), emae);
+ g_signal_connect(gui->smime_sign_key_clear, "clicked", G_CALLBACK(smime_sign_key_clear), emae);
+
+ gui->smime_sign_default = emae_account_toggle(emae, "smime_sign_default", E_ACCOUNT_SMIME_SIGN_DEFAULT, xml);
+
+ gui->smime_encrypt_key = emae_account_entry(emae, "smime_encrypt_key", E_ACCOUNT_SMIME_ENCRYPT_KEY, xml);
+ gui->smime_encrypt_key_select = (GtkButton *)glade_xml_get_widget (xml, "smime_encrypt_key_select");
+ gui->smime_encrypt_key_clear = (GtkButton *)glade_xml_get_widget (xml, "smime_encrypt_key_clear");
+ g_signal_connect(gui->smime_encrypt_key_select, "clicked", G_CALLBACK(smime_encrypt_key_select), emae);
+ g_signal_connect(gui->smime_encrypt_key_clear, "clicked", G_CALLBACK(smime_encrypt_key_clear), emae);
+
+ gui->smime_encrypt_default = emae_account_toggle(emae, "smime_encrypt_default", E_ACCOUNT_SMIME_ENCRYPT_DEFAULT, xml);
+ gui->smime_encrypt_to_self = emae_account_toggle(emae, "smime_encrypt_to_self", E_ACCOUNT_SMIME_ENCRYPT_TO_SELF, xml);
+ smime_changed(emae);
+#else
+ {
+ /* Since we don't have NSS, hide the S/MIME config options */
+ GtkWidget *frame;
+
+ frame = glade_xml_get_widget(xml, "smime_vbox");
+ gtk_widget_destroy(frame);
+ }
+#endif /* HAVE_NSS */
+
+ w = glade_xml_get_widget(xml, item->label);
+ gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Security")));
+
+ g_object_unref(xml);
+
+ return w;
+}
+
+static GtkWidget *
+emae_widget_glade(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ gint i;
+
+ for (i=0;emae->priv->widgets[i];i++)
+ if (!strcmp(emae->priv->widgets_name[i], item->label))
+ return emae->priv->widgets[i];
+
+ g_warning("Mail account widget '%s' not found", item->label);
+
+ return NULL;
+}
+
+/* plugin meta-data for "org.gnome.evolution.mail.config.accountEditor" */
+static EMConfigItem emae_editor_items[] = {
+ { E_CONFIG_BOOK, (gchar *) "" },
+ { E_CONFIG_PAGE, (gchar *) "00.identity", (gchar *) "vboxIdentityBorder", emae_identity_page },
+ { E_CONFIG_SECTION, (gchar *) "00.identity/00.name", (gchar *) "account_vbox", emae_widget_glade },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "00.identity/10.required", (gchar *) "identity_required_table", emae_widget_glade },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "00.identity/20.info", (gchar *) "identity_optional_table", emae_widget_glade },
+
+ { E_CONFIG_PAGE, (gchar *) "10.receive", (gchar *) "vboxSourceBorder", emae_receive_page },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "10.receive/00.type", (gchar *) "source_type_table", emae_widget_glade },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "10.receive/10.config", (gchar *) "table4", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "10.receive/20.security", (gchar *) "vbox181", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "10.receive/30.auth", (gchar *) "vbox179", emae_widget_glade },
+
+ /* Most sections for this is auto-generated from the camel config */
+ { E_CONFIG_PAGE, (gchar *) "20.receive_options", (gchar *) N_("Receiving Options"), },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "20.receive_options/10.mailcheck", (gchar *) N_("Checking for New Messages"), },
+ { E_CONFIG_ITEM_TABLE, (gchar *) "20.receive_options/10.mailcheck/00.autocheck", NULL, emae_receive_options_item, },
+
+ { E_CONFIG_PAGE, (gchar *) "30.send", (gchar *) "vboxTransportBorder", emae_send_page },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "30.send/00.type", (gchar *) "transport_type_table", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "30.send/10.config", (gchar *) "vbox12", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "30.send/20.security", (gchar *) "vbox183", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "30.send/30.auth", (gchar *) "vbox61", emae_widget_glade },
+
+ { E_CONFIG_PAGE, (gchar *) "40.defaults", (gchar *) "vboxFoldersBorder", emae_defaults_page },
+ { E_CONFIG_SECTION, (gchar *) "40.defaults/00.folders", (gchar *) "vbox184", emae_widget_glade },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "40.defaults/10.composing", (gchar *) "table8", emae_widget_glade },
+
+ { E_CONFIG_PAGE, (gchar *) "50.security", (gchar *) "vboxSecurityBorder", emae_security_page },
+ /* 1x1 table(!) not vbox: { E_CONFIG_SECTION, "50.security/00.gpg", "table19", emae_widget_glade }, */
+ /* table not vbox: { E_CONFIG_SECTION, "50.security/10.smime", "smime_table", emae_widget_glade }, */
+ { 0 },
+};
+static gboolean emae_editor_items_translated = FALSE;
+
+static GtkWidget *
+emae_management_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ EMAccountEditorPrivate *gui = emae->priv;
+ GtkWidget *w;
+
+ w = gui->management_frame;
+ if (((EConfig *)gui->config)->type == E_CONFIG_DRUID) {
+ GladeXML *druidxml;
+ GtkWidget *page;
+ gchar *gladefile;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ druidxml = glade_xml_new(gladefile, "management_page", NULL);
+ g_free (gladefile);
+
+ page = glade_xml_get_widget(druidxml, "management_page");
+
+ gtk_widget_reparent(w, ((GnomeDruidPageStandard *)page)->vbox);
+ w = page;
+ g_object_unref(druidxml);
+ gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)page);
+ }
+
+ return w;
+}
+
+static GtkWidget *
+emae_widget_druid_glade(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ GladeXML *druidxml;
+ GtkWidget *w;
+ gchar *gladefile;
+ EMAccountEditor *emae = (EMAccountEditor *)data;
+
+ if (emae->type == EMAE_PAGES)
+ return NULL;
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ druidxml = glade_xml_new(gladefile, item->label, NULL);
+ g_free (gladefile);
+
+ w = glade_xml_get_widget(druidxml, item->label);
+ /* i think the glade file has issues, we need to show all on at least the end page */
+ gtk_widget_show_all(w);
+ g_object_unref(druidxml);
+
+ gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)w);
+
+ return w;
+}
+
+/* plugin meta-data for "org.gnome.evolution.mail.config.accountDruid" */
+static EMConfigItem emae_druid_items[] = {
+ { E_CONFIG_DRUID, (gchar *) "" },
+ { E_CONFIG_PAGE_START, (gchar *) "0.start", (gchar *) "start_page", emae_widget_druid_glade },
+
+ { E_CONFIG_PAGE, (gchar *) "00.identity", (gchar *) "vboxIdentityBorder", emae_identity_page },
+ { E_CONFIG_SECTION, (gchar *) "00.identity/00.name", (gchar *) "account_vbox", emae_widget_glade },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "00.identity/10.required", (gchar *) "identity_required_table", emae_widget_glade },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "00.identity/20.info", (gchar *) "identity_optional_table", emae_widget_glade },
+
+ { E_CONFIG_PAGE, (gchar *) "10.receive", (gchar *) "vboxSourceBorder", emae_receive_page },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "10.receive/00.type", (gchar *) "source_type_table", emae_widget_glade },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "10.receive/10.config", (gchar *) "table4", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "10.receive/20.security", (gchar *) "vbox181", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "10.receive/30.auth", (gchar *) "vbox179", emae_widget_glade },
+
+ /* Most sections for this is auto-generated fromt the camel config */
+ { E_CONFIG_PAGE, (gchar *) "20.receive_options", (gchar *) N_("Receiving Options"), },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "20.receive_options/10.mailcheck", (gchar *) N_("Checking for New Messages"), },
+ { E_CONFIG_ITEM_TABLE, (gchar *) "20.receive_options/10.mailcheck/00.autocheck", NULL, emae_receive_options_item, },
+
+ { E_CONFIG_PAGE, (gchar *) "30.send", (gchar *) "vboxTransportBorder", emae_send_page },
+ { E_CONFIG_SECTION_TABLE, (gchar *) "30.send/00.type", (gchar *) "transport_type_table", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "30.send/10.config", (gchar *) "vbox12", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "30.send/20.security", (gchar *) "vbox183", emae_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "30.send/30.auth", (gchar *) "vbox61", emae_widget_glade },
+
+ { E_CONFIG_PAGE, (gchar *) "40.management", (gchar *) "management_frame", emae_management_page },
+
+ { E_CONFIG_PAGE_FINISH, (gchar *) "999.end", (gchar *) "finish_page", emae_widget_druid_glade },
+ { 0 },
+};
+static gboolean emae_druid_items_translated = FALSE;
+
+static void
+emae_free(EConfig *ec, GSList *items, gpointer data)
+{
+ g_slist_free(items);
+}
+
+static void
+emae_free_auto(EConfig *ec, GSList *items, gpointer data)
+{
+ GSList *l, *n;
+
+ for (l=items;l;) {
+ struct _receive_options_item *item = l->data;
+
+ n = g_slist_next(l);
+ g_free(item->item.path);
+ if (item->extra_table)
+ g_hash_table_destroy(item->extra_table);
+ g_free(item);
+ g_slist_free_1(l);
+ l = n;
+ }
+}
+
+static gboolean
+emae_service_complete(EMAccountEditor *emae, EMAccountEditorService *service)
+{
+ CamelURL *url;
+ gint ok = TRUE;
+ const gchar *uri;
+
+ if (service->provider == NULL)
+ return TRUE;
+
+ uri = e_account_get_string(emae->account, emae_service_info[service->type].account_uri_key);
+ if (uri == NULL || (url = camel_url_new(uri, NULL)) == NULL)
+ return FALSE;
+
+ if (CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_HOST)) {
+ if (url->host == NULL || url->host[0] == 0)
+ ok = FALSE;
+ }
+ /* We only need the user if the service needs auth as well, i think */
+ if (ok
+ && (service->needs_auth == NULL
+ || CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_AUTH)
+ || gtk_toggle_button_get_active(service->needs_auth))
+ && CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_USER)
+ && (url->user == NULL || url->user[0] == 0))
+ ok = FALSE;
+
+ if (ok
+ && CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_PATH)
+ && (url->path == NULL || url->path[0] == 0))
+ ok = FALSE;
+
+ camel_url_free(url);
+
+ return ok;
+}
+
+enum {
+ GMAIL = 0,
+ YAHOO,
+ AOL
+};
+struct _server_prefill {
+ const gchar *key;
+ const gchar *recv;
+ const gchar *send;
+ const gchar *proto;
+ const gchar *ssl;
+} mail_servers [] = {
+ {"gmail", "imap.gmail.com", "smtp.gmail.com", "imap", "always"},
+ {"yahoo", "pop3.yahoo.com", "smtp.yahoo.com", "pop", "never"},
+ {"aol", "imap.aol.com", "smtp.aol.com", "pop", "never"},
+ {"msn", "pop3.email.msn.com", "smtp.email.msn.com", "pop", "never"}
+};
+
+static gint
+check_servers (gchar *server)
+{
+ gint len = G_N_ELEMENTS(mail_servers), i;
+
+ for (i=0; i<len; i++) {
+ if (strstr(server, mail_servers[i].key) != NULL)
+ return i;
+ }
+
+ return -1;
+}
+
+static gboolean
+emae_check_complete(EConfig *ec, const gchar *pageid, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ gint ok = TRUE;
+ const gchar *tmp;
+ EAccount *ea;
+ gboolean refresh = FALSE;
+
+ /* We use the page-check of various pages to 'prepare' or
+ pre-load their values, only in the druid */
+ if (pageid
+ && ((EConfig *)emae->priv->config)->type == E_CONFIG_DRUID) {
+ if (!strcmp(pageid, "00.identity")) {
+ if (!emae->priv->identity_set) {
+ gchar *uname;
+
+ emae->priv->identity_set = 1;
+#ifndef G_OS_WIN32
+ uname = g_locale_to_utf8(g_get_real_name(), -1, NULL, NULL, NULL);
+#else
+ uname = g_strdup(g_get_real_name());
+#endif
+ if (uname) {
+ gtk_entry_set_text(emae->priv->identity_entries[1], uname);
+ g_free(uname);
+ }
+ }
+ } else if (!strcmp(pageid, "10.receive")) {
+ if (!emae->priv->receive_set) {
+ gchar *user, *at;
+ gint index;
+ gchar *uri = g_strdup(e_account_get_string(emae->account, E_ACCOUNT_SOURCE_URL));
+ CamelURL *url;
+
+ emae->priv->receive_set = 1;
+ tmp = (gchar *)e_account_get_string(emae->account, E_ACCOUNT_ID_ADDRESS);
+ at = strchr(tmp, '@');
+ user = g_alloca(at-tmp+1);
+ memcpy(user, tmp, at-tmp);
+ user[at-tmp] = 0;
+ at++;
+
+ index = check_servers(at);
+ gtk_entry_set_text(emae->priv->source.username, user);
+ gtk_entry_set_text(emae->priv->transport.username, user);
+ if (uri && (url = camel_url_new(uri, NULL)) != NULL) {
+ refresh = TRUE;
+ camel_url_set_protocol(url, mail_servers[index].proto);
+ camel_url_set_param(url, "use_ssl", mail_servers[index].ssl);
+ camel_url_set_host (url, mail_servers[index].recv);
+ camel_url_set_user (url, user);
+ gtk_entry_set_text(emae->priv->source.hostname, mail_servers[index].recv);
+ gtk_entry_set_text(emae->priv->transport.hostname, mail_servers[index].send);
+ uri = camel_url_to_string(url, 0);
+ e_account_set_string(emae->account, E_ACCOUNT_SOURCE_URL, uri);
+
+ g_free(uri);
+ camel_url_free(url);
+ }
+
+ }
+ } else if (!strcmp(pageid, "30.send")) {
+ CamelURL *url;
+ gchar *at, *user;
+ gint index;
+ gchar *uri = (gchar *)e_account_get_string(emae->account, E_ACCOUNT_TRANSPORT_URL);
+
+ tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_ADDRESS);
+ at = strchr(tmp, '@');
+ user = g_alloca(at-tmp+1);
+ memcpy(user, tmp, at-tmp);
+ user[at-tmp] = 0;
+ at++;
+
+ index = check_servers(at);
+ if (uri && (url = camel_url_new(uri, NULL)) != NULL) {
+ refresh = TRUE;
+ camel_url_set_protocol (url, "smtp");
+ camel_url_set_param(url, "use_ssl", mail_servers[index].ssl);
+ camel_url_set_host (url, mail_servers[index].send);
+ camel_url_set_user (url, user);
+ uri = camel_url_to_string(url, 0);
+ e_account_set_string(emae->account, E_ACCOUNT_TRANSPORT_URL, uri);
+ g_free(uri);
+ camel_url_free(url);
+ } else {
+ g_warning("buz2\n");
+ }
+
+ } else if (!strcmp(pageid, "20.receive_options")) {
+ if (emae->priv->source.provider
+ && emae->priv->extra_provider != emae->priv->source.provider) {
+ emae->priv->extra_provider = emae->priv->source.provider;
+ emae_auto_detect(emae);
+ }
+ } else if (!strcmp(pageid, "40.management")) {
+ if (!emae->priv->management_set) {
+ gchar *template;
+ guint i = 0, len;
+
+ emae->priv->management_set = 1;
+ tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_ADDRESS);
+ len = strlen(tmp);
+ template = alloca(len + 14);
+ strcpy(template, tmp);
+ while (e_get_account_by_name (template))
+ sprintf(template + len, " (%d)", i++);
+
+ gtk_entry_set_text(emae->priv->identity_entries[0], template);
+ }
+ }
+ }
+
+ /*
+ Setting a flag on the Account if it is marked as default. It is done in this way instead of
+ using a temporary variable so as to keep track of which account is marked as default in case of
+ editing multiple accounts at a time
+ */
+ if (gtk_toggle_button_get_active(emae->priv->default_account))
+ g_object_set_data (G_OBJECT (emae->account), "default_flagged", GINT_TO_POINTER(1));
+
+ if (pageid == NULL || !strcmp(pageid, "00.identity")) {
+ /* TODO: check the account name is set, and unique in the account list */
+ ok = (tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_NAME))
+ && tmp[0]
+ && (tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_ADDRESS))
+ && is_email(tmp)
+ && ((tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_REPLY_TO)) == NULL
+ || tmp[0] == 0
+ || is_email(tmp));
+ if (!ok) {
+ d(printf("identity incomplete\n"));
+ }
+ }
+
+ if (ok && (pageid == NULL || !strcmp(pageid, "10.receive"))) {
+ if (emae->type == EMAE_PAGES && refresh) {
+ emae_refresh_providers(emae, &emae->priv->source);
+ }
+ ok = emae_service_complete(emae, &emae->priv->source);
+ if (!ok) {
+ d(printf("receive page incomplete\n"));
+ }
+ }
+
+ if (ok && (pageid == NULL || !strcmp(pageid, "30.send"))) {
+ if (emae->type == EMAE_PAGES && refresh) {
+ emae_refresh_providers(emae, &emae->priv->transport);
+ }
+ ok = emae_service_complete(emae, &emae->priv->transport);
+ if (!ok) {
+ d(printf("send page incomplete\n"));
+ }
+ }
+
+ if (ok && (pageid == NULL || !strcmp(pageid, "40.management"))) {
+ ok = (tmp = e_account_get_string(emae->account, E_ACCOUNT_NAME))
+ && tmp[0]
+ && ((ea = e_get_account_by_name (tmp)) == NULL
+ || ea == emae->original);
+ if (!ok) {
+ d(printf("management page incomplete\n"));
+ }
+ }
+
+ return ok;
+}
+
+void
+em_account_editor_check (EMAccountEditor *emae, const gchar *page)
+{
+ emae_check_complete((EConfig *)emae->config, page, emae);
+}
+
+/* HACK: FIXME: the component should listen to the account object directly */
+static void
+add_new_store (gchar *uri, CamelStore *store, gpointer user_data)
+{
+#if 0 /* KILL-BONOBO: Try to actually fix this? */
+ MailComponent *component = mail_component_peek ();
+ EAccount *account = user_data;
+
+ if (store == NULL)
+ return;
+
+ mail_component_add_store (component, store, account->name);
+#endif
+}
+
+static void
+emae_commit(EConfig *ec, GSList *items, gpointer data)
+{
+ EMAccountEditor *emae = data;
+ EAccountList *accounts = e_get_account_list ();
+ EAccount *account;
+
+ /* the mail-config*acconts* api needs a lot of work */
+
+ if (emae->original) {
+ d(printf("Committing account '%s'\n", e_account_get_string(emae->account, E_ACCOUNT_NAME)));
+ e_account_import(emae->original, emae->account);
+ account = emae->original;
+ e_account_list_change(accounts, account);
+ } else {
+ d(printf("Adding new account '%s'\n", e_account_get_string(emae->account, E_ACCOUNT_NAME)));
+ e_account_list_add(accounts, emae->account);
+ account = emae->account;
+
+ /* HACK: this will add the account to the folder tree.
+ We should just be listening to the account list directly for changed events */
+ if (account->enabled
+ && emae->priv->source.provider
+ && (emae->priv->source.provider->flags & CAMEL_PROVIDER_IS_STORAGE))
+ mail_get_store(e_account_get_string(emae->account, E_ACCOUNT_SOURCE_URL), NULL, add_new_store, account);
+ }
+
+ if (gtk_toggle_button_get_active(emae->priv->default_account))
+ e_account_list_set_default(accounts, account);
+
+ e_account_list_save(accounts);
+}
+
+void
+em_account_editor_commit (EMAccountEditor *emae)
+{
+ emae_commit ((EConfig *)emae->config, NULL, emae);
+}
+
+static void
+emae_editor_destroyed(GtkWidget *dialog, EMAccountEditor *emae)
+{
+ emae->editor = NULL;
+ g_object_unref(emae);
+}
+
+static void
+em_account_editor_construct(EMAccountEditor *emae, EAccount *account, em_account_editor_t type, const gchar *id)
+{
+ EMAccountEditorPrivate *gui = emae->priv;
+ gint i, index;
+ GSList *l;
+ GList *prov;
+ EMConfig *ec;
+ EMConfigTargetAccount *target;
+ GHashTable *have;
+ EConfigItem *items;
+
+ emae->type = type;
+ emae->original = account;
+ if (emae->original) {
+ gchar *xml;
+
+ g_object_ref(emae->original);
+ xml = e_account_to_xml(emae->original);
+ emae->account = e_account_new_from_xml(xml);
+ g_free(xml);
+
+ emae->do_signature = TRUE;
+ } else {
+ /* TODO: have a get_default_account thing?? */
+ emae->account = e_account_new();
+ emae->account->enabled = TRUE;
+
+ e_account_set_string (
+ emae->account, E_ACCOUNT_DRAFTS_FOLDER_URI,
+ e_mail_local_get_folder_uri (E_MAIL_FOLDER_DRAFTS));
+
+ e_account_set_string (
+ emae->account, E_ACCOUNT_SENT_FOLDER_URI,
+ e_mail_local_get_folder_uri (E_MAIL_FOLDER_SENT));
+ }
+
+ /* sort the providers, remote first */
+ gui->providers = g_list_sort(camel_provider_list(TRUE), (GCompareFunc)provider_compare);
+
+ if (type == EMAE_NOTEBOOK) {
+ ec = em_config_new(E_CONFIG_BOOK, id);
+ items = emae_editor_items;
+ if (!emae_editor_items_translated) {
+ for (i=0;items[i].path;i++) {
+ if (items[i].label)
+ items[i].label = gettext(items[i].label);
+ }
+ emae_editor_items_translated = TRUE;
+ }
+ } else {
+ ec = em_config_new(E_CONFIG_DRUID, id);
+ items = emae_druid_items;
+ if (!emae_druid_items_translated) {
+ for (i=0;items[i].path;i++) {
+ if (items[i].label)
+ items[i].label = _(items[i].label);
+ }
+ emae_druid_items_translated = TRUE;
+ }
+ }
+
+ emae->config = gui->config = ec;
+ l = NULL;
+ for (i=0;items[i].path;i++)
+ l = g_slist_prepend(l, &items[i]);
+ e_config_add_items((EConfig *)ec, l, emae_commit, NULL, emae_free, emae);
+
+ /* This is kinda yuck, we're dynamically mapping from the 'old style' extensibility api to the new one */
+ l = NULL;
+ have = g_hash_table_new(g_str_hash, g_str_equal);
+ index = 20;
+ for (prov=gui->providers;prov;prov=g_list_next(prov)) {
+ CamelProviderConfEntry *entries = ((CamelProvider *)prov->data)->extra_conf;
+
+ for (i=0;entries && entries[i].type != CAMEL_PROVIDER_CONF_END;i++) {
+ struct _receive_options_item *item;
+ const gchar *name = entries[i].name;
+ gint myindex = index;
+
+ if (entries[i].type != CAMEL_PROVIDER_CONF_SECTION_START
+ || name == NULL
+ || g_hash_table_lookup(have, name))
+ continue;
+
+ /* override mailcheck since we also insert our own mailcheck item at this index */
+ if (name && !strcmp(name, "mailcheck"))
+ myindex = 10;
+
+ item = g_malloc0(sizeof(*item));
+ item->item.type = E_CONFIG_SECTION_TABLE;
+ item->item.path = g_strdup_printf("20.receive_options/%02d.%s", myindex, name?name:"unnamed");
+ item->item.label = g_strdup (entries[i].text);
+
+ l = g_slist_prepend(l, item);
+
+ item = g_malloc0(sizeof(*item));
+ item->item.type = E_CONFIG_ITEM_TABLE;
+ item->item.path = g_strdup_printf("20.receive_options/%02d.%s/80.camelitem", myindex, name?name:"unnamed");
+ item->item.factory = emae_receive_options_extra_item;
+ item->item.user_data = g_strdup (entries[i].name);
+
+ l = g_slist_prepend(l, item);
+
+ index += 10;
+ g_hash_table_insert(have, (gpointer)entries[i].name, have);
+ }
+ }
+ g_hash_table_destroy(have);
+ e_config_add_items((EConfig *)ec, l, NULL, NULL, emae_free_auto, emae);
+ gui->extra_items = l;
+
+ e_config_add_page_check((EConfig *)ec, NULL, emae_check_complete, emae);
+
+ target = em_config_target_new_account(ec, emae->account);
+ e_config_set_target((EConfig *)ec, (EConfigTarget *)target);
+
+ if (type != EMAE_PAGES) {
+ emae->editor = e_config_create_window((EConfig *)ec, NULL, type==EMAE_NOTEBOOK?_("Account Editor"):_("Evolution Account Assistant"));
+ g_signal_connect(emae->editor, "destroy", G_CALLBACK(emae_editor_destroyed), emae);
+ } else {
+ e_config_create_widget((EConfig *)ec);
+ }
+}
diff --git a/modules/mail/em-account-editor.h b/modules/mail/em-account-editor.h
new file mode 100644
index 0000000000..d8d96f58f0
--- /dev/null
+++ b/modules/mail/em-account-editor.h
@@ -0,0 +1,86 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj@ximian.com>
+ * Dan Winship <danw@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EM_ACCOUNT_EDITOR_H
+#define EM_ACCOUNT_EDITOR_H
+
+#include <gtk/gtk.h>
+
+#include <mail/em-config.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EMAccountEditor EMAccountEditor;
+typedef struct _EMAccountEditorClass EMAccountEditorClass;
+typedef struct _EMAccountEditorPrivate EMAccountEditorPrivate;
+
+typedef enum {
+ EMAE_NOTEBOOK,
+ EMAE_DRUID,
+ EMAE_PAGES
+} em_account_editor_t;
+
+struct _EMAccountEditor {
+ GObject gobject;
+
+ EMAccountEditorPrivate *priv;
+
+ em_account_editor_t type;
+ GtkWidget *editor; /* gtknotebook or druid, depending on type */
+
+ EMConfig *config; /* driver object */
+
+ EAccount *account; /* working account, must instant apply to this */
+ EAccount *original; /* original account, not changed unless commit is invoked */
+
+ GtkWidget **pages; /* Pages for Anjal's page type editor */
+
+ guint do_signature:1; /* allow editing signature */
+};
+
+struct _EMAccountEditorClass {
+ GObjectClass gobject_class;
+};
+
+GType em_account_editor_get_type(void);
+
+EMAccountEditor *em_account_editor_new(EAccount *account, em_account_editor_t type, const gchar *id);
+EMAccountEditor *em_account_editor_new_for_pages(EAccount *account, em_account_editor_t type, gchar *id, GtkWidget **pages);
+void em_account_editor_commit (EMAccountEditor *emae);
+void em_account_editor_check (EMAccountEditor *emae, const gchar *page);
+
+gboolean em_account_editor_save (EMAccountEditor *gui);
+void em_account_editor_destroy (EMAccountEditor *gui);
+
+gboolean em_account_editor_identity_complete (EMAccountEditor *gui, GtkWidget **incomplete);
+gboolean em_account_editor_source_complete (EMAccountEditor *gui, GtkWidget **incomplete);
+gboolean em_account_editor_transport_complete (EMAccountEditor *gui, GtkWidget **incomplete);
+gboolean em_account_editor_management_complete (EMAccountEditor *gui, GtkWidget **incomplete);
+
+void em_account_editor_build_extra_conf (EMAccountEditor *gui, const gchar *url);
+
+void em_account_editor_auto_detect_extra_conf (EMAccountEditor *gui);
+
+G_END_DECLS
+
+#endif /* EM_ACCOUNT_EDITOR_H */
diff --git a/modules/mail/em-account-prefs.c b/modules/mail/em-account-prefs.c
new file mode 100644
index 0000000000..f2c646d5ed
--- /dev/null
+++ b/modules/mail/em-account-prefs.c
@@ -0,0 +1,323 @@
+/*
+ * em-account-prefs.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/* XXX EAccountManager handles all the user interface stuff. This subclass
+ * applies policies using mailer resources that EAccountManager does not
+ * have access to. The desire is to someday move account management
+ * completely out of the mailer, perhaps to evolution-data-server. */
+
+#include "em-account-prefs.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <camel/camel-url.h>
+
+#include <glib/gi18n.h>
+
+#include "e-util/e-error.h"
+
+#include "e-mail-store.h"
+#include "em-config.h"
+#include "em-account-editor.h"
+
+#define EM_ACCOUNT_PREFS_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), EM_TYPE_ACCOUNT_PREFS, EMAccountPrefsPrivate))
+
+struct _EMAccountPrefsPrivate {
+ gpointer druid; /* weak pointer */
+ gpointer editor; /* weak pointer */
+};
+
+static gpointer parent_class;
+
+static void
+account_prefs_enable_account_cb (EAccountTreeView *tree_view)
+{
+ EAccount *account;
+
+ account = e_account_tree_view_get_selected (tree_view);
+ g_return_if_fail (account != NULL);
+
+ e_mail_store_add_by_uri (account->source->url, account->name);
+}
+
+static void
+account_prefs_disable_account_cb (EAccountTreeView *tree_view)
+{
+ EAccountList *account_list;
+ EAccount *account;
+ gpointer parent;
+ gint response;
+
+ account = e_account_tree_view_get_selected (tree_view);
+ g_return_if_fail (account != NULL);
+
+ account_list = e_account_tree_view_get_account_list (tree_view);
+ g_return_if_fail (account_list != NULL);
+
+ if (!e_account_list_account_has_proxies (account_list, account))
+ return;
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ response = e_error_run (
+ parent, "mail:ask-delete-proxy-accounts", NULL);
+
+ if (response != GTK_RESPONSE_YES) {
+ g_signal_stop_emission_by_name (tree_view, "disable-account");
+ return;
+ }
+
+ e_account_list_remove_account_proxies (account_list, account);
+
+ e_mail_store_remove_by_uri (account->source->url);
+}
+
+static void
+account_prefs_add_account (EAccountManager *manager)
+{
+ EMAccountPrefsPrivate *priv;
+ EMAccountEditor *emae;
+ gpointer parent;
+
+ priv = EM_ACCOUNT_PREFS_GET_PRIVATE (manager);
+
+ if (priv->druid != NULL) {
+ gtk_window_present (GTK_WINDOW (priv->druid));
+ return;
+ }
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (manager));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ /** @HookPoint-EMConfig: New Mail Account Druid
+ * @Id: org.gnome.evolution.mail.config.accountDruid
+ * @Type: E_CONFIG_DRUID
+ * @Class: org.gnome.evolution.mail.config:1.0
+ * @Target: EMConfigTargetAccount
+ *
+ * The new mail account druid.
+ */
+ emae = em_account_editor_new (
+ NULL, EMAE_DRUID,
+ "org.gnome.evolution.mail.config.accountDruid");
+ priv->druid = emae->editor;
+
+ g_object_add_weak_pointer (G_OBJECT (priv->druid), &priv->druid);
+ gtk_window_set_transient_for (GTK_WINDOW (priv->druid), parent);
+ gtk_widget_show (priv->druid);
+}
+
+static void
+account_prefs_edit_account (EAccountManager *manager)
+{
+ EMAccountPrefsPrivate *priv;
+ EMAccountEditor *emae;
+ EAccountTreeView *tree_view;
+ EAccountList *account_list;
+ EAccount *account;
+ gpointer parent;
+
+ priv = EM_ACCOUNT_PREFS_GET_PRIVATE (manager);
+
+ if (priv->editor != NULL) {
+ gtk_window_present (GTK_WINDOW (priv->editor));
+ return;
+ }
+
+ account_list = e_account_manager_get_account_list (manager);
+ tree_view = e_account_manager_get_tree_view (manager);
+ account = e_account_tree_view_get_selected (tree_view);
+ g_return_if_fail (account != NULL);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (manager));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ /** @HookPoint-EMConfig: Mail Account Editor
+ * @Id: org.gnome.evolution.mail.config.accountEditor
+ * @Type: E_CONFIG_BOOK
+ * @Class: org.gnome.evolution.mail.config:1.0
+ * @Target: EMConfigTargetAccount
+ *
+ * The account editor window.
+ */
+ emae = em_account_editor_new (
+ account, EMAE_NOTEBOOK,
+ "org.gnome.evolution.mail.config.accountEditor");
+ priv->editor = emae->editor;
+
+ g_object_add_weak_pointer (G_OBJECT (priv->editor), &priv->editor);
+ gtk_window_set_transient_for (GTK_WINDOW (priv->editor), parent);
+ gtk_widget_show (priv->editor);
+}
+
+static void
+account_prefs_delete_account (EAccountManager *manager)
+{
+ EMAccountPrefsPrivate *priv;
+ EAccountTreeView *tree_view;
+ EAccountList *account_list;
+ EAccount *account;
+ gboolean has_proxies;
+ gpointer parent;
+ gint response;
+
+ priv = EM_ACCOUNT_PREFS_GET_PRIVATE (manager);
+
+ account_list = e_account_manager_get_account_list (manager);
+ tree_view = e_account_manager_get_tree_view (manager);
+ account = e_account_tree_view_get_selected (tree_view);
+ g_return_if_fail (account != NULL);
+
+ /* Make sure we aren't editing anything... */
+ if (priv->editor != NULL)
+ return;
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (manager));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ has_proxies =
+ e_account_list_account_has_proxies (account_list, account);
+
+ response = e_error_run (
+ parent, has_proxies ?
+ "mail:ask-delete-account-with-proxies" :
+ "mail:ask-delete-account", NULL);
+
+ if (response != GTK_RESPONSE_YES) {
+ g_signal_stop_emission_by_name (manager, "delete-account");
+ return;
+ }
+
+ /* Remove the account from the folder tree. */
+ if (account->enabled && account->source && account->source->url)
+ e_mail_store_remove_by_uri (account->source->url);
+
+ /* Remove all the proxies the account has created. */
+ if (has_proxies)
+ e_account_list_remove_account_proxies (account_list, account);
+
+ /* Remove it from the config file. */
+ e_account_list_remove (account_list, account);
+
+ e_account_list_save (account_list);
+}
+
+static void
+account_prefs_dispose (GObject *object)
+{
+ EMAccountPrefsPrivate *priv;
+
+ priv = EM_ACCOUNT_PREFS_GET_PRIVATE (object);
+
+ if (priv->druid != NULL) {
+ g_object_remove_weak_pointer (
+ G_OBJECT (priv->druid), &priv->druid);
+ priv->druid = NULL;
+ }
+
+ if (priv->editor != NULL) {
+ g_object_remove_weak_pointer (
+ G_OBJECT (priv->editor), &priv->editor);
+ priv->editor = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+account_prefs_class_init (EMAccountPrefsClass *class)
+{
+ GObjectClass *object_class;
+ EAccountManagerClass *account_manager_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMAccountPrefsPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = account_prefs_dispose;
+
+ account_manager_class = E_ACCOUNT_MANAGER_CLASS (class);
+ account_manager_class->add_account = account_prefs_add_account;
+ account_manager_class->edit_account = account_prefs_edit_account;
+ account_manager_class->delete_account = account_prefs_delete_account;
+}
+
+static void
+account_prefs_init (EMAccountPrefs *prefs)
+{
+ EAccountManager *manager;
+ EAccountTreeView *tree_view;
+
+ prefs->priv = EM_ACCOUNT_PREFS_GET_PRIVATE (prefs);
+
+ manager = E_ACCOUNT_MANAGER (prefs);
+ tree_view = e_account_manager_get_tree_view (manager);
+
+ g_signal_connect (
+ tree_view, "enable-account",
+ G_CALLBACK (account_prefs_enable_account_cb), NULL);
+
+ g_signal_connect (
+ tree_view, "disable-account",
+ G_CALLBACK (account_prefs_disable_account_cb), NULL);
+}
+
+GType
+em_account_prefs_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EMAccountPrefsClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) account_prefs_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMAccountPrefs),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) account_prefs_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_ACCOUNT_MANAGER, "EMAccountPrefs",
+ &type_info, 0);
+ }
+
+ return type;
+}
+
+GtkWidget *
+em_account_prefs_new (EAccountList *account_list)
+{
+ g_return_val_if_fail (E_IS_ACCOUNT_LIST (account_list), NULL);
+
+ return g_object_new (
+ EM_TYPE_ACCOUNT_PREFS, "account-list", account_list, NULL);
+}
diff --git a/modules/mail/em-account-prefs.h b/modules/mail/em-account-prefs.h
new file mode 100644
index 0000000000..82df8fa941
--- /dev/null
+++ b/modules/mail/em-account-prefs.h
@@ -0,0 +1,69 @@
+/*
+ * em-account-prefs.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EM_ACCOUNT_PREFS_H
+#define EM_ACCOUNT_PREFS_H
+
+#include <gtk/gtk.h>
+#include <table/e-table.h>
+#include <libedataserver/e-account-list.h>
+#include <misc/e-account-manager.h>
+
+/* Standard GObject macros */
+#define EM_TYPE_ACCOUNT_PREFS \
+ (em_account_prefs_get_type ())
+#define EM_ACCOUNT_PREFS(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), EM_TYPE_ACCOUNT_PREFS, EMAccountPrefs))
+#define EM_ACCOUNT_PREFS_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), EM_TYPE_ACCOUNT_PREFS, EMAccountPrefsClass))
+#define EM_IS_ACCOUNT_PREFS(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), EM_TYPE_ACCOUNT_PREFS))
+#define EM_IS_ACCOUNT_PREFS_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), EM_TYPE_ACCOUNT_PREFS))
+#define EM_ACCOUNT_PREFS_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), EM_TYPE_ACCOUNT_PREFS, EMAccountPrefsClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMAccountPrefs EMAccountPrefs;
+typedef struct _EMAccountPrefsClass EMAccountPrefsClass;
+typedef struct _EMAccountPrefsPrivate EMAccountPrefsPrivate;
+
+struct _EMAccountPrefs {
+ EAccountManager parent;
+ EMAccountPrefsPrivate *priv;
+};
+
+struct _EMAccountPrefsClass {
+ EAccountManagerClass parent_class;
+};
+
+GType em_account_prefs_get_type (void);
+GtkWidget * em_account_prefs_new (EAccountList *account_list);
+
+G_END_DECLS
+
+#endif /* EM_ACCOUNT_PREFS_H */
diff --git a/modules/mail/em-composer-prefs.c b/modules/mail/em-composer-prefs.c
new file mode 100644
index 0000000000..18dae567ca
--- /dev/null
+++ b/modules/mail/em-composer-prefs.c
@@ -0,0 +1,621 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "e-util/e-binding.h"
+#include "e-util/e-signature-utils.h"
+#include "e-util/gconf-bridge.h"
+
+#include "em-composer-prefs.h"
+#include "composer/e-msg-composer.h"
+
+#include <camel/camel-iconv.h>
+
+#include <misc/e-gui-utils.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <gtkhtml/gtkhtml.h>
+#include <editor/gtkhtml-spell-language.h>
+
+#include "misc/e-charset-picker.h"
+#include "misc/e-signature-editor.h"
+#include "misc/e-signature-manager.h"
+#include "misc/e-signature-preview.h"
+#include "e-util/e-error.h"
+#include "e-util/e-util-private.h"
+
+#include "mail-config.h"
+#include "em-config.h"
+
+static gpointer parent_class;
+
+static gboolean
+transform_color_to_string (const GValue *src_value,
+ GValue *dst_value,
+ gpointer user_data)
+{
+ const GdkColor *color;
+ gchar *string;
+
+ color = g_value_get_boxed (src_value);
+ string = gdk_color_to_string (color);
+ g_value_set_string (dst_value, string);
+ g_free (string);
+
+ return TRUE;
+}
+
+static gboolean
+transform_string_to_color (const GValue *src_value,
+ GValue *dst_value,
+ gpointer user_data)
+{
+ GdkColor color;
+ const gchar *string;
+ gboolean success = FALSE;
+
+ string = g_value_get_string (src_value);
+ if (gdk_color_parse (string, &color)) {
+ g_value_set_boxed (dst_value, &color);
+ success = TRUE;
+ }
+
+ return success;
+}
+
+static gboolean
+transform_old_to_new_reply_style (const GValue *src_value,
+ GValue *dst_value,
+ gpointer user_data)
+{
+ gboolean success = TRUE;
+
+ /* XXX This is the kind of legacy crap we wind up
+ * with when we don't migrate things properly. */
+
+ switch (g_value_get_int (src_value)) {
+ case 0: /* Quoted: 0 -> 2 */
+ g_value_set_int (dst_value, 2);
+ break;
+
+ case 1: /* Do Not Quote: 1 -> 3 */
+ g_value_set_int (dst_value, 3);
+ break;
+
+ case 2: /* Attach: 2 -> 0 */
+ g_value_set_int (dst_value, 0);
+ break;
+
+ case 3: /* Outlook: 3 -> 1 */
+ g_value_set_int (dst_value, 1);
+ break;
+
+ default:
+ success = FALSE;
+ break;
+ }
+
+ return success;
+}
+
+static gboolean
+transform_new_to_old_reply_style (const GValue *src_value,
+ GValue *dst_value,
+ gpointer user_data)
+{
+ gboolean success = TRUE;
+
+ /* XXX This is the kind of legacy crap we wind up
+ * with when we don't migrate things properly. */
+
+ switch (g_value_get_int (src_value)) {
+ case 0: /* Attach: 0 -> 2 */
+ g_value_set_int (dst_value, 2);
+ break;
+
+ case 1: /* Outlook: 1 -> 3 */
+ g_value_set_int (dst_value, 3);
+ break;
+
+ case 2: /* Quoted: 2 -> 0 */
+ g_value_set_int (dst_value, 0);
+ break;
+
+ case 3: /* Do Not Quote: 3 -> 1 */
+ g_value_set_int (dst_value, 1);
+ break;
+
+ default:
+ success = FALSE;
+ break;
+ }
+
+ return success;
+}
+
+static void
+composer_prefs_finalize (GObject *object)
+{
+ EMComposerPrefs *prefs = (EMComposerPrefs *) object;
+
+ g_object_unref (prefs->gui);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+composer_prefs_class_init (EMComposerPrefsClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = composer_prefs_finalize;
+}
+
+static void
+composer_prefs_init (EMComposerPrefs *prefs)
+{
+}
+
+GType
+em_composer_prefs_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EMComposerPrefsClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) composer_prefs_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMComposerPrefs),
+ 0, /* n_allocs */
+ (GInstanceInitFunc) composer_prefs_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_VBOX, "EMComposerPrefs", &type_info, 0);
+ }
+
+ return type;
+}
+
+void
+em_composer_prefs_new_signature (GtkWindow *parent,
+ gboolean html_mode)
+{
+ GtkWidget *editor;
+
+ editor = e_signature_editor_new ();
+ gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (editor), html_mode);
+ gtk_window_set_transient_for (GTK_WINDOW (editor), parent);
+ gtk_widget_show (editor);
+}
+
+static void
+spell_language_toggled_cb (GtkCellRendererToggle *renderer,
+ const gchar *path_string,
+ EMComposerPrefs *prefs)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean active;
+ gboolean valid;
+
+ model = prefs->language_model;
+
+ /* Convert the path string to a tree iterator. */
+ path = gtk_tree_path_new_from_string (path_string);
+ valid = gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+ g_return_if_fail (valid);
+
+ /* Toggle the active state. */
+ gtk_tree_model_get (model, &iter, 0, &active, -1);
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, !active, -1);
+}
+
+static void
+spell_language_save (EMComposerPrefs *prefs)
+{
+ GList *spell_languages = NULL;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ model = prefs->language_model;
+
+ /* Build a list of active spell languages. */
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+ while (valid) {
+ const GtkhtmlSpellLanguage *language;
+ gboolean active;
+
+ gtk_tree_model_get (
+ model, &iter, 0, &active, 2, &language, -1);
+
+ if (active)
+ spell_languages = g_list_prepend (
+ spell_languages, (gpointer) language);
+
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+ spell_languages = g_list_reverse (spell_languages);
+
+ /* Update the GConf value. */
+ e_save_spell_languages (spell_languages);
+
+ g_list_free (spell_languages);
+}
+
+static void
+spell_setup (EMComposerPrefs *prefs)
+{
+ const GList *available_languages;
+ GList *active_languages;
+ GtkListStore *store;
+
+ store = GTK_LIST_STORE (prefs->language_model);
+ available_languages = gtkhtml_spell_language_get_available ();
+
+ active_languages = e_load_spell_languages ();
+
+ /* Populate the GtkListStore. */
+ while (available_languages != NULL) {
+ const GtkhtmlSpellLanguage *language;
+ GtkTreeIter tree_iter;
+ const gchar *name;
+ gboolean active;
+
+ language = available_languages->data;
+ name = gtkhtml_spell_language_get_name (language);
+ active = (g_list_find (active_languages, language) != NULL);
+
+ gtk_list_store_append (store, &tree_iter);
+
+ gtk_list_store_set (
+ store, &tree_iter,
+ 0, active, 1, name, 2, language, -1);
+
+ available_languages = available_languages->next;
+ }
+
+ g_list_free (active_languages);
+}
+
+static void
+charset_activate (GtkWidget *item,
+ EMComposerPrefs *prefs)
+{
+ GConfClient *client;
+ GtkWidget *menu;
+ gchar *string;
+
+ client = mail_config_get_gconf_client ();
+ menu = gtk_option_menu_get_menu (prefs->charset);
+ string = e_charset_picker_get_charset (menu);
+
+ if (string == NULL)
+ string = g_strdup (camel_iconv_locale_charset ());
+
+ gconf_client_set_string (
+ client, "/apps/evolution/mail/composer/charset",
+ string, NULL);
+
+ g_free (string);
+}
+
+static void
+option_menu_connect (EMComposerPrefs *prefs,
+ GtkOptionMenu *omenu,
+ GCallback callback,
+ const gchar *key)
+{
+ GConfClient *client;
+ GtkWidget *menu;
+ GList *list;
+
+ client = mail_config_get_gconf_client ();
+ menu = gtk_option_menu_get_menu (omenu);
+ list = GTK_MENU_SHELL (menu)->children;
+
+ while (list != NULL) {
+ GtkWidget *widget = list->data;
+
+ g_object_set_data (G_OBJECT (widget), "key", (gpointer) key);
+ g_signal_connect (widget, "activate", callback, prefs);
+ list = list->next;
+ }
+
+ if (!gconf_client_key_is_writable (client, key, NULL))
+ gtk_widget_set_sensitive (GTK_WIDGET (omenu), FALSE);
+}
+
+static GtkWidget *
+emcp_widget_glade (EConfig *ec,
+ EConfigItem *item,
+ GtkWidget *parent,
+ GtkWidget *old,
+ gpointer data)
+{
+ EMComposerPrefs *prefs = data;
+
+ return glade_xml_get_widget (prefs->gui, item->label);
+}
+
+/* plugin meta-data */
+static EMConfigItem emcp_items[] = {
+ { E_CONFIG_BOOK, (gchar *) "", (gchar *) "composer_toplevel", emcp_widget_glade },
+ { E_CONFIG_PAGE, (gchar *) "00.general", (gchar *) "vboxGeneral", emcp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "00.general/00.behavior", (gchar *) "vboxBehavior", emcp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "00.general/10.alerts", (gchar *) "vboxAlerts", emcp_widget_glade },
+ { E_CONFIG_PAGE, (gchar *) "10.signatures", (gchar *) "vboxSignatures", emcp_widget_glade },
+ /* signature/signatures and signature/preview parts not usable */
+
+ { E_CONFIG_PAGE, (gchar *) "20.spellcheck", (gchar *) "vboxSpellChecking", emcp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "20.spellcheck/00.languages", (gchar *) "vbox178", emcp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "20.spellcheck/00.options", (gchar *) "vboxOptions", emcp_widget_glade },
+};
+
+static void
+emcp_free (EConfig *ec, GSList *items, gpointer data)
+{
+ /* the prefs data is freed automagically */
+ g_slist_free (items);
+}
+
+static void
+em_composer_prefs_construct (EMComposerPrefs *prefs,
+ EShell *shell)
+{
+ GtkWidget *toplevel, *widget, *menu, *info_pixmap;
+ GtkWidget *container;
+ EShellSettings *shell_settings;
+ ESignatureList *signature_list;
+ ESignatureTreeView *signature_tree_view;
+ GladeXML *gui;
+ GtkTreeView *view;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GConfBridge *bridge;
+ GConfClient *client;
+ gchar *buf;
+ EMConfig *ec;
+ EMConfigTargetPrefs *target;
+ GSList *l;
+ gint i;
+ gchar *gladefile;
+
+ bridge = gconf_bridge_get ();
+ client = mail_config_get_gconf_client ();
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ gui = glade_xml_new (gladefile, "composer_toplevel", NULL);
+ prefs->gui = gui;
+ g_free (gladefile);
+
+ /** @HookPoint-EMConfig: Mail Composer Preferences
+ * @Id: org.gnome.evolution.mail.composerPrefs
+ * @Type: E_CONFIG_BOOK
+ * @Class: org.gnome.evolution.mail.config:1.0
+ * @Target: EMConfigTargetPrefs
+ *
+ * The mail composer preferences settings page.
+ */
+ ec = em_config_new(E_CONFIG_BOOK, "org.gnome.evolution.mail.composerPrefs");
+ l = NULL;
+ for (i = 0; i < G_N_ELEMENTS (emcp_items); i++)
+ l = g_slist_prepend(l, &emcp_items[i]);
+ e_config_add_items((EConfig *)ec, l, NULL, NULL, emcp_free, prefs);
+
+ /* General tab */
+
+ /* Default Behavior */
+ widget = glade_xml_get_widget (gui, "chkSendHTML");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "composer-format-html",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "chkPromptEmptySubject");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "composer-prompt-empty-subject",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "chkPromptBccOnly");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "composer-prompt-only-bcc",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "chkAutoSmileys");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "composer-magic-smileys",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "chkRequestReceipt");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "composer-request-receipt",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "chkReplyStartBottom");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "composer-reply-start-bottom",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "chkOutlookFilenames");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "composer-outlook-filenames",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "chkTopSignature");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "composer-top-signature",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "chkEnableSpellChecking");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "composer-inline-spelling",
+ G_OBJECT (widget), "active");
+
+ prefs->charset = GTK_OPTION_MENU (
+ glade_xml_get_widget (gui, "omenuCharset1"));
+ buf = gconf_client_get_string (
+ client, "/apps/evolution/mail/composer/charset", NULL);
+ menu = e_charset_picker_new (
+ buf && *buf ? buf : camel_iconv_locale_charset ());
+ gtk_option_menu_set_menu (prefs->charset, GTK_WIDGET (menu));
+ option_menu_connect (
+ prefs, prefs->charset,
+ G_CALLBACK (charset_activate),
+ "/apps/evolution/mail/composer/charset");
+ g_free (buf);
+
+ /* Spell Checking */
+ widget = glade_xml_get_widget (gui, "listSpellCheckLanguage");
+ view = GTK_TREE_VIEW (widget);
+ store = gtk_list_store_new (
+ 3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
+ g_signal_connect_swapped (
+ store, "row-changed",
+ G_CALLBACK (spell_language_save), prefs);
+ prefs->language_model = GTK_TREE_MODEL (store);
+ gtk_tree_view_set_model (view, prefs->language_model);
+ renderer = gtk_cell_renderer_toggle_new ();
+ g_signal_connect (
+ renderer, "toggled",
+ G_CALLBACK (spell_language_toggled_cb), prefs);
+ gtk_tree_view_insert_column_with_attributes (
+ view, -1, _("Enabled"),
+ renderer, "active", 0, NULL);
+
+ gtk_tree_view_insert_column_with_attributes (
+ view, -1, _("Language(s)"),
+ gtk_cell_renderer_text_new (),
+ "text", 1, NULL);
+ selection = gtk_tree_view_get_selection (view);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
+ info_pixmap = glade_xml_get_widget (gui, "pixmapSpellInfo");
+ gtk_image_set_from_stock (
+ GTK_IMAGE (info_pixmap),
+ GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON);
+
+ widget = glade_xml_get_widget (gui, "colorButtonSpellCheckColor");
+ e_mutual_binding_new_full (
+ G_OBJECT (shell_settings), "composer-spell-color",
+ G_OBJECT (widget), "color",
+ transform_string_to_color,
+ transform_color_to_string,
+ NULL, NULL);
+
+ spell_setup (prefs);
+
+ /* Forwards and Replies */
+ widget = glade_xml_get_widget (gui, "comboboxForwardStyle");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-forward-style",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "comboboxReplyStyle");
+ e_mutual_binding_new_full (
+ G_OBJECT (shell_settings), "mail-reply-style",
+ G_OBJECT (widget), "active",
+ transform_old_to_new_reply_style,
+ transform_new_to_old_reply_style,
+ NULL, NULL);
+
+ /* Signatures */
+ signature_list = e_get_signature_list ();
+ container = glade_xml_get_widget (gui, "alignSignatures");
+ widget = e_signature_manager_new (signature_list);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ /* The mail shell backend responds to the "window-created" signal
+ * that this triggers and configures it with composer preferences. */
+ g_signal_connect_swapped (
+ widget, "editor-created",
+ G_CALLBACK (e_shell_watch_window), shell);
+
+ e_binding_new (
+ G_OBJECT (shell_settings), "composer-format-html",
+ G_OBJECT (widget), "prefer-html");
+
+ e_binding_new_with_negation (
+ G_OBJECT (shell_settings), "disable-command-line",
+ G_OBJECT (widget), "allow-scripts");
+
+ signature_tree_view = e_signature_manager_get_tree_view (
+ E_SIGNATURE_MANAGER (widget));
+
+ container = glade_xml_get_widget (gui, "scrolled-sig");
+ widget = e_signature_preview_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ e_binding_new_with_negation (
+ G_OBJECT (shell_settings), "disable-command-line",
+ G_OBJECT (widget), "allow-scripts");
+
+ e_binding_new (
+ G_OBJECT (signature_tree_view), "selected",
+ G_OBJECT (widget), "signature");
+
+ /* get our toplevel widget */
+ target = em_config_target_new_prefs (ec, client);
+ e_config_set_target ((EConfig *)ec, (EConfigTarget *)target);
+ toplevel = e_config_create_widget ((EConfig *)ec);
+ gtk_container_add (GTK_CONTAINER (prefs), toplevel);
+}
+
+GtkWidget *
+em_composer_prefs_new (EShell *shell)
+{
+ EMComposerPrefs *prefs;
+
+ g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+
+ prefs = g_object_new (EM_TYPE_COMPOSER_PREFS, NULL);
+ em_composer_prefs_construct (prefs, shell);
+
+ return GTK_WIDGET (prefs);
+}
diff --git a/modules/mail/em-composer-prefs.h b/modules/mail/em-composer-prefs.h
new file mode 100644
index 0000000000..3b848f1a2f
--- /dev/null
+++ b/modules/mail/em-composer-prefs.h
@@ -0,0 +1,93 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EM_COMPOSER_PREFS_H
+#define EM_COMPOSER_PREFS_H
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <gtkhtml/gtkhtml.h>
+
+#include <shell/e-shell.h>
+
+/* Standard GObject macros */
+#define EM_TYPE_COMPOSER_PREFS \
+ (em_composer_prefs_get_type ())
+#define EM_COMPOSER_PREFS(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), EM_TYPE_COMPOSER_PREFS, EMComposerPrefs))
+#define EM_COMPOSER_PREFS_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), EM_TYPE_COMPOSER_PREFS, EMComposerPrefsClass))
+#define EM_IS_COMPOSER_PREFS(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), EM_TYPE_COMPOSER_PREFS))
+#define EM_IS_COMPOSER_PREFS_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), EM_TYPE_COMPOSER_PREFS))
+#define EM_COMPOSER_PREFS_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), EM_TYPE_COMPOSER_PREFS, EMComposerPrefsClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMComposerPrefs EMComposerPrefs;
+typedef struct _EMComposerPrefsClass EMComposerPrefsClass;
+
+struct _ESignature;
+
+struct _EMComposerPrefs {
+ GtkVBox parent;
+
+ GladeXML *gui;
+
+ /* General tab */
+
+ /* Default Behavior */
+ GtkOptionMenu *charset;
+
+ GtkTreeModel *language_model;
+
+ /* Forwards and Replies */
+ GtkComboBox *forward_style;
+ GtkComboBox *reply_style;
+
+ /* Signatures */
+ GtkHTML *sig_preview;
+};
+
+struct _EMComposerPrefsClass {
+ GtkVBoxClass parent_class;
+};
+
+GType em_composer_prefs_get_type (void);
+GtkWidget * em_composer_prefs_new (EShell *shell);
+void em_composer_prefs_new_signature (GtkWindow *parent,
+ gboolean html_mode);
+
+/* needed by global config */
+#define EM_COMPOSER_PREFS_CONTROL_ID \
+ "OAFIID:GNOME_Evolution_Mail_ComposerPrefs_ConfigControl:" BASE_VERSION
+
+G_END_DECLS
+
+#endif /* EM_COMPOSER_PREFS_H */
diff --git a/modules/mail/em-mailer-prefs.c b/modules/mail/em-mailer-prefs.c
new file mode 100644
index 0000000000..fa450dc2ee
--- /dev/null
+++ b/modules/mail/em-mailer-prefs.c
@@ -0,0 +1,1332 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include "em-mailer-prefs.h"
+#include "em-format/em-format.h"
+
+#include <camel/camel-iconv.h>
+#include <gtkhtml/gtkhtml-properties.h>
+#include <libxml/tree.h>
+#include "misc/e-charset-picker.h"
+
+#include <glade/glade.h>
+
+#include <gconf/gconf-client.h>
+
+#include "libedataserverui/e-cell-renderer-color.h"
+
+#include "e-util/e-binding.h"
+#include "e-util/e-util-private.h"
+
+#include "e-mail-label-manager.h"
+#include "mail-config.h"
+#include "em-junk-hook.h"
+#include "em-config.h"
+#include "mail-session.h"
+
+static void em_mailer_prefs_class_init (EMMailerPrefsClass *class);
+static void em_mailer_prefs_init (EMMailerPrefs *dialog);
+static void em_mailer_prefs_finalize (GObject *object);
+
+static GtkVBoxClass *parent_class = NULL;
+
+enum {
+ HEADER_LIST_NAME_COLUMN, /* displayable name of the header (may be a translation) */
+ HEADER_LIST_ENABLED_COLUMN, /* is the header enabled? */
+ HEADER_LIST_IS_DEFAULT_COLUMN, /* is this header a default header, eg From: */
+ HEADER_LIST_HEADER_COLUMN, /* the real name of this header */
+ HEADER_LIST_N_COLUMNS
+};
+
+static GType col_types[] = {
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING
+};
+
+/* temporarily copied from em-format.c */
+static const gchar *default_headers[] = {
+ N_("From"),
+ N_("Reply-To"),
+ N_("To"),
+ N_("Cc"),
+ N_("Bcc"),
+ N_("Subject"),
+ N_("Date"),
+ N_("Newsgroups"),
+ N_("Face"),
+ "x-evolution-mailer", /* DO NOT translate */
+};
+
+#define EM_FORMAT_HEADER_XMAILER "x-evolution-mailer"
+
+/* for empty trash on exit frequency */
+static const struct {
+ const gchar *label;
+ gint days;
+} empty_trash_frequency[] = {
+ { N_("Every time"), 0 },
+ { N_("Once per day"), 1 },
+ { N_("Once per week"), 7 },
+ { N_("Once per month"), 30 },
+};
+
+GType
+em_mailer_prefs_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ GTypeInfo type_info = {
+ sizeof (EMMailerPrefsClass),
+ NULL, NULL,
+ (GClassInitFunc) em_mailer_prefs_class_init,
+ NULL, NULL,
+ sizeof (EMMailerPrefs),
+ 0,
+ (GInstanceInitFunc) em_mailer_prefs_init,
+ };
+
+ type = g_type_register_static (gtk_vbox_get_type (), "EMMailerPrefs", &type_info, 0);
+ }
+
+ return type;
+}
+
+static void
+em_mailer_prefs_class_init (EMMailerPrefsClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) klass;
+ parent_class = g_type_class_ref (gtk_vbox_get_type ());
+
+ object_class->finalize = em_mailer_prefs_finalize;
+}
+
+static void
+em_mailer_prefs_init (EMMailerPrefs *preferences)
+{
+ preferences->gconf = mail_config_get_gconf_client ();
+}
+
+static void
+em_mailer_prefs_finalize (GObject *obj)
+{
+ EMMailerPrefs *prefs = (EMMailerPrefs *) obj;
+
+ g_object_unref (prefs->gui);
+
+ if (prefs->labels_change_notify_id) {
+ gconf_client_notify_remove (prefs->gconf, prefs->labels_change_notify_id);
+
+ prefs->labels_change_notify_id = 0;
+ }
+
+ ((GObjectClass *)(parent_class))->finalize (obj);
+}
+
+static gboolean
+mark_seen_timeout_transform (const GValue *src_value,
+ GValue *dst_value,
+ gpointer user_data)
+{
+ gdouble v_double;
+
+ /* Shell Settings (gint) -> Spin Button (double) */
+ v_double = (gdouble) g_value_get_int (src_value);
+ g_value_set_double (dst_value, v_double / 1000.0);
+
+ return TRUE;
+}
+
+static gboolean
+mark_seen_timeout_reverse_transform (const GValue *src_value,
+ GValue *dst_value,
+ gpointer user_data)
+{
+ gdouble v_double;
+
+ /* Spin Button (double) -> Shell Settings (gint) */
+ v_double = g_value_get_double (src_value);
+ g_value_set_int (dst_value, v_double * 1000);
+
+ return TRUE;
+}
+
+enum {
+ JH_LIST_COLUMN_NAME,
+ JH_LIST_COLUMN_VALUE
+};
+
+static void
+jh_tree_refill (EMMailerPrefs *prefs)
+{
+ GtkListStore *store = prefs->junk_header_list_store;
+ GSList *l, *cjh = gconf_client_get_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, NULL);
+
+ gtk_list_store_clear (store);
+
+ for (l = cjh; l; l = l->next) {
+ GtkTreeIter iter;
+ gchar **tokens = g_strsplit (l->data, "=", 2);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (
+ store, &iter,
+ JH_LIST_COLUMN_NAME , tokens[0] ? tokens[0] : "",
+ JH_LIST_COLUMN_VALUE, tokens[1] ? tokens[1] : "" ,
+ -1);
+ g_strfreev (tokens);
+ }
+
+ g_slist_foreach (cjh, (GFunc) g_free, NULL);
+ g_slist_free (cjh);
+}
+
+static void
+jh_add_cb (GtkWidget *widget, gpointer user_data)
+{
+ EMMailerPrefs *prefs = (EMMailerPrefs *) user_data;
+ GtkWidget *dialog, *l1, *l2, *entry1, *entry2, *vbox, *hbox;
+ gint response;
+ dialog = gtk_dialog_new_with_buttons (_("Add Custom Junk Header"), (GtkWindow *)gtk_widget_get_toplevel (widget), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ hbox = gtk_hbox_new (FALSE, 0);
+ l1 = gtk_label_new_with_mnemonic (_("Header Name:"));
+ l2 = gtk_label_new_with_mnemonic (_("Header Value Contains:"));
+ entry1 = gtk_entry_new ();
+ entry2 = gtk_entry_new ();
+ gtk_box_pack_start ((GtkBox *) hbox, l1, FALSE, FALSE, 6);
+ gtk_box_pack_start ((GtkBox *)hbox, entry1, FALSE, FALSE, 6);
+ gtk_box_pack_start ((GtkBox *)vbox, hbox, FALSE, FALSE, 6);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start ((GtkBox *)hbox, l2, FALSE, FALSE, 6);
+ gtk_box_pack_start ((GtkBox *)hbox, entry2, FALSE, FALSE, 6);
+ gtk_box_pack_start ((GtkBox *)vbox, hbox, FALSE, FALSE, 6);
+
+ gtk_widget_show_all (vbox);
+ gtk_container_add ((GtkContainer *)((GtkDialog *)dialog)->vbox, vbox);
+ response = gtk_dialog_run ((GtkDialog *)dialog);
+ if (response == GTK_RESPONSE_ACCEPT) {
+ const gchar *name = gtk_entry_get_text ((GtkEntry *)entry1);
+ const gchar *value = gtk_entry_get_text ((GtkEntry *)entry2);
+ gchar *tok;
+ GSList *list = gconf_client_get_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, NULL);
+
+ /* FIXME: Validate the values */
+
+ tok = g_strdup_printf ("%s=%s", name, value);
+ list = g_slist_append (list, tok);
+ gconf_client_set_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, list, NULL);
+ g_slist_foreach (list, (GFunc)g_free, NULL);
+
+ g_slist_free (list);
+ }
+ gtk_widget_destroy (dialog);
+ jh_tree_refill (prefs);
+}
+
+static void
+jh_remove_cb (GtkWidget *widget, gpointer user_data)
+{
+ EMMailerPrefs *prefs = user_data;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_if_fail (prefs != NULL);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (prefs->junk_header_tree));
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gchar *name=NULL, *value=NULL;
+ GSList *prev = NULL, *node, *list = gconf_client_get_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, NULL);
+ gtk_tree_model_get (model, &iter, JH_LIST_COLUMN_NAME, &name, JH_LIST_COLUMN_VALUE, &value, -1);
+ node = list;
+ while (node) {
+ gchar *test;
+ gint len = strlen (name);
+ test = strncmp (node->data, name, len) == 0 ? (gchar *) node->data+len:NULL;
+
+ if (test) {
+ test++;
+ if (strcmp (test, value) == 0)
+ break;
+ }
+
+ prev = node;
+ node = node->next;
+ }
+
+ if (prev && !node) {
+ /* Not found. So what? */
+ } else if (prev && node) {
+ prev->next = node->next;
+ g_free (node->data);
+ } else if (!prev && node) {
+ list = list->next;
+ g_free (node->data);
+ }
+
+ gconf_client_set_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, list, NULL);
+
+ g_slist_foreach (list, (GFunc)g_free, NULL);
+ g_slist_free (list);
+ g_free (name);
+ g_free (value);
+
+ jh_tree_refill (prefs);
+ }
+}
+
+
+static GtkListStore *
+init_junk_tree (GtkWidget *label_tree, EMMailerPrefs *prefs)
+{
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ gint col;
+
+ g_return_val_if_fail (label_tree != NULL, NULL);
+ g_return_val_if_fail (prefs != NULL, NULL);
+
+ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (label_tree), GTK_TREE_MODEL (store));
+
+ renderer = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (label_tree), -1, _("Header"), renderer, "text", JH_LIST_COLUMN_NAME, NULL);
+ g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (label_tree), -1, _("Contains Value"), renderer, "text", JH_LIST_COLUMN_VALUE, NULL);
+ g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
+
+ return store;
+}
+
+static void
+emmp_header_remove_sensitivity (EMMailerPrefs *prefs)
+{
+ GtkTreeIter iter;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (prefs->header_list);
+ gboolean is_default;
+
+ /* remove button should be sensitive if the currenlty selected entry in the list view
+ is not a default header. if there are no entries, or none is selected, it should be
+ disabled
+ */
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (prefs->header_list_store), &iter,
+ HEADER_LIST_IS_DEFAULT_COLUMN, &is_default,
+ -1);
+ if (is_default)
+ gtk_widget_set_sensitive (GTK_WIDGET (prefs->remove_header), FALSE);
+ else
+ gtk_widget_set_sensitive (GTK_WIDGET (prefs->remove_header), TRUE);
+ } else {
+ gtk_widget_set_sensitive (GTK_WIDGET (prefs->remove_header), FALSE);
+ }
+}
+
+static gboolean
+emmp_header_is_valid (const gchar *header)
+{
+ gint len = g_utf8_strlen (header, -1);
+
+ if (header[0] == 0
+ || g_utf8_strchr (header, len, ':') != NULL
+ || g_utf8_strchr (header, len, ' ') != NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+emmp_header_add_sensitivity (EMMailerPrefs *prefs)
+{
+ const gchar *entry_contents;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ /* the add header button should be sensitive if the text box contains
+ a valid header string, that is not a duplicate with something already
+ in the list view
+ */
+ entry_contents = gtk_entry_get_text (GTK_ENTRY (prefs->entry_header));
+ if (!emmp_header_is_valid (entry_contents)) {
+ gtk_widget_set_sensitive (GTK_WIDGET (prefs->add_header), FALSE);
+ return;
+ }
+
+ /* check if this is a duplicate */
+ valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (prefs->header_list_store), &iter);
+ while (valid) {
+ gchar *header_name;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (prefs->header_list_store), &iter,
+ HEADER_LIST_HEADER_COLUMN, &header_name,
+ -1);
+ if (g_ascii_strcasecmp (header_name, entry_contents) == 0) {
+ gtk_widget_set_sensitive (GTK_WIDGET (prefs->add_header), FALSE);
+ return;
+ }
+
+ valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (prefs->header_list_store), &iter);
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (prefs->add_header), TRUE);
+}
+
+static void
+emmp_save_headers (EMMailerPrefs *prefs)
+{
+ GSList *header_list;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ /* Headers */
+ header_list = NULL;
+ valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (prefs->header_list_store), &iter);
+ while (valid) {
+ struct _EMMailerPrefsHeader h;
+ gboolean enabled;
+ gchar *xml;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (prefs->header_list_store), &iter,
+ HEADER_LIST_HEADER_COLUMN, &h.name,
+ HEADER_LIST_ENABLED_COLUMN, &enabled,
+ -1);
+ h.enabled = enabled;
+
+ if ((xml = em_mailer_prefs_header_to_xml (&h)))
+ header_list = g_slist_append (header_list, xml);
+
+ valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (prefs->header_list_store), &iter);
+ }
+
+ gconf_client_set_list (prefs->gconf, "/apps/evolution/mail/display/headers", GCONF_VALUE_STRING, header_list, NULL);
+ g_slist_foreach (header_list, (GFunc) g_free, NULL);
+ g_slist_free (header_list);
+}
+
+static void
+emmp_header_list_enabled_toggled (GtkCellRendererToggle *cell, const gchar *path_string, EMMailerPrefs *prefs)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (prefs->header_list_store);
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+ GtkTreeIter iter;
+ gint enabled;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter, HEADER_LIST_ENABLED_COLUMN, &enabled, -1);
+ enabled = !enabled;
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, HEADER_LIST_ENABLED_COLUMN,
+ enabled, -1);
+ gtk_tree_path_free (path);
+
+ emmp_save_headers (prefs);
+}
+
+static void
+emmp_header_add_header (GtkWidget *widget, EMMailerPrefs *prefs)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (prefs->header_list_store);
+ GtkTreeIter iter;
+ const gchar *text = gtk_entry_get_text (prefs->entry_header);
+
+ g_strstrip ((gchar *)text);
+
+ if (text && (strlen (text)>0)) {
+ gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ HEADER_LIST_NAME_COLUMN, text,
+ HEADER_LIST_ENABLED_COLUMN, TRUE,
+ HEADER_LIST_HEADER_COLUMN, text,
+ HEADER_LIST_IS_DEFAULT_COLUMN, FALSE,
+ -1);
+ gtk_entry_set_text (prefs->entry_header, "");
+ emmp_header_remove_sensitivity (prefs);
+ emmp_header_add_sensitivity (prefs);
+
+ emmp_save_headers (prefs);
+ }
+}
+
+static void
+emmp_header_remove_header (GtkWidget *button, gpointer user_data)
+{
+ EMMailerPrefs *prefs = (EMMailerPrefs *) user_data;
+ GtkTreeModel *model = GTK_TREE_MODEL (prefs->header_list_store);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (prefs->header_list);
+ GtkTreeIter iter;
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+ emmp_header_remove_sensitivity (prefs);
+
+ emmp_save_headers (prefs);
+}
+
+static void
+emmp_header_list_row_selected (GtkTreeSelection *selection, gpointer user_data)
+{
+ EMMailerPrefs *prefs = (EMMailerPrefs *) user_data;
+
+ emmp_header_remove_sensitivity (prefs);
+}
+
+static void
+emmp_header_entry_changed (GtkWidget *entry, gpointer user_data)
+{
+ EMMailerPrefs *prefs = (EMMailerPrefs *) user_data;
+
+ emmp_header_add_sensitivity (prefs);
+}
+
+static void
+toggle_button_toggled (GtkToggleButton *toggle, EMMailerPrefs *prefs)
+{
+ const gchar *key;
+
+ key = g_object_get_data ((GObject *) toggle, "key");
+ gconf_client_set_bool (prefs->gconf, key, gtk_toggle_button_get_active (toggle), NULL);
+}
+
+static void
+junk_book_lookup_button_toggled (GtkToggleButton *toggle, EMMailerPrefs *prefs)
+{
+ toggle_button_toggled (toggle, prefs);
+ gtk_widget_set_sensitive (GTK_WIDGET (prefs->junk_lookup_local_only), gtk_toggle_button_get_active (toggle));
+}
+
+static void
+custom_junk_button_toggled (GtkToggleButton *toggle, EMMailerPrefs *prefs)
+{
+ toggle_button_toggled (toggle, prefs);
+ if (gtk_toggle_button_get_active (toggle)) {
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_remove, TRUE);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_add, TRUE);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_tree, TRUE);
+ } else {
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_tree, FALSE);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_add, FALSE);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_remove, FALSE);
+ }
+
+
+}
+
+static void
+toggle_button_init (EMMailerPrefs *prefs, GtkToggleButton *toggle, gint not, const gchar *key, GCallback toggled)
+{
+ gboolean bool;
+
+ bool = gconf_client_get_bool (prefs->gconf, key, NULL);
+ gtk_toggle_button_set_active (toggle, not ? !bool : bool);
+
+ if (toggled) {
+ g_object_set_data ((GObject *) toggle, "key", (gpointer) key);
+ g_signal_connect (toggle, "toggled", toggled, prefs);
+ }
+
+ if (!gconf_client_key_is_writable (prefs->gconf, key, NULL))
+ gtk_widget_set_sensitive ((GtkWidget *) toggle, FALSE);
+}
+
+static void
+charset_activate (GtkWidget *item, EMMailerPrefs *prefs)
+{
+ GtkWidget *menu;
+ gchar *string;
+
+ menu = gtk_option_menu_get_menu (prefs->charset);
+ if (!(string = e_charset_picker_get_charset (menu)))
+ string = g_strdup (camel_iconv_locale_charset ());
+
+ gconf_client_set_string (prefs->gconf, "/apps/evolution/mail/display/charset", string, NULL);
+ g_free (string);
+}
+
+static void
+charset_menu_init (EMMailerPrefs *prefs)
+{
+ GtkWidget *menu, *item;
+ GList *items;
+ gchar *buf;
+
+ buf = gconf_client_get_string (prefs->gconf, "/apps/evolution/mail/display/charset", NULL);
+ menu = e_charset_picker_new (buf && *buf ? buf : camel_iconv_locale_charset ());
+ gtk_option_menu_set_menu (prefs->charset, GTK_WIDGET (menu));
+ g_free (buf);
+
+ items = GTK_MENU_SHELL (menu)->children;
+ while (items) {
+ item = items->data;
+ g_signal_connect (item, "activate", G_CALLBACK (charset_activate), prefs);
+ items = items->next;
+ }
+
+ if (!gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/display/charset", NULL))
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->charset, FALSE);
+}
+
+static void
+trash_days_activate (GtkWidget *item, EMMailerPrefs *prefs)
+{
+ gint days;
+
+ days = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "days"));
+ gconf_client_set_int (prefs->gconf, "/apps/evolution/mail/trash/empty_on_exit_days", days, NULL);
+}
+
+static void
+emmp_empty_trash_init (EMMailerPrefs *prefs)
+{
+ gint locked, days, hist = 0, i;
+ GtkWidget *menu, *item;
+
+ days = gconf_client_get_int(prefs->gconf, "/apps/evolution/mail/trash/empty_on_exit_days", NULL);
+ menu = gtk_menu_new();
+ for (i = 0; i < G_N_ELEMENTS (empty_trash_frequency); i++) {
+ if (days >= empty_trash_frequency[i].days)
+ hist = i;
+
+ item = gtk_menu_item_new_with_label (_(empty_trash_frequency[i].label));
+ g_object_set_data ((GObject *) item, "days", GINT_TO_POINTER (empty_trash_frequency[i].days));
+ g_signal_connect (item, "activate", G_CALLBACK (trash_days_activate), prefs);
+
+ gtk_widget_show (item);
+ gtk_menu_shell_append((GtkMenuShell *)menu, item);
+ }
+
+ gtk_widget_show(menu);
+ gtk_option_menu_set_menu((GtkOptionMenu *)prefs->empty_trash_days, menu);
+ gtk_option_menu_set_history((GtkOptionMenu *)prefs->empty_trash_days, hist);
+
+ locked = !gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/trash/empty_on_exit_days", NULL);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->empty_trash_days, !locked);
+}
+
+static void
+junk_days_activate (GtkWidget *item, EMMailerPrefs *prefs)
+{
+ gint days;
+
+ days = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "days"));
+ gconf_client_set_int (prefs->gconf, "/apps/evolution/mail/junk/empty_on_exit_days", days, NULL);
+}
+
+static void
+emmp_empty_junk_init (EMMailerPrefs *prefs)
+{
+ gint locked, days, hist = 0, i;
+ GtkWidget *menu, *item;
+
+ toggle_button_init (prefs, prefs->empty_junk, FALSE,
+ "/apps/evolution/mail/junk/empty_on_exit",
+ G_CALLBACK (toggle_button_toggled));
+
+ days = gconf_client_get_int(prefs->gconf, "/apps/evolution/mail/junk/empty_on_exit_days", NULL);
+ menu = gtk_menu_new();
+ for (i = 0; i < G_N_ELEMENTS (empty_trash_frequency); i++) {
+ if (days >= empty_trash_frequency[i].days)
+ hist = i;
+
+ item = gtk_menu_item_new_with_label (_(empty_trash_frequency[i].label));
+ g_object_set_data ((GObject *) item, "days", GINT_TO_POINTER (empty_trash_frequency[i].days));
+ g_signal_connect (item, "activate", G_CALLBACK (junk_days_activate), prefs);
+
+ gtk_widget_show (item);
+ gtk_menu_shell_append((GtkMenuShell *)menu, item);
+ }
+
+ gtk_widget_show(menu);
+ gtk_option_menu_set_menu((GtkOptionMenu *)prefs->empty_junk_days, menu);
+ gtk_option_menu_set_history((GtkOptionMenu *)prefs->empty_junk_days, hist);
+
+ locked = !gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/junk/empty_on_exit_days", NULL);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->empty_junk_days, !locked);
+}
+
+static void
+http_images_changed (GtkWidget *widget, EMMailerPrefs *prefs)
+{
+ gint when;
+
+ if (gtk_toggle_button_get_active (prefs->images_always))
+ when = MAIL_CONFIG_HTTP_ALWAYS;
+ else if (gtk_toggle_button_get_active (prefs->images_sometimes))
+ when = MAIL_CONFIG_HTTP_SOMETIMES;
+ else
+ when = MAIL_CONFIG_HTTP_NEVER;
+
+ gconf_client_set_int (prefs->gconf, "/apps/evolution/mail/display/load_http_images", when, NULL);
+}
+
+
+static GtkWidget *
+emmp_widget_glade(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMMailerPrefs *prefs = data;
+
+ return glade_xml_get_widget(prefs->gui, item->label);
+}
+
+/* plugin meta-data */
+static EMConfigItem emmp_items[] = {
+ { E_CONFIG_BOOK, (gchar *) "", (gchar *) "preferences_toplevel", emmp_widget_glade },
+ { E_CONFIG_PAGE, (gchar *) "00.general", (gchar *) "vboxGeneral", emmp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "00.general/00.fonts", (gchar *) "vboxMessageFonts", emmp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "00.general/10.display", (gchar *) "vboxMessageDisplay", emmp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "00.general/20.delete", (gchar *) "vboxDeletingMail", emmp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "00.general/30.newmail", (gchar *) "vboxNewMailNotify", emmp_widget_glade },
+ { E_CONFIG_PAGE, (gchar *) "10.html", (gchar *) "vboxHtmlMail", emmp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "10.html/00.general", (gchar *) "vbox173", emmp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "10.html/10.images", (gchar *) "vbox190", emmp_widget_glade },
+ { E_CONFIG_PAGE, (gchar *) "20.labels", (gchar *) "frameColours", emmp_widget_glade },
+ /* this is a table, so we can't use it { E_CONFIG_SECTION, "20.labels/00.labels", "tableColours", emmp_widget_glade }, */
+ { E_CONFIG_PAGE, (gchar *) "30.headers", (gchar *) "vboxHeaderTab", emmp_widget_glade },
+ /* no subvbox for section { E_CONFIG_PAGE, "30.headers/00.headers", "vbox199", emmp_widget_glade }, */
+ { E_CONFIG_PAGE, (gchar *) "40.junk", (gchar *) "vbox161", emmp_widget_glade },
+ /* no subvbox for section { E_CONFIG_SECTION, "40.junk/00.general", xxx, emmp_widget_glade } */
+ { E_CONFIG_SECTION, (gchar *) "40.junk/10.options", (gchar *) "vbox204", emmp_widget_glade },
+};
+
+static void
+emmp_free(EConfig *ec, GSList *items, gpointer data)
+{
+ /* the prefs data is freed automagically */
+
+ g_slist_free(items);
+}
+
+static void
+junk_plugin_changed (GtkWidget *combo, EMMailerPrefs *prefs)
+{
+ gchar *def_plugin = gtk_combo_box_get_active_text(GTK_COMBO_BOX (combo));
+ const GList *plugins = mail_session_get_junk_plugins();
+
+ gconf_client_set_string (prefs->gconf, "/apps/evolution/mail/junk/default_plugin", def_plugin, NULL);
+ while (plugins) {
+ struct _EMJunkHookItem *item = plugins->data;;
+
+ if (item->plugin_name && def_plugin && !strcmp (item->plugin_name, def_plugin)) {
+ gboolean status;
+
+ session->junk_plugin = CAMEL_JUNK_PLUGIN (&(item->csp));
+ status = e_plugin_invoke (item->hook->hook.plugin, item->validate_binary, NULL) != NULL;
+ if ((gboolean)status == TRUE) {
+ gchar *text, *html;
+ gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-info", GTK_ICON_SIZE_MENU);
+ text = g_strdup_printf (_("%s plugin is available and the binary is installed."), item->plugin_name);
+ html = g_strdup_printf ("<i>%s</i>", text);
+ gtk_label_set_markup (prefs->plugin_status, html);
+ g_free (html);
+ g_free (text);
+ } else {
+ gchar *text, *html;
+ gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-warning", GTK_ICON_SIZE_MENU);
+ text = g_strdup_printf (_("%s plugin is not available. Please check whether the package is installed."), item->plugin_name);
+ html = g_strdup_printf ("<i>%s</i>", text);
+ gtk_label_set_markup (prefs->plugin_status, html);
+ g_free (html);
+ g_free (text);
+ }
+ break;
+ }
+ plugins = plugins->next;
+ }
+}
+
+static void
+junk_plugin_setup (GtkWidget *combo, EMMailerPrefs *prefs)
+{
+ gint index = 0;
+ gboolean def_set = FALSE;
+ const GList *plugins = mail_session_get_junk_plugins();
+ gchar *pdefault = gconf_client_get_string (prefs->gconf, "/apps/evolution/mail/junk/default_plugin", NULL);
+
+ if (!plugins || !g_list_length ((GList *)plugins)) {
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("No Junk plugin available"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
+ gtk_widget_set_sensitive (GTK_WIDGET (combo), FALSE);
+ gtk_widget_hide (GTK_WIDGET (prefs->plugin_image));
+ gtk_widget_hide (GTK_WIDGET (prefs->plugin_status));
+ gtk_image_set_from_stock (prefs->plugin_image, NULL, 0);
+ g_free (pdefault);
+
+ return;
+ }
+
+ while (plugins) {
+ struct _EMJunkHookItem *item = plugins->data;;
+
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo), item->plugin_name);
+ if (!def_set && pdefault && item->plugin_name && !strcmp(pdefault, item->plugin_name)) {
+ gboolean status;
+
+ def_set = TRUE;
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), index);
+ status = e_plugin_invoke (item->hook->hook.plugin, item->validate_binary, NULL) != NULL;
+ if (status) {
+ gchar *text, *html;
+ gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-info", GTK_ICON_SIZE_MENU);
+ /* May be a better text */
+ text = g_strdup_printf (_("%s plugin is available and the binary is installed."), item->plugin_name);
+ html = g_strdup_printf ("<i>%s</i>", text);
+ gtk_label_set_markup (prefs->plugin_status, html);
+ g_free (html);
+ g_free (text);
+ } else {
+ gchar *text, *html;
+ gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-warning", GTK_ICON_SIZE_MENU);
+ /* May be a better text */
+ text = g_strdup_printf (_("%s plugin is not available. Please check whether the package is installed."), item->plugin_name);
+ html = g_strdup_printf ("<i>%s</i>", text);
+ gtk_label_set_markup (prefs->plugin_status, html);
+ g_free (html);
+ g_free (text);
+ }
+ }
+ plugins = plugins->next;
+ index++;
+ }
+
+ g_signal_connect (combo, "changed", G_CALLBACK(junk_plugin_changed), prefs);
+ g_free (pdefault);
+}
+
+GtkWidget *
+create_combo_text_widget (void) {
+ return gtk_combo_box_new_text ();
+}
+
+static void
+em_mailer_prefs_construct (EMMailerPrefs *prefs,
+ EShell *shell)
+{
+ GSList *header_config_list, *header_add_list, *p;
+ EShellSettings *shell_settings;
+ GHashTable *default_header_hash;
+ GtkWidget *toplevel;
+ GtkWidget *container;
+ GtkWidget *widget;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeIter iter;
+ GladeXML *gui;
+ gboolean locked;
+ gint val, i;
+ EMConfig *ec;
+ EMConfigTargetPrefs *target;
+ GSList *l;
+ gchar *gladefile;
+
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ gui = glade_xml_new (gladefile, "preferences_toplevel", NULL);
+ g_free (gladefile);
+
+ prefs->gui = gui;
+
+ /** @HookPoint-EMConfig: Mail Preferences Page
+ * @Id: org.gnome.evolution.mail.prefs
+ * @Type: E_CONFIG_BOOK
+ * @Class: org.gnome.evolution.mail.config:1.0
+ * @Target: EMConfigTargetPrefs
+ *
+ * The main mail preferences page.
+ */
+ ec = em_config_new(E_CONFIG_BOOK, "org.gnome.evolution.mail.prefs");
+ l = NULL;
+ for (i=0;i<sizeof(emmp_items)/sizeof(emmp_items[0]);i++)
+ l = g_slist_prepend(l, &emmp_items[i]);
+ e_config_add_items((EConfig *)ec, l, NULL, NULL, emmp_free, prefs);
+
+ /* General tab */
+
+ /* Message Display */
+ widget = glade_xml_get_widget (gui, "chkMarkTimeout");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-mark-seen",
+ G_OBJECT (widget), "active");
+
+ /* The "mark seen" timeout requires special transform functions
+ * because we display the timeout value to the user in seconds
+ * but store the settings value in milliseconds. */
+ widget = glade_xml_get_widget (gui, "spinMarkTimeout");
+ prefs->timeout = GTK_SPIN_BUTTON (widget);
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-mark-seen",
+ G_OBJECT (widget), "sensitive");
+ e_mutual_binding_new_full (
+ G_OBJECT (shell_settings), "mail-mark-seen-timeout",
+ G_OBJECT (widget), "value",
+ mark_seen_timeout_transform,
+ mark_seen_timeout_reverse_transform,
+ NULL, NULL);
+
+ widget = glade_xml_get_widget (gui, "mlimit_checkbutton");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-force-message-limit",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "mlimit_spin");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-force-message-limit",
+ G_OBJECT (widget), "sensitive");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-message-text-part-limit",
+ G_OBJECT (widget), "value");
+
+ widget = glade_xml_get_widget (gui, "address_checkbox");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-address-compress",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "address_spin");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-address-compress",
+ G_OBJECT (widget), "sensitive");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-address-count",
+ G_OBJECT (widget), "value");
+
+ widget = glade_xml_get_widget (gui, "magic_spacebar_checkbox");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-magic-spacebar",
+ G_OBJECT (widget), "active");
+
+ prefs->charset = GTK_OPTION_MENU (glade_xml_get_widget (gui, "omenuCharset"));
+ charset_menu_init (prefs);
+
+ widget = glade_xml_get_widget (gui, "chkHighlightCitations");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-mark-citations",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "colorButtonHighlightCitations");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-mark-citations",
+ G_OBJECT (widget), "sensitive");
+ e_mutual_binding_new_full (
+ G_OBJECT (shell_settings), "mail-citation-color",
+ G_OBJECT (widget), "color",
+ e_binding_transform_string_to_color,
+ e_binding_transform_color_to_string,
+ NULL, NULL);
+
+ widget = glade_xml_get_widget (gui, "chkEnableSearchFolders");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-enable-search-folders",
+ G_OBJECT (widget), "active");
+
+ /* Deleting Mail */
+ widget = glade_xml_get_widget (gui, "chkEmptyTrashOnExit");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-empty-trash-on-exit",
+ G_OBJECT (widget), "active");
+
+ prefs->empty_trash_days = GTK_OPTION_MENU (glade_xml_get_widget (gui, "omenuEmptyTrashDays"));
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-empty-trash-on-exit",
+ G_OBJECT (prefs->empty_trash_days), "sensitive");
+ emmp_empty_trash_init (prefs);
+
+ widget = glade_xml_get_widget (gui, "chkConfirmExpunge");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-confirm-expunge",
+ G_OBJECT (widget), "active");
+
+ /* Mail Fonts */
+ widget = glade_xml_get_widget (gui, "radFontUseSame");
+ e_mutual_binding_new_with_negation (
+ G_OBJECT (shell_settings), "mail-use-custom-fonts",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "FontFixed");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-font-monospace",
+ G_OBJECT (widget), "font-name");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-use-custom-fonts",
+ G_OBJECT (widget), "sensitive");
+
+ widget = glade_xml_get_widget (gui, "FontVariable");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-font-variable",
+ G_OBJECT (widget), "font-name");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-use-custom-fonts",
+ G_OBJECT (widget), "sensitive");
+
+ /* HTML Mail tab */
+
+ /* Loading Images */
+ locked = !gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/display/load_http_images", NULL);
+
+ val = gconf_client_get_int (prefs->gconf, "/apps/evolution/mail/display/load_http_images", NULL);
+ prefs->images_never = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "radImagesNever"));
+ gtk_toggle_button_set_active (prefs->images_never, val == MAIL_CONFIG_HTTP_NEVER);
+ if (locked)
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->images_never, FALSE);
+
+ prefs->images_sometimes = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "radImagesSometimes"));
+ gtk_toggle_button_set_active (prefs->images_sometimes, val == MAIL_CONFIG_HTTP_SOMETIMES);
+ if (locked)
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->images_sometimes, FALSE);
+
+ prefs->images_always = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "radImagesAlways"));
+ gtk_toggle_button_set_active (prefs->images_always, val == MAIL_CONFIG_HTTP_ALWAYS);
+ if (locked)
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->images_always, FALSE);
+
+ g_signal_connect (prefs->images_never, "toggled", G_CALLBACK (http_images_changed), prefs);
+ g_signal_connect (prefs->images_sometimes, "toggled", G_CALLBACK (http_images_changed), prefs);
+ g_signal_connect (prefs->images_always, "toggled", G_CALLBACK (http_images_changed), prefs);
+
+ widget = glade_xml_get_widget (gui, "chkShowAnimatedImages");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-show-animated-images",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "chkPromptWantHTML");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-confirm-unwanted-html",
+ G_OBJECT (widget), "active");
+
+ container = glade_xml_get_widget (gui, "labels-alignment");
+ widget = e_mail_label_manager_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ e_binding_new (
+ G_OBJECT (shell_settings), "mail-label-list-store",
+ G_OBJECT (widget), "list-store");
+
+ /* headers */
+ locked = !gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/display/headers", NULL);
+
+ widget = glade_xml_get_widget (gui, "photo_show");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-show-sender-photo",
+ G_OBJECT (widget), "active");
+
+ widget = glade_xml_get_widget (gui, "photo_local");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-show-sender-photo",
+ G_OBJECT (widget), "sensitive");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-only-local-photos",
+ G_OBJECT (widget), "active");
+
+ /* always de-sensitised until the user types something in the entry */
+ prefs->add_header = GTK_BUTTON (glade_xml_get_widget (gui, "cmdHeadersAdd"));
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->add_header, FALSE);
+
+ /* always de-sensitised until the user selects a header in the list */
+ prefs->remove_header = GTK_BUTTON (glade_xml_get_widget (gui, "cmdHeadersRemove"));
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->remove_header, FALSE);
+
+ prefs->entry_header = GTK_ENTRY (glade_xml_get_widget (gui, "txtHeaders"));
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->entry_header, !locked);
+
+ prefs->header_list = GTK_TREE_VIEW (glade_xml_get_widget (gui, "treeHeaders"));
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->header_list, !locked);
+
+ selection = gtk_tree_view_get_selection (prefs->header_list);
+ g_signal_connect (selection, "changed", G_CALLBACK (emmp_header_list_row_selected), prefs);
+ g_signal_connect (prefs->entry_header, "changed", G_CALLBACK (emmp_header_entry_changed), prefs);
+ g_signal_connect (prefs->entry_header, "activate", G_CALLBACK (emmp_header_add_header), prefs);
+ /* initialise the tree with appropriate headings */
+ prefs->header_list_store = gtk_list_store_newv (HEADER_LIST_N_COLUMNS, col_types);
+ g_signal_connect (prefs->add_header, "clicked", G_CALLBACK (emmp_header_add_header), prefs);
+ g_signal_connect (prefs->remove_header, "clicked", G_CALLBACK (emmp_header_remove_header), prefs);
+ gtk_tree_view_set_model (prefs->header_list, GTK_TREE_MODEL (prefs->header_list_store));
+
+ renderer = gtk_cell_renderer_toggle_new ();
+ g_object_set (renderer, "activatable", TRUE, NULL);
+ g_signal_connect (renderer, "toggled", G_CALLBACK (emmp_header_list_enabled_toggled), prefs);
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (prefs->header_list), -1,
+ "Enabled", renderer,
+ "active", HEADER_LIST_ENABLED_COLUMN,
+ NULL);
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (prefs->header_list), -1,
+ "Name", renderer,
+ "text", HEADER_LIST_NAME_COLUMN,
+ NULL);
+
+ /* populated the listview with entries; firstly we add all the default headers, and then
+ we add read header configuration out of gconf. If a header in gconf is a default header,
+ we update the enabled flag accordingly
+ */
+ header_add_list = NULL;
+ default_header_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ for (i = 0; i < G_N_ELEMENTS (default_headers); i++) {
+ struct _EMMailerPrefsHeader *h;
+
+ h = g_malloc (sizeof (struct _EMMailerPrefsHeader));
+ h->is_default = TRUE;
+ h->name = g_strdup (default_headers[i]);
+ h->enabled = strcmp ((gchar *)default_headers[i], "x-evolution-mailer") != 0;
+ g_hash_table_insert (default_header_hash, (gpointer) default_headers[i], h);
+ header_add_list = g_slist_append (header_add_list, h);
+ }
+
+ /* read stored headers from gconf */
+ header_config_list = gconf_client_get_list (prefs->gconf, "/apps/evolution/mail/display/headers", GCONF_VALUE_STRING, NULL);
+ p = header_config_list;
+ while (p) {
+ struct _EMMailerPrefsHeader *h, *def;
+ gchar *xml = (gchar *) p->data;
+
+ h = em_mailer_prefs_header_from_xml (xml);
+ if (h) {
+ def = g_hash_table_lookup (default_header_hash, h->name);
+ if (def) {
+ def->enabled = h->enabled;
+ em_mailer_prefs_header_free (h);
+ } else {
+ h->is_default = FALSE;
+ header_add_list = g_slist_append (header_add_list, h);
+ }
+ }
+
+ p = p->next;
+ }
+
+ g_hash_table_destroy (default_header_hash);
+ g_slist_foreach (header_config_list, (GFunc) g_free, NULL);
+ g_slist_free (header_config_list);
+
+ p = header_add_list;
+ while (p) {
+ struct _EMMailerPrefsHeader *h = (struct _EMMailerPrefsHeader *) p->data;
+ const gchar *name;
+
+ if (g_ascii_strcasecmp (h->name, EM_FORMAT_HEADER_XMAILER) == 0)
+ name = _("Mailer");
+ else
+ name = _(h->name);
+
+ gtk_list_store_append (prefs->header_list_store, &iter);
+ gtk_list_store_set (prefs->header_list_store, &iter,
+ HEADER_LIST_NAME_COLUMN, name,
+ HEADER_LIST_ENABLED_COLUMN, h->enabled,
+ HEADER_LIST_IS_DEFAULT_COLUMN, h->is_default,
+ HEADER_LIST_HEADER_COLUMN, h->name,
+ -1);
+
+ em_mailer_prefs_header_free (h);
+ p = p->next;
+ }
+
+ g_slist_free (header_add_list);
+
+ /* Junk prefs */
+ widget = glade_xml_get_widget (gui, "chkCheckIncomingMail");
+ e_mutual_binding_new (
+ G_OBJECT (shell_settings), "mail-check-for-junk",
+ G_OBJECT (widget), "active");
+
+ prefs->empty_junk = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "junk_empty_check"));
+ prefs->empty_junk_days = GTK_OPTION_MENU (glade_xml_get_widget (gui, "junk_empty_combo"));
+ emmp_empty_junk_init (prefs);
+
+ prefs->default_junk_plugin = GTK_COMBO_BOX (glade_xml_get_widget (gui, "default_junk_plugin"));
+ prefs->plugin_status = GTK_LABEL (glade_xml_get_widget (gui, "plugin_status"));
+ prefs->plugin_image = GTK_IMAGE (glade_xml_get_widget (gui, "plugin_image"));
+ junk_plugin_setup (GTK_WIDGET (prefs->default_junk_plugin), prefs);
+
+ prefs->junk_header_check = (GtkToggleButton *)glade_xml_get_widget (gui, "junk_header_check");
+ prefs->junk_header_tree = (GtkTreeView *)glade_xml_get_widget (gui, "junk_header_tree");
+ prefs->junk_header_add = (GtkButton *)glade_xml_get_widget (gui, "junk_header_add");
+ prefs->junk_header_remove = (GtkButton *)glade_xml_get_widget (gui, "junk_header_remove");
+ prefs->junk_book_lookup = (GtkToggleButton *)glade_xml_get_widget (gui, "lookup_book");
+ prefs->junk_lookup_local_only = (GtkToggleButton *)glade_xml_get_widget (gui, "junk_lookup_local_only");
+ toggle_button_init (prefs, prefs->junk_book_lookup, FALSE,
+ "/apps/evolution/mail/junk/lookup_addressbook",
+ G_CALLBACK (junk_book_lookup_button_toggled));
+
+ toggle_button_init (prefs, prefs->junk_lookup_local_only, FALSE,
+ "/apps/evolution/mail/junk/lookup_addressbook_local_only",
+ G_CALLBACK (toggle_button_toggled));
+
+ junk_book_lookup_button_toggled (prefs->junk_book_lookup, prefs);
+
+ prefs->junk_header_list_store = init_junk_tree ((GtkWidget *)prefs->junk_header_tree, prefs);
+ toggle_button_init (prefs, prefs->junk_header_check, FALSE,
+ "/apps/evolution/mail/junk/check_custom_header",
+ G_CALLBACK (custom_junk_button_toggled));
+
+ custom_junk_button_toggled (prefs->junk_header_check, prefs);
+ jh_tree_refill (prefs);
+ g_signal_connect (G_OBJECT (prefs->junk_header_add), "clicked", G_CALLBACK (jh_add_cb), prefs);
+ g_signal_connect (G_OBJECT (prefs->junk_header_remove), "clicked", G_CALLBACK (jh_remove_cb), prefs);
+
+ /* get our toplevel widget */
+ target = em_config_target_new_prefs(ec, prefs->gconf);
+ e_config_set_target((EConfig *)ec, (EConfigTarget *)target);
+ toplevel = e_config_create_widget((EConfig *)ec);
+ gtk_container_add (GTK_CONTAINER (prefs), toplevel);
+}
+
+GtkWidget *
+em_mailer_prefs_new (EShell *shell)
+{
+ EMMailerPrefs *new;
+
+ g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+
+ new = g_object_new (EM_TYPE_MAILER_PREFS, NULL);
+
+ /* FIXME Kill this function. */
+ em_mailer_prefs_construct (new, shell);
+
+ return GTK_WIDGET (new);
+}
+
+
+static struct _EMMailerPrefsHeader *
+emmp_header_from_xmldoc (xmlDocPtr doc)
+{
+ struct _EMMailerPrefsHeader *h;
+ xmlNodePtr root;
+ xmlChar *name;
+
+ if (doc == NULL)
+ return NULL;
+
+ root = doc->children;
+ if (strcmp ((gchar *)root->name, "header") != 0)
+ return NULL;
+
+ name = xmlGetProp (root, (const guchar *)"name");
+ if (name == NULL)
+ return NULL;
+
+ h = g_malloc0 (sizeof (struct _EMMailerPrefsHeader));
+ h->name = g_strdup ((gchar *)name);
+ xmlFree (name);
+
+ if (xmlHasProp (root, (const guchar *)"enabled"))
+ h->enabled = 1;
+ else
+ h->enabled = 0;
+
+ return h;
+}
+
+/**
+ * em_mailer_prefs_header_from_xml
+ * @xml: XML configuration data
+ *
+ * Parses passed XML data, which should be of
+ * the format <header name="foo" enabled />, and
+ * returns a EMMailerPrefs structure, or NULL if there
+ * is an error.
+ **/
+struct _EMMailerPrefsHeader *
+em_mailer_prefs_header_from_xml (const gchar *xml)
+{
+ struct _EMMailerPrefsHeader *header;
+ xmlDocPtr doc;
+
+ if (!(doc = xmlParseDoc ((guchar *) xml)))
+ return NULL;
+
+ header = emmp_header_from_xmldoc (doc);
+ xmlFreeDoc (doc);
+
+ return header;
+}
+
+/**
+ * em_mailer_prefs_header_free
+ * @header: header to free
+ *
+ * Frees the memory associated with the passed header
+ * structure.
+ */
+void
+em_mailer_prefs_header_free (struct _EMMailerPrefsHeader *header)
+{
+ if (header == NULL)
+ return;
+
+ g_free (header->name);
+ g_free (header);
+}
+
+/**
+ * em_mailer_prefs_header_to_xml
+ * @header: header from which to generate XML
+ *
+ * Returns the passed header as a XML structure,
+ * or NULL on error
+ */
+gchar *
+em_mailer_prefs_header_to_xml (struct _EMMailerPrefsHeader *header)
+{
+ xmlDocPtr doc;
+ xmlNodePtr root;
+ xmlChar *xml;
+ gchar *out;
+ gint size;
+
+ g_return_val_if_fail (header != NULL, NULL);
+ g_return_val_if_fail (header->name != NULL, NULL);
+
+ doc = xmlNewDoc ((const guchar *)"1.0");
+
+ root = xmlNewDocNode (doc, NULL, (const guchar *)"header", NULL);
+ xmlSetProp (root, (const guchar *)"name", (guchar *)header->name);
+ if (header->enabled)
+ xmlSetProp (root, (const guchar *)"enabled", NULL);
+
+ xmlDocSetRootElement (doc, root);
+ xmlDocDumpMemory (doc, &xml, &size);
+ xmlFreeDoc (doc);
+
+ out = g_malloc (size + 1);
+ memcpy (out, xml, size);
+ out[size] = '\0';
+ xmlFree (xml);
+
+ return out;
+}
diff --git a/modules/mail/em-mailer-prefs.h b/modules/mail/em-mailer-prefs.h
new file mode 100644
index 0000000000..b33e620392
--- /dev/null
+++ b/modules/mail/em-mailer-prefs.h
@@ -0,0 +1,141 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EM_MAILER_PREFS_H
+#define EM_MAILER_PREFS_H
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <gconf/gconf-client.h>
+#include <shell/e-shell.h>
+
+/* Standard GObject macros */
+#define EM_TYPE_MAILER_PREFS \
+ (em_mailer_prefs_get_type ())
+#define EM_MAILER_PREFS(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), EM_TYPE_MAILER_PREFS, EMMailerPrefs))
+#define EM_MAILER_PREFS_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), EM_TYPE_MAILER_PREFS, EMMailerPrefsClass))
+#define EM_IS_MAILER_PREFS(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), EM_TYPE_MAILER_PREFS))
+#define EM_IS_MAILER_PREFS_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), EM_TYPE_MAILER_PREFS))
+#define EM_MAILER_PREFS_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), EM_TYPE_MAILER_PREFS))
+
+G_BEGIN_DECLS
+
+typedef struct _EMMailerPrefs EMMailerPrefs;
+typedef struct _EMMailerPrefsClass EMMailerPrefsClass;
+typedef struct _EMMailerPrefsHeader EMMailerPrefsHeader;
+
+struct _EMMailerPrefsHeader {
+ gchar *name;
+ guint enabled:1;
+ guint is_default:1;
+};
+
+struct _EMMailerPrefs {
+ GtkVBox parent_object;
+
+ GladeXML *gui;
+ GConfClient *gconf;
+
+ /* General tab */
+
+ /* Message Display */
+ GtkSpinButton *timeout;
+ GtkOptionMenu *charset;
+
+ /* Deleting Mail */
+ GtkToggleButton *empty_trash;
+ GtkComboBox *empty_trash_days;
+ GtkToggleButton *confirm_expunge;
+
+ /* HTML Mail tab */
+ GtkFontButton *font_variable;
+ GtkFontButton *font_fixed;
+ GtkToggleButton *font_share;
+
+ /* Loading Images */
+ GtkToggleButton *images_always;
+ GtkToggleButton *images_sometimes;
+ GtkToggleButton *images_never;
+
+ GtkToggleButton *autodetect_links;
+
+ /* Labels and Colours tab */
+ GtkWidget *label_add;
+ GtkWidget *label_edit;
+ GtkWidget *label_remove;
+ GtkWidget *label_tree;
+ GtkListStore *label_list_store;
+ guint labels_change_notify_id; /* mail_config's notify id */
+
+ /* Headers tab */
+ GtkButton *add_header;
+ GtkButton *remove_header;
+ GtkEntry *entry_header;
+ GtkTreeView *header_list;
+ GtkListStore *header_list_store;
+
+ /* Junk prefs */
+ GtkToggleButton *empty_junk;
+ GtkComboBox *empty_junk_days;
+
+ GtkToggleButton *sa_local_tests_only;
+ GtkToggleButton *sa_use_daemon;
+ GtkComboBox *default_junk_plugin;
+ GtkLabel *plugin_status;
+ GtkImage *plugin_image;
+
+ GtkToggleButton *junk_header_check;
+ GtkTreeView *junk_header_tree;
+ GtkListStore *junk_header_list_store;
+ GtkButton *junk_header_add;
+ GtkButton *junk_header_remove;
+ GtkToggleButton *junk_book_lookup;
+ GtkToggleButton *junk_lookup_local_only;
+};
+
+struct _EMMailerPrefsClass {
+ GtkVBoxClass parent_class;
+};
+
+GType em_mailer_prefs_get_type (void);
+GtkWidget * create_combo_text_widget (void);
+
+GtkWidget * em_mailer_prefs_new (EShell *shell);
+
+EMMailerPrefsHeader *
+ em_mailer_prefs_header_from_xml (const gchar *xml);
+gchar * em_mailer_prefs_header_to_xml (EMMailerPrefsHeader *header);
+void em_mailer_prefs_header_free (EMMailerPrefsHeader *header);
+
+G_END_DECLS
+
+#endif /* EM_MAILER_PREFS_H */
diff --git a/modules/mail/em-network-prefs.c b/modules/mail/em-network-prefs.c
new file mode 100644
index 0000000000..e530d4d076
--- /dev/null
+++ b/modules/mail/em-network-prefs.c
@@ -0,0 +1,499 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Veerapuram Varadhan <vvaradhan@novell.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "em-network-prefs.h"
+
+#include <gdk/gdkkeysyms.h>
+#include <gconf/gconf-client.h>
+#include <glade/glade.h>
+
+#include <glib/gstdio.h>
+
+#include "e-util/e-error.h"
+#include "e-util/e-util-private.h"
+
+#include "mail-config.h"
+#include "em-config.h"
+
+#define d(x)
+
+#define GCONF_E_SHELL_NETWORK_CONFIG_PATH "/apps/evolution/shell/network_config/"
+#define GCONF_E_HTTP_HOST_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "http_host"
+#define GCONF_E_HTTP_PORT_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "http_port"
+#define GCONF_E_HTTPS_HOST_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "secure_host"
+#define GCONF_E_HTTPS_PORT_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "secure_port"
+#define GCONF_E_SOCKS_HOST_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "socks_host"
+#define GCONF_E_SOCKS_PORT_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "socks_port"
+#define GCONF_E_IGNORE_HOSTS_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "ignore_hosts"
+#define GCONF_E_USE_AUTH_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "use_authentication"
+#define GCONF_E_PROXY_TYPE_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "proxy_type"
+#define GCONF_E_AUTH_USER_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "authentication_user"
+#define GCONF_E_AUTH_PWD_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "authentication_password"
+#define GCONF_E_USE_PROXY_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "use_http_proxy"
+#define GCONF_E_AUTOCONFIG_URL_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "autoconfig_url"
+
+static void em_network_prefs_class_init (EMNetworkPrefsClass *class);
+static void em_network_prefs_init (EMNetworkPrefs *dialog);
+static void em_network_prefs_destroy (GtkObject *obj);
+static void em_network_prefs_finalise (GObject *obj);
+
+
+static GtkVBoxClass *parent_class = NULL;
+
+
+GType
+em_network_prefs_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (EMNetworkPrefsClass),
+ NULL, NULL,
+ (GClassInitFunc) em_network_prefs_class_init,
+ NULL, NULL,
+ sizeof (EMNetworkPrefs),
+ 0,
+ (GInstanceInitFunc) em_network_prefs_init,
+ };
+
+ type = g_type_register_static (gtk_vbox_get_type (), "EMNetworkPrefs", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+em_network_prefs_class_init (EMNetworkPrefsClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_ref (gtk_vbox_get_type ());
+
+ object_class->destroy = em_network_prefs_destroy;
+ gobject_class->finalize = em_network_prefs_finalise;
+}
+
+static void
+em_network_prefs_init (EMNetworkPrefs *prefs)
+{
+ /* do something here */
+}
+
+static void
+em_network_prefs_finalise (GObject *obj)
+{
+ d(g_print ("Network preferences finalize is called\n"));
+
+ /* do something here */
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+em_network_prefs_destroy (GtkObject *obj)
+{
+ d(g_print ("Network preferences destroy is called\n"));
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (obj);
+}
+
+static void
+toggle_button_toggled (GtkToggleButton *toggle, EMNetworkPrefs *prefs)
+{
+ const gchar *key;
+
+ key = g_object_get_data ((GObject *) toggle, "key");
+ gconf_client_set_bool (prefs->gconf, key, gtk_toggle_button_get_active (toggle), NULL);
+ if (toggle == prefs->use_auth) {
+ gboolean sensitivity = gtk_toggle_button_get_active (prefs->use_auth);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_auth_user, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_auth_pwd, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->auth_user, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->auth_pwd, sensitivity);
+ }
+}
+
+static void
+toggle_button_init (EMNetworkPrefs *prefs, GtkToggleButton *toggle, const gchar *key)
+{
+ gboolean bool;
+
+ bool = gconf_client_get_bool (prefs->gconf, key, NULL);
+ gtk_toggle_button_set_active (toggle, bool);
+
+ g_object_set_data ((GObject *) toggle, "key", (gpointer) key);
+ g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_button_toggled), prefs);
+
+ if (!gconf_client_key_is_writable (prefs->gconf, key, NULL))
+ gtk_widget_set_sensitive ((GtkWidget *) toggle, FALSE);
+}
+
+static GtkWidget *
+emnp_widget_glade(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data)
+{
+ EMNetworkPrefs *prefs = data;
+
+ return glade_xml_get_widget(prefs->gui, item->label);
+}
+
+static void
+emnp_set_sensitiveness (EMNetworkPrefs *prefs, NetworkConfigProxyType type, gboolean sensitivity)
+{
+#if 0
+ if (type == NETWORK_PROXY_AUTOCONFIG) {
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->auto_proxy_url, sensitivity);
+ d(g_print ("Setting sensitivity of autoconfig to: %d\n", sensitivity));
+ } else
+#endif
+ if (type == NETWORK_PROXY_MANUAL) {
+ gboolean state;
+
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->http_host, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->https_host, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->ignore_hosts, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->use_auth, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->http_port, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->https_port, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_ignore_hosts, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_http_host, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_http_port, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_https_host, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_https_port, sensitivity);
+#if 0
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->socks_host, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->socks_port, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_socks_host, sensitivity);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_socks_port, sensitivity);
+#endif
+ state = sensitivity && gtk_toggle_button_get_active (prefs->use_auth);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_auth_user, state);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_auth_pwd, state);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->auth_user, state);
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->auth_pwd, state);
+
+ d(g_print ("Setting sensitivity of manual proxy to: %d\n", sensitivity));
+ }
+}
+
+static void
+notify_proxy_type_changed (GtkWidget *widget, EMNetworkPrefs *prefs)
+{
+ gint type;
+
+ if (gtk_toggle_button_get_active (prefs->sys_proxy))
+ type = NETWORK_PROXY_SYS_SETTINGS;
+ else if (gtk_toggle_button_get_active (prefs->no_proxy))
+ type = NETWORK_PROXY_DIRECT_CONNECTION;
+ else if (gtk_toggle_button_get_active (prefs->manual_proxy))
+ type = NETWORK_PROXY_MANUAL;
+ else
+#if 0
+ type = NETWORK_PROXY_AUTOCONFIG;
+#else
+ type = NETWORK_PROXY_SYS_SETTINGS;
+#endif
+
+ gconf_client_set_int (prefs->gconf, "/apps/evolution/shell/network_config/proxy_type", type, NULL);
+
+ if (type == NETWORK_PROXY_DIRECT_CONNECTION ||
+ type == NETWORK_PROXY_SYS_SETTINGS) {
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, FALSE);
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, FALSE);
+ } else if (type == NETWORK_PROXY_AUTOCONFIG) {
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, FALSE);
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, TRUE);
+ } else if (type == NETWORK_PROXY_MANUAL) {
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, FALSE);
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, TRUE);
+ }
+
+ if (type != NETWORK_PROXY_DIRECT_CONNECTION)
+ gconf_client_set_bool (prefs->gconf, GCONF_E_USE_PROXY_KEY, TRUE, NULL);
+ else if (type != NETWORK_PROXY_SYS_SETTINGS)
+ gconf_client_set_bool (prefs->gconf, GCONF_E_USE_PROXY_KEY, FALSE, NULL);
+
+}
+
+static void
+widget_entry_changed_cb (GtkWidget *widget, gpointer data)
+{
+ const gchar *value;
+ gint port = -1;
+ GConfClient *gconf = mail_config_get_gconf_client ();
+
+ /*
+ Do not change the order of comparison -
+ GtkSpinButton is an extended form of GtkEntry
+ */
+ if (GTK_IS_SPIN_BUTTON (widget)) {
+ port = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+ gconf_client_set_int (gconf, (const gchar *)data, port, NULL);
+ d(g_print ("%s:%s: %s is SpinButton: value = [%d]\n", G_STRLOC, G_STRFUNC, (const gchar *)data, port));
+ } else if (GTK_IS_ENTRY (widget)) {
+ value = gtk_entry_get_text (GTK_ENTRY (widget));
+ gconf_client_set_string (gconf, (const gchar *)data, value, NULL);
+ d(g_print ("%s:%s: %s is Entry: value = [%s]\n", G_STRLOC, G_STRFUNC, (const gchar *)data, value));
+ }
+
+}
+
+/* plugin meta-data */
+static EMConfigItem emnp_items[] = {
+ { E_CONFIG_BOOK, (gchar *) "", (gchar *) "network_preferences_toplevel", emnp_widget_glade },
+ { E_CONFIG_PAGE, (gchar *) "00.general", (gchar *) "vboxGeneral", emnp_widget_glade },
+ { E_CONFIG_SECTION, (gchar *) "00.general/00.proxy", (gchar *) "frameProxy", emnp_widget_glade },
+};
+
+static void
+emnp_free(EConfig *ec, GSList *items, gpointer data)
+{
+ /* the prefs data is freed automagically */
+
+ g_slist_free(items);
+}
+
+static void
+emnp_set_markups (EMNetworkPrefs *prefs)
+{
+ gtk_label_set_use_markup (GTK_LABEL (GTK_BIN(prefs->sys_proxy)->child), TRUE);
+ gtk_label_set_use_markup (GTK_LABEL (GTK_BIN(prefs->no_proxy)->child), TRUE);
+ gtk_label_set_use_markup (GTK_LABEL (GTK_BIN(prefs->manual_proxy)->child), TRUE);
+#if 0
+ gtk_label_set_use_markup (GTK_LABEL (GTK_BIN(prefs->auto_proxy)->child), TRUE);
+#endif
+}
+
+static void
+em_network_prefs_construct (EMNetworkPrefs *prefs)
+{
+ GtkWidget *toplevel;
+ GladeXML *gui;
+ GSList* l;
+ gchar *buf;
+ EMConfig *ec;
+ EMConfigTargetPrefs *target;
+ gboolean locked;
+ gint i, val, port;
+ gchar *gladefile;
+
+ prefs->gconf = mail_config_get_gconf_client ();
+
+ gladefile = g_build_filename (EVOLUTION_GLADEDIR,
+ "mail-config.glade",
+ NULL);
+ gui = glade_xml_new (gladefile, "network_preferences_toplevel", NULL);
+ prefs->gui = gui;
+ g_free (gladefile);
+
+ /** @HookPoint-EMConfig: Network Preferences
+ * @Id: org.gnome.evolution.mail.networkPrefs
+ * @Type: E_CONFIG_BOOK
+ * @Class: org.gnome.evolution.mail.config:1.0
+ * @Target: EMConfigTargetPrefs
+ *
+ * The network preferences settings page.
+ */
+ ec = em_config_new(E_CONFIG_BOOK, "org.gnome.evolution.mail.networkPrefs");
+ l = NULL;
+ for (i=0;i<sizeof(emnp_items)/sizeof(emnp_items[0]);i++)
+ l = g_slist_prepend(l, &emnp_items[i]);
+ e_config_add_items((EConfig *)ec, l, NULL, NULL, emnp_free, prefs);
+
+ /* Proxy tab */
+
+ /* Default Behavior */
+ locked = !gconf_client_key_is_writable (prefs->gconf, GCONF_E_PROXY_TYPE_KEY, NULL);
+
+ val = gconf_client_get_int (prefs->gconf, GCONF_E_PROXY_TYPE_KEY, NULL);
+
+ /* no auto-proxy at the moment */
+ if (val == NETWORK_PROXY_AUTOCONFIG)
+ val = NETWORK_PROXY_SYS_SETTINGS;
+
+ prefs->sys_proxy = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "rdoSysSettings"));
+ gtk_toggle_button_set_active (prefs->sys_proxy, val == NETWORK_PROXY_SYS_SETTINGS);
+ if (locked)
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->sys_proxy, FALSE);
+
+ d(g_print ("Sys settings ----!!! \n"));
+
+ prefs->no_proxy = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "rdoNoProxy"));
+ gtk_toggle_button_set_active (prefs->no_proxy, val == NETWORK_PROXY_DIRECT_CONNECTION);
+ if (locked)
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->no_proxy, FALSE);
+
+ d(g_print ("No proxy settings ----!!! \n"));
+
+ /* no auto-proxy at the moment */
+#if 0
+ prefs->auto_proxy = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "rdoAutoConfig"));
+ prefs->auto_proxy_url = GTK_ENTRY (glade_xml_get_widget (gui, "txtAutoConfigUrl"));
+
+ gtk_toggle_button_set_active (prefs->auto_proxy, val == NETWORK_PROXY_AUTOCONFIG);
+
+ g_signal_connect(prefs->auto_proxy_url, "changed", G_CALLBACK(widget_entry_changed_cb), GCONF_E_AUTOCONFIG_URL_KEY);
+ if (locked)
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->auto_proxy, FALSE);
+#endif
+
+ d(g_print ("Auto config settings ----!!! \n"));
+
+ prefs->manual_proxy = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "rdoManualProxy"));
+ prefs->http_host = GTK_ENTRY (glade_xml_get_widget (gui, "txtHttpHost"));
+ prefs->https_host = GTK_ENTRY (glade_xml_get_widget (gui, "txtHttpsHost"));
+ prefs->ignore_hosts = GTK_ENTRY (glade_xml_get_widget (gui, "txtIgnoreHosts"));
+ prefs->http_port = GTK_SPIN_BUTTON (glade_xml_get_widget (gui, "spnHttpPort"));
+ prefs->https_port = GTK_SPIN_BUTTON (glade_xml_get_widget (gui, "spnHttpsPort"));
+ prefs->lbl_http_host = GTK_LABEL (glade_xml_get_widget (gui, "lblHttpHost"));
+ prefs->lbl_http_port = GTK_LABEL (glade_xml_get_widget (gui, "lblHttpPort"));
+ prefs->lbl_https_host = GTK_LABEL (glade_xml_get_widget (gui, "lblHttpsHost"));
+ prefs->lbl_https_port = GTK_LABEL (glade_xml_get_widget (gui, "lblHttpsPort"));
+ prefs->lbl_ignore_hosts = GTK_LABEL (glade_xml_get_widget (gui, "lblIgnoreHosts"));
+ prefs->use_auth = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "chkUseAuth"));
+ toggle_button_init (prefs, prefs->use_auth, GCONF_E_USE_AUTH_KEY);
+ prefs->lbl_auth_user = GTK_LABEL (glade_xml_get_widget (gui, "lblAuthUser"));
+ prefs->lbl_auth_pwd = GTK_LABEL (glade_xml_get_widget (gui, "lblAuthPwd"));
+ prefs->auth_user = GTK_ENTRY (glade_xml_get_widget (gui, "txtAuthUser"));
+ prefs->auth_pwd = GTK_ENTRY (glade_xml_get_widget (gui, "txtAuthPwd"));
+
+#if 0
+ prefs->socks_host = GTK_ENTRY (glade_xml_get_widget (gui, "txtSocksHost"));
+ prefs->socks_port = GTK_SPIN_BUTTON (glade_xml_get_widget (gui, "spnSocksPort"));
+ prefs->lbl_socks_host = GTK_LABEL (glade_xml_get_widget (gui, "lblSocksHost"));
+ prefs->lbl_socks_port = GTK_LABEL (glade_xml_get_widget (gui, "lblSocksPort"));
+ g_signal_connect (prefs->socks_host, "changed",
+ G_CALLBACK(widget_entry_changed_cb), GCONF_E_SOCKS_HOST_KEY);
+ g_signal_connect (prefs->socks_port, "value_changed",
+ G_CALLBACK(widget_entry_changed_cb), GCONF_E_SOCKS_PORT_KEY);
+#endif
+
+ /* Manual proxy options */
+ g_signal_connect (prefs->http_host, "changed",
+ G_CALLBACK(widget_entry_changed_cb),
+ (gpointer) GCONF_E_HTTP_HOST_KEY);
+ g_signal_connect (prefs->https_host, "changed",
+ G_CALLBACK(widget_entry_changed_cb),
+ (gpointer) GCONF_E_HTTPS_HOST_KEY);
+ g_signal_connect (prefs->ignore_hosts, "changed",
+ G_CALLBACK(widget_entry_changed_cb),
+ (gpointer) GCONF_E_IGNORE_HOSTS_KEY);
+ g_signal_connect (prefs->http_port, "value_changed",
+ G_CALLBACK(widget_entry_changed_cb),
+ (gpointer) GCONF_E_HTTP_PORT_KEY);
+ g_signal_connect (prefs->https_port, "value_changed",
+ G_CALLBACK(widget_entry_changed_cb),
+ (gpointer) GCONF_E_HTTPS_PORT_KEY);
+ g_signal_connect (prefs->auth_user, "changed",
+ G_CALLBACK(widget_entry_changed_cb),
+ (gpointer) GCONF_E_AUTH_USER_KEY);
+ g_signal_connect (prefs->auth_pwd, "changed",
+ G_CALLBACK(widget_entry_changed_cb),
+ (gpointer) GCONF_E_AUTH_PWD_KEY);
+
+ gtk_toggle_button_set_active (prefs->manual_proxy, val == NETWORK_PROXY_MANUAL);
+ g_signal_connect (prefs->sys_proxy, "toggled", G_CALLBACK (notify_proxy_type_changed), prefs);
+ g_signal_connect (prefs->no_proxy, "toggled", G_CALLBACK (notify_proxy_type_changed), prefs);
+#if 0
+ g_signal_connect (prefs->auto_proxy, "toggled", G_CALLBACK (notify_proxy_type_changed), prefs);
+#endif
+ g_signal_connect (prefs->manual_proxy, "toggled", G_CALLBACK (notify_proxy_type_changed), prefs);
+
+ if (locked)
+ gtk_widget_set_sensitive ((GtkWidget *) prefs->manual_proxy, FALSE);
+ d(g_print ("Manual settings ----!!! \n"));
+
+ buf = gconf_client_get_string (prefs->gconf, GCONF_E_HTTP_HOST_KEY, NULL);
+ gtk_entry_set_text (prefs->http_host, buf ? buf : "");
+ g_free (buf);
+
+ buf = gconf_client_get_string (prefs->gconf, GCONF_E_HTTPS_HOST_KEY, NULL);
+ gtk_entry_set_text (prefs->https_host, buf ? buf : "");
+ g_free (buf);
+
+ buf = gconf_client_get_string (prefs->gconf, GCONF_E_IGNORE_HOSTS_KEY, NULL);
+ gtk_entry_set_text (prefs->ignore_hosts, buf ? buf : "");
+ g_free (buf);
+
+ buf = gconf_client_get_string (prefs->gconf, GCONF_E_AUTH_USER_KEY, NULL);
+ gtk_entry_set_text (prefs->auth_user, buf ? buf : "");
+ g_free (buf);
+
+ buf = gconf_client_get_string (prefs->gconf, GCONF_E_AUTH_PWD_KEY, NULL);
+ gtk_entry_set_text (prefs->auth_pwd, buf ? buf : "");
+ g_free (buf);
+
+ port = gconf_client_get_int (prefs->gconf, GCONF_E_HTTP_PORT_KEY, NULL);
+ gtk_spin_button_set_value (prefs->http_port, (gdouble)port);
+
+ port = gconf_client_get_int (prefs->gconf, GCONF_E_HTTPS_PORT_KEY, NULL);
+ gtk_spin_button_set_value (prefs->https_port, (gdouble)port);
+
+#if 0
+ buf = gconf_client_get_string (prefs->gconf, GCONF_E_SOCKS_HOST_KEY, NULL);
+ gtk_entry_set_text (prefs->socks_host, buf ? buf : "");
+ g_free (buf);
+
+ port = gconf_client_get_int (prefs->gconf, GCONF_E_SOCKS_PORT_KEY, NULL);
+ gtk_spin_button_set_value (prefs->socks_port, (gdouble)port);
+#endif
+ emnp_set_markups (prefs);
+
+ if (val == NETWORK_PROXY_DIRECT_CONNECTION ||
+ val == NETWORK_PROXY_SYS_SETTINGS) {
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, FALSE);
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, FALSE);
+ } else if (val == NETWORK_PROXY_AUTOCONFIG) {
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, FALSE);
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, TRUE);
+ } else if (val == NETWORK_PROXY_MANUAL) {
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, FALSE);
+ emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, TRUE);
+ }
+
+ /* get our toplevel widget */
+ target = em_config_target_new_prefs(ec, prefs->gconf);
+ e_config_set_target((EConfig *)ec, (EConfigTarget *)target);
+ toplevel = e_config_create_widget((EConfig *)ec);
+ gtk_container_add (GTK_CONTAINER (prefs), toplevel);
+}
+
+GtkWidget *
+em_network_prefs_new (void)
+{
+ EMNetworkPrefs *new;
+
+ new = (EMNetworkPrefs *) g_object_new (em_network_prefs_get_type (), NULL);
+ em_network_prefs_construct (new);
+
+ return (GtkWidget *) new;
+}
diff --git a/modules/mail/em-network-prefs.h b/modules/mail/em-network-prefs.h
new file mode 100644
index 0000000000..057247b750
--- /dev/null
+++ b/modules/mail/em-network-prefs.h
@@ -0,0 +1,102 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Veerapuram Varadhan <vvaradhan@novell.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EM_NETWORK_PREFS_H
+#define EM_NETWORK_PREFS_H
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <gconf/gconf-client.h>
+
+/* Standard GObject macros */
+#define EM_TYPE_NETWORK_PREFS \
+ (em_network_prefs_get_type ())
+#define EM_NETWORK_PREFS(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), EM_TYPE_NETWORK_PREFS, EMNetworkPrefs))
+#define EM_NETWORK_PREFS_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), EM_TYPE_NETWORK_PREFS, EMNetworkPrefsClass))
+#define EM_IS_NETWORK_PREFS(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), EM_TYPE_NETWORK_PREFS))
+#define EM_IS_NETWORK_PREFS_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), EM_TYPE_NETWORK_PREFS))
+#define EM_NETWORK_PREFS_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), EM_TYPE_NETWORK_PREFS, EMNetworkPrefsClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMNetworkPrefs EMNetworkPrefs;
+typedef struct _EMNetworkPrefsClass EMNetworkPrefsClass;
+
+typedef enum {
+ NETWORK_PROXY_SYS_SETTINGS,
+ NETWORK_PROXY_DIRECT_CONNECTION,
+ NETWORK_PROXY_MANUAL,
+ NETWORK_PROXY_AUTOCONFIG
+} NetworkConfigProxyType;
+
+struct _EMNetworkPrefs {
+ GtkVBox parent_object;
+
+ GConfClient *gconf;
+
+ GladeXML *gui;
+
+ /* Default Behavior */
+ GtkToggleButton *sys_proxy;
+ GtkToggleButton *no_proxy;
+ GtkToggleButton *manual_proxy;
+ GtkToggleButton *use_auth;
+
+ GtkEntry *http_host;
+ GtkEntry *https_host;
+ GtkEntry *socks_host;
+ GtkEntry *ignore_hosts;
+ GtkEntry *auth_user;
+ GtkEntry *auth_pwd;
+
+ GtkLabel *lbl_http_host;
+ GtkLabel *lbl_http_port;
+ GtkLabel *lbl_https_host;
+ GtkLabel *lbl_https_port;
+ GtkLabel *lbl_ignore_hosts;
+ GtkLabel *lbl_auth_user;
+ GtkLabel *lbl_auth_pwd;
+
+ GtkSpinButton *http_port;
+ GtkSpinButton *https_port;
+};
+
+struct _EMNetworkPrefsClass {
+ GtkVBoxClass parent_class;
+};
+
+GType em_network_prefs_get_type (void);
+GtkWidget * em_network_prefs_new (void);
+
+G_END_DECLS
+
+#endif /* EM_NETWORK_PREFS_H */
diff --git a/modules/mail/evolution-module-mail.c b/modules/mail/evolution-module-mail.c
new file mode 100644
index 0000000000..97bc953c41
--- /dev/null
+++ b/modules/mail/evolution-module-mail.c
@@ -0,0 +1,59 @@
+/*
+ * evolution-module-mail.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-mail-shell-backend.h"
+#include "e-mail-shell-content.h"
+#include "e-mail-shell-sidebar.h"
+#include "e-mail-shell-view.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+const gchar * g_module_check_init (GModule *module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ /* Register dynamically loaded types. */
+
+ e_mail_shell_backend_register_type (type_module);
+ e_mail_shell_content_register_type (type_module);
+ e_mail_shell_sidebar_register_type (type_module);
+ e_mail_shell_view_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
+
+G_MODULE_EXPORT const gchar *
+g_module_check_init (GModule *module)
+{
+ /* FIXME Until mail is split into a module library and a
+ * reusable shared library, prevent the module from
+ * being unloaded. Unloading the module resets all
+ * static variables, which screws up foo_get_type()
+ * functions among other things. */
+ g_module_make_resident (module);
+
+ return NULL;
+}