evolution-3.6.4/addressbook/gui/widgets/e-addressbook-selector.c

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
  2 /* e-addressbook-selector.c
  3  *
  4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  5  *
  6  * This program is free software; you can redistribute it and/or
  7  * modify it under the terms of version 2 of the GNU General Public
  8  * License as published by the Free Software Foundation.
  9  *
 10  * This program is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  * General Public License for more details.
 14  *
 15  * You should have received a copy of the GNU General Public
 16  * License along with this program; if not, write to the
 17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 18  * Boston, MA 02110-1301, USA.
 19  */
 20 
 21 #ifdef HAVE_CONFIG_H
 22 #include <config.h>
 23 #endif
 24 
 25 #include "e-addressbook-selector.h"
 26 
 27 #include <e-util/e-selection.h>
 28 
 29 #include <eab-book-util.h>
 30 #include <eab-contact-merging.h>
 31 
 32 #define E_ADDRESSBOOK_SELECTOR_GET_PRIVATE(obj) \
 33 	(G_TYPE_INSTANCE_GET_PRIVATE \
 34 	((obj), E_TYPE_ADDRESSBOOK_SELECTOR, EAddressbookSelectorPrivate))
 35 
 36 typedef struct _MergeContext MergeContext;
 37 
 38 struct _EAddressbookSelectorPrivate {
 39 	EAddressbookView *current_view;
 40 };
 41 
 42 struct _MergeContext {
 43 	ESourceRegistry *registry;
 44 	EBookClient *source_client;
 45 	EBookClient *target_client;
 46 
 47 	EContact *current_contact;
 48 	GSList *remaining_contacts;
 49 	guint pending_removals;
 50 	gboolean pending_adds;
 51 
 52 	gint remove_from_source : 1;
 53 	gint copy_done          : 1;
 54 };
 55 
 56 enum {
 57 	PROP_0,
 58 	PROP_CURRENT_VIEW
 59 };
 60 
 61 static GtkTargetEntry drag_types[] = {
 62 	{ (gchar *) "text/x-source-vcard", 0, 0 }
 63 };
 64 
 65 G_DEFINE_TYPE (
 66 	EAddressbookSelector,
 67 	e_addressbook_selector,
 68 	E_TYPE_SOURCE_SELECTOR)
 69 
 70 static void
 71 merge_context_next (MergeContext *merge_context)
 72 {
 73 	GSList *list;
 74 
 75 	merge_context->current_contact = NULL;
 76 	if (!merge_context->remaining_contacts)
 77 		return;
 78 
 79 	list = merge_context->remaining_contacts;
 80 	merge_context->current_contact = list->data;
 81 	list = g_slist_delete_link (list, list);
 82 	merge_context->remaining_contacts = list;
 83 }
 84 
 85 static MergeContext *
 86 merge_context_new (ESourceRegistry *registry,
 87                    EBookClient *source_client,
 88                    EBookClient *target_client,
 89                    GSList *contact_list)
 90 {
 91 	MergeContext *merge_context;
 92 
 93 	merge_context = g_slice_new0 (MergeContext);
 94 	merge_context->registry = g_object_ref (registry);
 95 	merge_context->source_client = source_client;
 96 	merge_context->target_client = target_client;
 97 	merge_context->remaining_contacts = contact_list;
 98 	merge_context_next (merge_context);
 99 
100 	return merge_context;
101 }
102 
103 static void
104 merge_context_free (MergeContext *merge_context)
105 {
106 	if (merge_context->registry != NULL)
107 		g_object_unref (merge_context->registry);
108 
109 	if (merge_context->source_client != NULL)
110 		g_object_unref (merge_context->source_client);
111 
112 	if (merge_context->target_client != NULL)
113 		g_object_unref (merge_context->target_client);
114 
115 	g_slice_free (MergeContext, merge_context);
116 }
117 
118 static void
119 addressbook_selector_removed_cb (GObject *source_object,
120                                  GAsyncResult *result,
121                                  gpointer user_data)
122 {
123 	EBookClient *book_client = E_BOOK_CLIENT (source_object);
124 	MergeContext *merge_context = user_data;
125 	GError *error = NULL;
126 
127 	e_book_client_remove_contact_finish (book_client, result, &error);
128 
129 	if (error != NULL) {
130 		g_warning (
131 			"%s: Failed to remove contact: %s",
132 			G_STRFUNC, error->message);
133 		g_error_free (error);
134 	}
135 
136 	merge_context->pending_removals--;
137 
138 	if (merge_context->pending_adds)
139 		return;
140 
141 	if (merge_context->pending_removals > 0)
142 		return;
143 
144 	merge_context_free (merge_context);
145 }
146 
147 static void
148 addressbook_selector_merge_next_cb (EBookClient *book_client,
149                                     const GError *error,
150                                     const gchar *id,
151                                     gpointer closure)
152 {
153 	MergeContext *merge_context = closure;
154 
155 	if (merge_context->remove_from_source && !error) {
156 		/* Remove previous contact from source. */
157 		e_book_client_remove_contact (
158 			merge_context->source_client,
159 			merge_context->current_contact, NULL,
160 			addressbook_selector_removed_cb, merge_context);
161 		merge_context->pending_removals++;
162 	}
163 
164 	g_object_unref (merge_context->current_contact);
165 
166 	if (merge_context->remaining_contacts != NULL) {
167 		merge_context_next (merge_context);
168 		eab_merging_book_add_contact (
169 			merge_context->registry,
170 			merge_context->target_client,
171 			merge_context->current_contact,
172 			addressbook_selector_merge_next_cb, merge_context);
173 
174 	} else if (merge_context->pending_removals == 0) {
175 		merge_context_free (merge_context);
176 	} else
177 		merge_context->pending_adds = FALSE;
178 }
179 
180 static void
181 addressbook_selector_set_property (GObject *object,
182                                    guint property_id,
183                                    const GValue *value,
184                                    GParamSpec *pspec)
185 {
186 	switch (property_id) {
187 		case PROP_CURRENT_VIEW:
188 			e_addressbook_selector_set_current_view (
189 				E_ADDRESSBOOK_SELECTOR (object),
190 				g_value_get_object (value));
191 			return;
192 	}
193 
194 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
195 }
196 
197 static void
198 addressbook_selector_get_property (GObject *object,
199                                    guint property_id,
200                                    GValue *value,
201                                    GParamSpec *pspec)
202 {
203 	switch (property_id) {
204 		case PROP_CURRENT_VIEW:
205 			g_value_set_object (
206 				value,
207 				e_addressbook_selector_get_current_view (
208 				E_ADDRESSBOOK_SELECTOR (object)));
209 			return;
210 	}
211 
212 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
213 }
214 
215 static void
216 addressbook_selector_dispose (GObject *object)
217 {
218 	EAddressbookSelectorPrivate *priv;
219 
220 	priv = E_ADDRESSBOOK_SELECTOR_GET_PRIVATE (object);
221 
222 	if (priv->current_view != NULL) {
223 		g_object_unref (priv->current_view);
224 		priv->current_view = NULL;
225 	}
226 
227 	/* Chain up to parent's dispose() method. */
228 	G_OBJECT_CLASS (e_addressbook_selector_parent_class)->dispose (object);
229 }
230 
231 static void
232 addressbook_selector_constructed (GObject *object)
233 {
234 	ESourceSelector *selector;
235 	ESourceRegistry *registry;
236 	ESource *source;
237 
238 	selector = E_SOURCE_SELECTOR (object);
239 	registry = e_source_selector_get_registry (selector);
240 	source = e_source_registry_ref_default_address_book (registry);
241 	e_source_selector_set_primary_selection (selector, source);
242 	g_object_unref (source);
243 
244 	/* Chain up to parent's constructed() method. */
245 	G_OBJECT_CLASS (e_addressbook_selector_parent_class)->
246 		constructed (object);
247 }
248 
249 static void
250 target_client_open_ready_cb (GObject *source_object,
251                              GAsyncResult *result,
252                              gpointer user_data)
253 {
254 	ESource *source = E_SOURCE (source_object);
255 	MergeContext *merge_context = user_data;
256 	EClient *client = NULL;
257 	GError *error = NULL;
258 
259 	g_return_if_fail (merge_context != NULL);
260 
261 	e_client_utils_open_new_finish (source, result, &client, &error);
262 
263 	if (error != NULL) {
264 		g_warn_if_fail (client == NULL);
265 		g_warning (
266 			"%s: Failed to open targer client: %s",
267 			G_STRFUNC, error->message);
268 		g_error_free (error);
269 	}
270 
271 	g_return_if_fail (E_IS_CLIENT (client));
272 
273 	merge_context->target_client = client ? E_BOOK_CLIENT (client) : NULL;
274 
275 	if (!merge_context->target_client) {
276 		g_slist_foreach (
277 			merge_context->remaining_contacts,
278 			(GFunc) g_object_unref, NULL);
279 		g_slist_free (merge_context->remaining_contacts);
280 
281 		merge_context_free (merge_context);
282 		return;
283 	}
284 
285 	eab_merging_book_add_contact (
286 		merge_context->registry,
287 		merge_context->target_client,
288 		merge_context->current_contact,
289 		addressbook_selector_merge_next_cb, merge_context);
290 }
291 
292 static gboolean
293 addressbook_selector_data_dropped (ESourceSelector *selector,
294                                    GtkSelectionData *selection_data,
295                                    ESource *destination,
296                                    GdkDragAction action,
297                                    guint info)
298 {
299 	EAddressbookSelectorPrivate *priv;
300 	MergeContext *merge_context;
301 	EAddressbookModel *model;
302 	EBookClient *source_client = NULL;
303 	ESourceRegistry *registry;
304 	GSList *list;
305 	const gchar *string;
306 	gboolean remove_from_source;
307 
308 	priv = E_ADDRESSBOOK_SELECTOR_GET_PRIVATE (selector);
309 	g_return_val_if_fail (priv->current_view != NULL, FALSE);
310 
311 	string = (const gchar *) gtk_selection_data_get_data (selection_data);
312 	remove_from_source = (action == GDK_ACTION_MOVE);
313 
314 	model = e_addressbook_view_get_model (priv->current_view);
315 	registry = e_addressbook_model_get_registry (model);
316 
317 	/* XXX Function assumes both out arguments are provided.  All we
318 	 *     care about is the contact list; source_client will be NULL. */
319 	eab_book_and_contact_list_from_string (
320 		registry, string, &source_client, &list);
321 	if (source_client)
322 		g_object_unref (source_client);
323 
324 	if (list == NULL)
325 		return FALSE;
326 
327 	source_client = e_addressbook_model_get_client (model);
328 	g_return_val_if_fail (E_IS_BOOK_CLIENT (source_client), FALSE);
329 
330 	merge_context = merge_context_new (
331 		registry, g_object_ref (source_client), NULL, list);
332 	merge_context->remove_from_source = remove_from_source;
333 	merge_context->pending_adds = TRUE;
334 
335 	e_client_utils_open_new (
336 		destination, E_CLIENT_SOURCE_TYPE_CONTACTS, FALSE, NULL,
337 		target_client_open_ready_cb, merge_context);
338 
339 	return TRUE;
340 }
341 
342 static void
343 e_addressbook_selector_class_init (EAddressbookSelectorClass *class)
344 {
345 	GObjectClass *object_class;
346 	ESourceSelectorClass *selector_class;
347 
348 	g_type_class_add_private (class, sizeof (EAddressbookSelectorPrivate));
349 
350 	object_class = G_OBJECT_CLASS (class);
351 	object_class->set_property = addressbook_selector_set_property;
352 	object_class->get_property = addressbook_selector_get_property;
353 	object_class->dispose = addressbook_selector_dispose;
354 	object_class->constructed = addressbook_selector_constructed;
355 
356 	selector_class = E_SOURCE_SELECTOR_CLASS (class);
357 	selector_class->data_dropped = addressbook_selector_data_dropped;
358 
359 	g_object_class_install_property (
360 		object_class,
361 		PROP_CURRENT_VIEW,
362 		g_param_spec_object (
363 			"current-view",
364 			NULL,
365 			NULL,
366 			E_TYPE_ADDRESSBOOK_VIEW,
367 			G_PARAM_READWRITE));
368 }
369 
370 static void
371 e_addressbook_selector_init (EAddressbookSelector *selector)
372 {
373 	selector->priv = E_ADDRESSBOOK_SELECTOR_GET_PRIVATE (selector);
374 
375 	e_source_selector_set_show_colors (
376 		E_SOURCE_SELECTOR (selector), FALSE);
377 
378 	e_source_selector_set_show_toggles (
379 		E_SOURCE_SELECTOR (selector), FALSE);
380 
381 	gtk_drag_dest_set (
382 		GTK_WIDGET (selector), GTK_DEST_DEFAULT_ALL,
383 		drag_types, G_N_ELEMENTS (drag_types),
384 		GDK_ACTION_COPY | GDK_ACTION_MOVE);
385 
386 	e_drag_dest_add_directory_targets (GTK_WIDGET (selector));
387 }
388 
389 GtkWidget *
390 e_addressbook_selector_new (ESourceRegistry *registry)
391 {
392 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
393 
394 	return g_object_new (
395 		E_TYPE_ADDRESSBOOK_SELECTOR,
396 		"extension-name", E_SOURCE_EXTENSION_ADDRESS_BOOK,
397 		"registry", registry, NULL);
398 }
399 
400 EAddressbookView *
401 e_addressbook_selector_get_current_view (EAddressbookSelector *selector)
402 {
403 	g_return_val_if_fail (E_IS_ADDRESSBOOK_SELECTOR (selector), NULL);
404 
405 	return selector->priv->current_view;
406 }
407 
408 void
409 e_addressbook_selector_set_current_view (EAddressbookSelector *selector,
410                                          EAddressbookView *current_view)
411 {
412 	/* XXX This is only needed for moving contacts via drag-and-drop.
413 	 *     The selection data doesn't include the source of the data
414 	 *     (the model for the currently selected address book view),
415 	 *     so we have to rely on it being provided to us.  I would
416 	 *     be happy to see this function go away. */
417 
418 	g_return_if_fail (E_IS_ADDRESSBOOK_SELECTOR (selector));
419 
420 	if (current_view != NULL)
421 		g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (current_view));
422 
423 	if (selector->priv->current_view == current_view)
424 		return;
425 
426 	if (selector->priv->current_view != NULL) {
427 		g_object_unref (selector->priv->current_view);
428 		selector->priv->current_view = NULL;
429 	}
430 
431 	if (current_view != NULL)
432 		g_object_ref (current_view);
433 
434 	selector->priv->current_view = current_view;
435 
436 	g_object_notify (G_OBJECT (selector), "current-view");
437 }