No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Chris Toshok <toshok@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "e-contact-list-editor.h"
28 #include <e-util/e-util-private.h>
29 #include <libevolution-utils/e-alert-dialog.h>
30 #include <e-util/e-selection.h>
31 #include "shell/e-shell.h"
32
33 #include <string.h>
34
35 #include <gtk/gtk.h>
36 #include <glib/gi18n.h>
37 #include <gdk/gdkkeysyms.h>
38
39 #include <camel/camel.h>
40
41 #include "e-util/e-util.h"
42 #include "addressbook/gui/widgets/eab-gui-util.h"
43 #include "addressbook/util/eab-book-util.h"
44
45 #include "eab-editor.h"
46 #include "e-contact-editor.h"
47 #include "e-contact-list-model.h"
48 #include "eab-contact-merging.h"
49
50 #define E_CONTACT_LIST_EDITOR_GET_PRIVATE(obj) \
51 (G_TYPE_INSTANCE_GET_PRIVATE \
52 ((obj), E_TYPE_CONTACT_LIST_EDITOR, EContactListEditorPrivate))
53
54 #define E_CONTACT_LIST_EDITOR_GET_PRIVATE(obj) \
55 (G_TYPE_INSTANCE_GET_PRIVATE \
56 ((obj), E_TYPE_CONTACT_LIST_EDITOR, EContactListEditorPrivate))
57
58 #define CONTACT_LIST_EDITOR_WIDGET(editor, name) \
59 (e_builder_get_widget \
60 (E_CONTACT_LIST_EDITOR_GET_PRIVATE (editor)->builder, name))
61
62 /* More macros, less typos. */
63 #define CONTACT_LIST_EDITOR_WIDGET_ADD_BUTTON(editor) \
64 CONTACT_LIST_EDITOR_WIDGET ((editor), "add-button")
65 #define CONTACT_LIST_EDITOR_WIDGET_CHECK_BUTTON(editor) \
66 CONTACT_LIST_EDITOR_WIDGET ((editor), "check-button")
67 #define CONTACT_LIST_EDITOR_WIDGET_DIALOG(editor) \
68 CONTACT_LIST_EDITOR_WIDGET ((editor), "dialog")
69 #define CONTACT_LIST_EDITOR_WIDGET_EMAIL_ENTRY(editor) \
70 E_CONTACT_LIST_EDITOR_GET_PRIVATE (editor)->email_entry
71 #define CONTACT_LIST_EDITOR_WIDGET_LIST_NAME_ENTRY(editor) \
72 CONTACT_LIST_EDITOR_WIDGET ((editor), "list-name-entry")
73 #define CONTACT_LIST_EDITOR_WIDGET_MEMBERS_VBOX(editor) \
74 CONTACT_LIST_EDITOR_WIDGET ((editor), "members-vbox")
75 #define CONTACT_LIST_EDITOR_WIDGET_OK_BUTTON(editor) \
76 CONTACT_LIST_EDITOR_WIDGET ((editor), "ok-button")
77 #define CONTACT_LIST_EDITOR_WIDGET_REMOVE_BUTTON(editor) \
78 CONTACT_LIST_EDITOR_WIDGET ((editor), "remove-button")
79 #define CONTACT_LIST_EDITOR_WIDGET_SOURCE_MENU(editor) \
80 CONTACT_LIST_EDITOR_WIDGET ((editor), "source-combo-box")
81 #define CONTACT_LIST_EDITOR_WIDGET_TREE_VIEW(editor) \
82 CONTACT_LIST_EDITOR_WIDGET ((editor), "tree-view")
83 #define CONTACT_LIST_EDITOR_WIDGET_TOP_BUTTON(editor) \
84 CONTACT_LIST_EDITOR_WIDGET ((editor), "top-button")
85 #define CONTACT_LIST_EDITOR_WIDGET_UP_BUTTON(editor) \
86 CONTACT_LIST_EDITOR_WIDGET ((editor), "up-button")
87 #define CONTACT_LIST_EDITOR_WIDGET_DOWN_BUTTON(editor) \
88 CONTACT_LIST_EDITOR_WIDGET ((editor), "down-button")
89 #define CONTACT_LIST_EDITOR_WIDGET_BOTTOM_BUTTON(editor) \
90 CONTACT_LIST_EDITOR_WIDGET ((editor), "bottom-button")
91
92 /* Shorthand, requires a variable named "editor". */
93 #define WIDGET(name) (CONTACT_LIST_EDITOR_WIDGET_##name (editor))
94
95 #define TOPLEVEL_KEY (g_type_name (E_TYPE_CONTACT_LIST_EDITOR))
96
97 enum {
98 PROP_0,
99 PROP_CLIENT,
100 PROP_CONTACT,
101 PROP_IS_NEW_LIST,
102 PROP_EDITABLE
103 };
104
105 typedef struct {
106 EContactListEditor *editor;
107 gboolean should_close;
108 } EditorCloseStruct;
109
110 struct _EContactListEditorPrivate {
111
112 EBookClient *book_client;
113 EContact *contact;
114
115 GtkBuilder *builder;
116 GtkTreeModel *model;
117 ENameSelector *name_selector;
118
119 /* This is kept here because the builder has an old widget
120 * which was changed with this one. */
121 ENameSelectorEntry *email_entry;
122
123 /* Whether we are editing a new contact or an existing one. */
124 guint is_new_list : 1;
125
126 /* Whether the contact has been changed since bringing up the
127 * contact editor. */
128 guint changed : 1;
129
130 /* Whether the contact editor will accept modifications. */
131 guint editable : 1;
132
133 /* Whether the target book accepts storing of contact lists. */
134 guint allows_contact_lists : 1;
135
136 /* Whether an async wombat call is in progress. */
137 guint in_async_call : 1;
138 };
139
140 G_DEFINE_TYPE (EContactListEditor, e_contact_list_editor, EAB_TYPE_EDITOR)
141
142 static EContactListEditor *
143 contact_list_editor_extract (GtkWidget *widget)
144 {
145 GtkWidget *toplevel;
146
147 toplevel = gtk_widget_get_toplevel (widget);
148 return g_object_get_data (G_OBJECT (toplevel), TOPLEVEL_KEY);
149 }
150
151 static void
152 contact_list_editor_scroll_to_end (EContactListEditor *editor)
153 {
154 GtkTreeView *view;
155 GtkTreePath *path;
156 gint n_rows;
157
158 view = GTK_TREE_VIEW (WIDGET (TREE_VIEW));
159 n_rows = gtk_tree_model_iter_n_children (editor->priv->model, NULL);
160
161 path = gtk_tree_path_new_from_indices (n_rows - 1, -1);
162 gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0., 0.);
163 gtk_tree_view_set_cursor (view, path, NULL, FALSE);
164 gtk_tree_path_free (path);
165 }
166
167 static void
168 contact_list_editor_update (EContactListEditor *editor)
169 {
170 EContactListEditorPrivate *priv = editor->priv;
171
172 gtk_widget_set_sensitive (
173 WIDGET (OK_BUTTON),
174 eab_editor_is_valid (EAB_EDITOR (editor)) &&
175 priv->allows_contact_lists);
176
177 gtk_widget_set_sensitive (
178 WIDGET (SOURCE_MENU), priv->is_new_list);
179 }
180
181 static void
182 contact_list_editor_notify_cb (EContactListEditor *editor,
183 GParamSpec *pspec)
184 {
185 EContactListEditorPrivate *priv = editor->priv;
186 gboolean sensitive;
187
188 sensitive = priv->editable && priv->allows_contact_lists;
189
190 gtk_widget_set_sensitive (WIDGET (LIST_NAME_ENTRY), sensitive);
191 gtk_widget_set_sensitive (WIDGET (MEMBERS_VBOX), sensitive);
192 }
193
194 static gboolean
195 contact_list_editor_add_destination (GtkWidget *widget,
196 EDestination *dest)
197 {
198 EContactListEditor *editor = contact_list_editor_extract (widget);
199 EContactListModel *model = E_CONTACT_LIST_MODEL (editor->priv->model);
200 GtkTreeView *treeview = GTK_TREE_VIEW (WIDGET (TREE_VIEW));
201 GtkTreePath *path;
202 gboolean ignore_conflicts = TRUE;
203
204 if (e_destination_is_evolution_list (dest)) {
205 const gchar *id = e_destination_get_contact_uid (dest);
206 const gchar *name = e_destination_get_name (dest);
207
208 if (e_contact_list_model_has_uid (model, id)) {
209 gint response;
210
211 response = e_alert_run_dialog_for_args (
212 GTK_WINDOW (WIDGET (DIALOG)),
213 "addressbook:ask-list-add-list-exists",
214 name, NULL);
215 if (response != GTK_RESPONSE_YES)
216 return FALSE;
217 } else {
218 const GList *l_dests, *l_dest;
219 gint reply;
220
221 /* Check the new list mail-by-mail for conflicts and
222 * eventually ask user what to do with all conflicts. */
223 l_dests = e_destination_list_get_dests (dest);
224 for (l_dest = l_dests; l_dest; l_dest = l_dest->next) {
225 if (e_contact_list_model_has_email (model, e_destination_get_email (l_dest->data))) {
226 reply = e_alert_run_dialog_for_args (
227 GTK_WINDOW (WIDGET (DIALOG)),
228 "addressbook:ask-list-add-some-mails-exist", NULL);
229 if (reply == GTK_RESPONSE_YES) {
230 ignore_conflicts = TRUE;
231 break;
232 } else if (reply == GTK_RESPONSE_NO) {
233 ignore_conflicts = FALSE;
234 break;
235 } else {
236 return FALSE;
237 }
238 }
239 }
240 }
241
242 } else {
243 const gchar *email = e_destination_get_email (dest);
244 const gchar *tag = "addressbook:ask-list-add-exists";
245
246 if (e_contact_list_model_has_email (model, email) &&
247 (e_alert_run_dialog_for_args (GTK_WINDOW (WIDGET (DIALOG)), tag, email, NULL) != GTK_RESPONSE_YES))
248 return FALSE;
249 }
250
251 /* always add to the root level */
252 path = e_contact_list_model_add_destination (
253 model, dest, NULL, ignore_conflicts);
254 if (path) {
255 contact_list_editor_scroll_to_end (editor);
256 gtk_tree_view_expand_to_path (treeview, path);
257 gtk_tree_path_free (path);
258
259 return TRUE;
260 }
261
262 return FALSE;
263 }
264
265 static void
266 contact_list_editor_add_email (EContactListEditor *editor,
267 const gchar *email)
268 {
269 CamelInternetAddress *addr;
270 EContactListEditorPrivate *priv = editor->priv;
271 EDestination *dest = NULL;
272 gint addr_length;
273
274 addr = camel_internet_address_new ();
275 addr_length = camel_address_unformat (CAMEL_ADDRESS (addr), email);
276 if (addr_length >= 1) {
277 const gchar *name, *mail;
278 gint ii;
279
280 for (ii = 0; ii < addr_length; ii++) {
281 camel_internet_address_get (addr, ii, &name, &mail);
282
283 if (name || mail) {
284 dest = e_destination_new ();
285 if (mail)
286 e_destination_set_email (dest, mail);
287 if (name)
288 e_destination_set_name (dest, name);
289
290 priv->changed = contact_list_editor_add_destination (WIDGET (DIALOG), dest)
291 || priv->changed;
292 }
293 }
294 } else {
295 dest = e_destination_new ();
296 e_destination_set_email (dest, email);
297
298 priv->changed = contact_list_editor_add_destination (WIDGET (DIALOG), dest)
299 || priv->changed;
300 }
301 g_object_unref (addr);
302
303 contact_list_editor_update (editor);
304 }
305
306 static void
307 contact_list_editor_book_loaded_cb (GObject *source_object,
308 GAsyncResult *result,
309 gpointer user_data)
310 {
311 ESource *source = E_SOURCE (source_object);
312 EContactListEditor *editor = user_data;
313 EContactListEditorPrivate *priv = editor->priv;
314 EContactStore *contact_store;
315 ENameSelectorEntry *entry;
316 EClient *client = NULL;
317 EBookClient *book_client;
318 GError *error = NULL;
319
320 e_client_utils_open_new_finish (source, result, &client, &error);
321
322 if (error != NULL) {
323 GtkWindow *parent;
324
325 g_warn_if_fail (client == NULL);
326
327 parent = eab_editor_get_window (EAB_EDITOR (editor));
328 eab_load_error_dialog (GTK_WIDGET (parent), NULL, source, error);
329
330 e_source_combo_box_set_active (
331 E_SOURCE_COMBO_BOX (WIDGET (SOURCE_MENU)),
332 e_client_get_source (E_CLIENT (priv->book_client)));
333
334 g_error_free (error);
335 goto exit;
336 }
337
338 g_return_if_fail (E_IS_CLIENT (client));
339
340 book_client = E_BOOK_CLIENT (client);
341
342 entry = E_NAME_SELECTOR_ENTRY (WIDGET (EMAIL_ENTRY));
343 contact_store = e_name_selector_entry_peek_contact_store (entry);
344 e_contact_store_add_client (contact_store, book_client);
345 e_contact_list_editor_set_client (editor, book_client);
346
347 g_object_unref (client);
348
349 exit:
350 g_object_unref (editor);
351 }
352
353 static void
354 contact_list_editor_list_added_cb (EBookClient *book_client,
355 const GError *error,
356 const gchar *id,
357 gpointer closure)
358 {
359 EditorCloseStruct *ecs = closure;
360 EContactListEditor *editor = ecs->editor;
361 EContactListEditorPrivate *priv = editor->priv;
362 gboolean should_close = ecs->should_close;
363
364 gtk_widget_set_sensitive (WIDGET (DIALOG), TRUE);
365 priv->in_async_call = FALSE;
366
367 e_contact_set (priv->contact, E_CONTACT_UID, (gchar *) id);
368
369 eab_editor_contact_added (
370 EAB_EDITOR (editor), error, priv->contact);
371
372 if (!error) {
373 priv->is_new_list = FALSE;
374
375 if (should_close)
376 eab_editor_close (EAB_EDITOR (editor));
377 else
378 contact_list_editor_update (editor);
379 }
380
381 g_object_unref (editor);
382 g_free (ecs);
383 }
384
385 static void
386 contact_list_editor_list_modified_cb (EBookClient *book_client,
387 const GError *error,
388 gpointer closure)
389 {
390 EditorCloseStruct *ecs = closure;
391 EContactListEditor *editor = ecs->editor;
392 EContactListEditorPrivate *priv = editor->priv;
393 gboolean should_close = ecs->should_close;
394
395 gtk_widget_set_sensitive (WIDGET (DIALOG), TRUE);
396 priv->in_async_call = FALSE;
397
398 eab_editor_contact_modified (
399 EAB_EDITOR (editor), error, priv->contact);
400
401 if (!error) {
402 if (should_close)
403 eab_editor_close (EAB_EDITOR (editor));
404 }
405
406 g_object_unref (editor);
407 g_free (ecs);
408 }
409
410 static void
411 contact_list_editor_render_destination (GtkTreeViewColumn *column,
412 GtkCellRenderer *renderer,
413 GtkTreeModel *model,
414 GtkTreeIter *iter)
415 {
416 /* XXX Would be nice if EDestination had a text property
417 * that we could just bind the GtkCellRenderer to. */
418
419 EDestination *destination = NULL;
420 gchar *name = NULL, *email = NULL;
421 const gchar *textrep;
422 gchar *out;
423
424 g_return_if_fail (GTK_IS_TREE_MODEL (model));
425
426 gtk_tree_model_get (model, iter, 0, &destination, -1);
427 g_return_if_fail (destination && E_IS_DESTINATION (destination));
428
429 textrep = e_destination_get_textrep (destination, TRUE);
430 if (eab_parse_qp_email (textrep, &name, &email)) {
431 if (e_destination_is_evolution_list (destination)) {
432 g_object_set (renderer, "text", name, NULL);
433 } else {
434 out = g_strdup_printf ("%s <%s>", name, email);
435 g_object_set (renderer, "text", out, NULL);
436 g_free (out);
437 }
438 g_free (email);
439 g_free (name);
440 } else {
441 g_object_set (renderer, "text", textrep, NULL);
442 }
443
444 g_object_unref (destination);
445 }
446
447 static void
448 contact_list_editor_selection_changed_cb (GtkTreeSelection *selection,
449 gpointer user_data)
450 {
451 EContactListEditor *editor = user_data;
452 GtkTreeModel *model;
453 GtkTreeIter iter;
454 GtkTreePath *first_item;
455 GList *selected;
456
457 model = gtk_tree_view_get_model (GTK_TREE_VIEW (WIDGET (TREE_VIEW)));
458
459 /* Is selected anything at all? */
460 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
461 gtk_widget_set_sensitive (WIDGET (TOP_BUTTON), FALSE);
462 gtk_widget_set_sensitive (WIDGET (UP_BUTTON), FALSE);
463 gtk_widget_set_sensitive (WIDGET (DOWN_BUTTON), FALSE);
464 gtk_widget_set_sensitive (WIDGET (BOTTOM_BUTTON), FALSE);
465 gtk_widget_set_sensitive (WIDGET (REMOVE_BUTTON), FALSE);
466 return;
467 }
468
469 gtk_widget_set_sensitive (WIDGET (REMOVE_BUTTON), TRUE);
470
471 /* Item before selected item exists => enable Top/Up buttons */
472 selected = gtk_tree_selection_get_selected_rows (selection, &model);
473
474 /* Don't update path in the list! */
475 first_item = gtk_tree_path_copy (selected->data);
476 if (gtk_tree_path_prev (first_item)) {
477 gtk_widget_set_sensitive (WIDGET (TOP_BUTTON), TRUE);
478 gtk_widget_set_sensitive (WIDGET (UP_BUTTON), TRUE);
479 } else {
480 gtk_widget_set_sensitive (WIDGET (TOP_BUTTON), FALSE);
481 gtk_widget_set_sensitive (WIDGET (UP_BUTTON), FALSE);
482 }
483
484 gtk_tree_model_get_iter (model, &iter, g_list_last (selected)->data);
485 /* Item below last selected exists => enable Down/Bottom buttons */
486 if (gtk_tree_model_iter_next (model, &iter)) {
487 gtk_widget_set_sensitive (WIDGET (DOWN_BUTTON), TRUE);
488 gtk_widget_set_sensitive (WIDGET (BOTTOM_BUTTON), TRUE);
489 } else {
490 gtk_widget_set_sensitive (WIDGET (DOWN_BUTTON), FALSE);
491 gtk_widget_set_sensitive (WIDGET (BOTTOM_BUTTON), FALSE);
492 }
493
494 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
495 g_list_free (selected);
496 gtk_tree_path_free (first_item);
497 }
498
499 static void
500 contact_list_editor_add_from_email_entry (EContactListEditor *editor,
501 ENameSelectorEntry *entry)
502 {
503 EDestinationStore *store;
504 GList *dests, *diter;
505 gboolean added = FALSE;
506
507 g_return_if_fail (E_IS_CONTACT_LIST_EDITOR (editor));
508 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (entry));
509
510 store = e_name_selector_entry_peek_destination_store (entry);
511 dests = e_destination_store_list_destinations (store);
512
513 for (diter = dests; diter; diter = g_list_next (diter)) {
514 EDestination *dest = diter->data;
515
516 if (dest && e_destination_get_address (dest)) {
517 editor->priv->changed = contact_list_editor_add_destination (WIDGET (DIALOG), dest)
518 || editor->priv->changed;
519 added = TRUE;
520 }
521 }
522
523 g_list_free (dests);
524
525 if (!added)
526 contact_list_editor_add_email (editor, gtk_entry_get_text (GTK_ENTRY (entry)));
527 }
528
529 /*********************** Autoconnected Signal Handlers ***********************/
530
531 void
532 contact_list_editor_add_button_clicked_cb (GtkWidget *widget);
533
534 void
535 contact_list_editor_add_button_clicked_cb (GtkWidget *widget)
536 {
537 EContactListEditor *editor;
538
539 editor = contact_list_editor_extract (widget);
540
541 contact_list_editor_add_from_email_entry (
542 editor,
543 E_NAME_SELECTOR_ENTRY (WIDGET (EMAIL_ENTRY)));
544 gtk_entry_set_text (GTK_ENTRY (WIDGET (EMAIL_ENTRY)), "");
545 }
546
547 void
548 contact_list_editor_cancel_button_clicked_cb (GtkWidget *widget);
549
550 void
551 contact_list_editor_cancel_button_clicked_cb (GtkWidget *widget)
552 {
553 EContactListEditor *editor;
554 GtkWindow *window;
555
556 editor = contact_list_editor_extract (widget);
557 window = GTK_WINDOW (WIDGET (DIALOG));
558
559 eab_editor_prompt_to_save_changes (EAB_EDITOR (editor), window);
560 }
561
562 void
563 contact_list_editor_check_button_toggled_cb (GtkWidget *widget);
564
565 void
566 contact_list_editor_check_button_toggled_cb (GtkWidget *widget)
567 {
568 EContactListEditor *editor;
569
570 editor = contact_list_editor_extract (widget);
571
572 editor->priv->changed = TRUE;
573 contact_list_editor_update (editor);
574 }
575
576 gboolean
577 contact_list_editor_delete_event_cb (GtkWidget *widget,
578 GdkEvent *event);
579
580 gboolean
581 contact_list_editor_delete_event_cb (GtkWidget *widget,
582 GdkEvent *event)
583 {
584 EContactListEditor *editor;
585 GtkWindow *window;
586
587 editor = contact_list_editor_extract (widget);
588 window = GTK_WINDOW (WIDGET (DIALOG));
589
590 /* If we're in an async call, don't allow the dialog to close. */
591 if (!editor->priv->in_async_call)
592 eab_editor_prompt_to_save_changes (
593 EAB_EDITOR (editor), window);
594
595 return TRUE;
596 }
597
598 void
599 contact_list_editor_drag_data_received_cb (GtkWidget *widget,
600 GdkDragContext *context,
601 gint x,
602 gint y,
603 GtkSelectionData *selection_data,
604 guint info,
605 guint time);
606
607 void
608 contact_list_editor_drag_data_received_cb (GtkWidget *widget,
609 GdkDragContext *context,
610 gint x,
611 gint y,
612 GtkSelectionData *selection_data,
613 guint info,
614 guint time)
615 {
616 CamelInternetAddress *address;
617 EContactListEditor *editor;
618 gboolean changed = FALSE;
619 gboolean handled = FALSE;
620 const guchar *data;
621 GSList *list, *iter;
622 GdkAtom target;
623 gint n_addresses = 0;
624 gchar *text;
625
626 editor = contact_list_editor_extract (widget);
627
628 target = gtk_selection_data_get_target (selection_data);
629
630 /* Sanity check the selection target. */
631
632 if (gtk_targets_include_text (&target, 1))
633 goto handle_text;
634
635 if (!e_targets_include_directory (&target, 1))
636 goto exit;
637
638 data = gtk_selection_data_get_data (selection_data);
639 list = eab_contact_list_from_string ((gchar *) data);
640
641 if (list != NULL)
642 handled = TRUE;
643
644 for (iter = list; iter != NULL; iter = iter->next) {
645 EContact *contact = iter->data;
646 EDestination *dest;
647
648 dest = e_destination_new ();
649 e_destination_set_contact (dest, contact, 0);
650
651 changed = contact_list_editor_add_destination (widget, dest) || changed;
652
653 g_object_unref (dest);
654 }
655
656 e_client_util_free_object_slist (list);
657
658 contact_list_editor_scroll_to_end (editor);
659
660 if (changed) {
661 editor->priv->changed = TRUE;
662 contact_list_editor_update (editor);
663 }
664
665 goto exit;
666
667 handle_text:
668
669 address = camel_internet_address_new ();
670 text = (gchar *) gtk_selection_data_get_text (selection_data);
671
672 /* See if Camel can parse a valid email address from the text. */
673 if (text != NULL && *text != '\0') {
674 camel_url_decode (text);
675 if (g_ascii_strncasecmp (text, "mailto:", 7) == 0)
676 n_addresses = camel_address_decode (
677 CAMEL_ADDRESS (address), text + 7);
678 else
679 n_addresses = camel_address_decode (
680 CAMEL_ADDRESS (address), text);
681 }
682
683 if (n_addresses == 1) {
684 g_free (text);
685
686 text = camel_address_format (CAMEL_ADDRESS (address));
687 contact_list_editor_add_email (editor, text);
688
689 contact_list_editor_scroll_to_end (editor);
690 editor->priv->changed = TRUE;
691
692 contact_list_editor_update (editor);
693 handled = TRUE;
694 }
695
696 g_free (text);
697
698 exit:
699 gtk_drag_finish (context, handled, FALSE, time);
700 }
701
702 void
703 contact_list_editor_email_entry_activate_cb (GtkWidget *widget);
704
705 void
706 contact_list_editor_email_entry_activate_cb (GtkWidget *widget)
707 {
708 EContactListEditor *editor;
709 GtkEntry *entry;
710
711 editor = contact_list_editor_extract (widget);
712 entry = GTK_ENTRY (WIDGET (EMAIL_ENTRY));
713
714 contact_list_editor_add_from_email_entry (
715 editor,
716 E_NAME_SELECTOR_ENTRY (entry));
717 gtk_entry_set_text (entry, "");
718 }
719
720 void
721 contact_list_editor_email_entry_changed_cb (GtkWidget *widget);
722
723 void
724 contact_list_editor_email_entry_changed_cb (GtkWidget *widget)
725 {
726 EContactListEditor *editor;
727 const gchar *text;
728 gboolean sensitive;
729
730 editor = contact_list_editor_extract (widget);
731 text = gtk_entry_get_text (GTK_ENTRY (widget));
732
733 sensitive = (text != NULL && *text != '\0');
734 gtk_widget_set_sensitive (WIDGET (ADD_BUTTON), sensitive);
735 }
736
737 gboolean
738 contact_list_editor_email_entry_key_press_event_cb (GtkWidget *widget,
739 GdkEventKey *event);
740
741 gboolean
742 contact_list_editor_email_entry_key_press_event_cb (GtkWidget *widget,
743 GdkEventKey *event)
744 {
745 EContactListEditor *editor;
746 gboolean can_comma = FALSE;
747
748 editor = contact_list_editor_extract (widget);
749
750 if (event->keyval == GDK_KEY_comma) {
751 GtkEntry *entry;
752 gint cpos = -1;
753
754 entry = GTK_ENTRY (WIDGET (EMAIL_ENTRY));
755 g_object_get (entry, "cursor-position", &cpos, NULL);
756
757 /* not the first letter */
758 if (cpos > 0) {
759 const gchar *text;
760 gint quotes = 0, i;
761
762 text = gtk_entry_get_text (entry);
763
764 for (i = 0; text && text[i] && i < cpos; i++) {
765 if (text[i] == '\"')
766 quotes++;
767 }
768
769 /* even count of quotes */
770 can_comma = (quotes & 1) == 0;
771 }
772 }
773
774 if (can_comma || event->keyval == GDK_KEY_Return) {
775 g_signal_emit_by_name (WIDGET (EMAIL_ENTRY), "activate", 0);
776
777 return TRUE;
778 }
779
780 return FALSE;
781 }
782
783 void
784 contact_list_editor_list_name_entry_changed_cb (GtkWidget *widget);
785
786 void
787 contact_list_editor_list_name_entry_changed_cb (GtkWidget *widget)
788 {
789 EContactListEditor *editor;
790 const gchar *title;
791
792 editor = contact_list_editor_extract (widget);
793
794 title = gtk_entry_get_text (GTK_ENTRY (widget));
795
796 if (title == NULL || *title == '\0')
797 title = _("Contact List Editor");
798
799 gtk_window_set_title (GTK_WINDOW (WIDGET (DIALOG)), title);
800
801 editor->priv->changed = TRUE;
802 contact_list_editor_update (editor);
803 }
804
805 void
806 contact_list_editor_ok_button_clicked_cb (GtkWidget *widget);
807
808 void
809 contact_list_editor_ok_button_clicked_cb (GtkWidget *widget)
810 {
811 EContactListEditor *editor;
812 gboolean save_contact;
813
814 editor = contact_list_editor_extract (widget);
815
816 save_contact =
817 editor->priv->editable &&
818 editor->priv->allows_contact_lists;
819
820 if (save_contact)
821 eab_editor_save_contact (EAB_EDITOR (editor), TRUE);
822 else
823 eab_editor_close (EAB_EDITOR (editor));
824 }
825
826 void
827 contact_list_editor_remove_button_clicked_cb (GtkWidget *widget);
828
829 void
830 contact_list_editor_remove_button_clicked_cb (GtkWidget *widget)
831 {
832 EContactListEditor *editor;
833 GtkTreeSelection *selection;
834 GtkTreeRowReference *new_selection = NULL;
835 GtkTreeModel *model;
836 GtkTreeView *view;
837 GtkTreePath *path;
838 GList *list, *liter;
839
840 editor = contact_list_editor_extract (widget);
841
842 view = GTK_TREE_VIEW (WIDGET (TREE_VIEW));
843 selection = gtk_tree_view_get_selection (view);
844 list = gtk_tree_selection_get_selected_rows (selection, &model);
845
846 /* Convert the GtkTreePaths to GtkTreeRowReferences. */
847 for (liter = list; liter != NULL; liter = liter->next) {
848 path = liter->data;
849
850 liter->data = gtk_tree_row_reference_new (model, path);
851
852 /* Store reference to next item below current selection */
853 if (!liter->next) {
854 gtk_tree_path_next (path);
855 new_selection = gtk_tree_row_reference_new (model, path);
856 }
857
858 gtk_tree_path_free (path);
859 }
860
861 /* Delete each row in the list. */
862 for (liter = list; liter != NULL; liter = liter->next) {
863 GtkTreeRowReference *reference = liter->data;
864 GtkTreeIter iter;
865 gboolean valid;
866
867 path = gtk_tree_row_reference_get_path (reference);
868 valid = gtk_tree_model_get_iter (model, &iter, path);
869 gtk_tree_path_free (path);
870 g_assert (valid);
871
872 e_contact_list_model_remove_row (E_CONTACT_LIST_MODEL (model), &iter);
873 gtk_tree_row_reference_free (reference);
874 }
875
876 /* new_selection != NULL when there is at least one item below the
877 * removed selection */
878 if (new_selection) {
879 path = gtk_tree_row_reference_get_path (new_selection);
880 gtk_tree_selection_select_path (selection, path);
881 gtk_tree_path_free (path);
882 gtk_tree_row_reference_free (new_selection);
883 } else {
884 /* If selection was including the last item in the list, then
885 * find and select the new last item */
886 GtkTreeIter iter, iter2;
887
888 /* When FALSE is returned, there are no items in the list to be selected */
889 if (gtk_tree_model_get_iter_first (model, &iter)) {
890 iter2 = iter;
891
892 while (gtk_tree_model_iter_next (model, &iter))
893 iter2 = iter;
894
895 gtk_tree_selection_select_iter (selection, &iter2);
896 }
897 }
898
899 g_list_free (list);
900
901 editor->priv->changed = TRUE;
902 contact_list_editor_update (editor);
903 }
904
905 void
906 contact_list_editor_select_button_clicked_cb (GtkWidget *widget);
907
908 void
909 contact_list_editor_select_button_clicked_cb (GtkWidget *widget)
910 {
911 EContactListEditor *editor;
912 ENameSelectorDialog *dialog;
913 EDestinationStore *store;
914 GList *list, *iter;
915 GtkWindow *window;
916
917 editor = contact_list_editor_extract (widget);
918
919 dialog = e_name_selector_peek_dialog (editor->priv->name_selector);
920 gtk_window_set_title (GTK_WINDOW (dialog), _("Contact List Members"));
921
922 /* We need to empty out the destination store, since we copy its
923 * contents every time. This sucks, we should really be wired
924 * directly to the EDestinationStore that the name selector uses
925 * in true MVC fashion. */
926
927 e_name_selector_model_peek_section (
928 e_name_selector_peek_model (editor->priv->name_selector),
929 "Members", NULL, &store);
930
931 list = e_destination_store_list_destinations (store);
932
933 for (iter = list; iter != NULL; iter = iter->next)
934 e_destination_store_remove_destination (store, iter->data);
935
936 g_list_free (list);
937
938 window = eab_editor_get_window (EAB_EDITOR (editor));
939 e_name_selector_show_dialog (
940 editor->priv->name_selector, GTK_WIDGET (window));
941 gtk_dialog_run (GTK_DIALOG (dialog));
942 gtk_widget_hide (GTK_WIDGET (dialog));
943
944 list = e_destination_store_list_destinations (store);
945
946 for (iter = list; iter != NULL; iter = iter->next) {
947 EDestination *destination = iter->data;
948
949 contact_list_editor_add_destination (widget, destination);
950 e_destination_store_remove_destination (store, destination);
951 }
952
953 g_list_free (list);
954
955 gtk_entry_set_text (GTK_ENTRY (WIDGET (EMAIL_ENTRY)), "");
956
957 editor->priv->changed = TRUE;
958 contact_list_editor_update (editor);
959 }
960
961 void
962 contact_list_editor_source_menu_changed_cb (GtkWidget *widget);
963
964 void
965 contact_list_editor_source_menu_changed_cb (GtkWidget *widget)
966 {
967 ESourceComboBox *combo_box;
968 EContactListEditor *editor;
969 ESource *active_source;
970 ESource *client_source;
971 EClient *client;
972
973 editor = contact_list_editor_extract (widget);
974
975 combo_box = E_SOURCE_COMBO_BOX (widget);
976 active_source = e_source_combo_box_ref_active (combo_box);
977 g_return_if_fail (active_source != NULL);
978
979 client = E_CLIENT (editor->priv->book_client);
980 client_source = e_client_get_source (client);
981
982 if (!e_source_equal (client_source, active_source))
983 e_client_utils_open_new (
984 active_source, E_CLIENT_SOURCE_TYPE_CONTACTS,
985 FALSE, NULL, contact_list_editor_book_loaded_cb,
986 g_object_ref (editor));
987
988 g_object_unref (active_source);
989 }
990
991 gboolean
992 contact_list_editor_tree_view_key_press_event_cb (GtkWidget *widget,
993 GdkEventKey *event);
994 gboolean
995 contact_list_editor_tree_view_key_press_event_cb (GtkWidget *widget,
996 GdkEventKey *event)
997 {
998 EContactListEditor *editor;
999
1000 editor = contact_list_editor_extract (widget);
1001
1002 if (event->keyval == GDK_KEY_Delete) {
1003 g_signal_emit_by_name (WIDGET (REMOVE_BUTTON), "clicked");
1004 return TRUE;
1005 }
1006
1007 return FALSE;
1008 }
1009
1010 void
1011 contact_list_editor_top_button_clicked_cb (GtkButton *button);
1012
1013 void
1014 contact_list_editor_top_button_clicked_cb (GtkButton *button)
1015 {
1016 EContactListEditor *editor;
1017 GtkTreeView *tree_view;
1018 GtkTreeModel *model;
1019 GtkTreeSelection *selection;
1020 GtkTreeIter iter;
1021 GtkTreePath *path;
1022 GList *references = NULL;
1023 GList *l, *selected;
1024
1025 editor = contact_list_editor_extract (GTK_WIDGET (button));
1026
1027 tree_view = GTK_TREE_VIEW (WIDGET (TREE_VIEW));
1028 model = gtk_tree_view_get_model (tree_view);
1029 selection = gtk_tree_view_get_selection (tree_view);
1030
1031 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1032
1033 for (l = selected; l; l = l->next)
1034 references = g_list_prepend (
1035 references,
1036 gtk_tree_row_reference_new (model, l->data));
1037
1038 for (l = references; l; l = l->next) {
1039 path = gtk_tree_row_reference_get_path (l->data);
1040 gtk_tree_model_get_iter (model, &iter, path);
1041 gtk_tree_store_move_after (
1042 GTK_TREE_STORE (model), &iter, NULL);
1043 gtk_tree_path_free (path);
1044 }
1045
1046 g_list_foreach (references, (GFunc) gtk_tree_row_reference_free, NULL);
1047 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1048 g_list_free (references);
1049 g_list_free (selected);
1050
1051 contact_list_editor_selection_changed_cb (selection, editor);
1052 }
1053
1054 void
1055 contact_list_editor_up_button_clicked_cb (GtkButton *button);
1056
1057 void
1058 contact_list_editor_up_button_clicked_cb (GtkButton *button)
1059 {
1060 EContactListEditor *editor;
1061 GtkTreeView *tree_view;
1062 GtkTreeModel *model;
1063 GtkTreeSelection *selection;
1064 GtkTreeIter iter, iter2;
1065 GtkTreePath *path;
1066 GList *selected;
1067
1068 editor = contact_list_editor_extract (GTK_WIDGET (button));
1069
1070 tree_view = GTK_TREE_VIEW (WIDGET (TREE_VIEW));
1071 model = gtk_tree_view_get_model (tree_view);
1072 selection = gtk_tree_view_get_selection (tree_view);
1073
1074 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1075
1076 /* Get iter of item above the first selected item */
1077 path = gtk_tree_path_copy (selected->data);
1078 gtk_tree_path_prev (path);
1079 gtk_tree_model_get_iter (model, &iter, path);
1080 gtk_tree_path_free (path);
1081
1082 /* Get iter of the last selected item */
1083 gtk_tree_model_get_iter (model, &iter2, g_list_last (selected)->data);
1084
1085 gtk_tree_store_move_after (GTK_TREE_STORE (model), &iter, &iter2);
1086
1087 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1088 g_list_free (selected);
1089
1090 contact_list_editor_selection_changed_cb (selection, editor);
1091 }
1092
1093 void
1094 contact_list_editor_down_button_clicked_cb (GtkButton *button);
1095
1096 void
1097 contact_list_editor_down_button_clicked_cb (GtkButton *button)
1098 {
1099 EContactListEditor *editor;
1100 GtkTreeView *tree_view;
1101 GtkTreeModel *model;
1102 GtkTreeSelection *selection;
1103 GtkTreeIter iter, iter2;
1104 GList *selected;
1105
1106 editor = contact_list_editor_extract (GTK_WIDGET (button));
1107
1108 tree_view = GTK_TREE_VIEW (WIDGET (TREE_VIEW));
1109 model = gtk_tree_view_get_model (tree_view);
1110 selection = gtk_tree_view_get_selection (tree_view);
1111
1112 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1113
1114 /* Iter of the first selected item */
1115 gtk_tree_model_get_iter (model, &iter, selected->data);
1116
1117 /* Iter of item below the last selected item */
1118 gtk_tree_model_get_iter (model, &iter2, g_list_last (selected)->data);
1119 gtk_tree_model_iter_next (model, &iter2);
1120
1121 gtk_tree_store_move_before (GTK_TREE_STORE (model), &iter2, &iter);
1122
1123 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1124 g_list_free (selected);
1125
1126 contact_list_editor_selection_changed_cb (selection, editor);
1127 }
1128
1129 void
1130 contact_list_editor_bottom_button_clicked_cb (GtkButton *button);
1131
1132 void
1133 contact_list_editor_bottom_button_clicked_cb (GtkButton *button)
1134 {
1135 EContactListEditor *editor;
1136 GtkTreeView *tree_view;
1137 GtkTreeModel *model;
1138 GtkTreeSelection *selection;
1139 GtkTreeIter iter;
1140 GtkTreePath *path;
1141 GList *references = NULL;
1142
1143 GList *l, *selected;
1144
1145 editor = contact_list_editor_extract (GTK_WIDGET (button));
1146
1147 tree_view = GTK_TREE_VIEW (WIDGET (TREE_VIEW));
1148 model = gtk_tree_view_get_model (tree_view);
1149 selection = gtk_tree_view_get_selection (tree_view);
1150
1151 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1152 for (l = selected; l; l = l->next)
1153 references = g_list_prepend (
1154 references,
1155 gtk_tree_row_reference_new (model, l->data));
1156 references = g_list_reverse (references);
1157
1158 for (l = references; l; l = l->next) {
1159 path = gtk_tree_row_reference_get_path (l->data);
1160 gtk_tree_model_get_iter (model, &iter, path);
1161 gtk_tree_store_move_before (
1162 GTK_TREE_STORE (model), &iter, NULL);
1163 gtk_tree_path_free (path);
1164 }
1165
1166 g_list_foreach (references, (GFunc) gtk_tree_row_reference_free, NULL);
1167 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1168 g_list_free (references);
1169 g_list_free (selected);
1170
1171 contact_list_editor_selection_changed_cb (selection, editor);
1172 }
1173
1174 /******************** GtkBuilder Custom Widgets Functions ********************/
1175
1176 static gpointer
1177 contact_editor_fudge_new (EBookClient *book_client,
1178 EContact *contact,
1179 gboolean is_new,
1180 gboolean editable)
1181 {
1182 EShell *shell = e_shell_get_default ();
1183
1184 /* XXX Putting this function signature in libedataserverui
1185 * was a terrible idea. Now we're stuck with it. */
1186
1187 return e_contact_editor_new (
1188 shell, book_client, contact, is_new, editable);
1189 }
1190
1191 static gpointer
1192 contact_list_editor_fudge_new (EBookClient *book_client,
1193 EContact *contact,
1194 gboolean is_new,
1195 gboolean editable)
1196 {
1197 EShell *shell = e_shell_get_default ();
1198
1199 /* XXX Putting this function signature in libedataserverui
1200 * was a terrible idea. Now we're stuck with it. */
1201
1202 return e_contact_list_editor_new (
1203 shell, book_client, contact, is_new, editable);
1204 }
1205
1206 static void
1207 setup_custom_widgets (EContactListEditor *editor)
1208 {
1209 EShell *shell;
1210 ESourceRegistry *registry;
1211 GtkWidget *combo_box;
1212 ENameSelectorEntry *name_selector_entry;
1213 GtkWidget *old, *parent;
1214 EContactListEditorPrivate *priv;
1215 guint ba = 0, la = 0, ra = 0, ta = 0, xo = 0, xp = 0, yo = 0, yp = 0;
1216
1217 g_return_if_fail (editor != NULL);
1218
1219 priv = E_CONTACT_LIST_EDITOR_GET_PRIVATE (editor);
1220
1221 shell = eab_editor_get_shell (EAB_EDITOR (editor));
1222 registry = e_shell_get_registry (shell);
1223
1224 combo_box = WIDGET (SOURCE_MENU);
1225
1226 e_source_combo_box_set_registry (
1227 E_SOURCE_COMBO_BOX (combo_box), registry);
1228
1229 g_signal_connect (
1230 combo_box, "changed", G_CALLBACK (
1231 contact_list_editor_source_menu_changed_cb), NULL);
1232
1233 old = CONTACT_LIST_EDITOR_WIDGET (editor, "email-entry");
1234 g_return_if_fail (old != NULL);
1235
1236 name_selector_entry = e_name_selector_peek_section_entry (
1237 priv->name_selector, "Members");
1238
1239 gtk_widget_set_name (
1240 GTK_WIDGET (name_selector_entry),
1241 gtk_widget_get_name (old));
1242 parent = gtk_widget_get_parent (old);
1243
1244 gtk_container_child_get (
1245 GTK_CONTAINER (parent), old,
1246 "bottom-attach", &ba,
1247 "left-attach", &la,
1248 "right-attach", &ra,
1249 "top-attach", &ta,
1250 "x-options", &xo,
1251 "x-padding", &xp,
1252 "y-options", &yo,
1253 "y-padding", &yp,
1254 NULL);
1255
1256 /* only hide it... */
1257 gtk_widget_hide (old);
1258
1259 /* ... and place the new name selector to the
1260 * exact place as is the old one in UI file */
1261 gtk_widget_show (GTK_WIDGET (name_selector_entry));
1262 gtk_table_attach (
1263 GTK_TABLE (parent), GTK_WIDGET (name_selector_entry),
1264 la, ra, ta, ba, xo, yo, xp, yp);
1265 priv->email_entry = name_selector_entry;
1266
1267 e_name_selector_entry_set_contact_editor_func (
1268 name_selector_entry, contact_editor_fudge_new);
1269 e_name_selector_entry_set_contact_list_editor_func (
1270 name_selector_entry, contact_list_editor_fudge_new);
1271
1272 g_signal_connect (
1273 name_selector_entry, "activate", G_CALLBACK (
1274 contact_list_editor_email_entry_activate_cb), NULL);
1275 g_signal_connect (
1276 name_selector_entry, "changed", G_CALLBACK (
1277 contact_list_editor_email_entry_changed_cb), NULL);
1278 g_signal_connect (
1279 name_selector_entry, "key-press-event", G_CALLBACK (
1280 contact_list_editor_email_entry_key_press_event_cb), NULL);
1281 }
1282
1283 /***************************** GObject Callbacks *****************************/
1284
1285 static void
1286 contact_list_editor_set_property (GObject *object,
1287 guint property_id,
1288 const GValue *value,
1289 GParamSpec *pspec)
1290 {
1291 switch (property_id) {
1292 case PROP_CLIENT:
1293 e_contact_list_editor_set_client (
1294 E_CONTACT_LIST_EDITOR (object),
1295 g_value_get_object (value));
1296 return;
1297
1298 case PROP_CONTACT:
1299 e_contact_list_editor_set_contact (
1300 E_CONTACT_LIST_EDITOR (object),
1301 g_value_get_object (value));
1302 return;
1303
1304 case PROP_IS_NEW_LIST:
1305 e_contact_list_editor_set_is_new_list (
1306 E_CONTACT_LIST_EDITOR (object),
1307 g_value_get_boolean (value));
1308 return;
1309
1310 case PROP_EDITABLE:
1311 e_contact_list_editor_set_editable (
1312 E_CONTACT_LIST_EDITOR (object),
1313 g_value_get_boolean (value));
1314 return;
1315 }
1316
1317 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1318 }
1319
1320 static void
1321 contact_list_editor_get_property (GObject *object,
1322 guint property_id,
1323 GValue *value,
1324 GParamSpec *pspec)
1325 {
1326 switch (property_id) {
1327 case PROP_CLIENT:
1328 g_value_set_object (
1329 value,
1330 e_contact_list_editor_get_client (
1331 E_CONTACT_LIST_EDITOR (object)));
1332 return;
1333
1334 case PROP_CONTACT:
1335 g_value_set_object (
1336 value,
1337 e_contact_list_editor_get_contact (
1338 E_CONTACT_LIST_EDITOR (object)));
1339 return;
1340
1341 case PROP_IS_NEW_LIST:
1342 g_value_set_boolean (
1343 value,
1344 e_contact_list_editor_get_is_new_list (
1345 E_CONTACT_LIST_EDITOR (object)));
1346 return;
1347
1348 case PROP_EDITABLE:
1349 g_value_set_boolean (
1350 value,
1351 e_contact_list_editor_get_editable (
1352 E_CONTACT_LIST_EDITOR (object)));
1353 return;
1354 }
1355
1356 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1357 }
1358
1359 static void
1360 contact_list_editor_dispose (GObject *object)
1361 {
1362 EContactListEditor *editor = E_CONTACT_LIST_EDITOR (object);
1363 EContactListEditorPrivate *priv = editor->priv;
1364
1365 if (priv->name_selector) {
1366 e_name_selector_cancel_loading (priv->name_selector);
1367 g_object_unref (priv->name_selector);
1368 priv->name_selector = NULL;
1369 }
1370
1371 if (priv->contact) {
1372 g_object_unref (priv->contact);
1373 priv->contact = NULL;
1374 }
1375
1376 if (priv->builder) {
1377 g_object_unref (priv->builder);
1378 priv->builder = NULL;
1379 }
1380
1381 /* Chain up to parent's dispose() method. */
1382 G_OBJECT_CLASS (e_contact_list_editor_parent_class)->dispose (object);
1383 }
1384
1385 static void
1386 contact_list_editor_constructed (GObject *object)
1387 {
1388 EContactListEditor *editor;
1389 GtkTreeViewColumn *column;
1390 GtkCellRenderer *renderer;
1391 GtkTreeView *view;
1392 GtkTreeSelection *selection;
1393 ESourceRegistry *registry;
1394 EShell *shell;
1395
1396 editor = E_CONTACT_LIST_EDITOR (object);
1397
1398 /* Chain up to parent's constructed() method. */
1399 G_OBJECT_CLASS (e_contact_list_editor_parent_class)->
1400 constructed (object);
1401
1402 shell = eab_editor_get_shell (EAB_EDITOR (editor));
1403 registry = e_shell_get_registry (shell);
1404
1405 editor->priv->editable = TRUE;
1406 editor->priv->allows_contact_lists = TRUE;
1407
1408 editor->priv->builder = gtk_builder_new ();
1409 e_load_ui_builder_definition (
1410 editor->priv->builder, "contact-list-editor.ui");
1411 gtk_builder_connect_signals (editor->priv->builder, NULL);
1412
1413 /* Embed a pointer to the EContactListEditor in the top-level
1414 * widget. Signal handlers can then access the pointer from any
1415 * child widget by calling contact_list_editor_extract(widget). */
1416 g_object_set_data (G_OBJECT (WIDGET (DIALOG)), TOPLEVEL_KEY, editor);
1417
1418 view = GTK_TREE_VIEW (WIDGET (TREE_VIEW));
1419 editor->priv->model = e_contact_list_model_new ();
1420 gtk_tree_view_set_model (view, editor->priv->model);
1421
1422 selection = gtk_tree_view_get_selection (view);
1423 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1424 g_signal_connect (
1425 selection, "changed",
1426 G_CALLBACK (contact_list_editor_selection_changed_cb), editor);
1427
1428 gtk_tree_view_enable_model_drag_dest (view, NULL, 0, GDK_ACTION_LINK);
1429 e_drag_dest_add_directory_targets (WIDGET (TREE_VIEW));
1430 gtk_drag_dest_add_text_targets (WIDGET (TREE_VIEW));
1431
1432 column = gtk_tree_view_column_new ();
1433 renderer = gtk_cell_renderer_text_new ();
1434 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1435 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1436 gtk_tree_view_append_column (view, column);
1437
1438 gtk_tree_view_column_set_cell_data_func (
1439 column, renderer, (GtkTreeCellDataFunc)
1440 contact_list_editor_render_destination, NULL, NULL);
1441
1442 editor->priv->name_selector = e_name_selector_new (registry);
1443
1444 e_name_selector_model_add_section (
1445 e_name_selector_peek_model (editor->priv->name_selector),
1446 "Members", _("_Members"), NULL);
1447
1448 g_signal_connect (
1449 editor, "notify::book",
1450 G_CALLBACK (contact_list_editor_notify_cb), NULL);
1451 g_signal_connect (
1452 editor, "notify::editable",
1453 G_CALLBACK (contact_list_editor_notify_cb), NULL);
1454
1455 gtk_widget_show_all (WIDGET (DIALOG));
1456
1457 setup_custom_widgets (editor);
1458
1459 e_name_selector_load_books (editor->priv->name_selector);
1460
1461 contact_list_editor_update (E_CONTACT_LIST_EDITOR (object));
1462 }
1463
1464 /**************************** EABEditor Callbacks ****************************/
1465
1466 static void
1467 contact_list_editor_show (EABEditor *editor)
1468 {
1469 gtk_widget_show (WIDGET (DIALOG));
1470 }
1471
1472 static void
1473 contact_list_editor_close (EABEditor *editor)
1474 {
1475 gtk_widget_destroy (WIDGET (DIALOG));
1476 eab_editor_closed (editor);
1477 }
1478
1479 static void
1480 contact_list_editor_raise (EABEditor *editor)
1481 {
1482 GdkWindow *window;
1483
1484 window = gtk_widget_get_window (WIDGET (DIALOG));
1485 gdk_window_raise (window);
1486 }
1487
1488 static void
1489 contact_list_editor_save_contact (EABEditor *eab_editor,
1490 gboolean should_close)
1491 {
1492 EContactListEditor *editor = E_CONTACT_LIST_EDITOR (eab_editor);
1493 EContactListEditorPrivate *priv = editor->priv;
1494 ESourceRegistry *registry;
1495 EditorCloseStruct *ecs;
1496 EContact *contact;
1497 EShell *shell;
1498
1499 shell = eab_editor_get_shell (eab_editor);
1500 registry = e_shell_get_registry (shell);
1501
1502 contact = e_contact_list_editor_get_contact (editor);
1503
1504 if (priv->book_client == NULL)
1505 return;
1506
1507 ecs = g_new (EditorCloseStruct, 1);
1508 ecs->editor = g_object_ref (editor);
1509 ecs->should_close = should_close;
1510
1511 gtk_widget_set_sensitive (WIDGET (DIALOG), FALSE);
1512 priv->in_async_call = TRUE;
1513
1514 if (priv->is_new_list)
1515 eab_merging_book_add_contact (
1516 registry, priv->book_client, contact,
1517 contact_list_editor_list_added_cb, ecs);
1518 else
1519 eab_merging_book_modify_contact (
1520 registry, priv->book_client, contact,
1521 contact_list_editor_list_modified_cb, ecs);
1522
1523 priv->changed = FALSE;
1524 }
1525
1526 static gboolean
1527 contact_list_editor_is_valid (EABEditor *editor)
1528 {
1529 GtkEditable *editable;
1530 gboolean valid;
1531 gchar *chars;
1532
1533 editable = GTK_EDITABLE (WIDGET (LIST_NAME_ENTRY));
1534 chars = gtk_editable_get_chars (editable, 0, -1);
1535 valid = (chars != NULL && *chars != '\0');
1536 g_free (chars);
1537
1538 return valid;
1539 }
1540
1541 static gboolean
1542 contact_list_editor_is_changed (EABEditor *editor)
1543 {
1544 return E_CONTACT_LIST_EDITOR_GET_PRIVATE (editor)->changed;
1545 }
1546
1547 static GtkWindow *
1548 contact_list_editor_get_window (EABEditor *editor)
1549 {
1550 return GTK_WINDOW (WIDGET (DIALOG));
1551 }
1552
1553 static void
1554 contact_list_editor_contact_added (EABEditor *editor,
1555 const GError *error,
1556 EContact *contact)
1557 {
1558 if (!error)
1559 return;
1560
1561 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
1562 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1563 return;
1564
1565 eab_error_dialog (NULL, _("Error adding list"), error);
1566 }
1567
1568 static void
1569 contact_list_editor_contact_modified (EABEditor *editor,
1570 const GError *error,
1571 EContact *contact)
1572 {
1573 if (!error)
1574 return;
1575
1576 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
1577 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1578 return;
1579
1580 eab_error_dialog (NULL, _("Error modifying list"), error);
1581 }
1582
1583 static void
1584 contact_list_editor_contact_deleted (EABEditor *editor,
1585 const GError *error,
1586 EContact *contact)
1587 {
1588 if (!error)
1589 return;
1590
1591 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
1592 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1593 return;
1594
1595 eab_error_dialog (NULL, _("Error removing list"), error);
1596 }
1597
1598 static void
1599 contact_list_editor_closed (EABEditor *editor)
1600 {
1601 g_object_unref (editor);
1602 }
1603
1604 /****************************** GType Callbacks ******************************/
1605
1606 static void
1607 e_contact_list_editor_class_init (EContactListEditorClass *class)
1608 {
1609 GObjectClass *object_class;
1610 EABEditorClass *editor_class;
1611
1612 g_type_class_add_private (class, sizeof (EContactListEditorPrivate));
1613
1614 object_class = G_OBJECT_CLASS (class);
1615 object_class->set_property = contact_list_editor_set_property;
1616 object_class->get_property = contact_list_editor_get_property;
1617 object_class->dispose = contact_list_editor_dispose;
1618 object_class->constructed = contact_list_editor_constructed;
1619
1620 editor_class = EAB_EDITOR_CLASS (class);
1621 editor_class->show = contact_list_editor_show;
1622 editor_class->close = contact_list_editor_close;
1623 editor_class->raise = contact_list_editor_raise;
1624 editor_class->save_contact = contact_list_editor_save_contact;
1625 editor_class->is_valid = contact_list_editor_is_valid;
1626 editor_class->is_changed = contact_list_editor_is_changed;
1627 editor_class->get_window = contact_list_editor_get_window;
1628 editor_class->contact_added = contact_list_editor_contact_added;
1629 editor_class->contact_modified = contact_list_editor_contact_modified;
1630 editor_class->contact_deleted = contact_list_editor_contact_deleted;
1631 editor_class->editor_closed = contact_list_editor_closed;
1632
1633 g_object_class_install_property (
1634 object_class,
1635 PROP_CLIENT,
1636 g_param_spec_object (
1637 "client",
1638 "EBookClient",
1639 NULL,
1640 E_TYPE_BOOK_CLIENT,
1641 G_PARAM_READWRITE));
1642
1643 g_object_class_install_property (
1644 object_class,
1645 PROP_CONTACT,
1646 g_param_spec_object (
1647 "contact",
1648 "Contact",
1649 NULL,
1650 E_TYPE_CONTACT,
1651 G_PARAM_READWRITE));
1652
1653 g_object_class_install_property (
1654 object_class,
1655 PROP_IS_NEW_LIST,
1656 g_param_spec_boolean (
1657 "is_new_list",
1658 "Is New List",
1659 NULL,
1660 FALSE,
1661 G_PARAM_READWRITE));
1662
1663 g_object_class_install_property (
1664 object_class,
1665 PROP_EDITABLE,
1666 g_param_spec_boolean (
1667 "editable",
1668 "Editable",
1669 NULL,
1670 FALSE,
1671 G_PARAM_READWRITE));
1672 }
1673
1674 static void
1675 e_contact_list_editor_init (EContactListEditor *editor)
1676 {
1677 editor->priv = E_CONTACT_LIST_EDITOR_GET_PRIVATE (editor);
1678 }
1679
1680 /***************************** Public Interface ******************************/
1681
1682 EABEditor *
1683 e_contact_list_editor_new (EShell *shell,
1684 EBookClient *book_client,
1685 EContact *list_contact,
1686 gboolean is_new_list,
1687 gboolean editable)
1688 {
1689 EABEditor *editor;
1690
1691 g_return_val_if_fail (E_IS_SHELL (shell), NULL);
1692
1693 editor = g_object_new (
1694 E_TYPE_CONTACT_LIST_EDITOR,
1695 "shell", shell, NULL);
1696
1697 g_object_set (
1698 editor,
1699 "client", book_client,
1700 "contact", list_contact,
1701 "is_new_list", is_new_list,
1702 "editable", editable,
1703 NULL);
1704
1705 return editor;
1706 }
1707
1708 EBookClient *
1709 e_contact_list_editor_get_client (EContactListEditor *editor)
1710 {
1711 g_return_val_if_fail (E_IS_CONTACT_LIST_EDITOR (editor), NULL);
1712
1713 return editor->priv->book_client;
1714 }
1715
1716 void
1717 e_contact_list_editor_set_client (EContactListEditor *editor,
1718 EBookClient *book_client)
1719 {
1720 g_return_if_fail (E_IS_CONTACT_LIST_EDITOR (editor));
1721 g_return_if_fail (E_IS_BOOK_CLIENT (book_client));
1722
1723 if (book_client == editor->priv->book_client)
1724 return;
1725
1726 if (editor->priv->book_client != NULL)
1727 g_object_unref (editor->priv->book_client);
1728 editor->priv->book_client = g_object_ref (book_client);
1729
1730 editor->priv->allows_contact_lists = e_client_check_capability (
1731 E_CLIENT (editor->priv->book_client), "contact-lists");
1732
1733 contact_list_editor_update (editor);
1734
1735 g_object_notify (G_OBJECT (editor), "client");
1736 }
1737
1738 static void
1739 save_contact_list (GtkTreeModel *model,
1740 GtkTreeIter *iter,
1741 GSList **attrs,
1742 gint *parent_id)
1743 {
1744 EDestination *dest;
1745 EVCardAttribute *attr;
1746 gchar *pid_str = g_strdup_printf ("%d", *parent_id);
1747
1748 do {
1749 gtk_tree_model_get (model, iter, 0, &dest, -1);
1750
1751 if (gtk_tree_model_iter_has_child (model, iter)) {
1752 GtkTreeIter new_iter;
1753 gchar *uid;
1754
1755 (*parent_id)++;
1756 uid = g_strdup_printf ("%d", *parent_id);
1757
1758 attr = e_vcard_attribute_new (NULL, EVC_CONTACT_LIST);
1759 e_vcard_attribute_add_param_with_value (
1760 attr,
1761 e_vcard_attribute_param_new (EVC_CL_UID), uid);
1762 e_vcard_attribute_add_value (
1763 attr,
1764 e_destination_get_name (dest));
1765
1766 g_free (uid);
1767
1768 /* Set new_iter to first child of iter */
1769 gtk_tree_model_iter_children (model, &new_iter, iter);
1770
1771 /* Go recursive */
1772 save_contact_list (model, &new_iter, attrs, parent_id);
1773 } else {
1774 attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
1775 e_destination_export_to_vcard_attribute (dest, attr);
1776 }
1777
1778 e_vcard_attribute_add_param_with_value (
1779 attr,
1780 e_vcard_attribute_param_new (EVC_PARENT_CL), pid_str);
1781
1782 *attrs = g_slist_prepend (*attrs, attr);
1783
1784 g_object_unref (dest);
1785
1786 } while (gtk_tree_model_iter_next (model, iter));
1787
1788 g_free (pid_str);
1789 }
1790
1791 EContact *
1792 e_contact_list_editor_get_contact (EContactListEditor *editor)
1793 {
1794 GtkTreeModel *model;
1795 EContact *contact;
1796 GtkTreeIter iter;
1797 const gchar *text;
1798 GSList *attrs = NULL, *a;
1799 gint parent_id = 0;
1800
1801 g_return_val_if_fail (E_IS_CONTACT_LIST_EDITOR (editor), NULL);
1802
1803 model = editor->priv->model;
1804 contact = editor->priv->contact;
1805
1806 if (contact == NULL)
1807 return NULL;
1808
1809 text = gtk_entry_get_text (GTK_ENTRY (WIDGET (LIST_NAME_ENTRY)));
1810 if (text != NULL && *text != '\0') {
1811 e_contact_set (contact, E_CONTACT_FILE_AS, text);
1812 e_contact_set (contact, E_CONTACT_FULL_NAME, text);
1813 }
1814
1815 e_contact_set (contact, E_CONTACT_LOGO, NULL);
1816 e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE));
1817
1818 e_contact_set (
1819 contact, E_CONTACT_LIST_SHOW_ADDRESSES,
1820 GINT_TO_POINTER (!gtk_toggle_button_get_active (
1821 GTK_TOGGLE_BUTTON (WIDGET (CHECK_BUTTON)))));
1822
1823 e_vcard_remove_attributes (E_VCARD (contact), "", EVC_EMAIL);
1824 e_vcard_remove_attributes (E_VCARD (contact), "", EVC_CONTACT_LIST);
1825
1826 if (gtk_tree_model_get_iter_first (model, &iter))
1827 save_contact_list (model, &iter, &attrs, &parent_id);
1828
1829 /* Put it in reverse order because e_vcard_add_attribute also uses prepend,
1830 * but we want to keep order of mails there. Hopefully noone will change
1831 * the behaviour of the e_vcard_add_attribute. */
1832 for (a = attrs; a; a = a->next) {
1833 e_vcard_add_attribute (E_VCARD (contact), a->data);
1834 }
1835
1836 g_slist_free (attrs);
1837
1838 return contact;
1839 }
1840
1841 void
1842 e_contact_list_editor_set_contact (EContactListEditor *editor,
1843 EContact *contact)
1844 {
1845 EContactListEditorPrivate *priv;
1846
1847 g_return_if_fail (E_IS_CONTACT_LIST_EDITOR (editor));
1848 g_return_if_fail (E_IS_CONTACT (contact));
1849
1850 priv = editor->priv;
1851
1852 if (priv->contact != NULL)
1853 g_object_unref (priv->contact);
1854
1855 priv->contact = e_contact_duplicate (contact);
1856
1857 if (priv->contact != NULL) {
1858 const gchar *file_as;
1859 gboolean show_addresses;
1860 const GList *dests, *dest;
1861
1862 /* The root destination */
1863 EDestination *list_dest = e_destination_new ();
1864
1865 file_as = e_contact_get_const (
1866 priv->contact, E_CONTACT_FILE_AS);
1867 show_addresses = GPOINTER_TO_INT (e_contact_get (
1868 priv->contact, E_CONTACT_LIST_SHOW_ADDRESSES));
1869
1870 if (file_as == NULL)
1871 file_as = "";
1872
1873 gtk_entry_set_text (
1874 GTK_ENTRY (WIDGET (LIST_NAME_ENTRY)), file_as);
1875
1876 gtk_toggle_button_set_active (
1877 GTK_TOGGLE_BUTTON (WIDGET (CHECK_BUTTON)),
1878 !show_addresses);
1879
1880 e_contact_list_model_remove_all (
1881 E_CONTACT_LIST_MODEL (priv->model));
1882
1883 e_destination_set_name (list_dest, file_as);
1884 e_destination_set_contact (list_dest, priv->contact, 0);
1885
1886 dests = e_destination_list_get_root_dests (list_dest);
1887 for (dest = dests; dest; dest = dest->next) {
1888 GtkTreePath *path;
1889 path = e_contact_list_model_add_destination (
1890 E_CONTACT_LIST_MODEL (priv->model),
1891 dest->data, NULL, TRUE);
1892 gtk_tree_path_free (path);
1893 }
1894
1895 g_object_unref (list_dest);
1896
1897 gtk_tree_view_expand_all (GTK_TREE_VIEW (WIDGET (TREE_VIEW)));
1898 }
1899
1900 if (priv->book_client != NULL) {
1901 e_source_combo_box_set_active (
1902 E_SOURCE_COMBO_BOX (WIDGET (SOURCE_MENU)),
1903 e_client_get_source (E_CLIENT (priv->book_client)));
1904 gtk_widget_set_sensitive (
1905 WIDGET (SOURCE_MENU), priv->is_new_list);
1906 }
1907
1908 priv->changed = FALSE;
1909 contact_list_editor_update (editor);
1910
1911 g_object_notify (G_OBJECT (editor), "contact");
1912
1913 }
1914
1915 gboolean
1916 e_contact_list_editor_get_is_new_list (EContactListEditor *editor)
1917 {
1918 g_return_val_if_fail (E_IS_CONTACT_LIST_EDITOR (editor), FALSE);
1919
1920 return editor->priv->is_new_list;
1921 }
1922
1923 void
1924 e_contact_list_editor_set_is_new_list (EContactListEditor *editor,
1925 gboolean is_new_list)
1926 {
1927
1928 g_return_if_fail (E_IS_CONTACT_LIST_EDITOR (editor));
1929
1930 if (editor->priv->is_new_list == is_new_list)
1931 return;
1932
1933 editor->priv->is_new_list = is_new_list;
1934 contact_list_editor_update (editor);
1935
1936 g_object_notify (G_OBJECT (editor), "is_new_list");
1937 }
1938
1939 gboolean
1940 e_contact_list_editor_get_editable (EContactListEditor *editor)
1941 {
1942 g_return_val_if_fail (E_IS_CONTACT_LIST_EDITOR (editor), FALSE);
1943
1944 return editor->priv->editable;
1945 }
1946
1947 void
1948 e_contact_list_editor_set_editable (EContactListEditor *editor,
1949 gboolean editable)
1950 {
1951 g_return_if_fail (E_IS_CONTACT_LIST_EDITOR (editor));
1952
1953 if (editor->priv->editable == editable)
1954 return;
1955
1956 editor->priv->editable = editable;
1957 contact_list_editor_update (editor);
1958
1959 g_object_notify (G_OBJECT (editor), "editable");
1960 }