evolution-3.6.4/modules/addressbook/e-book-shell-view-private.c

No issues found

  1 /*
  2  * e-book-shell-view-private.c
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  *
 18  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 19  *
 20  */
 21 
 22 #ifdef HAVE_CONFIG_H
 23 #include <config.h>
 24 #endif
 25 
 26 #include "e-util/e-util-private.h"
 27 
 28 #include "e-book-shell-view-private.h"
 29 
 30 #include "widgets/menus/gal-view-factory-etable.h"
 31 #include "addressbook/gui/widgets/gal-view-factory-minicard.h"
 32 
 33 static void
 34 open_contact (EBookShellView *book_shell_view,
 35               EContact *contact,
 36               gboolean is_new_contact,
 37               EAddressbookView *view)
 38 {
 39 	EShell *shell;
 40 	EShellView *shell_view;
 41 	EShellWindow *shell_window;
 42 	EAddressbookModel *model;
 43 	EABEditor *editor;
 44 	EBookClient *book;
 45 	gboolean editable;
 46 
 47 	shell_view = E_SHELL_VIEW (book_shell_view);
 48 	shell_window = e_shell_view_get_shell_window (shell_view);
 49 	shell = e_shell_window_get_shell (shell_window);
 50 
 51 	model = e_addressbook_view_get_model (view);
 52 	book = e_addressbook_model_get_client (model);
 53 	editable = e_addressbook_model_get_editable (model);
 54 
 55 	if (e_contact_get (contact, E_CONTACT_IS_LIST))
 56 		editor = e_contact_list_editor_new (
 57 			shell, book, contact, is_new_contact, editable);
 58 	else
 59 		editor = e_contact_editor_new (
 60 			shell, book, contact, is_new_contact, editable);
 61 
 62 	eab_editor_show (editor);
 63 }
 64 
 65 static void
 66 popup_event (EBookShellView *book_shell_view,
 67              GdkEventButton *event)
 68 {
 69 	EShellView *shell_view;
 70 	const gchar *widget_path;
 71 
 72 	widget_path = "/contact-popup";
 73 	shell_view = E_SHELL_VIEW (book_shell_view);
 74 
 75 	e_shell_view_show_popup_menu (shell_view, widget_path, event);
 76 }
 77 
 78 static void
 79 book_shell_view_selection_change_foreach (gint row,
 80                                           EBookShellView *book_shell_view)
 81 {
 82 	EBookShellContent *book_shell_content;
 83 	EAddressbookView *view;
 84 	EAddressbookModel *model;
 85 	EContact *contact;
 86 
 87 	/* XXX A "foreach" function is kind of a silly way to retrieve
 88 	 *     the one and only selected contact, but this is the only
 89 	 *     means that ESelectionModel provides. */
 90 
 91 	book_shell_content = book_shell_view->priv->book_shell_content;
 92 	view = e_book_shell_content_get_current_view (book_shell_content);
 93 	model = e_addressbook_view_get_model (view);
 94 	contact = e_addressbook_model_get_contact (model, row);
 95 
 96 	e_book_shell_content_set_preview_contact (book_shell_content, contact);
 97 	book_shell_view->priv->preview_index = row;
 98 
 99 	if (contact)
100 		g_object_unref (contact);
101 }
102 
103 static void
104 selection_change (EBookShellView *book_shell_view,
105                   EAddressbookView *view)
106 {
107 	EBookShellContent *book_shell_content;
108 	EAddressbookView *current_view;
109 	ESelectionModel *selection_model;
110 	EShellView *shell_view;
111 	gint n_selected;
112 
113 	shell_view = E_SHELL_VIEW (book_shell_view);
114 	book_shell_content = book_shell_view->priv->book_shell_content;
115 	current_view = e_book_shell_content_get_current_view (book_shell_content);
116 
117 	if (view != current_view)
118 		return;
119 
120 	e_shell_view_update_actions (shell_view);
121 
122 	selection_model = e_addressbook_view_get_selection_model (view);
123 
124 	n_selected = (selection_model != NULL) ?
125 		e_selection_model_selected_count (selection_model) : 0;
126 
127 	if (n_selected == 1)
128 		e_selection_model_foreach (
129 			selection_model, (EForeachFunc)
130 			book_shell_view_selection_change_foreach,
131 			book_shell_view);
132 	else {
133 		e_book_shell_content_set_preview_contact (
134 			book_shell_content, NULL);
135 		book_shell_view->priv->preview_index = -1;
136 	}
137 }
138 
139 static void
140 contact_changed (EBookShellView *book_shell_view,
141                  gint index,
142                  EAddressbookModel *model)
143 {
144 	EBookShellContent *book_shell_content;
145 	EContact *contact;
146 
147 	g_return_if_fail (E_IS_SHELL_VIEW (book_shell_view));
148 	g_return_if_fail (book_shell_view->priv != NULL);
149 
150 	book_shell_content = book_shell_view->priv->book_shell_content;
151 
152 	contact = e_addressbook_model_contact_at (model, index);
153 
154 	if (book_shell_view->priv->preview_index != index)
155 		return;
156 
157 	/* Re-render the same contact. */
158 	e_book_shell_content_set_preview_contact (book_shell_content, contact);
159 }
160 
161 static void
162 contacts_removed (EBookShellView *book_shell_view,
163                   GArray *removed_indices,
164                   EAddressbookModel *model)
165 {
166 	EBookShellContent *book_shell_content;
167 	EContact *preview_contact;
168 
169 	g_return_if_fail (E_IS_SHELL_VIEW (book_shell_view));
170 	g_return_if_fail (book_shell_view->priv != NULL);
171 
172 	book_shell_content = book_shell_view->priv->book_shell_content;
173 
174 	preview_contact =
175 		e_book_shell_content_get_preview_contact (book_shell_content);
176 
177 	if (preview_contact == NULL)
178 		return;
179 
180 	/* Is the displayed contact still in the model? */
181 	if (e_addressbook_model_find (model, preview_contact) < 0)
182 		return;
183 
184 	/* If not, clear the contact display. */
185 	e_book_shell_content_set_preview_contact (book_shell_content, NULL);
186 	book_shell_view->priv->preview_index = -1;
187 }
188 
189 static void
190 model_query_changed_cb (EBookShellView *book_shell_view,
191                         GParamSpec *param,
192                         EAddressbookModel *model)
193 {
194 	EBookShellContent *book_shell_content;
195 	EAddressbookView *current_view;
196 
197 	book_shell_content = book_shell_view->priv->book_shell_content;
198 	current_view = e_book_shell_content_get_current_view (book_shell_content);
199 
200 	if (!current_view || e_addressbook_view_get_model (current_view) != model)
201 		return;
202 
203 	/* clear the contact preview when model's query changed */
204 	e_book_shell_content_set_preview_contact (book_shell_content, NULL);
205 	book_shell_view->priv->preview_index = -1;
206 }
207 
208 static void
209 book_shell_view_loaded_cb (GObject *source_object,
210                            GAsyncResult *result,
211                            gpointer user_data)
212 {
213 	ESource *source = E_SOURCE (source_object);
214 	EAddressbookView *view = user_data;
215 	EClient *client = NULL;
216 	EAddressbookModel *model;
217 	GError *error = NULL;
218 
219 	e_client_utils_open_new_finish (source, result, &client, &error);
220 
221 	/* Ignore cancellations. */
222 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
223 		g_warn_if_fail (client == NULL);
224 		g_error_free (error);
225 		goto exit;
226 
227 	} else if (error != NULL) {
228 		EShellView *shell_view;
229 		EShellContent *shell_content;
230 		EAlertSink *alert_sink;
231 
232 		g_warn_if_fail (client == NULL);
233 
234 		shell_view = e_addressbook_view_get_shell_view (view);
235 		shell_content = e_shell_view_get_shell_content (shell_view);
236 		alert_sink = E_ALERT_SINK (shell_content);
237 
238 		eab_load_error_dialog (NULL, alert_sink, source, error);
239 
240 		g_error_free (error);
241 		goto exit;
242 	}
243 
244 	g_return_if_fail (E_IS_CLIENT (client));
245 
246 	model = e_addressbook_view_get_model (view);
247 	e_addressbook_model_set_client (model, E_BOOK_CLIENT (client));
248 	e_addressbook_model_force_folder_bar_message (model);
249 
250 exit:
251 	g_object_unref (view);
252 }
253 
254 static void
255 book_shell_view_activate_selected_source (EBookShellView *book_shell_view,
256                                           ESourceSelector *selector)
257 {
258 	EShellView *shell_view;
259 	EBookShellContent *book_shell_content;
260 	EAddressbookView *view;
261 	EAddressbookModel *model;
262 	ESource *source;
263 	GalViewInstance *view_instance;
264 	GHashTable *hash_table;
265 	GtkWidget *widget;
266 	const gchar *uid;
267 	gchar *view_id;
268 
269 	shell_view = E_SHELL_VIEW (book_shell_view);
270 
271 	book_shell_content = book_shell_view->priv->book_shell_content;
272 	source = e_source_selector_ref_primary_selection (selector);
273 
274 	if (source == NULL)
275 		return;
276 
277 	uid = e_source_get_uid (source);
278 	hash_table = book_shell_view->priv->uid_to_view;
279 	widget = g_hash_table_lookup (hash_table, uid);
280 
281 	if (widget != NULL) {
282 		/* There is a view for this UID.  Make sure the view
283 		 * actually contains an EBook.  The absence of an EBook
284 		 * suggests a previous load failed, so try again. */
285 		view = E_ADDRESSBOOK_VIEW (widget);
286 		model = e_addressbook_view_get_model (view);
287 		source = e_addressbook_view_get_source (view);
288 
289 		if (e_addressbook_model_get_client (model) == NULL)
290 			/* XXX No way to cancel this? */
291 			e_client_utils_open_new (
292 				source, E_CLIENT_SOURCE_TYPE_CONTACTS,
293 				FALSE, NULL,
294 				book_shell_view_loaded_cb,
295 				g_object_ref (view));
296 
297 	} else {
298 		/* Create a view for this UID. */
299 		widget = e_addressbook_view_new (shell_view, source);
300 		gtk_widget_show (widget);
301 
302 		/* Default searching options for a new view. */
303 		e_addressbook_view_set_search (
304 			E_ADDRESSBOOK_VIEW (widget),
305 			CONTACT_FILTER_ANY_CATEGORY,
306 			CONTACT_SEARCH_NAME_CONTAINS,
307 			NULL, NULL);
308 
309 		e_book_shell_content_insert_view (
310 			book_shell_content,
311 			E_ADDRESSBOOK_VIEW (widget));
312 
313 		g_hash_table_insert (
314 			hash_table, g_strdup (uid),
315 			g_object_ref (widget));
316 
317 		g_signal_connect_object (
318 			widget, "open-contact", G_CALLBACK (open_contact),
319 			book_shell_view, G_CONNECT_SWAPPED);
320 
321 		g_signal_connect_object (
322 			widget, "popup-event", G_CALLBACK (popup_event),
323 			book_shell_view, G_CONNECT_SWAPPED);
324 
325 		g_signal_connect_object (
326 			widget, "command-state-change",
327 			G_CALLBACK (e_shell_view_update_actions),
328 			book_shell_view, G_CONNECT_SWAPPED);
329 
330 		g_signal_connect_object (
331 			widget, "selection-change", G_CALLBACK (selection_change),
332 			book_shell_view, G_CONNECT_SWAPPED);
333 
334 		view = E_ADDRESSBOOK_VIEW (widget);
335 		model = e_addressbook_view_get_model (view);
336 
337 		/* XXX No way to cancel this? */
338 		e_client_utils_open_new (
339 			source, E_CLIENT_SOURCE_TYPE_CONTACTS, FALSE, NULL,
340 			book_shell_view_loaded_cb, g_object_ref (view));
341 
342 		g_signal_connect_object (
343 			model, "contact-changed",
344 			G_CALLBACK (contact_changed),
345 			book_shell_view, G_CONNECT_SWAPPED);
346 
347 		g_signal_connect_object (
348 			model, "contacts-removed",
349 			G_CALLBACK (contacts_removed),
350 			book_shell_view, G_CONNECT_SWAPPED);
351 
352 		g_signal_connect_object (
353 			model, "notify::query",
354 			G_CALLBACK (model_query_changed_cb),
355 			book_shell_view, G_CONNECT_SWAPPED);
356 	}
357 
358 	e_book_shell_content_set_current_view (
359 		book_shell_content, E_ADDRESSBOOK_VIEW (widget));
360 
361 	/* XXX We have to keep the addressbook selector informed of the
362 	 *     current view so it can move contacts via drag-and-drop. */
363 	e_addressbook_selector_set_current_view (
364 		E_ADDRESSBOOK_SELECTOR (selector),
365 		E_ADDRESSBOOK_VIEW (widget));
366 
367 	view_instance = e_addressbook_view_get_view_instance (view);
368 
369 	/* This must come after e_book_shell_content_set_current_view()
370 	 * because book_shell_view_notify_view_id_cb() relies on it. */
371 	gal_view_instance_load (view_instance);
372 
373 	view_id = gal_view_instance_get_current_view_id (view_instance);
374 	e_shell_view_set_view_id (shell_view, view_id);
375 	g_free (view_id);
376 
377 	e_addressbook_model_force_folder_bar_message (model);
378 	selection_change (book_shell_view, view);
379 
380 	g_object_unref (source);
381 }
382 
383 static gboolean
384 book_shell_view_show_popup_menu (GdkEventButton *event,
385                                  EShellView *shell_view)
386 {
387 	const gchar *widget_path;
388 
389 	widget_path = "/address-book-popup";
390 	e_shell_view_show_popup_menu (shell_view, widget_path, event);
391 
392 	return TRUE;
393 }
394 
395 static gboolean
396 book_shell_view_selector_button_press_event_cb (EShellView *shell_view,
397                                                 GdkEventButton *event)
398 {
399 	/* XXX Use ESourceSelector's "popup-event" signal instead. */
400 
401 	if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
402 		return book_shell_view_show_popup_menu (event, shell_view);
403 
404 	return FALSE;
405 }
406 
407 static gboolean
408 book_shell_view_selector_popup_menu_cb (EShellView *shell_view)
409 {
410 	/* XXX Use ESourceSelector's "popup-event" signal instead. */
411 
412 	return book_shell_view_show_popup_menu (NULL, shell_view);
413 }
414 
415 static void
416 book_shell_view_source_removed_cb (ESourceRegistry *registry,
417                                    ESource *source,
418                                    EBookShellView *book_shell_view)
419 {
420 	EBookShellViewPrivate *priv = book_shell_view->priv;
421 	EBookShellContent *book_shell_content;
422 	EAddressbookView *view;
423 	const gchar *uid;
424 
425 	uid = e_source_get_uid (source);
426 
427 	book_shell_content = book_shell_view->priv->book_shell_content;
428 
429 	/* Remove the EAddressbookView for the deleted source. */
430 	view = g_hash_table_lookup (priv->uid_to_view, uid);
431 	if (view != NULL) {
432 		e_book_shell_content_remove_view (book_shell_content, view);
433 		g_hash_table_remove (priv->uid_to_view, uid);
434 	}
435 
436 	e_shell_view_update_actions (E_SHELL_VIEW (book_shell_view));
437 }
438 
439 static void
440 book_shell_view_load_view_collection (EShellViewClass *shell_view_class)
441 {
442 	GalViewCollection *collection;
443 	GalViewFactory *factory;
444 	ETableSpecification *spec;
445 	const gchar *base_dir;
446 	gchar *filename;
447 
448 	collection = shell_view_class->view_collection;
449 
450 	base_dir = EVOLUTION_ETSPECDIR;
451 	spec = e_table_specification_new ();
452 	filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL);
453 	if (!e_table_specification_load_from_file (spec, filename))
454 		g_critical (
455 			"Unable to load ETable specification file "
456 			"for address book");
457 	g_free (filename);
458 
459 	factory = gal_view_factory_etable_new (spec);
460 	gal_view_collection_add_factory (collection, factory);
461 	g_object_unref (factory);
462 	g_object_unref (spec);
463 
464 	factory = gal_view_factory_minicard_new ();
465 	gal_view_collection_add_factory (collection, factory);
466 	g_object_unref (factory);
467 
468 	gal_view_collection_load (collection);
469 }
470 
471 static void
472 book_shell_view_notify_view_id_cb (EBookShellView *book_shell_view)
473 {
474 	EBookShellContent *book_shell_content;
475 	EAddressbookView *address_view;
476 	GalViewInstance *view_instance;
477 	const gchar *view_id;
478 
479 	book_shell_content = book_shell_view->priv->book_shell_content;
480 	address_view = e_book_shell_content_get_current_view (book_shell_content);
481 	view_instance = e_addressbook_view_get_view_instance (address_view);
482 	view_id = e_shell_view_get_view_id (E_SHELL_VIEW (book_shell_view));
483 
484 	/* A NULL view ID implies we're in a custom view.  But you can
485 	 * only get to a custom view via the "Define Views" dialog, which
486 	 * would have already modified the view instance appropriately.
487 	 * Furthermore, there's no way to refer to a custom view by ID
488 	 * anyway, since custom views have no IDs. */
489 	if (view_id == NULL)
490 		return;
491 
492 	gal_view_instance_set_current_view_id (view_instance, view_id);
493 }
494 
495 void
496 e_book_shell_view_private_init (EBookShellView *book_shell_view,
497                                 EShellViewClass *shell_view_class)
498 {
499 	EBookShellViewPrivate *priv = book_shell_view->priv;
500 	GHashTable *uid_to_view;
501 
502 	uid_to_view = g_hash_table_new_full (
503 		g_str_hash, g_str_equal,
504 		(GDestroyNotify) g_free,
505 		(GDestroyNotify) g_object_unref);
506 
507 	priv->uid_to_view = uid_to_view;
508 	priv->preview_index = -1;
509 
510 	if (!gal_view_collection_loaded (shell_view_class->view_collection))
511 		book_shell_view_load_view_collection (shell_view_class);
512 
513 	g_signal_connect (
514 		book_shell_view, "notify::view-id",
515 		G_CALLBACK (book_shell_view_notify_view_id_cb), NULL);
516 }
517 
518 void
519 e_book_shell_view_private_constructed (EBookShellView *book_shell_view)
520 {
521 	EBookShellViewPrivate *priv = book_shell_view->priv;
522 	EShell *shell;
523 	EShellView *shell_view;
524 	EShellWindow *shell_window;
525 	EShellContent *shell_content;
526 	EShellSidebar *shell_sidebar;
527 	EShellBackend *shell_backend;
528 	ESourceSelector *selector;
529 
530 	shell_view = E_SHELL_VIEW (book_shell_view);
531 	shell_backend = e_shell_view_get_shell_backend (shell_view);
532 	shell_content = e_shell_view_get_shell_content (shell_view);
533 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
534 	shell_window = e_shell_view_get_shell_window (shell_view);
535 	shell = e_shell_window_get_shell (shell_window);
536 
537 	e_shell_window_add_action_group (shell_window, "contacts");
538 	e_shell_window_add_action_group (shell_window, "contacts-filter");
539 
540 	/* Cache these to avoid lots of awkward casting. */
541 	priv->book_shell_backend = g_object_ref (shell_backend);
542 	priv->book_shell_content = g_object_ref (shell_content);
543 	priv->book_shell_sidebar = g_object_ref (shell_sidebar);
544 
545 	/* Keep our own reference to this so we can
546 	 * disconnect our signal handler in dispose(). */
547 	priv->registry = g_object_ref (e_shell_get_registry (shell));
548 
549 	selector = e_book_shell_sidebar_get_selector (
550 		E_BOOK_SHELL_SIDEBAR (shell_sidebar));
551 
552 	g_signal_connect (
553 		priv->registry, "source-removed",
554 		G_CALLBACK (book_shell_view_source_removed_cb),
555 		book_shell_view);
556 
557 	g_signal_connect_object (
558 		selector, "button-press-event",
559 		G_CALLBACK (book_shell_view_selector_button_press_event_cb),
560 		book_shell_view, G_CONNECT_SWAPPED);
561 
562 	g_signal_connect_object (
563 		selector, "popup-menu",
564 		G_CALLBACK (book_shell_view_selector_popup_menu_cb),
565 		book_shell_view, G_CONNECT_SWAPPED);
566 
567 	g_signal_connect_object (
568 		selector, "primary-selection-changed",
569 		G_CALLBACK (book_shell_view_activate_selected_source),
570 		book_shell_view, G_CONNECT_SWAPPED);
571 
572 	e_categories_add_change_hook (
573 		(GHookFunc) e_book_shell_view_update_search_filter,
574 		book_shell_view);
575 
576 	e_book_shell_view_actions_init (book_shell_view);
577 	book_shell_view_activate_selected_source (book_shell_view, selector);
578 	e_book_shell_view_update_search_filter (book_shell_view);
579 }
580 
581 void
582 e_book_shell_view_private_dispose (EBookShellView *book_shell_view)
583 {
584 	EBookShellViewPrivate *priv = book_shell_view->priv;
585 
586 	DISPOSE (priv->book_shell_backend);
587 	DISPOSE (priv->book_shell_content);
588 	DISPOSE (priv->book_shell_sidebar);
589 
590 	if (priv->registry != NULL) {
591 		g_signal_handlers_disconnect_matched (
592 			priv->registry, G_SIGNAL_MATCH_DATA,
593 			0, 0, NULL, NULL, book_shell_view);
594 		g_object_unref (priv->registry);
595 		priv->registry = NULL;
596 	}
597 
598 	g_hash_table_remove_all (priv->uid_to_view);
599 }
600 
601 void
602 e_book_shell_view_private_finalize (EBookShellView *book_shell_view)
603 {
604 	EBookShellViewPrivate *priv = book_shell_view->priv;
605 
606 	g_hash_table_destroy (priv->uid_to_view);
607 }