diff options
Diffstat (limited to 'modules/addressbook')
23 files changed, 6892 insertions, 0 deletions
diff --git a/modules/addressbook/Makefile.am b/modules/addressbook/Makefile.am new file mode 100644 index 0000000000..8faf8a3967 --- /dev/null +++ b/modules/addressbook/Makefile.am @@ -0,0 +1,91 @@ +module_LTLIBRARIES = module-addressbook.la + +module_addressbook_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DG_LOG_DOMAIN=\"evolution-addressbook\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/shell \ + -I$(top_builddir)/shell \ + -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 \ + -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \ + -DEVOLUTION_GALVIEWSDIR=\""$(viewsdir)"\" \ + -DEVOLUTION_UIDIR=\""$(uidir)"\" \ + -DEVOLUTION_LOCALEDIR=\""$(localedir)"\" \ + -DEVOLUTION_UIDIR=\""$(uidir)"\" \ + -DPREFIX=\""$(prefix)"\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(CHAMPLAIN_CFLAGS) \ + $(GTKHTML_CFLAGS) \ + $(LDAP_CFLAGS) + +module_addressbook_la_SOURCES = \ + evolution-module-addressbook.c \ + autocompletion-config.c \ + autocompletion-config.h \ + eab-composer-util.c \ + eab-composer-util.h \ + e-book-config-hook.c \ + e-book-config-hook.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 + +module_addressbook_la_LIBADD = \ + $(SMIME_LIB) \ + $(top_builddir)/e-util/libevolution-util.la \ + $(top_builddir)/composer/libevolution-mail-composer.la \ + $(top_builddir)/addressbook/printing/libecontactprint.la \ + $(top_builddir)/shell/libevolution-shell.la \ + $(top_builddir)/addressbook/gui/merging/libeabbookmerging.la \ + $(top_builddir)/addressbook/gui/widgets/libeabwidgets.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)/addressbook/importers/libevolution-addressbook-importers.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(CHAMPLAIN_LIBS) \ + $(GTKHTML_LIBS) \ + $(LDAP_LIBS) + + +module_addressbook_la_LDFLAGS = \ + -module -avoid-version $(NO_UNDEFINED) + +EXTRA_DIST = \ + 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/autocompletion-config.c b/modules/addressbook/autocompletion-config.c new file mode 100644 index 0000000000..bad6040c09 --- /dev/null +++ b/modules/addressbook/autocompletion-config.c @@ -0,0 +1,225 @@ +/* + * 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 <glib/gi18n.h> + +#include "addressbook/gui/widgets/eab-config.h" + +static GtkWidget * +add_section (GtkWidget *container, + const gchar *caption, + gboolean expand) +{ + GtkWidget *widget; + gchar *markup; + + widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (container), widget, expand, expand, 0); + gtk_widget_show (widget); + + container = widget; + + markup = g_markup_printf_escaped ("<b>%s</b>", caption); + widget = gtk_label_new (markup); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); + gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + g_free (markup); + + widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0); + gtk_box_pack_start (GTK_BOX (container), widget, expand, expand, 0); + gtk_widget_show (widget); + + container = widget; + + widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_container_add (GTK_CONTAINER (container), widget); + gtk_widget_show (widget); + + return widget; +} + +static GtkWidget * +get_main_notebook (EConfig *config, + EConfigItem *item, + GtkWidget *parent, + GtkWidget *old, + gint position, + gpointer user_data) +{ + GtkWidget *notebook; + + if (old != NULL) + return old; + + notebook = gtk_notebook_new (); + gtk_widget_show (notebook); + + return notebook; +} + +static GtkWidget * +get_general_page (EConfig *config, + EConfigItem *item, + GtkWidget *parent, + GtkWidget *old, + gint position, + gpointer user_data) +{ + GSettings *settings; + ESourceRegistry *registry; + GtkWidget *container; + GtkWidget *itembox; + GtkWidget *widget; + GtkWidget *vbox; + EShell *shell; + + if (old != NULL) + return old; + + shell = E_SHELL (user_data); + registry = e_shell_get_registry (shell); + + settings = g_settings_new ("org.gnome.evolution.addressbook"); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_notebook_append_page ( + GTK_NOTEBOOK (parent), vbox, + gtk_label_new (_("General"))); + gtk_widget_show (vbox); + + itembox = add_section (vbox, _("Date/Time Format"), FALSE); + + widget = gtk_table_new (1, 3, FALSE); + gtk_box_pack_start (GTK_BOX (itembox), widget, FALSE, FALSE, 0); + e_datetime_format_add_setup_widget ( + widget, 0, "addressbook", "table", + DTFormatKindDateTime, _("_Table column:")); + gtk_widget_show (widget); + + itembox = add_section (vbox, _("Address formatting"), FALSE); + + widget = gtk_check_button_new_with_mnemonic ( + _("_Format address according to standard of its destination country")); + g_settings_bind ( + settings, "address-formatting", + widget, "active", + G_SETTINGS_BIND_DEFAULT); + gtk_box_pack_start (GTK_BOX (itembox), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + itembox = add_section (vbox, _("Autocompletion"), TRUE); + + widget = gtk_check_button_new_with_mnemonic ( + _("Always _show address of the autocompleted contact")); + g_settings_bind ( + settings, "completion-show-address", + widget, "active", + G_SETTINGS_BIND_DEFAULT); + gtk_box_pack_start (GTK_BOX (itembox), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (itembox), widget, TRUE, TRUE, 0); + gtk_widget_show (widget); + + container = widget; + + widget = e_autocomplete_selector_new (registry); + gtk_container_add (GTK_CONTAINER (container), widget); + gtk_widget_show (widget); + + g_object_unref (settings); + + return vbox; +} + +static EConfigItem config_items[] = { + { E_CONFIG_BOOK, (gchar *) "", (gchar *) "main-notebook", get_main_notebook }, + { E_CONFIG_PAGE, (gchar *) "00.general", (gchar *) "general", get_general_page } +}; + +static void +config_items_free (EConfig *config, + GSList *items, + gpointer user_data) +{ + g_slist_free (items); +} + +GtkWidget * +autocompletion_config_new (EPreferencesWindow *window) +{ + EShell *shell; + EABConfig *config; + EABConfigTargetPrefs *target; + GSettings *settings; + GtkWidget *vbox; + GtkWidget *widget; + GSList *items = NULL; + gint ii; + + shell = e_preferences_window_get_shell (window); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 0); + gtk_widget_show (vbox); + + /** @HookPoint-EABConfig: Contacts Preferences Page + * @Id: org.gnome.evolution.addressbook.prefs + * @Type: E_CONFIG_BOOK + * @Class: org.gnome.evolution.addressbook.config:1.0 + * @Target: EABConfigTargetPrefs + * + * The main contacts preferences page. + */ + config = eab_config_new ("org.gnome.evolution.addressbook.prefs"); + + for (ii = 0; ii < G_N_ELEMENTS (config_items); ii++) + items = g_slist_prepend (items, &config_items[ii]); + e_config_add_items ( + E_CONFIG (config), items, config_items_free, shell); + + settings = g_settings_new ("org.gnome.evolution.addressbook"); + + target = eab_config_target_new_prefs (config, settings); + e_config_set_target (E_CONFIG (config), (EConfigTarget *) target); + widget = e_config_create_widget (E_CONFIG (config)); + gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0); + + g_object_unref (settings); + + return vbox; +} + diff --git a/modules/addressbook/autocompletion-config.h b/modules/addressbook/autocompletion-config.h new file mode 100644 index 0000000000..2c60301922 --- /dev/null +++ b/modules/addressbook/autocompletion-config.h @@ -0,0 +1,36 @@ +/* + * 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 <shell/e-shell.h> + +G_BEGIN_DECLS + +GtkWidget * autocompletion_config_new (EPreferencesWindow *window); + +G_END_DECLS + +#endif /* _AUTOCOMPLETION_CONFIG_H */ diff --git a/modules/addressbook/e-book-config-hook.c b/modules/addressbook/e-book-config-hook.c new file mode 100644 index 0000000000..8ee342473e --- /dev/null +++ b/modules/addressbook/e-book-config-hook.c @@ -0,0 +1,75 @@ +/* + * e-book-config-hook.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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-book-config-hook.h" + +#include "addressbook/gui/widgets/eab-config.h" + +static const EConfigHookTargetMask no_masks[] = { + { NULL } +}; + +static const EConfigHookTargetMap targets[] = { + { "source", EAB_CONFIG_TARGET_SOURCE, no_masks }, + { "prefs", EAB_CONFIG_TARGET_PREFS, no_masks }, + { NULL } +}; + +static void +book_config_hook_class_init (EConfigHookClass *class) +{ + EPluginHookClass *plugin_hook_class; + gint ii; + + plugin_hook_class = E_PLUGIN_HOOK_CLASS (class); + plugin_hook_class->id = "org.gnome.evolution.addressbook.config:1.0"; + + class->config_class = g_type_class_ref (eab_config_get_type ()); + + for (ii = 0; targets[ii].type != NULL; ii++) + e_config_hook_class_add_target_map ( + (EConfigHookClass *) class, &targets[ii]); +} + +void +e_book_config_hook_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (EConfigHookClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) book_config_hook_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EConfigHook), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + NULL /* value_table */ + }; + + g_type_module_register_type ( + type_module, e_config_hook_get_type (), + "EBookConfigHook", &type_info, 0); +} diff --git a/modules/addressbook/e-book-config-hook.h b/modules/addressbook/e-book-config-hook.h new file mode 100644 index 0000000000..a5d9e06457 --- /dev/null +++ b/modules/addressbook/e-book-config-hook.h @@ -0,0 +1,33 @@ +/* + * e-book-config-hook.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_CONFIG_HOOK_H +#define E_BOOK_CONFIG_HOOK_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +void e_book_config_hook_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_BOOK_CONFIG_HOOK_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..bfa19826ef --- /dev/null +++ b/modules/addressbook/e-book-shell-backend.c @@ -0,0 +1,524 @@ +/* + * 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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-book-shell-backend.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libebook/libebook.h> + +#include "shell/e-shell.h" +#include "shell/e-shell-window.h" + +#include "addressbook/gui/widgets/eab-gui-util.h" +#include "addressbook/gui/widgets/e-addressbook-view.h" +#include "addressbook/gui/contact-editor/e-contact-editor.h" +#include "addressbook/gui/contact-editor/e-contact-quick-add.h" +#include "addressbook/gui/contact-list-editor/e-contact-list-editor.h" +#include "addressbook/importers/evolution-addressbook-importers.h" + +#include "autocompletion-config.h" + +#include "e-book-shell-content.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)) + +struct _EBookShellBackendPrivate { + gint placeholder; +}; + +G_DEFINE_DYNAMIC_TYPE ( + EBookShellBackend, + e_book_shell_backend, + E_TYPE_SHELL_BACKEND) + +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_new_contact_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + EShell *shell = user_data; + EClient *client; + EContact *contact; + EABEditor *editor; + GError *error = NULL; + + client = e_client_cache_get_client_finish ( + E_CLIENT_CACHE (source_object), result, &error); + + /* Sanity check. */ + g_return_if_fail ( + ((client != NULL) && (error == NULL)) || + ((client == NULL) && (error != NULL))); + + /* XXX Handle errors better. */ + if (error != NULL) { + g_warning ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + goto exit; + } + + contact = e_contact_new (); + + editor = e_contact_editor_new ( + shell, E_BOOK_CLIENT (client), contact, TRUE, TRUE); + + eab_editor_show (editor); + + g_object_unref (contact); + g_object_unref (client); + +exit: + g_object_unref (shell); +} + +static void +book_shell_backend_new_contact_list_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + EShell *shell = user_data; + EClient *client; + EContact *contact; + EABEditor *editor; + GError *error = NULL; + + client = e_client_cache_get_client_finish ( + E_CLIENT_CACHE (source_object), result, &error); + + /* Sanity check. */ + g_return_if_fail ( + ((client != NULL) && (error == NULL)) || + ((client == NULL) && (error != NULL))); + + /* XXX Handle errors better. */ + if (error != NULL) { + g_warning ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + goto exit; + } + + contact = e_contact_new (); + + editor = e_contact_list_editor_new ( + shell, E_BOOK_CLIENT (client), contact, TRUE, TRUE); + + eab_editor_show (editor); + + g_object_unref (contact); + g_object_unref (client); + +exit: + g_object_unref (shell); +} + +static void +action_contact_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + EShell *shell; + ESource *source = NULL; + ESourceRegistry *registry; + EClientCache *client_cache; + const gchar *action_name; + + /* This callback is used for both contacts and contact lists. */ + + shell = e_shell_window_get_shell (shell_window); + client_cache = e_shell_get_client_cache (shell); + + if (g_strcmp0 (e_shell_window_get_active_view (shell_window), "addressbook") == 0) { + EShellView *shell_view = e_shell_window_get_shell_view (shell_window, "addressbook"); + + if (shell_view && E_IS_BOOK_SHELL_VIEW (shell_view)) { + EBookShellContent *book_shell_content; + EAddressbookView *view; + EAddressbookModel *model; + EBookClient *book_client; + + book_shell_content = NULL; + g_object_get (G_OBJECT (shell_view), "shell-content", &book_shell_content, NULL); + g_return_if_fail (book_shell_content != NULL); + + 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_client = e_addressbook_model_get_client (model); + g_return_if_fail (book_client != NULL); + + source = g_object_ref (e_client_get_source (E_CLIENT (book_client))); + + g_object_unref (book_shell_content); + } + } + + if (!source) { + registry = e_shell_get_registry (shell); + source = e_source_registry_ref_default_address_book (registry); + } + + /* Use a callback function appropriate for the action. */ + action_name = gtk_action_get_name (action); + if (strcmp (action_name, "contact-new") == 0) + e_client_cache_get_client ( + client_cache, source, + E_SOURCE_EXTENSION_ADDRESS_BOOK, + NULL, + book_shell_backend_new_contact_cb, + g_object_ref (shell)); + if (strcmp (action_name, "contact-new-list") == 0) + e_client_cache_get_client ( + client_cache, source, + E_SOURCE_EXTENSION_ADDRESS_BOOK, + NULL, + book_shell_backend_new_contact_list_cb, + g_object_ref (shell)); + + g_object_unref (source); +} + +static void +action_address_book_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + EShell *shell; + ESourceRegistry *registry; + GtkWidget *config; + GtkWidget *dialog; + const gchar *icon_name; + + shell = e_shell_window_get_shell (shell_window); + + registry = e_shell_get_registry (shell); + config = e_book_source_config_new (registry, NULL); + + dialog = e_source_config_dialog_new (E_SOURCE_CONFIG (config)); + + gtk_window_set_transient_for ( + GTK_WINDOW (dialog), GTK_WINDOW (shell_window)); + + icon_name = gtk_action_get_icon_name (action); + gtk_window_set_icon_name (GTK_WINDOW (dialog), icon_name); + + gtk_window_set_title (GTK_WINDOW (dialog), _("New Address Book")); + + gtk_widget_show (dialog); +} + +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", + NC_("New", "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_init_preferences (EShell *shell) +{ + GtkWidget *preferences_window; + + /* This is a main loop idle callback. */ + + preferences_window = e_shell_get_preferences_window (shell); + + e_preferences_window_add_page ( + E_PREFERENCES_WINDOW (preferences_window), + "contacts", + "preferences-autocompletion", + _("Contacts"), + "index#contacts", + autocompletion_config_new, + 200); + +#ifdef ENABLE_SMIME + preferences_window = e_shell_get_preferences_window (shell); + e_preferences_window_add_page ( + E_PREFERENCES_WINDOW (preferences_window), + "certificates", + "preferences-certificates", + _("Certificates"), + "mail-encryption-s-mime-manage", + e_cert_manager_config_new, + 700); +#endif + + return FALSE; +} + +static void +book_shell_backend_quick_add_email_cb (EShell *shell, + const gchar *email) +{ + EClientCache *client_cache; + + /* XXX This is an ugly hack but it's the only way I could think + * of to integrate this feature with other shell modules. */ + + client_cache = e_shell_get_client_cache (shell); + e_contact_quick_add_email (client_cache, email, NULL, NULL); +} + +static void +book_shell_backend_quick_add_vcard_cb (EShell *shell, + const gchar *vcard) +{ + EClientCache *client_cache; + + /* XXX This is an ugly hack but it's the only way I could think + * of to integrate this feature with other shell modules. */ + + client_cache = e_shell_get_client_cache (shell); + e_contact_quick_add_vcard (client_cache, vcard, NULL, NULL); +} + +static gboolean +book_shell_backend_handle_uri_cb (EShellBackend *shell_backend, + const gchar *uri) +{ + SoupURI *soup_uri; + const gchar *cp; + gchar *source_uid = NULL; + gchar *contact_uid = NULL; + + if (!g_str_has_prefix (uri, "contacts:")) + return FALSE; + + soup_uri = soup_uri_new (uri); + + if (soup_uri == NULL) + return FALSE; + + cp = soup_uri_get_query (soup_uri); + + if (cp == NULL) { + soup_uri_free (soup_uri); + 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); + + soup_uri_free (soup_uri); + + return TRUE; +} + +static void +book_shell_backend_window_added_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_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 (); +#endif + + book_shell_backend_init_importers (); + + g_signal_connect ( + shell, "event::contact-quick-add-email", + G_CALLBACK (book_shell_backend_quick_add_email_cb), NULL); + + g_signal_connect_swapped ( + shell, "event::contact-quick-add-vcard", + G_CALLBACK (book_shell_backend_quick_add_vcard_cb), NULL); + + g_signal_connect_swapped ( + shell, "handle-uri", + G_CALLBACK (book_shell_backend_handle_uri_cb), + shell_backend); + + g_signal_connect_swapped ( + shell, "window-added", + G_CALLBACK (book_shell_backend_window_added_cb), + shell_backend); + + /* Initialize preferences after the main loop starts so + * that all EPlugins and EPluginHooks are loaded first. */ + g_idle_add ((GSourceFunc) book_shell_backend_init_preferences, shell); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_book_shell_backend_parent_class)->constructed (object); +} + +static void +e_book_shell_backend_class_init (EBookShellBackendClass *class) +{ + GObjectClass *object_class; + EShellBackendClass *shell_backend_class; + + g_type_class_add_private (class, sizeof (EBookShellBackendPrivate)); + + object_class = G_OBJECT_CLASS (class); + 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->preferences_page = "contacts"; + shell_backend_class->start = NULL; + shell_backend_class->migrate = e_book_shell_backend_migrate; +} + +static void +e_book_shell_backend_class_finalize (EBookShellBackendClass *class) +{ +} + +static void +e_book_shell_backend_init (EBookShellBackend *book_shell_backend) +{ + book_shell_backend->priv = + E_BOOK_SHELL_BACKEND_GET_PRIVATE (book_shell_backend); +} + +void +e_book_shell_backend_type_register (GTypeModule *type_module) +{ + /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration + * function, so we have to wrap it with a public function in + * order to register types from a separate compilation unit. */ + e_book_shell_backend_register_type (type_module); +} diff --git a/modules/addressbook/e-book-shell-backend.h b/modules/addressbook/e-book-shell-backend.h new file mode 100644 index 0000000000..73a799a130 --- /dev/null +++ b/modules/addressbook/e-book-shell-backend.h @@ -0,0 +1,67 @@ +/* + * 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> + +/* 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_type_register + (GTypeModule *type_module); + +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..26b370f6d6 --- /dev/null +++ b/modules/addressbook/e-book-shell-content.c @@ -0,0 +1,749 @@ +/* + * 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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-book-shell-content.h" + +#include <glib/gi18n.h> + +#include "shell/e-shell-utils.h" +#include "e-book-shell-view.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_pane; + + GtkOrientation orientation; + + gboolean preview_show_maps; + guint preview_visible : 1; +}; + +enum { + PROP_0, + PROP_CURRENT_VIEW, + PROP_ORIENTATION, + PROP_PREVIEW_CONTACT, + PROP_PREVIEW_VISIBLE, + PROP_PREVIEW_SHOW_MAPS +}; + +G_DEFINE_DYNAMIC_TYPE_EXTENDED ( + EBookShellContent, + e_book_shell_content, + E_TYPE_SHELL_CONTENT, + 0, + G_IMPLEMENT_INTERFACE_DYNAMIC ( + GTK_TYPE_ORIENTABLE, NULL)) + +static void +book_shell_content_send_message_cb (EBookShellContent *book_shell_content, + EDestination *destination, + EABContactDisplay *display) +{ + EShell *shell; + EShellContent *shell_content; + EShellWindow *shell_window; + EShellView *shell_view; + GSList node = { destination, NULL }; + + shell_content = E_SHELL_CONTENT (book_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + eab_send_as_to (shell, &node); +} + +static void +book_shell_content_restore_state_cb (EShellWindow *shell_window, + EShellView *shell_view, + EShellContent *shell_content) +{ + EBookShellContentPrivate *priv; + GSettings *settings; + + priv = E_BOOK_SHELL_CONTENT_GET_PRIVATE (shell_content); + + /* Bind GObject properties to GSettings keys. */ + + settings = g_settings_new ("org.gnome.evolution.addressbook"); + + g_settings_bind ( + settings, "hpane-position", + priv->paned, "hposition", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind ( + settings, "vpane-position", + priv->paned, "vposition", + G_SETTINGS_BIND_DEFAULT); + + g_object_unref (settings); +} + +static GtkOrientation +book_shell_content_get_orientation (EBookShellContent *book_shell_content) +{ + return book_shell_content->priv->orientation; +} + +static void +book_shell_content_set_orientation (EBookShellContent *book_shell_content, + GtkOrientation orientation) +{ + if (book_shell_content->priv->orientation == orientation) + return; + + book_shell_content->priv->orientation = orientation; + + g_object_notify (G_OBJECT (book_shell_content), "orientation"); +} + +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_ORIENTATION: + book_shell_content_set_orientation ( + E_BOOK_SHELL_CONTENT (object), + g_value_get_enum (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; + + case PROP_PREVIEW_SHOW_MAPS: + e_book_shell_content_set_preview_show_maps ( + 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_ORIENTATION: + g_value_set_enum ( + value, + book_shell_content_get_orientation ( + 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; + + case PROP_PREVIEW_SHOW_MAPS: + g_value_set_boolean ( + value, + e_book_shell_content_get_preview_show_maps ( + 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_pane != NULL) { + g_object_unref (priv->preview_pane); + priv->preview_pane = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_book_shell_content_parent_class)->dispose (object); +} + +static void +book_shell_content_constructed (GObject *object) +{ + EBookShellContentPrivate *priv; + EShellView *shell_view; + EShellWindow *shell_window; + EShellContent *shell_content; + EShellTaskbar *shell_taskbar; + GtkWidget *container; + GtkWidget *widget; + + priv = E_BOOK_SHELL_CONTENT_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_book_shell_content_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_taskbar = e_shell_view_get_shell_taskbar (shell_view); + + container = GTK_WIDGET (object); + + widget = e_paned_new (GTK_ORIENTATION_VERTICAL); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->paned = g_object_ref (widget); + gtk_widget_show (widget); + + g_object_bind_property ( + object, "orientation", + widget, "orientation", + G_BINDING_SYNC_CREATE); + + 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_pack1 (GTK_PANED (container), widget, TRUE, FALSE); + priv->notebook = g_object_ref (widget); + gtk_widget_show (widget); + + widget = eab_contact_display_new (); + eab_contact_display_set_mode ( + EAB_CONTACT_DISPLAY (widget), + EAB_CONTACT_DISPLAY_RENDER_NORMAL); + + eab_contact_display_set_show_maps ( + EAB_CONTACT_DISPLAY (widget), + priv->preview_show_maps); + + g_object_bind_property ( + object, "preview-show-maps", + widget, "show-maps", + G_BINDING_SYNC_CREATE); + + gtk_widget_show (widget); + + g_signal_connect_swapped ( + widget, "send-message", + G_CALLBACK (book_shell_content_send_message_cb), object); + + g_signal_connect_swapped ( + widget, "status-message", + G_CALLBACK (e_shell_taskbar_set_message), + shell_taskbar); + + widget = e_preview_pane_new (E_WEB_VIEW (widget)); + gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE); + priv->preview_pane = g_object_ref (widget); + gtk_widget_show (widget); + + g_object_bind_property ( + object, "preview-visible", + widget, "visible", + G_BINDING_SYNC_CREATE); + + /* Restore pane positions from the last session once + * the shell view is fully initialized and visible. */ + g_signal_connect ( + shell_window, "shell-view-created::addressbook", + G_CALLBACK (book_shell_content_restore_state_cb), + shell_content); +} + +static void +book_shell_content_check_state_foreach (gint row, + gpointer user_data) +{ + EContact *contact; + + struct { + EAddressbookModel *model; + GList *list; + } *foreach_data = user_data; + + contact = e_addressbook_model_get_contact (foreach_data->model, row); + g_return_if_fail (E_IS_CONTACT (contact)); + + foreach_data->list = g_list_prepend (foreach_data->list, contact); +} + +static guint32 +book_shell_content_check_state (EShellContent *shell_content) +{ + EBookShellContent *book_shell_content; + ESelectionModel *selection_model; + EAddressbookModel *model; + EAddressbookView *view; + GtkNotebook *notebook; + gboolean has_email = TRUE; + gboolean is_contact_list = TRUE; + guint32 state = 0; + gint n_selected; + + struct { + EAddressbookModel *model; + GList *list; + } foreach_data; + + book_shell_content = E_BOOK_SHELL_CONTENT (shell_content); + + /* This function may be triggered at startup before any address + * book views are added. Check for that and return silently. */ + notebook = GTK_NOTEBOOK (book_shell_content->priv->notebook); + if (gtk_notebook_get_n_pages (notebook) == 0) + return 0; + + 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_selected = (selection_model != NULL) ? + e_selection_model_selected_count (selection_model) : 0; + + foreach_data.model = model; + foreach_data.list = NULL; + + if (selection_model != NULL) + e_selection_model_foreach ( + selection_model, (EForeachFunc) + book_shell_content_check_state_foreach, + &foreach_data); + + while (foreach_data.list != NULL) { + EContact *contact = E_CONTACT (foreach_data.list->data); + GList *email_list; + + email_list = e_contact_get (contact, E_CONTACT_EMAIL); + has_email &= (email_list != NULL); + g_list_foreach (email_list, (GFunc) g_free, NULL); + g_list_free (email_list); + + is_contact_list &= + (e_contact_get (contact, E_CONTACT_IS_LIST) != NULL); + + g_object_unref (contact); + + foreach_data.list = g_list_delete_link ( + foreach_data.list, foreach_data.list); + } + + if (n_selected == 1) + state |= E_BOOK_SHELL_CONTENT_SELECTION_SINGLE; + if (n_selected > 1) + state |= E_BOOK_SHELL_CONTENT_SELECTION_MULTIPLE; + if (n_selected > 0 && has_email) + state |= E_BOOK_SHELL_CONTENT_SELECTION_HAS_EMAIL; + if (n_selected == 1 && is_contact_list) + state |= E_BOOK_SHELL_CONTENT_SELECTION_IS_CONTACT_LIST; + 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; + + return state; +} + +static void +book_shell_content_focus_search_results (EShellContent *shell_content) +{ + EBookShellContent *book_shell_content; + EAddressbookView *view; + + book_shell_content = E_BOOK_SHELL_CONTENT (shell_content); + view = e_book_shell_content_get_current_view (book_shell_content); + + gtk_widget_grab_focus (GTK_WIDGET (view)); +} + +static void +e_book_shell_content_class_init (EBookShellContentClass *class) +{ + GObjectClass *object_class; + EShellContentClass *shell_content_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; + shell_content_class->focus_search_results = + book_shell_content_focus_search_results; + + 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 | + G_PARAM_CONSTRUCT)); + + g_object_class_override_property ( + object_class, PROP_ORIENTATION, "orientation"); + + g_object_class_install_property ( + object_class, + PROP_PREVIEW_SHOW_MAPS, + g_param_spec_boolean ( + "preview-show-maps", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); +} + +static void +e_book_shell_content_class_finalize (EBookShellContentClass *class) +{ +} + +static void +e_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. */ +} + +void +e_book_shell_content_type_register (GTypeModule *type_module) +{ + /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration + * function, so we have to wrap it with a public function in + * order to register types from a separate compilation unit. */ + e_book_shell_content_register_type (type_module); +} + +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) +{ + EShellView *shell_view; + EShellContent *shell_content; + EShellSearchbar *searchbar; + EBookShellView *book_shell_view; + GtkNotebook *notebook; + GtkWidget *child; + gint page_num, old_page_num; + + g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content)); + g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (addressbook_view)); + + shell_content = E_SHELL_CONTENT (book_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + + book_shell_view = E_BOOK_SHELL_VIEW (shell_view); + searchbar = e_book_shell_content_get_searchbar (book_shell_content); + + 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); + + old_page_num = gtk_notebook_get_current_page (notebook); + gtk_notebook_set_current_page (notebook, page_num); + + if (old_page_num != page_num) { + EActionComboBox *combo_box; + GtkRadioAction *action; + gint filter_id = 0, search_id = 0; + gchar *search_text = NULL; + EFilterRule *advanced_search = NULL; + + e_book_shell_view_disable_searching (book_shell_view); + + e_addressbook_view_get_search ( + addressbook_view, &filter_id, &search_id, + &search_text, &advanced_search); + + combo_box = e_shell_searchbar_get_filter_combo_box (searchbar); + e_action_combo_box_set_current_value (combo_box, filter_id); + + action = e_shell_searchbar_get_search_option (searchbar); + gtk_radio_action_set_current_value (action, search_id); + + e_shell_searchbar_set_search_text (searchbar, search_text); + + e_shell_view_set_search_rule (shell_view, advanced_search); + + g_free (search_text); + + if (advanced_search) + g_object_unref (advanced_search); + + e_book_shell_view_enable_searching (book_shell_view); + } + + g_object_notify (G_OBJECT (book_shell_content), "current-view"); +} + +EContact * +e_book_shell_content_get_preview_contact (EBookShellContent *book_shell_content) +{ + EPreviewPane *preview_pane; + EABContactDisplay *display; + EWebView *web_view; + + g_return_val_if_fail ( + E_IS_BOOK_SHELL_CONTENT (book_shell_content), NULL); + + preview_pane = E_PREVIEW_PANE (book_shell_content->priv->preview_pane); + web_view = e_preview_pane_get_web_view (preview_pane); + display = EAB_CONTACT_DISPLAY (web_view); + + return eab_contact_display_get_contact (display); +} + +void +e_book_shell_content_set_preview_contact (EBookShellContent *book_shell_content, + EContact *preview_contact) +{ + EPreviewPane *preview_pane; + EABContactDisplay *display; + EWebView *web_view; + + g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content)); + + preview_pane = E_PREVIEW_PANE (book_shell_content->priv->preview_pane); + web_view = e_preview_pane_get_web_view (preview_pane); + display = EAB_CONTACT_DISPLAY (web_view); + + eab_contact_display_set_contact (display, preview_contact); + g_object_notify (G_OBJECT (book_shell_content), "preview-contact"); +} + +EPreviewPane * +e_book_shell_content_get_preview_pane (EBookShellContent *book_shell_content) +{ + g_return_val_if_fail ( + E_IS_BOOK_SHELL_CONTENT (book_shell_content), NULL); + + return E_PREVIEW_PANE (book_shell_content->priv->preview_pane); +} + +gboolean +e_book_shell_content_get_preview_visible (EBookShellContent *book_shell_content) +{ + g_return_val_if_fail ( + E_IS_BOOK_SHELL_CONTENT (book_shell_content), FALSE); + + return book_shell_content->priv->preview_visible; +} + +void +e_book_shell_content_set_preview_visible (EBookShellContent *book_shell_content, + gboolean preview_visible) +{ + g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content)); + + if (book_shell_content->priv->preview_visible == preview_visible) + return; + + book_shell_content->priv->preview_visible = preview_visible; + + g_object_notify (G_OBJECT (book_shell_content), "preview-visible"); +} + +gboolean +e_book_shell_content_get_preview_show_maps (EBookShellContent *book_shell_content) +{ + g_return_val_if_fail ( + E_IS_BOOK_SHELL_CONTENT (book_shell_content), FALSE); + + return book_shell_content->priv->preview_show_maps; +} + +void +e_book_shell_content_set_preview_show_maps (EBookShellContent *book_shell_content, + gboolean show_maps) +{ + g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content)); + + if (book_shell_content->priv->preview_show_maps == show_maps) + return; + + book_shell_content->priv->preview_show_maps = show_maps; + + g_object_notify (G_OBJECT (book_shell_content), "preview-show-maps"); +} + +EShellSearchbar * +e_book_shell_content_get_searchbar (EBookShellContent *book_shell_content) +{ + EShellView *shell_view; + EShellContent *shell_content; + GtkWidget *widget; + + g_return_val_if_fail ( + E_IS_BOOK_SHELL_CONTENT (book_shell_content), NULL); + + shell_content = E_SHELL_CONTENT (book_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + widget = e_shell_view_get_searchbar (shell_view); + + return E_SHELL_SEARCHBAR (widget); +} diff --git a/modules/addressbook/e-book-shell-content.h b/modules/addressbook/e-book-shell-content.h new file mode 100644 index 0000000000..b0258d82ae --- /dev/null +++ b/modules/addressbook/e-book-shell-content.h @@ -0,0 +1,117 @@ +/* + * 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/libebook.h> + +#include <shell/e-shell-content.h> +#include <shell/e-shell-searchbar.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_SOURCE_IS_BUSY = 1 << 4, + E_BOOK_SHELL_CONTENT_SOURCE_IS_EDITABLE = 1 << 5 +}; + +struct _EBookShellContent { + EShellContent parent; + EBookShellContentPrivate *priv; +}; + +struct _EBookShellContentClass { + EShellContentClass parent_class; +}; + +GType e_book_shell_content_get_type (void); +void e_book_shell_content_type_register + (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); +EPreviewPane * e_book_shell_content_get_preview_pane + (EBookShellContent *book_shell_content); +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); +gboolean e_book_shell_content_get_preview_show_maps + (EBookShellContent *book_shell_content); +void e_book_shell_content_set_preview_show_maps + (EBookShellContent *book_shell_content, + gboolean show_maps); +EShellSearchbar * + e_book_shell_content_get_searchbar + (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..2e3de84b32 --- /dev/null +++ b/modules/addressbook/e-book-shell-migrate.c @@ -0,0 +1,41 @@ +/* + * 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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-book-shell-migrate.h" + +gboolean +e_book_shell_backend_migrate (EShellBackend *shell_backend, + gint major, + gint minor, + gint micro, + GError **error) +{ + g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), FALSE); + + 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..2bbf3f321f --- /dev/null +++ b/modules/addressbook/e-book-shell-migrate.h @@ -0,0 +1,40 @@ +/* + * 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 <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..b29bd9a08f --- /dev/null +++ b/modules/addressbook/e-book-shell-sidebar.c @@ -0,0 +1,319 @@ +/* + * 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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-book-shell-sidebar.h" + +#include <string.h> +#include <glib/gi18n.h> + +#include <e-util/e-util.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 +}; + +G_DEFINE_DYNAMIC_TYPE ( + EBookShellSidebar, + e_book_shell_sidebar, + E_TYPE_SHELL_SIDEBAR) + +static gboolean +book_shell_sidebar_map_uid_to_source (GValue *value, + GVariant *variant, + gpointer user_data) +{ + ESourceRegistry *registry; + ESource *source; + const gchar *uid; + + registry = E_SOURCE_REGISTRY (user_data); + uid = g_variant_get_string (variant, NULL); + if (uid != NULL && *uid != '\0') + source = e_source_registry_ref_source (registry, uid); + else + source = e_source_registry_ref_default_address_book (registry); + g_value_take_object (value, source); + + return (source != NULL); +} + +static GVariant * +book_shell_sidebar_map_source_to_uid (const GValue *value, + const GVariantType *expected_type, + gpointer user_data) +{ + GVariant *variant = NULL; + ESource *source; + + source = g_value_get_object (value); + + if (source != NULL) { + const gchar *uid; + + uid = e_source_get_uid (source); + variant = g_variant_new_string (uid); + } + + return variant; +} + +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 (e_book_shell_sidebar_parent_class)->dispose (object); +} + +static void +book_shell_sidebar_constructed (GObject *object) +{ + EBookShellSidebarPrivate *priv; + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + EShellSidebar *shell_sidebar; + EClientCache *client_cache; + GtkContainer *container; + GtkWidget *widget; + GSettings *settings; + + priv = E_BOOK_SHELL_SIDEBAR_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_book_shell_sidebar_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 = e_shell_backend_get_shell (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); + + client_cache = e_shell_get_client_cache (shell); + widget = e_addressbook_selector_new (client_cache); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->selector = g_object_ref (widget); + gtk_widget_show (widget); + + settings = g_settings_new ("org.gnome.evolution.addressbook"); + + g_settings_bind_with_mapping ( + settings, "primary-addressbook", + widget, "primary-selection", + G_SETTINGS_BIND_DEFAULT, + book_shell_sidebar_map_uid_to_source, + book_shell_sidebar_map_source_to_uid, + e_client_cache_ref_registry (client_cache), + (GDestroyNotify) g_object_unref); + + g_object_unref (settings); +} + +static guint32 +book_shell_sidebar_check_state (EShellSidebar *shell_sidebar) +{ + EBookShellSidebar *book_shell_sidebar; + ESourceSelector *selector; + ESourceRegistry *registry; + ESource *source; + gboolean is_writable = FALSE; + gboolean is_removable = FALSE; + gboolean is_remote_creatable = FALSE; + gboolean is_remote_deletable = FALSE; + gboolean in_collection = FALSE; + gboolean has_primary_source = FALSE; + gboolean refresh_supported = 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_ref_primary_selection (selector); + registry = e_source_selector_get_registry (selector); + + if (source != NULL) { + EClient *client; + ESource *collection; + + has_primary_source = TRUE; + is_writable = e_source_get_writable (source); + is_removable = e_source_get_removable (source); + is_remote_creatable = e_source_get_remote_creatable (source); + is_remote_deletable = e_source_get_remote_deletable (source); + + collection = e_source_registry_find_extension ( + registry, source, E_SOURCE_EXTENSION_COLLECTION); + if (collection != NULL) { + in_collection = TRUE; + g_object_unref (collection); + } + + client = e_client_selector_ref_cached_client ( + E_CLIENT_SELECTOR (selector), source); + + if (client != NULL) { + refresh_supported = + e_client_check_refresh_supported (client); + g_object_unref (client); + } + + g_object_unref (source); + } + + if (has_primary_source) + state |= E_BOOK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE; + if (is_writable) + state |= E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_WRITABLE; + if (is_removable) + state |= E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOVABLE; + if (is_remote_creatable) + state |= E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_CREATABLE; + if (is_remote_deletable) + state |= E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_DELETABLE; + if (in_collection) + state |= E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IN_COLLECTION; + if (refresh_supported) + state |= E_BOOK_SHELL_SIDEBAR_SOURCE_SUPPORTS_REFRESH; + + return state; +} + +static void +e_book_shell_sidebar_class_init (EBookShellSidebarClass *class) +{ + GObjectClass *object_class; + EShellSidebarClass *shell_sidebar_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 +e_book_shell_sidebar_class_finalize (EBookShellSidebarClass *class) +{ +} + +static void +e_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. */ +} + +void +e_book_shell_sidebar_type_register (GTypeModule *type_module) +{ + /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration + * function, so we have to wrap it with a public function in + * order to register types from a separate compilation unit. */ + e_book_shell_sidebar_register_type (type_module); +} + +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..d79fb27f26 --- /dev/null +++ b/modules/addressbook/e-book-shell-sidebar.h @@ -0,0 +1,83 @@ +/* + * 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 <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_WRITABLE = 1 << 1, + E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOVABLE = 1 << 2, + E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_CREATABLE = 1 << 3, + E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_DELETABLE = 1 << 4, + E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IN_COLLECTION = 1 << 5, + E_BOOK_SHELL_SIDEBAR_SOURCE_SUPPORTS_REFRESH = 1 << 6 +}; + +struct _EBookShellSidebar { + EShellSidebar parent; + EBookShellSidebarPrivate *priv; +}; + +struct _EBookShellSidebarClass { + EShellSidebarClass parent_class; +}; + +GType e_book_shell_sidebar_get_type (void); +void e_book_shell_sidebar_type_register + (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..d2fe86e0ca --- /dev/null +++ b/modules/addressbook/e-book-shell-view-actions.c @@ -0,0 +1,1422 @@ +/* + * 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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-book-shell-view-private.h" + +#include <e-util/e-util.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; + EBookShellSidebar *book_shell_sidebar; + ESource *source; + ESourceSelector *selector; + gint response; + + 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_ref_primary_selection (selector); + g_return_if_fail (source != NULL); + + if (e_source_get_remote_deletable (source)) { + response = e_alert_run_dialog_for_args ( + GTK_WINDOW (shell_window), + "addressbook:ask-delete-remote-addressbook", + e_source_get_display_name (source), NULL); + + if (response == GTK_RESPONSE_YES) + e_shell_view_remote_delete_source (shell_view, source); + + } else { + response = e_alert_run_dialog_for_args ( + GTK_WINDOW (shell_window), + "addressbook:ask-delete-addressbook", + e_source_get_display_name (source), NULL); + + if (response == GTK_RESPONSE_YES) + e_shell_view_remove_source (shell_view, source); + } + + g_object_unref (source); +} + +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; + ESourceRegistry *registry; + GtkWidget *config; + GtkWidget *dialog; + const gchar *icon_name; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + registry = book_shell_view->priv->registry; + config = e_book_source_config_new (registry, NULL); + + dialog = e_source_config_dialog_new (E_SOURCE_CONFIG (config)); + + gtk_window_set_transient_for ( + GTK_WINDOW (dialog), GTK_WINDOW (shell_window)); + + icon_name = gtk_action_get_icon_name (action); + gtk_window_set_icon_name (GTK_WINDOW (dialog), icon_name); + + gtk_window_set_title (GTK_WINDOW (dialog), _("New Address Book")); + + gtk_widget_show (dialog); +} + +static void +action_address_book_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, FALSE, print_action); +} + +static void +action_address_book_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, FALSE, print_action); +} + +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; + ESourceRegistry *registry; + GtkWidget *config; + GtkWidget *dialog; + const gchar *icon_name; + + 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_ref_primary_selection (selector); + g_return_if_fail (source != NULL); + + registry = e_source_selector_get_registry (selector); + config = e_book_source_config_new (registry, source); + + g_object_unref (source); + + dialog = e_source_config_dialog_new (E_SOURCE_CONFIG (config)); + + gtk_window_set_transient_for ( + GTK_WINDOW (dialog), GTK_WINDOW (shell_window)); + + icon_name = gtk_action_get_icon_name (action); + gtk_window_set_icon_name (GTK_WINDOW (dialog), icon_name); + + gtk_window_set_title ( + GTK_WINDOW (dialog), _("Address Book Properties")); + + gtk_widget_show (dialog); +} + +static void +address_book_refresh_done_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + EClient *client; + GError *error = NULL; + + g_return_if_fail (E_IS_CLIENT (source_object)); + + client = E_CLIENT (source_object); + + if (!e_client_refresh_finish (client, result, &error)) { + ESource *source = e_client_get_source (client); + + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) + g_warning ( + "%s: Failed to refresh '%s', %s", + G_STRFUNC, e_source_get_display_name (source), + error ? error->message : "Unknown error"); + + g_clear_error (&error); + } +} + +static void +action_address_book_refresh_cb (GtkAction *action, + EBookShellView *book_shell_view) +{ + EBookShellSidebar *book_shell_sidebar; + ESourceSelector *selector; + EClient *client = NULL; + ESource *source; + + book_shell_sidebar = book_shell_view->priv->book_shell_sidebar; + selector = e_book_shell_sidebar_get_selector (book_shell_sidebar); + + source = e_source_selector_ref_primary_selection (selector); + + if (source != NULL) { + client = e_client_selector_ref_cached_client ( + E_CLIENT_SELECTOR (selector), source); + g_object_unref (source); + } + + if (client == NULL) + return; + + g_return_if_fail (e_client_check_refresh_supported (client)); + + e_client_refresh (client, NULL, address_book_refresh_done_cb, book_shell_view); + + g_object_unref (client); +} + +#ifdef WITH_CONTACT_MAPS +static void +contact_editor_contact_modified_cb (EABEditor *editor, + const GError *error, + EContact *contact, + gpointer user_data) +{ + EContactMapWindow *window = user_data; + EContactMap *map; + const gchar *contact_uid; + + if (error) { + g_warning ("Error modifying contact: %s", error->message); + return; + } + + map = e_contact_map_window_get_map (window); + + contact_uid = e_contact_get_const (contact, E_CONTACT_UID); + + e_contact_map_remove_contact (map, contact_uid); + e_contact_map_add_contact (map, contact); +} + +static void +map_window_show_contact_editor_cb (EContactMapWindow *window, + const gchar *contact_uid, + gpointer user_data) +{ + EBookShellView *book_shell_view = user_data; + EBookShellSidebar *book_shell_sidebar; + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + ESource *source; + ESourceSelector *selector; + EClient *client; + EClientCache *client_cache; + EContact *contact; + EABEditor *editor; + GError *error = NULL; + + book_shell_sidebar = book_shell_view->priv->book_shell_sidebar; + selector = e_book_shell_sidebar_get_selector (book_shell_sidebar); + source = e_source_selector_ref_primary_selection (selector); + g_return_if_fail (source != NULL); + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + shell = e_shell_backend_get_shell (shell_backend); + client_cache = e_shell_get_client_cache (shell); + + /* FIXME This blocks. Needs to be asynchronous. */ + client = e_client_cache_get_client_sync ( + client_cache, source, + E_SOURCE_EXTENSION_ADDRESS_BOOK, + NULL, &error); + + g_object_unref (source); + + /* Sanity check. */ + g_return_if_fail ( + ((client != NULL) && (error == NULL)) || + ((client == NULL) && (error != NULL))); + + if (error != NULL) { + g_warning ("Error loading addressbook: %s", error->message); + g_error_free (error); + return; + } + + e_book_client_get_contact_sync ( + E_BOOK_CLIENT (client), contact_uid, &contact, NULL, &error); + if (error != NULL) { + g_warning ("Error getting contact from addressbook: %s", error->message); + g_error_free (error); + g_object_unref (client); + return; + } + + editor = e_contact_editor_new ( + shell, E_BOOK_CLIENT (client), contact, FALSE, TRUE); + + g_signal_connect ( + editor, "contact-modified", + G_CALLBACK (contact_editor_contact_modified_cb), window); + g_signal_connect_swapped ( + editor, "editor-closed", + G_CALLBACK (g_object_unref), editor); + + eab_editor_show (editor); + g_object_unref (client); +} +#endif + +/* We need this function to he defined all the time. */ +static void +action_address_book_map_cb (GtkAction *action, + EBookShellView *book_shell_view) +{ +#ifdef WITH_CONTACT_MAPS + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + EContactMapWindow *map_window; + EBookShellSidebar *book_shell_sidebar; + ESource *source; + ESourceSelector *selector; + EClient *client; + EClientCache *client_cache; + GError *error = NULL; + + book_shell_sidebar = book_shell_view->priv->book_shell_sidebar; + selector = e_book_shell_sidebar_get_selector (book_shell_sidebar); + source = e_source_selector_ref_primary_selection (selector); + g_return_if_fail (source != NULL); + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + shell = e_shell_backend_get_shell (shell_backend); + client_cache = e_shell_get_client_cache (shell); + + /* FIXME This blocks. Needs to be asynchronous. */ + client = e_client_cache_get_client_sync ( + client_cache, source, + E_SOURCE_EXTENSION_ADDRESS_BOOK, + NULL, &error); + + g_object_unref (source); + + /* Sanity check. */ + g_return_if_fail ( + ((client != NULL) && (error == NULL)) || + ((client == NULL) && (error != NULL))); + + if (error != NULL) { + g_warning ("Error loading addressbook: %s", error->message); + g_error_free (error); + return; + } + + map_window = e_contact_map_window_new (); + e_contact_map_window_load_addressbook ( + map_window, E_BOOK_CLIENT (client)); + + /* Free the map_window automatically when it is closed */ + g_signal_connect_swapped ( + map_window, "hide", + G_CALLBACK (gtk_widget_destroy), GTK_WIDGET (map_window)); + g_signal_connect ( + map_window, "show-contact-editor", + G_CALLBACK (map_window_show_contact_editor_cb), book_shell_view); + + gtk_widget_show_all (GTK_WIDGET (map_window)); + + g_object_unref (client); +#endif +} + +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) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EShellBackend *shell_backend; + EBookShellContent *book_shell_content; + EAddressbookModel *model; + EAddressbookView *view; + EActivity *activity; + EBookQuery *query; + EBookClient *book; + GSList *list = NULL; + GFile *file; + gchar *string; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + shell = e_shell_window_get_shell (shell_window); + + 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_client (model); + + query = e_book_query_any_field_contains (""); + string = e_book_query_to_string (query); + e_book_query_unref (query); + + e_book_client_get_contacts_sync (book, string, &list, NULL, NULL); + g_free (string); + + if (list == NULL) + goto exit; + + string = eab_suggest_filename (list); + file = e_shell_run_save_dialog ( + /* Translators: This is a save dialog title */ + shell, _("Save as vCard"), string, + "*.vcf:text/x-vcard,text/directory", NULL, NULL); + g_free (string); + + if (file == NULL) + goto exit; + + string = eab_contact_list_to_string (list); + if (string == NULL) { + g_warning ("Could not convert contact list to a string"); + g_object_unref (file); + goto exit; + } + + /* XXX No callback means errors are discarded. + * + * There's an EAlert for this which I'm not using + * until I figure out a better way to display errors: + * + * "addressbook:save-error" + */ + activity = e_file_replace_contents_async ( + file, string, strlen (string), NULL, FALSE, + G_FILE_CREATE_NONE, (GAsyncReadyCallback) NULL, NULL); + e_shell_backend_add_activity (shell_backend, activity); + + /* Free the string when the activity is finalized. */ + g_object_set_data_full ( + G_OBJECT (activity), + "file-content", string, + (GDestroyNotify) g_free); + + g_object_unref (file); + +exit: + g_slist_free_full (list, (GDestroyNotify) g_object_unref); +} + +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_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_selectable_delete_selection (E_SELECTABLE (view)); +} + +static void +action_contact_find_cb (GtkAction *action, + EBookShellView *book_shell_view) +{ + EBookShellContent *book_shell_content; + EPreviewPane *preview_pane; + + book_shell_content = book_shell_view->priv->book_shell_content; + preview_pane = e_book_shell_content_get_preview_pane (book_shell_content); + + e_preview_pane_show_search_bar (preview_pane); +} + +static void +action_contact_forward_cb (GtkAction *action, + EBookShellView *book_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EBookShellContent *book_shell_content; + EAddressbookView *view; + GSList *list, *iter; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + 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 (shell, list); + + g_slist_free_full (list, (GDestroyNotify) g_object_unref); +} + +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) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EBookShellContent *book_shell_content; + EAddressbookView *view; + EAddressbookModel *model; + EContact *contact; + EABEditor *editor; + EBookClient *book; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + 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_client (model); + g_return_if_fail (book != NULL); + + contact = e_contact_new (); + editor = e_contact_editor_new (shell, book, contact, TRUE, TRUE); + eab_editor_show (editor); + g_object_unref (contact); +} + +static void +action_contact_new_list_cb (GtkAction *action, + EBookShellView *book_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EBookShellContent *book_shell_content; + EAddressbookView *view; + EAddressbookModel *model; + EContact *contact; + EABEditor *editor; + EBookClient *book; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + 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_client (model); + g_return_if_fail (book != NULL); + + contact = e_contact_new (); + editor = e_contact_list_editor_new (shell, book, contact, TRUE, TRUE); + eab_editor_show (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_preview_show_maps_cb (GtkToggleAction *action, + EBookShellView *book_shell_view) +{ + EBookShellContent *book_shell_content; + gboolean show_maps; + + book_shell_content = book_shell_view->priv->book_shell_content; + show_maps = gtk_toggle_action_get_active (action); + e_book_shell_content_set_preview_show_maps (book_shell_content, show_maps); +} + +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, TRUE, print_action); +} + +static void +action_contact_save_as_cb (GtkAction *action, + EBookShellView *book_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EShellBackend *shell_backend; + EBookShellContent *book_shell_content; + EAddressbookView *view; + EActivity *activity; + GSList *list; + GFile *file; + gchar *string; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + shell = e_shell_window_get_shell (shell_window); + + 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); + + if (list == NULL) + goto exit; + + string = eab_suggest_filename (list); + file = e_shell_run_save_dialog ( + /* Translators: This is a save dialog title */ + shell, _("Save as vCard"), string, + "*.vcf:text/x-vcard,text/directory", NULL, NULL); + g_free (string); + + if (file == NULL) + goto exit; + + string = eab_contact_list_to_string (list); + if (string == NULL) { + g_warning ("Could not convert contact list to a string"); + g_object_unref (file); + goto exit; + } + + /* XXX No callback means errors are discarded. + * + * There's an EAlert for this which I'm not using + * until I figure out a better way to display errors: + * + * "addressbook:save-error" + */ + activity = e_file_replace_contents_async ( + file, string, strlen (string), NULL, FALSE, + G_FILE_CREATE_NONE, (GAsyncReadyCallback) NULL, NULL); + e_shell_backend_add_activity (shell_backend, activity); + + /* Free the string when the activity is finalized. */ + g_object_set_data_full ( + G_OBJECT (activity), + "file-content", string, + (GDestroyNotify) g_free); + + g_object_unref (file); + + exit: + g_slist_free_full (list, (GDestroyNotify) g_object_unref); +} + +static void +action_contact_send_message_cb (GtkAction *action, + EBookShellView *book_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EBookShellContent *book_shell_content; + EAddressbookView *view; + GSList *list, *iter; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + 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 (shell, list); + + g_slist_free_full (list, (GDestroyNotify) g_object_unref); +} + +static void +action_contact_view_cb (GtkRadioAction *action, + GtkRadioAction *current, + EBookShellView *book_shell_view) +{ + EBookShellContent *book_shell_content; + GtkOrientable *orientable; + GtkOrientation orientation; + + book_shell_content = book_shell_view->priv->book_shell_content; + orientable = GTK_ORIENTABLE (book_shell_content); + + switch (gtk_radio_action_get_current_value (action)) { + case 0: + orientation = GTK_ORIENTATION_VERTICAL; + break; + case 1: + orientation = GTK_ORIENTATION_HORIZONTAL; + break; + default: + g_return_if_reached (); + } + + gtk_orientable_set_orientation (orientable, orientation); +} + +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_("D_elete 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-refresh", + GTK_STOCK_REFRESH, + N_("Re_fresh"), + NULL, + N_("Refresh the selected address book"), + G_CALLBACK (action_address_book_refresh_cb) }, + + { "address-book-map", + NULL, + N_("Address Book _Map"), + NULL, + N_("Show map with all contacts from selected address book"), + G_CALLBACK (action_address_book_map_cb) }, + + { "address-book-rename", + NULL, + N_("_Rename..."), + "F2", + N_("Rename the selected address book"), + G_CALLBACK (action_address_book_rename_cb) }, + + { "address-book-stop", + GTK_STOCK_STOP, + NULL, + NULL, + N_("Stop loading"), + G_CALLBACK (action_address_book_stop_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-find", + GTK_STOCK_FIND, + N_("_Find in Contact..."), + "<Shift><Control>f", + N_("Search for text in the displayed contact"), + G_CALLBACK (action_contact_find_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 Contact"), + "<Control>o", + N_("View the current contact"), + G_CALLBACK (action_contact_open_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 ***/ + + { "contact-actions-menu", + NULL, + N_("_Actions"), + NULL, + NULL, + NULL }, + + { "contact-preview-menu", + NULL, + N_("_Preview"), + 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-refresh", + NULL, + "address-book-refresh" }, + + { "address-book-popup-map", + N_("Address Book Map"), + "address-book-map" }, + + { "address-book-popup-rename", + NULL, + "address-book-rename" }, + + { "contact-popup-copy", + NULL, + "contact-copy" }, + + { "contact-popup-forward", + NULL, + "contact-forward" }, + + { "contact-popup-move", + NULL, + "contact-move" }, + + { "contact-popup-open", + NULL, + "contact-open" }, + + { "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 }, + + { "contact-preview-show-maps", + NULL, + N_("Show _Maps"), + NULL, + N_("Show maps in contact preview window"), + G_CALLBACK (action_contact_preview_show_maps_cb), + FALSE } +}; + +static GtkRadioActionEntry contact_view_entries[] = { + + /* This action represents the initial active contact view. + * It should not be visible in the UI, nor should it be + * possible to switch to it from another shell view. */ + { "contact-view-initial", + NULL, + NULL, + NULL, + NULL, + -1 }, + + { "contact-view-classic", + NULL, + N_("_Classic View"), + NULL, + N_("Show contact preview below the contact list"), + 0 }, + + { "contact-view-vertical", + NULL, + N_("_Vertical View"), + NULL, + N_("Show contact preview alongside the contact list"), + 1 } +}; + +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-advanced-hidden", + NULL, + N_("Advanced Search"), + NULL, + NULL, + CONTACT_SEARCH_ADVANCED }, + + { "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[] = { + + { "address-book-print", + GTK_STOCK_PRINT, + NULL, + "<Control>p", + N_("Print all shown contacts"), + G_CALLBACK (action_address_book_print_cb) }, + + { "address-book-print-preview", + GTK_STOCK_PRINT_PREVIEW, + NULL, + NULL, + N_("Preview the contacts to be printed"), + G_CALLBACK (action_address_book_print_preview_cb) }, + + { "contact-print", + GTK_STOCK_PRINT, + NULL, + NULL, + N_("Print selected contacts"), + G_CALLBACK (action_contact_print_cb) } +}; + +static EPopupActionEntry lockdown_printing_popup_entries[] = { + + { "contact-popup-print", + NULL, + "contact-print" } +}; + +static GtkActionEntry lockdown_save_to_disk_entries[] = { + + { "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) }, + + { "contact-save-as", + GTK_STOCK_SAVE_AS, + /* Translators: This is an action label */ + N_("_Save as vCard..."), + NULL, + N_("Save selected contacts as a vCard"), + G_CALLBACK (action_contact_save_as_cb) } +}; + +static EPopupActionEntry lockdown_save_to_disk_popup_entries[] = { + + { "address-book-popup-save-as", + /* Translators: This is an action label */ + N_("_Save as vCard..."), + "address-book-save-as" }, + + { "contact-popup-save-as", + NULL, + "contact-save-as" } +}; + +void +e_book_shell_view_actions_init (EBookShellView *book_shell_view) +{ + EBookShellContent *book_shell_content; + EShellView *shell_view; + EShellWindow *shell_window; + EShellSearchbar *searchbar; + EPreviewPane *preview_pane; + EWebView *web_view; + GtkActionGroup *action_group; + GSettings *settings; + GtkAction *action; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + book_shell_content = book_shell_view->priv->book_shell_content; + searchbar = e_book_shell_content_get_searchbar (book_shell_content); + preview_pane = e_book_shell_content_get_preview_pane (book_shell_content); + web_view = e_preview_pane_get_web_view (preview_pane); + + /* 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_view_entries, + G_N_ELEMENTS (contact_view_entries), -1, + G_CALLBACK (action_contact_view_cb), book_shell_view); + gtk_action_group_add_radio_actions ( + action_group, contact_search_entries, + G_N_ELEMENTS (contact_search_entries), + -1, NULL, NULL); + + /* Advanced Search Action */ + action = ACTION (CONTACT_SEARCH_ADVANCED_HIDDEN); + gtk_action_set_visible (action, FALSE); + e_shell_searchbar_set_search_option ( + searchbar, GTK_RADIO_ACTION (action)); + + /* 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)); + + /* Lockdown Save-to-Disk Actions */ + action_group = ACTION_GROUP (LOCKDOWN_SAVE_TO_DISK); + gtk_action_group_add_actions ( + action_group, lockdown_save_to_disk_entries, + G_N_ELEMENTS (lockdown_save_to_disk_entries), + book_shell_view); + e_action_group_add_popup_actions ( + action_group, lockdown_save_to_disk_popup_entries, + G_N_ELEMENTS (lockdown_save_to_disk_popup_entries)); + + /* Bind GObject properties to GSettings keys. */ + + settings = g_settings_new ("org.gnome.evolution.addressbook"); + + g_settings_bind ( + settings, "show-preview", + ACTION (CONTACT_PREVIEW), "active", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind ( + settings, "layout", + ACTION (CONTACT_VIEW_VERTICAL), "current-value", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind ( + settings, "preview-show-maps", + ACTION (CONTACT_PREVIEW_SHOW_MAPS), "active", + G_SETTINGS_BIND_DEFAULT); + + g_object_unref (settings); + + /* Fine tuning. */ + + g_object_bind_property ( + ACTION (CONTACT_PREVIEW), "active", + ACTION (CONTACT_VIEW_CLASSIC), "sensitive", + G_BINDING_SYNC_CREATE); + + g_object_bind_property ( + ACTION (CONTACT_PREVIEW), "active", + ACTION (CONTACT_VIEW_VERTICAL), "sensitive", + G_BINDING_SYNC_CREATE); + + g_object_bind_property ( + ACTION (CONTACT_PREVIEW), "active", + ACTION (CONTACT_PREVIEW_SHOW_MAPS), "sensitive", + G_BINDING_SYNC_CREATE); + + e_web_view_set_open_proxy (web_view, ACTION (CONTACT_OPEN)); + e_web_view_set_print_proxy (web_view, ACTION (CONTACT_PRINT)); + e_web_view_set_save_as_proxy (web_view, ACTION (CONTACT_SAVE_AS)); + +#ifndef WITH_CONTACT_MAPS + gtk_action_set_visible (ACTION (CONTACT_PREVIEW_SHOW_MAPS), FALSE); + gtk_action_set_visible (ACTION (ADDRESS_BOOK_MAP), FALSE); + gtk_action_set_visible (ACTION (ADDRESS_BOOK_POPUP_MAP), FALSE); +#endif +} + +void +e_book_shell_view_update_search_filter (EBookShellView *book_shell_view) +{ + EBookShellContent *book_shell_content; + EShellView *shell_view; + EShellWindow *shell_window; + EShellSearchbar *searchbar; + EActionComboBox *combo_box; + GtkActionGroup *action_group; + GtkRadioAction *radio_action; + GList *list, *iter; + GSList *group; + gint ii; + + shell_view = E_SHELL_VIEW (book_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. No callback is needed + * because changes in the EActionComboBox are detected and + * handled by EShellSearchbar. */ + gtk_action_group_add_radio_actions ( + action_group, contact_filter_entries, + G_N_ELEMENTS (contact_filter_entries), + CONTACT_FILTER_ANY_CATEGORY, NULL, NULL); + + /* 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_util_get_searchable_categories (); + 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); + + book_shell_content = book_shell_view->priv->book_shell_content; + searchbar = e_book_shell_content_get_searchbar (book_shell_content); + combo_box = e_shell_searchbar_get_filter_combo_box (searchbar); + + e_shell_view_block_execute_search (shell_view); + + /* Use any action in the group; doesn't matter which. */ + e_action_combo_box_set_action (combo_box, radio_action); + + ii = CONTACT_FILTER_UNMATCHED; + e_action_combo_box_add_separator_after (combo_box, ii); + + e_shell_view_unblock_execute_search (shell_view); +} 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..32bf261a7c --- /dev/null +++ b/modules/addressbook/e-book-shell-view-actions.h @@ -0,0 +1,101 @@ +/* + * 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_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "address-book-print") +#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_PRINT_PREVIEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "address-book-print-preview") +#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_PROPERTIES(window) \ + E_SHELL_WINDOW_ACTION ((window), "address-book-properties") +#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_REFRESH(window) \ + E_SHELL_WINDOW_ACTION ((window), "address-book-refresh") +#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") +#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_MAP(window) \ + E_SHELL_WINDOW_ACTION ((window), "address-book-map") +#define E_SHELL_WINDOW_ACTION_ADDRESS_BOOK_POPUP_MAP(window) \ + E_SHELL_WINDOW_ACTION ((window), "address-book-popup-map") + +/* Contact Actions */ +#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_FIND(window) \ + E_SHELL_WINDOW_ACTION ((window), "contact-find") +#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_PREVIEW_SHOW_MAPS(window) \ + E_SHELL_WINDOW_ACTION ((window), "contact-preview-show-maps") +#define E_SHELL_WINDOW_ACTION_CONTACT_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "contact-print") +#define E_SHELL_WINDOW_ACTION_CONTACT_SAVE_AS(window) \ + E_SHELL_WINDOW_ACTION ((window), "contact-save-as") +#define E_SHELL_WINDOW_ACTION_CONTACT_SEND_MESSAGE(window) \ + E_SHELL_WINDOW_ACTION ((window), "contact-send-message") +#define E_SHELL_WINDOW_ACTION_CONTACT_VIEW_CLASSIC(window) \ + E_SHELL_WINDOW_ACTION ((window), "contact-view-classic") +#define E_SHELL_WINDOW_ACTION_CONTACT_VIEW_VERTICAL(window) \ + E_SHELL_WINDOW_ACTION ((window), "contact-view-vertical") + +/* Search Actions */ +#define E_SHELL_WINDOW_ACTION_CONTACT_SEARCH_ADVANCED_HIDDEN(window) \ + E_SHELL_WINDOW_ACTION ((window), "contact-search-advanced-hidden") +#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..b30ca5f04d --- /dev/null +++ b/modules/addressbook/e-book-shell-view-private.c @@ -0,0 +1,609 @@ +/* + * 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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-util/e-util-private.h" + +#include "e-book-shell-view-private.h" + +static void +open_contact (EBookShellView *book_shell_view, + EContact *contact, + gboolean is_new_contact, + EAddressbookView *view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EAddressbookModel *model; + EABEditor *editor; + EBookClient *book; + gboolean editable; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + model = e_addressbook_view_get_model (view); + book = e_addressbook_model_get_client (model); + editable = e_addressbook_model_get_editable (model); + + if (e_contact_get (contact, E_CONTACT_IS_LIST)) + editor = e_contact_list_editor_new ( + shell, book, contact, is_new_contact, editable); + else + editor = e_contact_editor_new ( + shell, book, contact, is_new_contact, editable); + + eab_editor_show (editor); +} + +static void +popup_event (EBookShellView *book_shell_view, + GdkEvent *button_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, button_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); + book_shell_view->priv->preview_index = row; + + if (contact) + g_object_unref (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); + book_shell_view->priv->preview_index = -1; + } +} + +static void +contact_changed (EBookShellView *book_shell_view, + gint index, + EAddressbookModel *model) +{ + EBookShellContent *book_shell_content; + EContact *contact; + + g_return_if_fail (E_IS_SHELL_VIEW (book_shell_view)); + g_return_if_fail (book_shell_view->priv != NULL); + + book_shell_content = book_shell_view->priv->book_shell_content; + + contact = e_addressbook_model_contact_at (model, index); + + if (book_shell_view->priv->preview_index != index) + 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; + + g_return_if_fail (E_IS_SHELL_VIEW (book_shell_view)); + g_return_if_fail (book_shell_view->priv != NULL); + + 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); + book_shell_view->priv->preview_index = -1; +} + +static void +model_query_changed_cb (EBookShellView *book_shell_view, + GParamSpec *param, + EAddressbookModel *model) +{ + EBookShellContent *book_shell_content; + EAddressbookView *current_view; + + book_shell_content = book_shell_view->priv->book_shell_content; + current_view = e_book_shell_content_get_current_view (book_shell_content); + + if (!current_view || e_addressbook_view_get_model (current_view) != model) + return; + + /* clear the contact preview when model's query changed */ + e_book_shell_content_set_preview_contact (book_shell_content, NULL); + book_shell_view->priv->preview_index = -1; +} + +static void +book_shell_view_client_connect_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + EAddressbookView *view = user_data; + EClient *client; + EAddressbookModel *model; + GError *error = NULL; + + client = e_client_selector_get_client_finish ( + E_CLIENT_SELECTOR (source_object), result, &error); + + /* Sanity check. */ + g_return_if_fail ( + ((client != NULL) && (error == NULL)) || + ((client == NULL) && (error != NULL))); + + /* Ignore cancellations. */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_error_free (error); + goto exit; + + } else if (error != NULL) { + EShellView *shell_view; + EShellContent *shell_content; + EAlertSink *alert_sink; + ESource *source; + + source = e_addressbook_view_get_source (view); + shell_view = e_addressbook_view_get_shell_view (view); + shell_content = e_shell_view_get_shell_content (shell_view); + alert_sink = E_ALERT_SINK (shell_content); + + eab_load_error_dialog (NULL, alert_sink, source, error); + + g_error_free (error); + goto exit; + } + + model = e_addressbook_view_get_model (view); + e_addressbook_model_set_client (model, E_BOOK_CLIENT (client)); + e_addressbook_model_force_folder_bar_message (model); + +exit: + g_object_unref (view); +} + +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_ref_primary_selection (selector); + + if (source == NULL) + return; + + uid = e_source_get_uid (source); + hash_table = book_shell_view->priv->uid_to_view; + widget = g_hash_table_lookup (hash_table, uid); + + if (widget != NULL) { + view = E_ADDRESSBOOK_VIEW (widget); + model = e_addressbook_view_get_model (view); + + } else { + /* Create a view for this UID. */ + widget = e_addressbook_view_new (shell_view, source); + gtk_widget_show (widget); + + /* Default searching options for a new view. */ + e_addressbook_view_set_search ( + E_ADDRESSBOOK_VIEW (widget), + CONTACT_FILTER_ANY_CATEGORY, + CONTACT_SEARCH_NAME_CONTAINS, + NULL, NULL); + + 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_object ( + widget, "open-contact", G_CALLBACK (open_contact), + book_shell_view, G_CONNECT_SWAPPED); + + g_signal_connect_object ( + widget, "popup-event", G_CALLBACK (popup_event), + book_shell_view, G_CONNECT_SWAPPED); + + g_signal_connect_object ( + widget, "command-state-change", + G_CALLBACK (e_shell_view_update_actions), + book_shell_view, G_CONNECT_SWAPPED); + + g_signal_connect_object ( + widget, "selection-change", G_CALLBACK (selection_change), + book_shell_view, G_CONNECT_SWAPPED); + + view = E_ADDRESSBOOK_VIEW (widget); + model = e_addressbook_view_get_model (view); + + g_signal_connect_object ( + model, "contact-changed", + G_CALLBACK (contact_changed), + book_shell_view, G_CONNECT_SWAPPED); + + g_signal_connect_object ( + model, "contacts-removed", + G_CALLBACK (contacts_removed), + book_shell_view, G_CONNECT_SWAPPED); + + g_signal_connect_object ( + model, "notify::query", + G_CALLBACK (model_query_changed_cb), + book_shell_view, G_CONNECT_SWAPPED); + } + + /* XXX No way to cancel this? */ + e_client_selector_get_client ( + E_CLIENT_SELECTOR (selector), + source, NULL, + book_shell_view_client_connect_cb, + g_object_ref (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); + + /* This must come after e_book_shell_content_set_current_view() + * because book_shell_view_notify_view_id_cb() relies on it. */ + gal_view_instance_load (view_instance); + + 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); + + g_object_unref (source); +} + +static gboolean +book_shell_view_show_popup_menu (GdkEvent *button_event, + EShellView *shell_view) +{ + const gchar *widget_path; + + widget_path = "/address-book-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, button_event); + + return TRUE; +} + +static gboolean +book_shell_view_selector_button_press_event_cb (EShellView *shell_view, + GdkEvent *button_event) +{ + guint event_button = 0; + + /* XXX Use ESourceSelector's "popup-event" signal instead. */ + + gdk_event_get_button (button_event, &event_button); + + if (button_event->type != GDK_BUTTON_PRESS || event_button != 3) + return FALSE; + + return book_shell_view_show_popup_menu (button_event, shell_view); +} + +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 void +book_shell_view_backend_error_cb (EClientCache *client_cache, + EClient *client, + EAlert *alert, + EBookShellView *book_shell_view) +{ + EBookShellContent *book_shell_content; + ESource *source; + const gchar *extension_name; + + book_shell_content = book_shell_view->priv->book_shell_content; + + source = e_client_get_source (client); + extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; + + /* Only submit alerts from address book backends. */ + if (e_source_has_extension (source, extension_name)) { + EAlertSink *alert_sink; + + alert_sink = E_ALERT_SINK (book_shell_content); + e_alert_sink_submit_alert (alert_sink, alert); + } +} + +static void +book_shell_view_source_removed_cb (ESourceRegistry *registry, + ESource *source, + EBookShellView *book_shell_view) +{ + EBookShellViewPrivate *priv = book_shell_view->priv; + EBookShellContent *book_shell_content; + EAddressbookView *view; + const gchar *uid; + + uid = e_source_get_uid (source); + + book_shell_content = book_shell_view->priv->book_shell_content; + + /* Remove the EAddressbookView for the deleted source. */ + view = g_hash_table_lookup (priv->uid_to_view, uid); + if (view != NULL) { + e_book_shell_content_remove_view (book_shell_content, view); + g_hash_table_remove (priv->uid_to_view, uid); + } + + e_shell_view_update_actions (E_SHELL_VIEW (book_shell_view)); +} + +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) +{ + EBookShellViewPrivate *priv = book_shell_view->priv; + GHashTable *uid_to_view; + + uid_to_view = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + priv->uid_to_view = uid_to_view; + priv->preview_index = -1; + + 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; + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EShellContent *shell_content; + EShellSidebar *shell_sidebar; + EShellBackend *shell_backend; + ESourceSelector *selector; + gulong handler_id; + + 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); + shell = e_shell_window_get_shell (shell_window); + + 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); + + /* Keep our own reference to this so we can + * disconnect our signal handler in dispose(). */ + priv->client_cache = g_object_ref (e_shell_get_client_cache (shell)); + + /* Keep our own reference to this so we can + * disconnect our signal handler in dispose(). */ + priv->registry = g_object_ref (e_shell_get_registry (shell)); + + selector = e_book_shell_sidebar_get_selector ( + E_BOOK_SHELL_SIDEBAR (shell_sidebar)); + + handler_id = g_signal_connect ( + priv->client_cache, "backend-error", + G_CALLBACK (book_shell_view_backend_error_cb), + book_shell_view); + priv->backend_error_handler_id = handler_id; + + handler_id = g_signal_connect ( + priv->registry, "source-removed", + G_CALLBACK (book_shell_view_source_removed_cb), + book_shell_view); + priv->source_removed_handler_id = handler_id; + + g_signal_connect_object ( + selector, "button-press-event", + G_CALLBACK (book_shell_view_selector_button_press_event_cb), + book_shell_view, G_CONNECT_SWAPPED); + + g_signal_connect_object ( + selector, "popup-menu", + G_CALLBACK (book_shell_view_selector_popup_menu_cb), + book_shell_view, G_CONNECT_SWAPPED); + + g_signal_connect_object ( + selector, "primary-selection-changed", + G_CALLBACK (book_shell_view_activate_selected_source), + book_shell_view, G_CONNECT_SWAPPED); + + e_categories_add_change_hook ( + (GHookFunc) 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; + + if (priv->backend_error_handler_id > 0) { + g_signal_handler_disconnect ( + priv->client_cache, + priv->backend_error_handler_id); + priv->backend_error_handler_id = 0; + } + + if (priv->source_removed_handler_id > 0) { + g_signal_handler_disconnect ( + priv->registry, + priv->source_removed_handler_id); + priv->source_removed_handler_id = 0; + } + + g_clear_object (&priv->book_shell_backend); + g_clear_object (&priv->book_shell_content); + g_clear_object (&priv->book_shell_sidebar); + + g_clear_object (&priv->client_cache); + g_clear_object (&priv->registry); + + g_hash_table_remove_all (priv->uid_to_view); +} + +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); +} 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..d5b6cc5c94 --- /dev/null +++ b/modules/addressbook/e-book-shell-view-private.h @@ -0,0 +1,120 @@ +/* + * 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/libebook.h> + +#include "shell/e-shell-content.h" +#include "shell/e-shell-searchbar.h" +#include "shell/e-shell-sidebar.h" +#include "shell/e-shell-utils.h" + +#include "addressbook/util/eab-book-util.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 "addressbook/gui/widgets/e-contact-map-window.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)) + +/* ETable Specifications */ +#define ETSPEC_FILENAME "e-addressbook-view.etspec" + +G_BEGIN_DECLS + +/* 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_ADVANCED = -1, + 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; + + EClientCache *client_cache; + gulong backend_error_handler_id; + + ESourceRegistry *registry; + gulong source_removed_handler_id; + + GHashTable *uid_to_view; + + gint preview_index; + + /* Can track whether search changed while locked, + * but it is not usable at the moment. */ + gint search_locked; +}; + +void e_book_shell_view_private_init + (EBookShellView *book_shell_view); +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_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..0c812413e2 --- /dev/null +++ b/modules/addressbook/e-book-shell-view.c @@ -0,0 +1,430 @@ +/* + * 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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-book-shell-view-private.h" + +#include "addressbook/gui/widgets/gal-view-minicard.h" + +G_DEFINE_DYNAMIC_TYPE ( + EBookShellView, + e_book_shell_view, + E_TYPE_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 (e_book_shell_view_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 (e_book_shell_view_parent_class)->finalize (object); +} + +static void +book_shell_view_constructed (GObject *object) +{ + EBookShellView *book_shell_view; + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_book_shell_view_parent_class)->constructed (object); + + book_shell_view = E_BOOK_SHELL_VIEW (object); + e_book_shell_view_private_constructed (book_shell_view); +} + +static void +book_shell_view_execute_search (EShellView *shell_view) +{ + EBookShellViewPrivate *priv; + EBookShellContent *book_shell_content; + EShellWindow *shell_window; + EShellContent *shell_content; + EShellSearchbar *searchbar; + EActionComboBox *combo_box; + GtkRadioAction *action; + EAddressbookView *view; + EAddressbookModel *model; + gchar *query; + gchar *temp; + gint filter_id, search_id; + gchar *search_text = NULL; + EFilterRule *advanced_search = NULL; + + priv = E_BOOK_SHELL_VIEW_GET_PRIVATE (shell_view); + + if (priv->search_locked) + return; + + shell_window = e_shell_view_get_shell_window (shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + + book_shell_content = E_BOOK_SHELL_CONTENT (shell_content); + searchbar = e_book_shell_content_get_searchbar (book_shell_content); + + action = GTK_RADIO_ACTION (ACTION (CONTACT_SEARCH_ANY_FIELD_CONTAINS)); + search_id = gtk_radio_action_get_current_value (action); + + if (search_id == CONTACT_SEARCH_ADVANCED) { + query = e_shell_view_get_search_query (shell_view); + + if (query == NULL) + query = g_strdup (""); + + /* internal pointer, no need to free it */ + advanced_search = e_shell_view_get_search_rule (shell_view); + } else { + const gchar *text; + const gchar *format; + GString *string; + + text = e_shell_searchbar_get_search_text (searchbar); + + if (text == NULL || *text == '\0') { + text = ""; + search_id = CONTACT_SEARCH_ANY_FIELD_CONTAINS; + } + + search_text = text && *text ? g_strdup (text) : NULL; + + switch (search_id) { + 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. */ + combo_box = e_shell_searchbar_get_filter_combo_box (searchbar); + filter_id = e_action_combo_box_get_current_value (combo_box); + switch (filter_id) { + 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_util_get_searchable_categories (); + category_name = g_list_nth_data (categories, filter_id); + g_list_free (categories); + + temp = g_strdup_printf ( + "(and (is \"category_list\" \"%s\") %s)", + category_name, query); + g_free (query); + query = temp; + } + } + + /* Submit the query. */ + 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); + e_addressbook_view_set_search ( + view, filter_id, search_id, search_text, advanced_search); + g_free (query); + g_free (search_text); +} + +static void +book_shell_view_update_actions (EShellView *shell_view) +{ + 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_writable; + gboolean primary_source_is_removable; + gboolean primary_source_is_remote_deletable; + gboolean primary_source_in_collection; + gboolean refresh_supported; + gboolean single_contact_selected; + gboolean selection_is_contact_list; + gboolean selection_has_email; + gboolean source_is_busy; + gboolean source_is_editable; + + /* Chain up to parent's update_actions() method. */ + E_SHELL_VIEW_CLASS (e_book_shell_view_parent_class)-> + update_actions (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); + + 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_writable = + (state & E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_WRITABLE); + primary_source_is_removable = + (state & E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOVABLE); + primary_source_is_remote_deletable = + (state & E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_DELETABLE); + primary_source_in_collection = + (state & E_BOOK_SHELL_SIDEBAR_PRIMARY_SOURCE_IN_COLLECTION); + refresh_supported = + (state & E_BOOK_SHELL_SIDEBAR_SOURCE_SUPPORTS_REFRESH); + + any_contacts_selected = + (single_contact_selected || multiple_contacts_selected); + + action = ACTION (ADDRESS_BOOK_MOVE); + sensitive = source_is_editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (ADDRESS_BOOK_DELETE); + sensitive = + primary_source_is_removable || + primary_source_is_remote_deletable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (ADDRESS_BOOK_PRINT); + sensitive = has_primary_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (ADDRESS_BOOK_PRINT_PREVIEW); + sensitive = has_primary_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (ADDRESS_BOOK_PROPERTIES); + sensitive = primary_source_is_writable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (ADDRESS_BOOK_REFRESH); + sensitive = refresh_supported; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (ADDRESS_BOOK_RENAME); + sensitive = + primary_source_is_writable && + !primary_source_in_collection; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (ADDRESS_BOOK_STOP); + sensitive = source_is_busy; + 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_FIND); + sensitive = single_contact_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"); + gtk_action_set_label (action, label); + + action = ACTION (CONTACT_MOVE); + sensitive = source_is_editable && any_contacts_selected; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CONTACT_NEW); + sensitive = source_is_editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CONTACT_NEW_LIST); + sensitive = source_is_editable; + 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_SAVE_AS); + sensitive = any_contacts_selected; + 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"); + gtk_action_set_label (action, label); +} + +static void +e_book_shell_view_class_finalize (EBookShellViewClass *class) +{ +} + +static void +e_book_shell_view_class_init (EBookShellViewClass *class) +{ + GObjectClass *object_class; + EShellViewClass *shell_view_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->execute_search = book_shell_view_execute_search; + shell_view_class->update_actions = book_shell_view_update_actions; + + /* Ensure the GalView types we need are registered. */ + g_type_ensure (GAL_TYPE_VIEW_ETABLE); + g_type_ensure (GAL_TYPE_VIEW_MINICARD); +} + +static void +e_book_shell_view_init (EBookShellView *book_shell_view) +{ + book_shell_view->priv = + E_BOOK_SHELL_VIEW_GET_PRIVATE (book_shell_view); + + e_book_shell_view_private_init (book_shell_view); +} + +void +e_book_shell_view_type_register (GTypeModule *type_module) +{ + /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration + * function, so we have to wrap it with a public function in + * order to register types from a separate compilation unit. */ + e_book_shell_view_register_type (type_module); +} + +void +e_book_shell_view_disable_searching (EBookShellView *book_shell_view) +{ + EBookShellViewPrivate *priv; + + g_return_if_fail (book_shell_view != NULL); + g_return_if_fail (E_IS_BOOK_SHELL_VIEW (book_shell_view)); + + priv = book_shell_view->priv; + priv->search_locked++; +} + +void +e_book_shell_view_enable_searching (EBookShellView *book_shell_view) +{ + EBookShellViewPrivate *priv; + + g_return_if_fail (book_shell_view != NULL); + g_return_if_fail (E_IS_BOOK_SHELL_VIEW (book_shell_view)); + + priv = book_shell_view->priv; + g_return_if_fail (priv->search_locked > 0); + + priv->search_locked--; +} diff --git a/modules/addressbook/e-book-shell-view.h b/modules/addressbook/e-book-shell-view.h new file mode 100644 index 0000000000..144effebba --- /dev/null +++ b/modules/addressbook/e-book-shell-view.h @@ -0,0 +1,69 @@ +/* + * 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_type_register (GTypeModule *type_module); + +void e_book_shell_view_disable_searching (EBookShellView *book_shell_view); +void e_book_shell_view_enable_searching (EBookShellView *book_shell_view); + +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..90af295ded --- /dev/null +++ b/modules/addressbook/eab-composer-util.c @@ -0,0 +1,206 @@ +/* + * 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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "eab-composer-util.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libebook/libebook.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 (EShell *shell, + GSList *destinations) +{ + EMsgComposer *composer; + EComposerHeaderTable *table; + GPtrArray *to_array; + GPtrArray *bcc_array; + + union { + gpointer *pdata; + EDestination **destinations; + } convert; + + g_return_if_fail (E_IS_SHELL (shell)); + + if (destinations == NULL) + return; + + composer = e_msg_composer_new (shell); + 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_slist_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. */ + + /* Set "To" destinations. */ + convert.pdata = to_array->pdata; + e_composer_header_table_set_destinations_to ( + table, convert.destinations); + g_ptr_array_free (to_array, FALSE); + + /* Add "Bcc" destinations. */ + convert.pdata = bcc_array->pdata; + e_composer_header_table_add_destinations_bcc ( + table, convert.destinations); + g_ptr_array_free (bcc_array, FALSE); + + 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 (EShell *shell, + GSList *destinations) +{ + EMsgComposer *composer; + EComposerHeaderTable *table; + CamelMimePart *attachment; + GSList *contacts, *iter; + gchar *data; + + g_return_if_fail (E_IS_SHELL (shell)); + + if (destinations == NULL) + return; + + composer = e_msg_composer_new (shell); + table = e_msg_composer_get_header_table (composer); + + attachment = camel_mime_part_new (); + + contacts = g_slist_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_slist_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); + g_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..b8644ab187 --- /dev/null +++ b/modules/addressbook/eab-composer-util.h @@ -0,0 +1,33 @@ +/* + * 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 <shell/e-shell.h> + +G_BEGIN_DECLS + +void eab_send_as_to (EShell *shell, + GSList *destinations); +void eab_send_as_attachment (EShell *shell, + GSList *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..e8a6dafa39 --- /dev/null +++ b/modules/addressbook/evolution-module-addressbook.c @@ -0,0 +1,53 @@ +/* + * 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) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-book-config-hook.h" + +#include "e-book-shell-view.h" +#include "e-book-shell-backend.h" +#include "e-book-shell-content.h" +#include "e-book-shell-sidebar.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_config_hook_register_type (type_module); + + e_book_shell_view_type_register (type_module); + e_book_shell_backend_type_register (type_module); + e_book_shell_content_type_register (type_module); + e_book_shell_sidebar_type_register (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} diff --git a/modules/addressbook/openldap-extract.h b/modules/addressbook/openldap-extract.h new file mode 100644 index 0000000000..d838e8b49b --- /dev/null +++ b/modules/addressbook/openldap-extract.h @@ -0,0 +1,1449 @@ +/* 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 +#ifndef LDAP_TYPE_OR_VALUE_EXISTS +#define LDAP_TYPE_OR_VALUE_EXISTS 0x14 +#endif +#ifndef LDAP_SCOPE_DEFAULT +#define LDAP_SCOPE_DEFAULT -1 +#endif +#ifndef LDAP_OPT_SUCCESS +#define LDAP_OPT_SUCCESS 0x00 +#endif +#ifndef LDAP_INSUFFICIENT_ACCESS +#define LDAP_INSUFFICIENT_ACCESS 0x32 +#endif + +#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; +} + |