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.
664 lines
19 KiB
C
664 lines
19 KiB
C
/* -*- 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 (§ion, 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);
|
|
}
|