aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-name-selector-model.c
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2012-12-10 21:09:59 +0800
committerMatthew Barnes <mbarnes@redhat.com>2012-12-13 03:33:43 +0800
commitd09d8de870b6697c8a8b262e7e077b871a69b315 (patch)
tree3b718882e7a0bb0a996daf2967a033d91714c9b5 /e-util/e-name-selector-model.c
parentb61331ed03ac1c7a9b8614e25510040b9c60ae02 (diff)
downloadgsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.gz
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.bz2
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.lz
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.xz
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.zst
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.zip
Consolidate base utility libraries into libeutil.
Evolution consists of entirely too many small utility libraries, which increases linking and loading time, places a burden on higher layers of the application (e.g. modules) which has to remember to link to all the small in-tree utility libraries, and makes it difficult to generate API documentation for these utility libraries in one Gtk-Doc module. Merge the following utility libraries under the umbrella of libeutil, and enforce a single-include policy on libeutil so we can reorganize the files as desired without disrupting its pseudo-public API. libemail-utils/libemail-utils.la libevolution-utils/libevolution-utils.la filter/libfilter.la widgets/e-timezone-dialog/libetimezonedialog.la widgets/menus/libmenus.la widgets/misc/libemiscwidgets.la widgets/table/libetable.la widgets/text/libetext.la This also merges libedataserverui from the Evolution-Data-Server module, since Evolution is its only consumer nowadays, and I'd like to make some improvements to those APIs without concern for backward-compatibility. And finally, start a Gtk-Doc module for libeutil. It's going to be a project just getting all the symbols _listed_ much less _documented_. But the skeletal structure is in place and I'm off to a good start.
Diffstat (limited to 'e-util/e-name-selector-model.c')
-rw-r--r--e-util/e-name-selector-model.c663
1 files changed, 663 insertions, 0 deletions
diff --git a/e-util/e-name-selector-model.c b/e-util/e-name-selector-model.c
new file mode 100644
index 0000000000..770f51422f
--- /dev/null
+++ b/e-util/e-name-selector-model.c
@@ -0,0 +1,663 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/* e-name-selector-model.c - Model for contact selection.
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Authors: Hans Petter Jansson <hpj@novell.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include "e-name-selector-model.h"
+
+#define E_NAME_SELECTOR_MODEL_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_NAME_SELECTOR_MODEL, ENameSelectorModelPrivate))
+
+typedef struct {
+ gchar *name;
+ gchar *pretty_name;
+
+ EDestinationStore *destination_store;
+}
+Section;
+
+struct _ENameSelectorModelPrivate {
+ GArray *sections;
+ EContactStore *contact_store;
+ ETreeModelGenerator *contact_filter;
+ GHashTable *destination_uid_hash;
+};
+
+static gint generate_contact_rows (EContactStore *contact_store, GtkTreeIter *iter,
+ ENameSelectorModel *name_selector_model);
+static void override_email_address (EContactStore *contact_store, GtkTreeIter *iter,
+ gint permutation_n, gint column, GValue *value,
+ ENameSelectorModel *name_selector_model);
+static void free_section (ENameSelectorModel *name_selector_model, gint n);
+
+/* ------------------ *
+ * Class/object setup *
+ * ------------------ */
+
+/* Signals */
+
+enum {
+ SECTION_ADDED,
+ SECTION_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (ENameSelectorModel, e_name_selector_model, G_TYPE_OBJECT)
+
+static void
+e_name_selector_model_init (ENameSelectorModel *name_selector_model)
+{
+ name_selector_model->priv =
+ E_NAME_SELECTOR_MODEL_GET_PRIVATE (name_selector_model);
+
+ name_selector_model->priv->sections = g_array_new (FALSE, FALSE, sizeof (Section));
+ name_selector_model->priv->contact_store = e_contact_store_new ();
+
+ name_selector_model->priv->contact_filter =
+ e_tree_model_generator_new (GTK_TREE_MODEL (name_selector_model->priv->contact_store));
+ e_tree_model_generator_set_generate_func (
+ name_selector_model->priv->contact_filter,
+ (ETreeModelGeneratorGenerateFunc) generate_contact_rows,
+ name_selector_model, NULL);
+ e_tree_model_generator_set_modify_func (name_selector_model->priv->contact_filter,
+ (ETreeModelGeneratorModifyFunc) override_email_address,
+ name_selector_model, NULL);
+
+ g_object_unref (name_selector_model->priv->contact_store);
+
+ name_selector_model->priv->destination_uid_hash = NULL;
+}
+
+static void
+name_selector_model_finalize (GObject *object)
+{
+ ENameSelectorModelPrivate *priv;
+ gint i;
+
+ priv = E_NAME_SELECTOR_MODEL_GET_PRIVATE (object);
+
+ for (i = 0; i < priv->sections->len; i++)
+ free_section (E_NAME_SELECTOR_MODEL (object), i);
+
+ g_array_free (priv->sections, TRUE);
+ g_object_unref (priv->contact_filter);
+
+ if (priv->destination_uid_hash)
+ g_hash_table_destroy (priv->destination_uid_hash);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_name_selector_model_parent_class)->finalize (object);
+}
+
+static void
+e_name_selector_model_class_init (ENameSelectorModelClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (ENameSelectorModelPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = name_selector_model_finalize;
+
+ signals[SECTION_ADDED] = g_signal_new (
+ "section-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ENameSelectorModelClass, section_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ signals[SECTION_REMOVED] = g_signal_new (
+ "section-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ENameSelectorModelClass, section_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+}
+
+/**
+ * e_name_selector_model_new:
+ *
+ * Creates a new #ENameSelectorModel.
+ *
+ * Returns: A new #ENameSelectorModel.
+ **/
+ENameSelectorModel *
+e_name_selector_model_new (void)
+{
+ return E_NAME_SELECTOR_MODEL (g_object_new (E_TYPE_NAME_SELECTOR_MODEL, NULL));
+}
+
+/* ---------------------------- *
+ * GtkTreeModelFilter filtering *
+ * ---------------------------- */
+
+static void
+deep_free_list (GList *list)
+{
+ GList *l;
+
+ for (l = list; l; l = g_list_next (l))
+ g_free (l->data);
+
+ g_list_free (list);
+}
+
+static gint
+generate_contact_rows (EContactStore *contact_store,
+ GtkTreeIter *iter,
+ ENameSelectorModel *name_selector_model)
+{
+ EContact *contact;
+ const gchar *contact_uid;
+ gint n_rows, used_rows = 0;
+ gint i;
+
+ contact = e_contact_store_get_contact (contact_store, iter);
+ g_assert (contact != NULL);
+
+ contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
+ if (!contact_uid)
+ return 0; /* Can happen with broken databases */
+
+ for (i = 0; i < name_selector_model->priv->sections->len; i++) {
+ Section *section;
+ GList *destinations;
+ GList *l;
+
+ section = &g_array_index (name_selector_model->priv->sections, Section, i);
+ destinations = e_destination_store_list_destinations (section->destination_store);
+
+ for (l = destinations; l; l = g_list_next (l)) {
+ EDestination *destination = l->data;
+ const gchar *destination_uid;
+
+ destination_uid = e_destination_get_contact_uid (destination);
+ if (destination_uid && !strcmp (contact_uid, destination_uid)) {
+ used_rows++;
+ }
+ }
+
+ g_list_free (destinations);
+ }
+
+ if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
+ n_rows = 1 - used_rows;
+ } else {
+ GList *email_list;
+
+ email_list = e_contact_get (contact, E_CONTACT_EMAIL);
+ n_rows = g_list_length (email_list) - used_rows;
+ deep_free_list (email_list);
+ }
+
+ g_return_val_if_fail (n_rows >= 0, 0);
+
+ return n_rows;
+}
+
+static void
+override_email_address (EContactStore *contact_store,
+ GtkTreeIter *iter,
+ gint permutation_n,
+ gint column,
+ GValue *value,
+ ENameSelectorModel *name_selector_model)
+{
+ if (column == E_CONTACT_EMAIL_1) {
+ EContact *contact;
+ GList *email_list;
+ gchar *email;
+
+ contact = e_contact_store_get_contact (contact_store, iter);
+ email_list = e_name_selector_model_get_contact_emails_without_used (name_selector_model, contact, TRUE);
+ g_return_if_fail (g_list_length (email_list) <= permutation_n);
+ email = g_strdup (g_list_nth_data (email_list, permutation_n));
+ g_value_set_string (value, email);
+ e_name_selector_model_free_emails_list (email_list);
+ } else {
+ gtk_tree_model_get_value (GTK_TREE_MODEL (contact_store), iter, column, value);
+ }
+}
+
+/* --------------- *
+ * Section helpers *
+ * --------------- */
+
+typedef struct
+{
+ ENameSelectorModel *name_selector_model;
+ GHashTable *other_hash;
+}
+HashCompare;
+
+static void
+emit_destination_uid_changes_cb (gchar *uid_num,
+ gpointer value,
+ HashCompare *hash_compare)
+{
+ EContactStore *contact_store = hash_compare->name_selector_model->priv->contact_store;
+
+ if (!hash_compare->other_hash || !g_hash_table_lookup (hash_compare->other_hash, uid_num)) {
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gchar *sep;
+
+ sep = strrchr (uid_num, ':');
+ g_return_if_fail (sep != NULL);
+
+ *sep = '\0';
+ if (e_contact_store_find_contact (contact_store, uid_num, &iter)) {
+ *sep = ':';
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (contact_store), &iter);
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (contact_store), path, &iter);
+ gtk_tree_path_free (path);
+ } else {
+ *sep = ':';
+ }
+ }
+}
+
+static void
+destinations_changed (ENameSelectorModel *name_selector_model)
+{
+ GHashTable *destination_uid_hash_new;
+ GHashTable *destination_uid_hash_old;
+ HashCompare hash_compare;
+ gint i;
+
+ destination_uid_hash_new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ for (i = 0; i < name_selector_model->priv->sections->len; i++) {
+ Section *section = &g_array_index (name_selector_model->priv->sections, Section, i);
+ GList *destinations;
+ GList *l;
+
+ destinations = e_destination_store_list_destinations (section->destination_store);
+
+ for (l = destinations; l; l = g_list_next (l)) {
+ EDestination *destination = l->data;
+ const gchar *destination_uid;
+
+ destination_uid = e_destination_get_contact_uid (destination);
+ if (destination_uid)
+ g_hash_table_insert (
+ destination_uid_hash_new,
+ g_strdup_printf (
+ "%s:%d", destination_uid,
+ e_destination_get_email_num (destination)),
+ GINT_TO_POINTER (TRUE));
+ }
+
+ g_list_free (destinations);
+ }
+
+ destination_uid_hash_old = name_selector_model->priv->destination_uid_hash;
+ name_selector_model->priv->destination_uid_hash = destination_uid_hash_new;
+
+ hash_compare.name_selector_model = name_selector_model;
+
+ hash_compare.other_hash = destination_uid_hash_old;
+ g_hash_table_foreach (
+ destination_uid_hash_new,
+ (GHFunc) emit_destination_uid_changes_cb,
+ &hash_compare);
+
+ if (destination_uid_hash_old) {
+ hash_compare.other_hash = destination_uid_hash_new;
+ g_hash_table_foreach (
+ destination_uid_hash_old,
+ (GHFunc) emit_destination_uid_changes_cb,
+ &hash_compare);
+
+ g_hash_table_destroy (destination_uid_hash_old);
+ }
+}
+
+static void
+free_section (ENameSelectorModel *name_selector_model,
+ gint n)
+{
+ Section *section;
+
+ g_assert (n >= 0);
+ g_assert (n < name_selector_model->priv->sections->len);
+
+ section = &g_array_index (name_selector_model->priv->sections, Section, n);
+
+ g_signal_handlers_disconnect_matched (
+ section->destination_store, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, name_selector_model);
+
+ g_free (section->name);
+ g_free (section->pretty_name);
+ g_object_unref (section->destination_store);
+}
+
+static gint
+find_section_by_name (ENameSelectorModel *name_selector_model,
+ const gchar *name)
+{
+ gint i;
+
+ g_assert (name != NULL);
+
+ for (i = 0; i < name_selector_model->priv->sections->len; i++) {
+ Section *section = &g_array_index (name_selector_model->priv->sections, Section, i);
+
+ if (!strcmp (name, section->name))
+ return i;
+ }
+
+ return -1;
+}
+
+/* ---------------------- *
+ * ENameSelectorModel API *
+ * ---------------------- */
+
+/**
+ * e_name_selector_model_peek_contact_store:
+ * @name_selector_model: an #ENameSelectorModel
+ *
+ * Gets the #EContactStore associated with @name_selector_model.
+ *
+ * Returns: An #EContactStore.
+ **/
+EContactStore *
+e_name_selector_model_peek_contact_store (ENameSelectorModel *name_selector_model)
+{
+ g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
+
+ return name_selector_model->priv->contact_store;
+}
+
+/**
+ * e_name_selector_model_peek_contact_filter:
+ * @name_selector_model: an #ENameSelectorModel
+ *
+ * Gets the #ETreeModelGenerator being used to filter and/or extend the
+ * list of contacts in @name_selector_model's #EContactStore.
+ *
+ * Returns: An #ETreeModelGenerator.
+ **/
+ETreeModelGenerator *
+e_name_selector_model_peek_contact_filter (ENameSelectorModel *name_selector_model)
+{
+ g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
+
+ return name_selector_model->priv->contact_filter;
+}
+
+/**
+ * e_name_selector_model_list_sections:
+ * @name_selector_model: an #ENameSelectorModel
+ *
+ * Gets a list of the destination sections in @name_selector_model.
+ *
+ * Returns: A #GList of pointers to strings. The #GList and the
+ * strings belong to the caller, and must be freed when no longer needed.
+ **/
+GList *
+e_name_selector_model_list_sections (ENameSelectorModel *name_selector_model)
+{
+ GList *section_names = NULL;
+ gint i;
+
+ g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
+
+ /* Do this backwards so we can use g_list_prepend () and get correct order */
+ for (i = name_selector_model->priv->sections->len - 1; i >= 0; i--) {
+ Section *section = &g_array_index (name_selector_model->priv->sections, Section, i);
+ gchar *name;
+
+ name = g_strdup (section->name);
+ section_names = g_list_prepend (section_names, name);
+ }
+
+ return section_names;
+}
+
+/**
+ * e_name_selector_model_add_section:
+ * @name_selector_model: an #ENameSelectorModel
+ * @name: internal name of this section
+ * @pretty_name: user-visible name of this section
+ * @destination_store: the #EDestinationStore to use to store the destinations for this
+ * section, or %NULL if @name_selector_model should create its own.
+ *
+ * Adds a destination section to @name_selector_model.
+ **/
+void
+e_name_selector_model_add_section (ENameSelectorModel *name_selector_model,
+ const gchar *name,
+ const gchar *pretty_name,
+ EDestinationStore *destination_store)
+{
+ Section section;
+
+ g_return_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (pretty_name != NULL);
+
+ if (find_section_by_name (name_selector_model, name) >= 0) {
+ g_warning ("ENameSelectorModel already has a section called '%s'!", name);
+ return;
+ }
+
+ memset (&section, 0, sizeof (Section));
+
+ section.name = g_strdup (name);
+ section.pretty_name = g_strdup (pretty_name);
+
+ if (destination_store)
+ section.destination_store = g_object_ref (destination_store);
+ else
+ section.destination_store = e_destination_store_new ();
+
+ g_signal_connect_swapped (
+ section.destination_store, "row-changed",
+ G_CALLBACK (destinations_changed), name_selector_model);
+ g_signal_connect_swapped (
+ section.destination_store, "row-deleted",
+ G_CALLBACK (destinations_changed), name_selector_model);
+ g_signal_connect_swapped (
+ section.destination_store, "row-inserted",
+ G_CALLBACK (destinations_changed), name_selector_model);
+
+ g_array_append_val (name_selector_model->priv->sections, section);
+
+ destinations_changed (name_selector_model);
+ g_signal_emit (name_selector_model, signals[SECTION_ADDED], 0, name);
+}
+
+/**
+ * e_name_selector_model_remove_section:
+ * @name_selector_model: an #ENameSelectorModel
+ * @name: internal name of the section to remove
+ *
+ * Removes a destination section from @name_selector_model.
+ **/
+void
+e_name_selector_model_remove_section (ENameSelectorModel *name_selector_model,
+ const gchar *name)
+{
+ gint n;
+
+ g_return_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model));
+ g_return_if_fail (name != NULL);
+
+ n = find_section_by_name (name_selector_model, name);
+ if (n < 0) {
+ g_warning ("ENameSelectorModel does not have a section called '%s'!", name);
+ return;
+ }
+
+ free_section (name_selector_model, n);
+ g_array_remove_index_fast (name_selector_model->priv->sections, n); /* Order doesn't matter */
+
+ destinations_changed (name_selector_model);
+ g_signal_emit (name_selector_model, signals[SECTION_REMOVED], 0, name);
+}
+
+/**
+ * e_name_selector_model_peek_section:
+ * @name_selector_model: an #ENameSelectorModel
+ * @name: internal name of the section to peek
+ * @pretty_name: location in which to store a pointer to the user-visible name of the section,
+ * or %NULL if undesired.
+ * @destination_store: location in which to store a pointer to the #EDestinationStore being used
+ * by the section, or %NULL if undesired
+ *
+ * Gets the parameters for a destination section.
+ **/
+gboolean
+e_name_selector_model_peek_section (ENameSelectorModel *name_selector_model,
+ const gchar *name,
+ gchar **pretty_name,
+ EDestinationStore **destination_store)
+{
+ Section *section;
+ gint n;
+
+ g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ n = find_section_by_name (name_selector_model, name);
+ if (n < 0) {
+ g_warning ("ENameSelectorModel does not have a section called '%s'!", name);
+ return FALSE;
+ }
+
+ section = &g_array_index (name_selector_model->priv->sections, Section, n);
+
+ if (pretty_name)
+ *pretty_name = g_strdup (section->pretty_name);
+ if (destination_store)
+ *destination_store = section->destination_store;
+
+ return TRUE;
+}
+
+/**
+ * e_name_selector_model_get_contact_emails_without_used:
+ * @name_selector_model: an #ENameSelectorModel
+ * @contact: to get emails from
+ * @remove_used: set to %TRUE to remove used from a list; or set to %FALSE to
+ * set used indexes to %NULL and keep them in the returned list
+ *
+ * Returns list of all email from @contact, without all used
+ * in any section. Each item is a string, an email address.
+ * Returned list should be freed with @e_name_selector_model_free_emails_list.
+ *
+ * Since: 2.30
+ **/
+GList *
+e_name_selector_model_get_contact_emails_without_used (ENameSelectorModel *name_selector_model,
+ EContact *contact,
+ gboolean remove_used)
+{
+ GList *email_list;
+ gint emails;
+ gint i;
+ const gchar *contact_uid;
+
+ g_return_val_if_fail (name_selector_model != NULL, NULL);
+ g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
+ g_return_val_if_fail (contact != NULL, NULL);
+ g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
+
+ contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
+ g_return_val_if_fail (contact_uid != NULL, NULL);
+
+ email_list = e_contact_get (contact, E_CONTACT_EMAIL);
+ emails = g_list_length (email_list);
+
+ for (i = 0; i < name_selector_model->priv->sections->len; i++) {
+ Section *section;
+ GList *destinations;
+ GList *l;
+
+ section = &g_array_index (name_selector_model->priv->sections, Section, i);
+ destinations = e_destination_store_list_destinations (section->destination_store);
+
+ for (l = destinations; l; l = g_list_next (l)) {
+ EDestination *destination = l->data;
+ const gchar *destination_uid;
+
+ destination_uid = e_destination_get_contact_uid (destination);
+ if (destination_uid && !strcmp (contact_uid, destination_uid)) {
+ gint email_num = e_destination_get_email_num (destination);
+
+ if (email_num < 0 || email_num >= emails) {
+ g_warning ("%s: Destination's email_num %d out of bounds 0..%d", G_STRFUNC, email_num, emails - 1);
+ } else {
+ GList *nth = g_list_nth (email_list, email_num);
+
+ g_return_val_if_fail (nth != NULL, NULL);
+
+ g_free (nth->data);
+ nth->data = NULL;
+ }
+ }
+ }
+
+ g_list_free (destinations);
+ }
+
+ if (remove_used) {
+ /* remove all with data NULL, which are those used already */
+ do {
+ emails = g_list_length (email_list);
+ email_list = g_list_remove (email_list, NULL);
+ } while (g_list_length (email_list) != emails);
+ }
+
+ return email_list;
+}
+
+/**
+ * e_name_selector_model_free_emails_list:
+ * @email_list: list of emails returned from @e_name_selector_model_get_contact_emails_without_used
+ *
+ * Frees a list of emails returned from @e_name_selector_model_get_contact_emails_without_used.
+ *
+ * Since: 2.30
+ **/
+void
+e_name_selector_model_free_emails_list (GList *email_list)
+{
+ deep_free_list (email_list);
+}