Set transient parent for the "Full Name" editor dialog
[evolution.git] / src / addressbook / gui / contact-editor / e-contact-editor.c
blobcbc19b54192ec590ece6952787452b17805ab028
1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 * Authors:
16 * Chris Lahey <clahey@ximian.com>
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 #include "evolution-config.h"
24 #include "eab-editor.h"
25 #include "e-contact-editor.h"
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
31 #include <gtk/gtk.h>
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <gdk-pixbuf/gdk-pixbuf.h>
36 #define GCR_API_SUBJECT_TO_CHANGE
37 #include <gcr/gcr.h>
38 #undef GCR_API_SUBJECT_TO_CHANGE
40 #include "shell/e-shell.h"
41 #include "e-util/e-util.h"
43 #include "addressbook/printing/e-contact-print.h"
44 #include "addressbook/gui/widgets/eab-gui-util.h"
45 #include "addressbook/util/eab-book-util.h"
47 #include "eab-contact-merging.h"
49 #include "e-contact-editor-fullname.h"
50 #include "e-contact-editor-dyntable.h"
52 #define SLOTS_PER_LINE 2
53 #define SLOTS_IN_COLLAPSED_STATE SLOTS_PER_LINE
54 #define EMAIL_SLOTS 50
55 #define PHONE_SLOTS 50
56 #define SIP_SLOTS 4
57 #define IM_SLOTS 50
58 #define ADDRESS_SLOTS 3
60 /* represents index in address_name */
61 #define ADDRESS_SLOT_HOME 1
62 #define ADDRESS_SLOT_WORK 0
63 #define ADDRESS_SLOT_OTHER 2
65 #define EVOLUTION_UI_SLOT_PARAM "X-EVOLUTION-UI-SLOT"
67 #define CHECK_PHONE 1
68 #define CHECK_SIP 2
69 #define CHECK_IM 3
70 #define CHECK_HOME 4
71 #define CHECK_WORK 5
72 #define CHECK_OTHER 6
73 #define CHECK_WEB 7
74 #define CHECK_JOB 8
75 #define CHECK_MISC 9
76 #define CHECK_NOTE 10
77 #define CHECK_CERTS 11
79 /* IM columns */
80 enum {
81 COLUMN_IM_ICON,
82 COLUMN_IM_SERVICE,
83 COLUMN_IM_SCREENNAME,
84 COLUMN_IM_LOCATION,
85 COLUMN_IM_LOCATION_TYPE,
86 COLUMN_IM_SERVICE_FIELD,
87 NUM_IM_COLUMNS
90 typedef struct {
91 EContactEditor *editor;
92 ESource *source;
93 } ConnectClosure;
95 static void e_contact_editor_set_property (GObject *object,
96 guint property_id,
97 const GValue *value,
98 GParamSpec *pspec);
99 static void e_contact_editor_get_property (GObject *object,
100 guint property_id,
101 GValue *value,
102 GParamSpec *pspec);
103 static void e_contact_editor_constructed (GObject *object);
104 static void e_contact_editor_dispose (GObject *object);
105 static void e_contact_editor_raise (EABEditor *editor);
106 static void e_contact_editor_show (EABEditor *editor);
107 static void e_contact_editor_save_contact (EABEditor *editor,
108 gboolean should_close);
109 static void e_contact_editor_close (EABEditor *editor);
110 static gboolean e_contact_editor_is_valid (EABEditor *editor);
111 static gboolean e_contact_editor_is_changed (EABEditor *editor);
112 static GtkWindow *
113 e_contact_editor_get_window (EABEditor *editor);
114 static void save_contact (EContactEditor *ce,
115 gboolean should_close);
116 static void entry_activated (EContactEditor *editor);
118 static void set_entry_text (EContactEditor *editor,
119 GtkEntry *entry,
120 const gchar *string);
121 static void sensitize_ok (EContactEditor *ce);
123 static EABEditorClass *parent_class = NULL;
125 enum {
126 PROP_0,
127 PROP_SOURCE_CLIENT,
128 PROP_TARGET_CLIENT,
129 PROP_CONTACT,
130 PROP_IS_NEW_CONTACT,
131 PROP_EDITABLE,
132 PROP_CHANGED,
133 PROP_WRITABLE_FIELDS,
134 PROP_REQUIRED_FIELDS
137 enum {
138 DYNAMIC_LIST_EMAIL,
139 DYNAMIC_LIST_PHONE,
140 DYNAMIC_LIST_ADDRESS
143 /* Defaults selected from eab_phone_types */
144 static const gint phones_default[] = { 1, 6, 9, 2, 7, 12, 10, 10 };
146 static EContactField addresses[] = {
147 E_CONTACT_ADDRESS_WORK,
148 E_CONTACT_ADDRESS_HOME,
149 E_CONTACT_ADDRESS_OTHER
152 static EContactField address_labels[] = {
153 E_CONTACT_ADDRESS_LABEL_WORK,
154 E_CONTACT_ADDRESS_LABEL_HOME,
155 E_CONTACT_ADDRESS_LABEL_OTHER
158 static const gchar *address_name[] = {
159 "work",
160 "home",
161 "other"
165 * keep fetch_set in sync with labels from eab_im_service
167 static EContactField
168 im_service_fetch_set[] =
170 E_CONTACT_IM_AIM,
171 E_CONTACT_IM_JABBER,
172 E_CONTACT_IM_YAHOO,
173 E_CONTACT_IM_GADUGADU,
174 E_CONTACT_IM_MSN,
175 E_CONTACT_IM_ICQ,
176 E_CONTACT_IM_GROUPWISE,
177 E_CONTACT_IM_SKYPE,
178 E_CONTACT_IM_TWITTER,
179 E_CONTACT_IM_GOOGLE_TALK
182 /* Defaults selected from eab_get_im_type_labels */
183 static const gint im_service_default[] = { 0, 2, 4, 5 };
186 /* Default from the table above */
187 static const gint email_default[] = { 0, 1, 2, 2 };
188 static const gint sips_default[] = { 0, 1, 2, 2 };
190 #define STRING_IS_EMPTY(x) (!(x) || !(*(x)))
191 #define STRING_MAKE_NON_NULL(x) ((x) ? (x) : "")
193 struct _EContactEditorPrivate
195 /* item specific fields */
196 EBookClient *source_client;
197 EBookClient *target_client;
198 EContact *contact;
200 GtkBuilder *builder;
201 GtkWidget *app;
203 GtkWidget *file_selector;
205 EContactName *name;
207 /* Whether we are editing a new contact or an existing one */
208 guint is_new_contact : 1;
210 /* Whether an image is associated with a contact. */
211 guint image_set : 1;
213 /* Whether the contact has been changed since bringing up the contact editor */
214 guint changed : 1;
216 /* Wheter should check for contact to merge. Only when name or email are changed */
217 guint check_merge : 1;
219 /* Whether the contact editor will accept modifications, save */
220 guint target_editable : 1;
222 /* Whether an async wombat call is in progress */
223 guint in_async_call : 1;
225 /* Whether an image is changed */
226 guint image_changed : 1;
228 /* Whether to try to reduce space used */
229 guint compress_ui : 1;
231 GSList *writable_fields;
233 GSList *required_fields;
235 GCancellable *cancellable;
237 /* signal ids for "writable_status" */
238 gint target_editable_id;
240 GtkWidget *fullname_dialog;
241 GtkWidget *categories_dialog;
243 GtkUIManager *ui_manager;
244 EFocusTracker *focus_tracker;
247 G_DEFINE_TYPE (EContactEditor, e_contact_editor, EAB_TYPE_EDITOR)
249 static GtkActionEntry undo_entries[] = {
251 { "undo-menu",
252 "Undo menu",
253 NULL,
254 NULL,
255 NULL,
256 NULL }, /* just a fake undo menu, to get shortcuts working */
258 { "undo",
259 "edit-undo",
260 N_("_Undo"),
261 "<Control>z",
262 N_("Undo"),
263 NULL }, /* Handled by EFocusTracker */
265 { "redo",
266 "edit-redo",
267 N_("_Redo"),
268 "<Control>y",
269 N_("Redo"),
270 NULL } /* Handled by EFocusTracker */
274 static void
275 connect_closure_free (ConnectClosure *connect_closure)
277 if (connect_closure->editor != NULL)
278 g_object_unref (connect_closure->editor);
280 if (connect_closure->source != NULL)
281 g_object_unref (connect_closure->source);
283 g_slice_free (ConnectClosure, connect_closure);
286 static void
287 e_contact_editor_contact_added (EABEditor *editor,
288 const GError *error,
289 EContact *contact)
291 GtkWindow *window;
292 const gchar *message;
294 if (error == NULL)
295 return;
297 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
298 return;
300 window = eab_editor_get_window (editor);
301 message = _("Error adding contact");
303 eab_error_dialog (NULL, window, message, error);
306 static void
307 e_contact_editor_contact_modified (EABEditor *editor,
308 const GError *error,
309 EContact *contact)
311 GtkWindow *window;
312 const gchar *message;
314 if (error == NULL)
315 return;
317 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
318 return;
320 window = eab_editor_get_window (editor);
321 message = _("Error modifying contact");
323 eab_error_dialog (NULL, window, message, error);
326 static void
327 e_contact_editor_contact_deleted (EABEditor *editor,
328 const GError *error,
329 EContact *contact)
331 GtkWindow *window;
332 const gchar *message;
334 if (error == NULL)
335 return;
337 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
338 return;
340 window = eab_editor_get_window (editor);
341 message = _("Error removing contact");
343 eab_error_dialog (NULL, window, message, error);
346 static void
347 e_contact_editor_closed (EABEditor *editor)
349 g_object_unref (editor);
352 static void
353 e_contact_editor_class_init (EContactEditorClass *class)
355 GObjectClass *object_class = G_OBJECT_CLASS (class);
356 EABEditorClass *editor_class = EAB_EDITOR_CLASS (class);
358 g_type_class_add_private (class, sizeof (EContactEditorPrivate));
360 parent_class = g_type_class_ref (EAB_TYPE_EDITOR);
362 object_class->set_property = e_contact_editor_set_property;
363 object_class->get_property = e_contact_editor_get_property;
364 object_class->constructed = e_contact_editor_constructed;
365 object_class->dispose = e_contact_editor_dispose;
367 editor_class->raise = e_contact_editor_raise;
368 editor_class->show = e_contact_editor_show;
369 editor_class->close = e_contact_editor_close;
370 editor_class->is_valid = e_contact_editor_is_valid;
371 editor_class->save_contact = e_contact_editor_save_contact;
372 editor_class->is_changed = e_contact_editor_is_changed;
373 editor_class->get_window = e_contact_editor_get_window;
374 editor_class->contact_added = e_contact_editor_contact_added;
375 editor_class->contact_modified = e_contact_editor_contact_modified;
376 editor_class->contact_deleted = e_contact_editor_contact_deleted;
377 editor_class->editor_closed = e_contact_editor_closed;
379 g_object_class_install_property (
380 object_class,
381 PROP_SOURCE_CLIENT,
382 g_param_spec_object (
383 "source_client",
384 "Source EBookClient",
385 NULL,
386 E_TYPE_BOOK_CLIENT,
387 G_PARAM_READWRITE));
389 g_object_class_install_property (
390 object_class,
391 PROP_TARGET_CLIENT,
392 g_param_spec_object (
393 "target_client",
394 "Target EBookClient",
395 NULL,
396 E_TYPE_BOOK_CLIENT,
397 G_PARAM_READWRITE));
399 g_object_class_install_property (
400 object_class,
401 PROP_CONTACT,
402 g_param_spec_object (
403 "contact",
404 "Contact",
405 NULL,
406 E_TYPE_CONTACT,
407 G_PARAM_READWRITE));
409 g_object_class_install_property (
410 object_class,
411 PROP_IS_NEW_CONTACT,
412 g_param_spec_boolean (
413 "is_new_contact",
414 "Is New Contact",
415 NULL,
416 FALSE,
417 G_PARAM_READWRITE));
419 g_object_class_install_property (
420 object_class,
421 PROP_WRITABLE_FIELDS,
422 g_param_spec_pointer (
423 "writable_fields",
424 "Writable Fields",
425 NULL,
426 G_PARAM_READWRITE));
428 g_object_class_install_property (
429 object_class,
430 PROP_REQUIRED_FIELDS,
431 g_param_spec_pointer (
432 "required_fields",
433 "Required Fields",
434 NULL,
435 G_PARAM_READWRITE));
437 g_object_class_install_property (
438 object_class,
439 PROP_EDITABLE,
440 g_param_spec_boolean (
441 "editable",
442 "Editable",
443 NULL,
444 FALSE,
445 G_PARAM_READWRITE));
447 g_object_class_install_property (
448 object_class,
449 PROP_CHANGED,
450 g_param_spec_boolean (
451 "changed",
452 "Changed",
453 NULL,
454 FALSE,
455 G_PARAM_READWRITE));
458 static void
459 entry_activated (EContactEditor *editor)
461 save_contact (editor, TRUE);
464 /* FIXME: Linear time... */
465 static gboolean
466 is_field_supported (EContactEditor *editor,
467 EContactField field_id)
469 GSList *fields, *iter;
470 const gchar *field;
472 fields = editor->priv->writable_fields;
473 if (!fields)
474 return FALSE;
476 field = e_contact_field_name (field_id);
477 if (!field)
478 return FALSE;
480 for (iter = fields; iter; iter = iter->next) {
481 const gchar *this_field = iter->data;
483 if (!this_field)
484 continue;
486 if (!strcmp (field, this_field))
487 return TRUE;
490 return FALSE;
493 /* This function tells you whether name_to_style will make sense. */
494 static gboolean
495 style_makes_sense (const EContactName *name,
496 const gchar *company,
497 gint style)
499 switch (style) {
500 case 0: /* Fall Through */
501 case 1:
502 return TRUE;
503 case 2:
504 if (name) {
505 if (name->additional && *name->additional)
506 return TRUE;
507 else
508 return FALSE;
510 return FALSE;
511 case 3:
512 if (company && *company)
513 return TRUE;
514 else
515 return FALSE;
516 case 4: /* Fall Through */
517 case 5:
518 if (company && *company && name &&
519 ((name->given && *name->given) ||
520 (name->family && *name->family)))
521 return TRUE;
522 else
523 return FALSE;
524 default:
525 return FALSE;
529 static gchar *
530 name_to_style (const EContactName *name,
531 const gchar *company,
532 gint style)
534 gchar *string;
535 gchar *strings[4], **stringptr;
536 gchar *midstring[4], **midstrptr;
537 gchar *substring;
538 switch (style) {
539 case 0:
540 stringptr = strings;
541 if (name) {
542 if (name->family && *name->family)
543 *(stringptr++) = name->family;
544 if (name->given && *name->given)
545 *(stringptr++) = name->given;
547 *stringptr = NULL;
548 string = g_strjoinv (", ", strings);
549 break;
550 case 1:
551 stringptr = strings;
552 if (name) {
553 if (name->given && *name->given)
554 *(stringptr++) = name->given;
555 if (name->family && *name->family)
556 *(stringptr++) = name->family;
558 *stringptr = NULL;
559 string = g_strjoinv (" ", strings);
560 break;
561 case 2:
562 midstrptr = midstring;
563 if (name) {
564 if (name->family && *name->family)
565 *(midstrptr++) = name->family;
566 if (name->given && *name->given)
567 *(midstrptr++) = name->given;
569 *midstrptr = NULL;
570 stringptr = strings;
571 *(stringptr++) = g_strjoinv(", ", midstring);
572 if (name) {
573 if (name->additional && *name->additional)
574 *(stringptr++) = name->additional;
576 *stringptr = NULL;
577 string = g_strjoinv (" ", strings);
578 break;
579 case 3:
580 string = g_strdup (company);
581 break;
582 case 4: /* Fall Through */
583 case 5:
584 stringptr = strings;
585 if (name) {
586 if (name->family && *name->family)
587 *(stringptr++) = name->family;
588 if (name->given && *name->given)
589 *(stringptr++) = name->given;
591 *stringptr = NULL;
592 substring = g_strjoinv (", ", strings);
593 if (!(company && *company))
594 company = "";
595 if (style == 4)
596 string = g_strdup_printf ("%s (%s)", substring, company);
597 else
598 string = g_strdup_printf ("%s (%s)", company, substring);
599 g_free (substring);
600 break;
601 default:
602 string = g_strdup ("");
604 return string;
607 static gint
608 file_as_get_style (EContactEditor *editor)
610 GtkEntry *file_as = GTK_ENTRY (
611 gtk_bin_get_child (GTK_BIN (
612 e_builder_get_widget (editor->priv->builder, "combo-file-as"))));
613 GtkEntry *company_w = GTK_ENTRY (
614 e_builder_get_widget (editor->priv->builder, "entry-company"));
615 gchar *filestring;
616 gchar *trystring;
617 EContactName *name = editor->priv->name;
618 const gchar *company;
619 gint i;
621 if (!(file_as && GTK_IS_ENTRY (file_as)))
622 return -1;
624 company = gtk_entry_get_text (GTK_ENTRY (company_w));
625 filestring = g_strdup (gtk_entry_get_text (file_as));
627 for (i = 0; i < 6; i++) {
628 trystring = name_to_style (name, company, i);
629 if (!strcmp (trystring, filestring)) {
630 g_free (trystring);
631 g_free (filestring);
632 return i;
634 g_free (trystring);
636 g_free (filestring);
637 return -1;
640 static void
641 file_as_set_style (EContactEditor *editor,
642 gint style)
644 gchar *string;
645 gint i;
646 GList *strings = NULL;
647 GtkComboBox *combo_file_as = GTK_COMBO_BOX (
648 e_builder_get_widget (editor->priv->builder, "combo-file-as"));
649 GtkEntry *company_w = GTK_ENTRY (
650 e_builder_get_widget (editor->priv->builder, "entry-company"));
651 const gchar *company;
653 if (!(combo_file_as && GTK_IS_COMBO_BOX (combo_file_as)))
654 return;
656 company = gtk_entry_get_text (GTK_ENTRY (company_w));
658 if (style == -1) {
659 GtkWidget *entry;
661 entry = gtk_bin_get_child (GTK_BIN (combo_file_as));
662 if (entry) {
663 string = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
664 strings = g_list_append (strings, string);
668 for (i = 0; i < 6; i++) {
669 if (style_makes_sense (editor->priv->name, company, i)) {
670 gchar *u;
671 u = name_to_style (editor->priv->name, company, i);
672 if (!STRING_IS_EMPTY (u))
673 strings = g_list_append (strings, u);
674 else
675 g_free (u);
679 if (combo_file_as) {
680 GList *l;
681 GtkListStore *list_store;
682 GtkTreeIter iter;
684 list_store = GTK_LIST_STORE (
685 gtk_combo_box_get_model (combo_file_as));
687 gtk_list_store_clear (list_store);
689 for (l = strings; l; l = l->next) {
690 gtk_list_store_append (list_store, &iter);
691 gtk_list_store_set (list_store, &iter, 0, l->data, -1);
695 g_list_foreach (strings, (GFunc) g_free, NULL);
696 g_list_free (strings);
698 if (style != -1) {
699 string = name_to_style (editor->priv->name, company, style);
700 set_entry_text (
701 editor, GTK_ENTRY (gtk_bin_get_child (
702 GTK_BIN (combo_file_as))), string);
703 g_free (string);
707 static void
708 name_entry_changed (GtkWidget *widget,
709 EContactEditor *editor)
711 gint style = 0;
712 const gchar *string;
714 style = file_as_get_style (editor);
715 e_contact_name_free (editor->priv->name);
716 string = gtk_entry_get_text (GTK_ENTRY (widget));
717 editor->priv->name = e_contact_name_from_string (string);
718 file_as_set_style (editor, style);
720 editor->priv->check_merge = TRUE;
722 sensitize_ok (editor);
723 if (string && !*string)
724 gtk_window_set_title (
725 GTK_WINDOW (editor->priv->app), _("Contact Editor"));
728 static void
729 file_as_combo_changed (GtkWidget *widget,
730 EContactEditor *editor)
732 GtkWidget *entry;
733 gchar *string = NULL;
735 entry = gtk_bin_get_child (GTK_BIN (widget));
736 if (entry)
737 string = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
739 if (string && *string) {
740 gchar *title;
741 title = g_strdup_printf (_("Contact Editor — %s"), string);
742 gtk_window_set_title (GTK_WINDOW (editor->priv->app), title);
743 g_free (title);
745 else {
746 gtk_window_set_title (
747 GTK_WINDOW (editor->priv->app), _("Contact Editor"));
749 sensitize_ok (editor);
751 g_free (string);
754 static void
755 company_entry_changed (GtkWidget *widget,
756 EContactEditor *editor)
758 gint style = 0;
760 style = file_as_get_style (editor);
761 file_as_set_style (editor, style);
764 static void
765 update_file_as_combo (EContactEditor *editor)
767 file_as_set_style (editor, file_as_get_style (editor));
770 static void
771 fill_in_source_field (EContactEditor *editor)
773 GtkWidget *source_menu;
775 if (!editor->priv->target_client)
776 return;
778 source_menu = e_builder_get_widget (
779 editor->priv->builder, "client-combo-box");
781 e_source_combo_box_set_active (
782 E_SOURCE_COMBO_BOX (source_menu),
783 e_client_get_source (E_CLIENT (editor->priv->target_client)));
786 static void
787 sensitize_ok (EContactEditor *ce)
789 GtkWidget *widget;
790 gboolean allow_save;
791 GtkWidget *entry_fullname =
792 e_builder_get_widget (ce->priv->builder, "entry-fullname");
793 GtkWidget *entry_file_as =
794 gtk_bin_get_child (GTK_BIN (
795 e_builder_get_widget (ce->priv->builder, "combo-file-as")));
796 GtkWidget *company_name =
797 e_builder_get_widget (ce->priv->builder, "entry-company");
798 const gchar *name_entry_string =
799 gtk_entry_get_text (GTK_ENTRY (entry_fullname));
800 const gchar *file_as_entry_string =
801 gtk_entry_get_text (GTK_ENTRY (entry_file_as));
802 const gchar *company_name_string =
803 gtk_entry_get_text (GTK_ENTRY (company_name));
805 allow_save = ce->priv->target_editable && ce->priv->changed;
807 if (!strcmp (name_entry_string, "") ||
808 !strcmp (file_as_entry_string, "")) {
809 if (strcmp (company_name_string , "")) {
810 allow_save = TRUE;
812 else
813 allow_save = FALSE;
815 widget = e_builder_get_widget (ce->priv->builder, "button-ok");
816 gtk_widget_set_sensitive (widget, allow_save);
819 static void
820 object_changed (GObject *object,
821 EContactEditor *editor)
823 if (!editor->priv->target_editable) {
824 g_warning ("non-editable contact editor has an editable field in it.");
825 return;
828 if (!editor->priv->check_merge && GTK_IS_WIDGET (object)) {
829 const gchar *widget_name;
831 widget_name = gtk_widget_get_name (GTK_WIDGET (object));
833 if (widget_name &&
834 ((g_str_equal (widget_name, "fullname")) ||
835 (g_str_equal (widget_name, "nickname")) ||
836 (g_str_equal (widget_name, "file-as")) ||
837 (g_str_has_prefix (widget_name, "email-"))))
838 editor->priv->check_merge = TRUE;
841 if (!editor->priv->changed) {
842 editor->priv->changed = TRUE;
843 sensitize_ok (editor);
847 static void
848 image_chooser_changed (GtkWidget *widget,
849 EContactEditor *editor)
851 editor->priv->image_set = TRUE;
852 editor->priv->image_changed = TRUE;
855 static void
856 set_entry_text (EContactEditor *editor,
857 GtkEntry *entry,
858 const gchar *string)
860 const gchar *oldstring = gtk_entry_get_text (entry);
862 if (!string)
863 string = "";
865 if (strcmp (string, oldstring)) {
866 g_signal_handlers_block_matched (
867 entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
868 gtk_entry_set_text (entry, string);
869 g_signal_handlers_unblock_matched (
870 entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
874 static void
875 init_email_record_location (EContactEditor *editor)
877 GtkWidget *w;
878 GtkListStore *store;
879 gint i, n_elements;
880 EContactEditorDynTable *dyntable;
881 const EABTypeLabel *email_types = eab_get_email_type_labels (&n_elements);
883 w = e_builder_get_widget (editor->priv->builder, "mail-dyntable");
884 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
885 store = e_contact_editor_dyntable_get_combo_store (dyntable);
887 for (i = 0; i < n_elements; i++) {
888 GtkTreeIter iter;
890 gtk_list_store_append (store, &iter);
891 gtk_list_store_set (store, &iter,
892 DYNTABLE_COMBO_COLUMN_TEXT, _(email_types[i].text),
893 DYNTABLE_COMBO_COLUMN_SENSITIVE, TRUE,
894 -1);
897 e_contact_editor_dyntable_set_combo_defaults (dyntable, email_default, G_N_ELEMENTS (email_default));
900 static EVCardAttributeParam *
901 get_ui_slot_param (EVCardAttribute *attr)
903 EVCardAttributeParam *param = NULL;
904 GList *param_list;
905 GList *l;
907 param_list = e_vcard_attribute_get_params (attr);
909 for (l = param_list; l; l = g_list_next (l)) {
910 const gchar *str;
912 param = l->data;
914 str = e_vcard_attribute_param_get_name (param);
915 if (!g_ascii_strcasecmp (str, EVOLUTION_UI_SLOT_PARAM))
916 break;
918 param = NULL;
921 return param;
924 static gint
925 get_ui_slot (EVCardAttribute *attr)
927 EVCardAttributeParam *param;
928 gint slot = -1;
930 param = get_ui_slot_param (attr);
932 if (param) {
933 GList *value_list;
935 value_list = e_vcard_attribute_param_get_values (param);
936 slot = atoi (value_list->data);
939 return slot;
942 static void
943 set_ui_slot (EVCardAttribute *attr,
944 gint slot)
946 EVCardAttributeParam *param;
947 gchar *slot_str;
949 param = get_ui_slot_param (attr);
950 if (!param) {
951 param = e_vcard_attribute_param_new (EVOLUTION_UI_SLOT_PARAM);
952 e_vcard_attribute_add_param (attr, param);
955 e_vcard_attribute_param_remove_values (param);
957 slot_str = g_strdup_printf ("%d", slot);
958 e_vcard_attribute_param_add_value (param, slot_str);
959 g_free (slot_str);
962 static void
963 fill_in_email (EContactEditor *editor)
965 GList *email_attr_list;
966 GList *l;
967 GtkWidget *w;
968 EContactEditorDynTable *dyntable;
969 GtkListStore *data_store;
970 GtkTreeIter iter;
972 w = e_builder_get_widget (editor->priv->builder, "mail-dyntable");
973 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
975 /* Clear */
977 e_contact_editor_dyntable_clear_data (dyntable);
979 /* Fill in */
981 data_store = e_contact_editor_dyntable_extract_data (dyntable);
983 email_attr_list = e_contact_get_attributes (
984 editor->priv->contact, E_CONTACT_EMAIL);
986 for (l = email_attr_list; l; l = g_list_next (l)) {
987 EVCardAttribute *attr = l->data;
988 gchar *email_address;
989 gint email_location;
990 gint slot;
992 email_address = e_vcard_attribute_get_value (attr);
993 email_location = eab_get_email_type_index (attr);
994 slot = get_ui_slot (attr);
995 if (slot < 1)
996 slot = EMAIL_SLOTS + 1; /* add at the end */
998 gtk_list_store_append (data_store, &iter);
999 gtk_list_store_set (data_store, &iter,
1000 DYNTABLE_STORE_COLUMN_SORTORDER, slot,
1001 DYNTABLE_STORE_COLUMN_SELECTED_ITEM, email_location,
1002 DYNTABLE_STORE_COLUMN_ENTRY_STRING, email_address,
1003 -1);
1005 g_free (email_address);
1008 g_list_free_full (email_attr_list, (GDestroyNotify) e_vcard_attribute_free);
1010 e_contact_editor_dyntable_fill_in_data (dyntable);
1013 static void
1014 extract_email (EContactEditor *editor)
1016 GList *attr_list = NULL;
1017 GList *old_attr_list;
1018 GList *ll;
1019 gint i;
1020 GtkWidget *w;
1021 EContactEditorDynTable *dyntable;
1022 GtkListStore *data_store;
1023 GtkTreeModel *tree_model;
1024 GtkTreeIter iter;
1025 gboolean valid;
1027 w = e_builder_get_widget (editor->priv->builder, "mail-dyntable");
1028 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
1029 data_store = e_contact_editor_dyntable_extract_data (dyntable);
1030 tree_model = GTK_TREE_MODEL (data_store);
1032 valid = gtk_tree_model_get_iter_first (tree_model, &iter);
1033 while (valid) {
1034 gchar *address;
1035 gint location;
1036 EVCardAttribute *attr;
1038 attr = e_vcard_attribute_new (
1039 "", e_contact_vcard_attribute (E_CONTACT_EMAIL));
1041 gtk_tree_model_get (tree_model,&iter,
1042 DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &location,
1043 DYNTABLE_STORE_COLUMN_ENTRY_STRING, &address,
1044 -1);
1046 if (location >= 0) {
1047 const gchar *type;
1048 eab_email_index_to_type (location, &type);
1049 e_vcard_attribute_add_param_with_value (
1050 attr,
1051 e_vcard_attribute_param_new (EVC_TYPE),
1052 type);
1055 e_vcard_attribute_add_value (attr, address);
1057 attr_list = g_list_prepend (attr_list, attr);
1059 valid = gtk_tree_model_iter_next (tree_model, &iter);
1061 attr_list = g_list_reverse (attr_list);
1063 /* Splice in the old attributes, minus the EMAIL_SLOTS first */
1065 old_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_EMAIL);
1066 for (ll = old_attr_list, i = 1; ll && i <= EMAIL_SLOTS; i++) {
1067 e_vcard_attribute_free (ll->data);
1068 ll = g_list_delete_link (ll, ll);
1071 old_attr_list = ll;
1072 attr_list = g_list_concat (attr_list, old_attr_list);
1074 e_contact_set_attributes (editor->priv->contact, E_CONTACT_EMAIL, attr_list);
1076 g_list_free_full (attr_list, (GDestroyNotify) e_vcard_attribute_free);
1079 static void
1080 sensitize_email (EContactEditor *editor)
1082 gboolean enabled = FALSE;
1083 GtkWidget *w;
1084 EContactEditorDynTable *dyntable;
1085 guint max_entries = SLOTS_IN_COLLAPSED_STATE;
1087 w = e_builder_get_widget (editor->priv->builder, "mail-dyntable");
1088 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
1090 if (editor->priv->target_editable) {
1091 if (is_field_supported (editor, E_CONTACT_EMAIL)) {
1092 enabled = TRUE;
1093 max_entries = EMAIL_SLOTS;
1094 } else if (is_field_supported (editor, E_CONTACT_EMAIL_4)) {
1095 enabled = TRUE;
1096 max_entries = 4;
1097 } else if (is_field_supported (editor, E_CONTACT_EMAIL_3)) {
1098 enabled = TRUE;
1099 max_entries = 3;
1100 } else if (is_field_supported (editor, E_CONTACT_EMAIL_2)) {
1101 enabled = TRUE;
1102 max_entries = 2;
1103 } else if (is_field_supported (editor, E_CONTACT_EMAIL_1)) {
1104 enabled = TRUE;
1105 max_entries = 1;
1109 gtk_widget_set_sensitive (w, enabled);
1110 e_contact_editor_dyntable_set_max_entries (dyntable, max_entries);
1113 static void
1114 row_added_cb (GtkExpander *expander)
1116 /* newly added row is always visible, setting expanded=true */
1117 gtk_expander_set_expanded (expander, TRUE);
1120 static void
1121 init_email (EContactEditor *editor)
1123 EContactEditorDynTable *dyntable;
1124 GtkExpander *expander;
1126 expander = GTK_EXPANDER (
1127 e_builder_get_widget (editor->priv->builder, "expander-contact-email"));
1128 dyntable = E_CONTACT_EDITOR_DYNTABLE (
1129 e_builder_get_widget (editor->priv->builder, "mail-dyntable"));
1131 e_contact_editor_dyntable_set_max_entries (dyntable, EMAIL_SLOTS);
1132 e_contact_editor_dyntable_set_num_columns (dyntable, SLOTS_PER_LINE, TRUE);
1133 e_contact_editor_dyntable_set_show_min (dyntable, SLOTS_IN_COLLAPSED_STATE);
1135 g_signal_connect (
1136 dyntable, "changed",
1137 G_CALLBACK (object_changed), editor);
1138 g_signal_connect_swapped (
1139 dyntable, "activate",
1140 G_CALLBACK (entry_activated), editor);
1141 g_signal_connect_swapped (
1142 dyntable, "row-added",
1143 G_CALLBACK (row_added_cb), expander);
1145 init_email_record_location (editor);
1147 gtk_expander_set_expanded (expander, TRUE);
1150 static void
1151 fill_in_phone (EContactEditor *editor)
1153 GList *tel_attr_list;
1154 GList *l;
1155 GtkWidget *w;
1156 EContactEditorDynTable *dyntable;
1157 GtkListStore *data_store;
1158 GtkTreeIter iter;
1160 w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
1161 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
1163 /* Clear */
1165 e_contact_editor_dyntable_clear_data (dyntable);
1167 /* Fill in */
1169 tel_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_TEL);
1171 data_store = e_contact_editor_dyntable_extract_data (dyntable);
1173 for (l = tel_attr_list; l; l = g_list_next (l)) {
1174 EVCardAttribute *attr = l->data;
1175 gchar *phone;
1176 gint slot;
1177 gint phone_type;
1179 slot = get_ui_slot (attr);
1180 if (slot < 0)
1181 slot = PHONE_SLOTS + 1; /* append at the end */
1183 phone_type = eab_get_phone_type_index (attr);
1184 phone = e_vcard_attribute_get_value (attr);
1186 gtk_list_store_append (data_store, &iter);
1187 gtk_list_store_set (data_store,&iter,
1188 DYNTABLE_STORE_COLUMN_SORTORDER, slot,
1189 DYNTABLE_STORE_COLUMN_SELECTED_ITEM, phone_type,
1190 DYNTABLE_STORE_COLUMN_ENTRY_STRING, phone,
1191 -1);
1193 g_free (phone);
1196 e_contact_editor_dyntable_fill_in_data (dyntable);
1198 g_list_free_full (tel_attr_list, (GDestroyNotify) e_vcard_attribute_free);
1201 static void
1202 extract_phone (EContactEditor *editor)
1204 GList *tel_attr_list = NULL;
1205 GList *old_attr_list;
1206 GList *ll;
1207 gint i;
1208 GtkWidget *w;
1209 EContactEditorDynTable *dyntable;
1210 GtkListStore *data_store;
1211 GtkTreeModel *tree_model;
1212 GtkTreeIter iter;
1213 gboolean valid;
1215 w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
1216 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
1217 data_store = e_contact_editor_dyntable_extract_data (dyntable);
1218 tree_model = GTK_TREE_MODEL (data_store);
1220 valid = gtk_tree_model_get_iter_first (tree_model, &iter);
1221 while (valid) {
1222 gint phone_type;
1223 gchar *phone;
1224 EVCardAttribute *attr;
1226 gtk_tree_model_get (tree_model,&iter,
1227 DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &phone_type,
1228 DYNTABLE_STORE_COLUMN_ENTRY_STRING, &phone,
1229 -1);
1231 attr = e_vcard_attribute_new ("", EVC_TEL);
1232 if (phone_type >= 0) {
1233 const gchar *type_1;
1234 const gchar *type_2;
1236 eab_phone_index_to_type (phone_type, &type_1, &type_2);
1238 e_vcard_attribute_add_param_with_value (
1239 attr, e_vcard_attribute_param_new (EVC_TYPE), type_1);
1241 if (type_2)
1242 e_vcard_attribute_add_param_with_value (
1243 attr, e_vcard_attribute_param_new (EVC_TYPE), type_2);
1246 e_vcard_attribute_add_value (attr, phone);
1248 tel_attr_list = g_list_prepend (tel_attr_list, attr);
1250 valid = gtk_tree_model_iter_next (tree_model, &iter);
1253 /* Splice in the old attributes, minus the PHONE_SLOTS first */
1255 tel_attr_list = g_list_reverse (tel_attr_list);
1256 old_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_TEL);
1257 for (ll = old_attr_list, i = 1; ll && i <= PHONE_SLOTS; i++) {
1258 e_vcard_attribute_free (ll->data);
1259 ll = g_list_delete_link (ll, ll);
1262 old_attr_list = ll;
1263 tel_attr_list = g_list_concat (tel_attr_list, old_attr_list);
1265 e_contact_set_attributes (editor->priv->contact, E_CONTACT_TEL, tel_attr_list);
1267 g_list_free_full (tel_attr_list, (GDestroyNotify) e_vcard_attribute_free);
1270 static void
1271 init_phone_record_type (EContactEditor *editor)
1273 GtkWidget *w;
1274 GtkListStore *store;
1275 gint i, n_elements;
1276 EContactEditorDynTable *dyntable;
1277 const EABTypeLabel *eab_phone_types;
1279 w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
1280 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
1281 store = e_contact_editor_dyntable_get_combo_store (dyntable);
1282 eab_phone_types = eab_get_phone_type_labels (&n_elements);
1284 for (i = 0; i < n_elements; i++) {
1285 GtkTreeIter iter;
1287 gtk_list_store_append (store, &iter);
1288 gtk_list_store_set (store, &iter,
1289 DYNTABLE_COMBO_COLUMN_TEXT, _(eab_phone_types[i].text),
1290 DYNTABLE_COMBO_COLUMN_SENSITIVE, TRUE,
1291 -1);
1294 e_contact_editor_dyntable_set_combo_defaults (dyntable, phones_default, G_N_ELEMENTS (phones_default));
1297 static void
1298 init_phone (EContactEditor *editor)
1300 EContactEditorDynTable *dyntable;
1301 GtkExpander *expander;
1303 expander = GTK_EXPANDER (
1304 e_builder_get_widget (editor->priv->builder, "expander-contact-phone"));
1305 dyntable = E_CONTACT_EDITOR_DYNTABLE (
1306 e_builder_get_widget (editor->priv->builder, "phone-dyntable"));
1308 e_contact_editor_dyntable_set_max_entries (dyntable, PHONE_SLOTS);
1309 e_contact_editor_dyntable_set_num_columns (dyntable, SLOTS_PER_LINE, TRUE);
1310 e_contact_editor_dyntable_set_show_min (dyntable, SLOTS_IN_COLLAPSED_STATE);
1312 g_signal_connect (
1313 dyntable, "changed",
1314 G_CALLBACK (object_changed), editor);
1315 g_signal_connect_swapped (
1316 dyntable, "activate",
1317 G_CALLBACK (entry_activated), editor);
1318 g_signal_connect_swapped (
1319 dyntable, "row-added",
1320 G_CALLBACK (row_added_cb), expander);
1322 init_phone_record_type (editor);
1324 gtk_expander_set_expanded (expander, TRUE);
1327 static void
1328 sensitize_phone_types (EContactEditor *editor)
1330 GtkWidget *w;
1331 GtkListStore *listStore;
1332 GtkTreeModel *model;
1333 GtkTreeIter iter;
1334 gint i, n_elements;
1335 gboolean valid;
1336 const EABTypeLabel *eab_phone_types;
1338 w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
1339 listStore = e_contact_editor_dyntable_get_combo_store (E_CONTACT_EDITOR_DYNTABLE (w));
1340 model = GTK_TREE_MODEL (listStore);
1342 valid = gtk_tree_model_get_iter_first (model, &iter);
1344 eab_phone_types = eab_get_phone_type_labels (&n_elements);
1345 for (i = 0; i < n_elements; i++) {
1346 if (!valid) {
1347 g_warning (G_STRLOC ": Unexpected end of phone items in combo box");
1348 return;
1351 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1352 DYNTABLE_COMBO_COLUMN_SENSITIVE,
1353 is_field_supported (editor, eab_phone_types[i].field_id),
1354 -1);
1356 valid = gtk_tree_model_iter_next (model, &iter);
1360 static void
1361 sensitize_phone (EContactEditor *editor)
1363 GtkWidget *w;
1364 gboolean enabled = FALSE;
1365 gint i, n_elements;
1366 const EABTypeLabel *eab_phone_types;
1368 w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
1370 eab_phone_types = eab_get_phone_type_labels (&n_elements);
1371 if (editor->priv->target_editable) {
1372 enabled = is_field_supported (editor, E_CONTACT_TEL);
1373 for (i = 0; i < n_elements && !enabled; i++) {
1374 enabled = is_field_supported (editor, eab_phone_types[i].field_id);
1378 gtk_widget_set_sensitive (w, enabled);
1380 sensitize_phone_types (editor);
1383 static void
1384 fill_in_sip (EContactEditor *editor)
1386 GList *sip_attr_list;
1387 GList *l;
1388 GtkWidget *w;
1389 EContactEditorDynTable *dyntable;
1390 GtkListStore *data_store;
1391 GtkTreeIter iter;
1393 w = e_builder_get_widget (editor->priv->builder, "sip-dyntable");
1394 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
1396 /* Clear */
1398 e_contact_editor_dyntable_clear_data (dyntable);
1400 /* Fill in */
1402 sip_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_SIP);
1404 data_store = e_contact_editor_dyntable_extract_data (dyntable);
1406 for (l = sip_attr_list; l; l = g_list_next (l)) {
1407 EVCardAttribute *attr = l->data;
1408 gchar *sip;
1409 gint sip_type;
1411 sip_type = eab_get_sip_type_index (attr);
1412 sip = e_vcard_attribute_get_value (attr);
1414 if (sip_type < 0)
1415 sip_type = 2;
1417 gtk_list_store_append (data_store, &iter);
1418 gtk_list_store_set (data_store,&iter,
1419 DYNTABLE_STORE_COLUMN_SORTORDER, -1,
1420 DYNTABLE_STORE_COLUMN_SELECTED_ITEM, sip_type,
1421 DYNTABLE_STORE_COLUMN_ENTRY_STRING, sip,
1422 -1);
1424 g_free (sip);
1427 e_contact_editor_dyntable_fill_in_data (dyntable);
1428 g_list_free_full (sip_attr_list, (GDestroyNotify) e_vcard_attribute_free);
1431 static void
1432 extract_sip (EContactEditor *editor)
1434 GList *sip_attr_list = NULL;
1435 GList *old_attr_list;
1436 GList *ll;
1437 gint i;
1438 GtkWidget *w;
1439 EContactEditorDynTable *dyntable;
1440 GtkListStore *data_store;
1441 GtkTreeModel *tree_model;
1442 GtkTreeIter iter;
1443 gboolean valid;
1445 w = e_builder_get_widget (editor->priv->builder, "sip-dyntable");
1446 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
1447 data_store = e_contact_editor_dyntable_extract_data (dyntable);
1448 tree_model = GTK_TREE_MODEL (data_store);
1450 valid = gtk_tree_model_get_iter_first (tree_model, &iter);
1451 while (valid) {
1452 gint sip_type;
1453 gchar *sip;
1454 EVCardAttribute *attr;
1456 gtk_tree_model_get (tree_model,&iter,
1457 DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &sip_type,
1458 DYNTABLE_STORE_COLUMN_ENTRY_STRING, &sip,
1459 -1);
1461 attr = e_vcard_attribute_new ("", EVC_X_SIP);
1462 if (sip_type >= 0) {
1463 const gchar *type_1;
1465 eab_sip_index_to_type (sip_type, &type_1);
1467 e_vcard_attribute_add_param_with_value (
1468 attr, e_vcard_attribute_param_new (EVC_TYPE), type_1);
1471 e_vcard_attribute_add_value (attr, sip);
1473 sip_attr_list = g_list_prepend (sip_attr_list, attr);
1475 valid = gtk_tree_model_iter_next (tree_model, &iter);
1478 /* Splice in the old attributes, minus the SIP_SLOTS first */
1480 sip_attr_list = g_list_reverse (sip_attr_list);
1481 old_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_SIP);
1482 for (ll = old_attr_list, i = 1; ll && i <= SIP_SLOTS; i++) {
1483 e_vcard_attribute_free (ll->data);
1484 ll = g_list_delete_link (ll, ll);
1487 old_attr_list = ll;
1488 sip_attr_list = g_list_concat (sip_attr_list, old_attr_list);
1490 e_contact_set_attributes (editor->priv->contact, E_CONTACT_SIP, sip_attr_list);
1492 g_list_free_full (sip_attr_list, (GDestroyNotify) e_vcard_attribute_free);
1495 static void
1496 init_sip_record_type (EContactEditor *editor)
1498 GtkWidget *w;
1499 GtkListStore *store;
1500 gint i, n_elements;
1501 EContactEditorDynTable *dyntable;
1502 const EABTypeLabel *sip_types = eab_get_sip_type_labels (&n_elements);
1504 w = e_builder_get_widget (editor->priv->builder, "sip-dyntable");
1505 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
1506 store = e_contact_editor_dyntable_get_combo_store (dyntable);
1508 for (i = 0; i < n_elements; i++) {
1509 GtkTreeIter iter;
1511 gtk_list_store_append (store, &iter);
1512 gtk_list_store_set (store, &iter,
1513 DYNTABLE_COMBO_COLUMN_TEXT, _(sip_types[i].text),
1514 DYNTABLE_COMBO_COLUMN_SENSITIVE, TRUE,
1515 -1);
1518 e_contact_editor_dyntable_set_combo_defaults (dyntable, sips_default, G_N_ELEMENTS (sips_default));
1521 static void
1522 init_sip (EContactEditor *editor)
1524 EContactEditorDynTable *dyntable;
1525 GtkExpander *expander;
1527 expander = GTK_EXPANDER (
1528 e_builder_get_widget (editor->priv->builder, "expander-contact-sip"));
1529 dyntable = E_CONTACT_EDITOR_DYNTABLE (
1530 e_builder_get_widget (editor->priv->builder, "sip-dyntable"));
1532 e_contact_editor_dyntable_set_max_entries (dyntable, SIP_SLOTS);
1533 e_contact_editor_dyntable_set_num_columns (dyntable, SLOTS_PER_LINE, TRUE);
1534 e_contact_editor_dyntable_set_show_min (dyntable, SLOTS_IN_COLLAPSED_STATE);
1536 g_signal_connect (
1537 dyntable, "changed",
1538 G_CALLBACK (object_changed), editor);
1539 g_signal_connect_swapped (
1540 dyntable, "activate",
1541 G_CALLBACK (entry_activated), editor);
1542 g_signal_connect_swapped (
1543 dyntable, "row-added",
1544 G_CALLBACK (row_added_cb), expander);
1546 init_sip_record_type (editor);
1548 gtk_expander_set_expanded (expander, TRUE);
1551 static gboolean
1552 check_dyntable_for_data (EContactEditor *editor,
1553 const gchar *name)
1555 EContactEditorDynTable *dyntable;
1556 GtkTreeModel *tree_model;
1557 GtkTreeIter iter;
1559 dyntable = E_CONTACT_EDITOR_DYNTABLE (e_builder_get_widget (editor->priv->builder, name));
1560 tree_model = GTK_TREE_MODEL (e_contact_editor_dyntable_extract_data (dyntable));
1562 return gtk_tree_model_get_iter_first (tree_model, &iter);
1565 static void
1566 extract_address_textview (EContactEditor *editor,
1567 gint record,
1568 EContactAddress *address)
1570 gchar *textview_name;
1571 GtkWidget *textview;
1572 GtkTextBuffer *text_buffer;
1573 GtkTextIter iter_1, iter_2;
1575 textview_name = g_strdup_printf ("textview-%s-address", address_name[record]);
1576 textview = e_builder_get_widget (editor->priv->builder, textview_name);
1577 g_free (textview_name);
1579 text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
1580 gtk_text_buffer_get_start_iter (text_buffer, &iter_1);
1582 /* Skip blank lines */
1583 while (gtk_text_iter_get_chars_in_line (&iter_1) < 1 &&
1584 !gtk_text_iter_is_end (&iter_1))
1585 gtk_text_iter_forward_line (&iter_1);
1587 if (gtk_text_iter_is_end (&iter_1))
1588 return;
1590 iter_2 = iter_1;
1591 gtk_text_iter_forward_to_line_end (&iter_2);
1593 /* Extract street (first line of text) */
1594 address->street = gtk_text_iter_get_text (&iter_1, &iter_2);
1596 iter_1 = iter_2;
1597 gtk_text_iter_forward_line (&iter_1);
1599 if (gtk_text_iter_is_end (&iter_1))
1600 return;
1602 gtk_text_iter_forward_to_end (&iter_2);
1604 /* Extract extended address (remaining lines of text) */
1605 address->ext = gtk_text_iter_get_text (&iter_1, &iter_2);
1608 static gchar *
1609 extract_address_field (EContactEditor *editor,
1610 gint record,
1611 const gchar *widget_field_name)
1613 gchar *entry_name;
1614 GtkWidget *entry;
1616 entry_name = g_strdup_printf (
1617 "entry-%s-%s", address_name[record], widget_field_name);
1618 entry = e_builder_get_widget (editor->priv->builder, entry_name);
1619 g_free (entry_name);
1621 return g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
1624 static void
1625 extract_address_from_gui (EContactEditor* editor,
1626 EContactAddress* address,
1627 gint record)
1629 extract_address_textview (editor, record, address);
1630 address->locality = extract_address_field (editor, record, "city");
1631 address->region = extract_address_field (editor, record, "state");
1632 address->code = extract_address_field (editor, record, "zip");
1633 address->country = extract_address_field (editor, record, "country");
1634 address->po = extract_address_field (editor, record, "pobox");
1637 static gboolean
1638 check_address_for_data (EContactEditor *editor,
1639 gint record)
1641 gboolean has_data = FALSE;
1642 EContactAddress *address;
1644 address = g_new0 (EContactAddress, 1);
1646 extract_address_from_gui (editor, address, record);
1647 if (!STRING_IS_EMPTY (address->street) ||
1648 !STRING_IS_EMPTY (address->ext) ||
1649 !STRING_IS_EMPTY (address->locality) ||
1650 !STRING_IS_EMPTY (address->region) ||
1651 !STRING_IS_EMPTY (address->code) ||
1652 !STRING_IS_EMPTY (address->po) ||
1653 !STRING_IS_EMPTY (address->country)) {
1654 has_data = TRUE;
1657 g_free (address);
1659 return has_data;
1662 static gboolean
1663 check_web_for_data (EContactEditor *editor)
1665 GtkBuilder *b = editor->priv->builder;
1667 return !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-homepage")))) ||
1668 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-weblog")))) ||
1669 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-caluri")))) ||
1670 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-fburl")))) ||
1671 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-videourl")))) ;
1674 static gboolean
1675 check_job_for_data (EContactEditor *editor)
1677 GtkBuilder *b = editor->priv->builder;
1679 return !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-manager")))) ||
1680 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-assistant")))) ||
1681 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-profession")))) ||
1682 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-jobtitle")))) ||
1683 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-company")))) ||
1684 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-department")))) ||
1685 !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-office"))));
1688 static gboolean
1689 check_misc_for_data (EContactEditor *editor)
1691 GtkBuilder *b = editor->priv->builder;
1692 gint year, month, day;
1694 return !STRING_IS_EMPTY (gtk_entry_get_text (GTK_ENTRY (e_builder_get_widget (b, "entry-spouse")))) ||
1695 e_date_edit_get_date (E_DATE_EDIT (e_builder_get_widget (b, "dateedit-birthday")), &year, &month, &day) ||
1696 e_date_edit_get_date (E_DATE_EDIT (e_builder_get_widget (b, "dateedit-anniversary")), &year, &month, &day);
1699 static gboolean
1700 check_notes_for_data (EContactEditor *editor)
1702 GtkWidget *tv = e_builder_get_widget (editor->priv->builder, "text-comments");
1703 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
1705 return gtk_text_buffer_get_char_count (buffer) > 0;
1708 static gboolean
1709 check_certs_for_data (EContactEditor *editor)
1711 GtkWidget *treeview = e_builder_get_widget (editor->priv->builder, "certs-treeview");
1712 GtkTreeModel *model;
1713 GtkTreeIter iter;
1715 model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
1716 return model && gtk_tree_model_get_iter_first (model, &iter);
1719 static gboolean
1720 check_section_for_data (EContactEditor *editor,
1721 gint check)
1723 gboolean has_data = TRUE;
1725 switch (check) {
1726 case CHECK_PHONE:
1727 has_data = check_dyntable_for_data (editor, "phone-dyntable");
1728 break;
1729 case CHECK_SIP:
1730 has_data = check_dyntable_for_data (editor, "sip-dyntable");
1731 break;
1732 case CHECK_IM:
1733 has_data = check_dyntable_for_data (editor, "im-dyntable");
1734 break;
1735 case CHECK_HOME:
1736 has_data = check_address_for_data (editor, ADDRESS_SLOT_HOME);
1737 break;
1738 case CHECK_WORK:
1739 has_data = check_address_for_data (editor, ADDRESS_SLOT_WORK);
1740 break;
1741 case CHECK_OTHER:
1742 has_data = check_address_for_data (editor, ADDRESS_SLOT_OTHER);
1743 break;
1744 case CHECK_WEB:
1745 has_data = check_web_for_data (editor);
1746 break;
1747 case CHECK_JOB:
1748 has_data = check_job_for_data (editor);
1749 break;
1750 case CHECK_MISC:
1751 has_data = check_misc_for_data (editor);
1752 break;
1753 case CHECK_NOTE:
1754 has_data = check_notes_for_data (editor);
1755 break;
1756 case CHECK_CERTS:
1757 has_data = check_certs_for_data (editor);
1758 break;
1759 default:
1760 g_warning ("unknown data check requested");
1763 return has_data;
1766 static void
1767 config_sensitize_item (EContactEditor *editor,
1768 const gchar *item_name,
1769 gint check)
1771 GtkWidget *item;
1772 gboolean has_data;
1774 has_data = check_section_for_data (editor, check);
1775 item = e_builder_get_widget (editor->priv->builder, item_name);
1777 if (has_data) {
1778 gtk_widget_set_sensitive (item, FALSE);
1779 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
1780 } else {
1781 gtk_widget_set_sensitive (item, TRUE);
1785 static void
1786 config_sensitize_cb (GtkWidget *button,
1787 EContactEditor *editor)
1789 config_sensitize_item (editor, "menuitem-config-phone", CHECK_PHONE);
1790 config_sensitize_item (editor, "menuitem-config-sip", CHECK_SIP);
1791 config_sensitize_item (editor, "menuitem-config-im", CHECK_IM);
1793 config_sensitize_item (editor, "menuitem-config-web", CHECK_WEB);
1794 config_sensitize_item (editor, "menuitem-config-job", CHECK_JOB);
1795 config_sensitize_item (editor, "menuitem-config-misc", CHECK_MISC);
1797 config_sensitize_item (editor, "menuitem-config-home", CHECK_HOME);
1798 config_sensitize_item (editor, "menuitem-config-work", CHECK_WORK);
1799 config_sensitize_item (editor, "menuitem-config-other", CHECK_OTHER);
1801 config_sensitize_item (editor, "menuitem-config-notes", CHECK_NOTE);
1802 config_sensitize_item (editor, "menuitem-config-certs", CHECK_CERTS);
1806 * get the value from GSettings and check if there is data in the widget.
1807 * if no data is found set_visible (value), set_visible (true) otherwise
1809 * Returns: the new visibility
1811 static gboolean
1812 configure_widget_visibility (EContactEditor *editor,
1813 GSettings *settings,
1814 const gchar *widget_name,
1815 const gchar *settings_name,
1816 gint check)
1818 gboolean config, has_data;
1819 GtkWidget *widget;
1821 config = g_settings_get_boolean (settings, settings_name);
1822 widget = e_builder_get_widget (editor->priv->builder, widget_name);
1823 has_data = check_section_for_data (editor, check);
1825 gtk_widget_set_visible (widget, config || has_data);
1827 return config || has_data;
1830 static void
1831 configure_visibility (EContactEditor *editor)
1833 gboolean show_tab;
1834 GSettings *settings = e_util_ref_settings ("org.gnome.evolution.addressbook");
1836 configure_widget_visibility (editor, settings, "vbox-contact-phone", "editor-show-contact-phone", CHECK_PHONE);
1837 configure_widget_visibility (editor, settings, "vbox-contact-sip", "editor-show-contact-sip", CHECK_SIP);
1838 configure_widget_visibility (editor, settings, "vbox-contact-im", "editor-show-contact-im", CHECK_IM);
1840 show_tab = configure_widget_visibility (editor, settings, "frame-mailing-home", "editor-show-mailing-home", CHECK_HOME);
1841 show_tab |= configure_widget_visibility (editor, settings, "frame-mailing-work", "editor-show-mailing-work", CHECK_WORK);
1842 show_tab |= configure_widget_visibility (editor, settings, "expander-address-other", "editor-show-mailing-other", CHECK_OTHER);
1843 gtk_widget_set_visible (
1844 e_builder_get_widget (editor->priv->builder, "scrolledwindow-mailing"),
1845 show_tab);
1847 show_tab = configure_widget_visibility (editor, settings, "expander-personal-web", "editor-show-personal-web", CHECK_WEB);
1848 show_tab |= configure_widget_visibility (editor, settings, "expander-personal-job", "editor-show-personal-job", CHECK_JOB);
1849 show_tab |= configure_widget_visibility (editor, settings, "expander-personal-misc", "editor-show-personal-misc", CHECK_MISC);
1850 gtk_widget_set_visible (
1851 e_builder_get_widget (editor->priv->builder, "scrolledwindow-personal"),
1852 show_tab);
1854 configure_widget_visibility (editor, settings, "scrolledwindow-notes", "editor-show-notes", CHECK_NOTE);
1855 configure_widget_visibility (editor, settings, "certs-grid", "editor-show-certs", CHECK_CERTS);
1857 g_object_unref (settings);
1860 static void
1861 config_menuitem_save (EContactEditor *editor,
1862 GSettings *settings,
1863 const gchar *item_name,
1864 const gchar *key)
1866 GtkWidget *item;
1867 gboolean active, sensitive;
1869 item = e_builder_get_widget (editor->priv->builder, item_name);
1870 active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
1871 sensitive = gtk_widget_get_sensitive (item);
1873 if (sensitive)
1874 g_settings_set_boolean (settings, key, active);
1877 static void
1878 config_save_cb (GtkWidget *button,
1879 EContactEditor *editor)
1881 GSettings *settings;
1883 settings = e_util_ref_settings ("org.gnome.evolution.addressbook");
1885 config_menuitem_save (editor, settings, "menuitem-config-phone", "editor-show-contact-phone");
1886 config_menuitem_save (editor, settings, "menuitem-config-sip", "editor-show-contact-sip");
1887 config_menuitem_save (editor, settings, "menuitem-config-im", "editor-show-contact-im");
1889 config_menuitem_save (editor, settings, "menuitem-config-web", "editor-show-personal-web");
1890 config_menuitem_save (editor, settings, "menuitem-config-job", "editor-show-personal-job");
1891 config_menuitem_save (editor, settings, "menuitem-config-misc", "editor-show-personal-misc");
1893 config_menuitem_save (editor, settings, "menuitem-config-home", "editor-show-mailing-home");
1894 config_menuitem_save (editor, settings, "menuitem-config-work", "editor-show-mailing-work");
1895 config_menuitem_save (editor, settings, "menuitem-config-other", "editor-show-mailing-other");
1897 config_menuitem_save (editor, settings, "menuitem-config-notes", "editor-show-notes");
1898 config_menuitem_save (editor, settings, "menuitem-config-certs", "editor-show-certs");
1900 g_object_unref (settings);
1902 configure_visibility (editor);
1905 static void
1906 init_config_menuitem (EContactEditor *editor,
1907 GSettings *settings,
1908 const gchar *item_name,
1909 const gchar *key)
1911 gboolean show;
1912 GtkWidget *item;
1914 show = g_settings_get_boolean (settings, key);
1915 item = e_builder_get_widget (editor->priv->builder, item_name);
1916 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), show);
1918 g_signal_connect (
1919 item, "activate",
1920 G_CALLBACK (config_save_cb), editor);
1923 static void
1924 init_config (EContactEditor *editor)
1926 GtkWidget *button, *menu;
1927 GSettings *settings;
1929 button = e_builder_get_widget (editor->priv->builder, "button-config");
1930 menu = e_builder_get_widget (editor->priv->builder, "menu-editor-config");
1931 gtk_menu_button_set_popup (GTK_MENU_BUTTON (button), menu);
1933 /* save resources by only doing the data checks and sensitizing upon request,
1934 * instead of doing it with each change in object_changed()
1936 g_signal_connect (
1937 button, "clicked",
1938 G_CALLBACK (config_sensitize_cb), editor);
1940 settings = e_util_ref_settings ("org.gnome.evolution.addressbook");
1942 init_config_menuitem (editor, settings, "menuitem-config-phone", "editor-show-contact-phone");
1943 init_config_menuitem (editor, settings, "menuitem-config-sip", "editor-show-contact-sip");
1944 init_config_menuitem (editor, settings, "menuitem-config-im", "editor-show-contact-im");
1946 init_config_menuitem (editor, settings, "menuitem-config-web", "editor-show-personal-web");
1947 init_config_menuitem (editor, settings, "menuitem-config-job", "editor-show-personal-job");
1948 init_config_menuitem (editor, settings, "menuitem-config-misc", "editor-show-personal-misc");
1950 init_config_menuitem (editor, settings, "menuitem-config-home", "editor-show-mailing-home");
1951 init_config_menuitem (editor, settings, "menuitem-config-work", "editor-show-mailing-work");
1952 init_config_menuitem (editor, settings, "menuitem-config-other", "editor-show-mailing-other");
1954 init_config_menuitem (editor, settings, "menuitem-config-notes", "editor-show-notes");
1955 init_config_menuitem (editor, settings, "menuitem-config-certs", "editor-show-certs");
1957 g_object_unref (settings);
1960 static void
1961 sensitize_sip_types (EContactEditor *editor)
1963 GtkWidget *w;
1964 GtkListStore *listStore;
1965 GtkTreeModel *model;
1966 GtkTreeIter iter;
1967 gint i, n_elements;
1968 gboolean valid;
1969 const EABTypeLabel *sip_types = eab_get_sip_type_labels (&n_elements);
1971 w = e_builder_get_widget (editor->priv->builder, "sip-dyntable");
1972 listStore = e_contact_editor_dyntable_get_combo_store (E_CONTACT_EDITOR_DYNTABLE (w));
1973 model = GTK_TREE_MODEL (listStore);
1975 valid = gtk_tree_model_get_iter_first (model, &iter);
1977 for (i = 0; i < n_elements; i++) {
1978 if (!valid) {
1979 g_warning (G_STRLOC ": Unexpected end of sip items in combo box");
1980 return;
1983 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1984 DYNTABLE_COMBO_COLUMN_SENSITIVE,
1985 is_field_supported (editor, sip_types[i].field_id),
1986 -1);
1988 valid = gtk_tree_model_iter_next (model, &iter);
1992 static void
1993 sensitize_sip (EContactEditor *editor)
1995 GtkWidget *w;
1996 gboolean enabled = TRUE;
1998 w = e_builder_get_widget (editor->priv->builder, "sip-dyntable");
2000 if (!editor->priv->target_editable ||
2001 !is_field_supported (editor, E_CONTACT_SIP))
2002 enabled = FALSE;
2004 gtk_widget_set_sensitive (w, enabled);
2006 sensitize_sip_types (editor);
2009 static void
2010 init_im_record_type (EContactEditor *editor)
2012 GtkWidget *w;
2013 GtkListStore *store;
2014 gint i, n_elements;
2015 EContactEditorDynTable *dyntable;
2016 const EABTypeLabel *im_service;
2018 w = e_builder_get_widget (editor->priv->builder, "im-dyntable");
2019 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
2020 store = e_contact_editor_dyntable_get_combo_store (dyntable);
2022 im_service = eab_get_im_type_labels (&n_elements);
2023 for (i = 0; i < n_elements; i++) {
2024 GtkTreeIter iter;
2026 gtk_list_store_append (store, &iter);
2027 gtk_list_store_set (store, &iter,
2028 DYNTABLE_COMBO_COLUMN_TEXT, _(im_service[i].text),
2029 DYNTABLE_COMBO_COLUMN_SENSITIVE, TRUE,
2030 -1);
2033 e_contact_editor_dyntable_set_combo_defaults (dyntable, im_service_default, G_N_ELEMENTS (im_service_default));
2036 static void
2037 init_im (EContactEditor *editor)
2039 EContactEditorDynTable *dyntable;
2040 GtkExpander *expander;
2042 expander = GTK_EXPANDER (
2043 e_builder_get_widget (editor->priv->builder, "expander-contact-im"));
2044 dyntable = E_CONTACT_EDITOR_DYNTABLE (
2045 e_builder_get_widget (editor->priv->builder, "im-dyntable"));
2047 e_contact_editor_dyntable_set_max_entries (dyntable, IM_SLOTS);
2048 e_contact_editor_dyntable_set_num_columns (dyntable, SLOTS_PER_LINE, TRUE);
2049 e_contact_editor_dyntable_set_show_min (dyntable, SLOTS_IN_COLLAPSED_STATE);
2051 g_signal_connect (
2052 dyntable, "changed",
2053 G_CALLBACK (object_changed), editor);
2054 g_signal_connect_swapped (
2055 dyntable, "activate",
2056 G_CALLBACK (entry_activated), editor);
2057 g_signal_connect_swapped (
2058 dyntable, "row-added",
2059 G_CALLBACK (row_added_cb), expander);
2061 init_im_record_type (editor);
2063 gtk_expander_set_expanded (expander, TRUE);
2066 static void
2067 fill_in_im (EContactEditor *editor)
2069 GList *im_attr_list;
2070 GList *l;
2071 GtkWidget *w;
2072 EContactEditorDynTable *dyntable;
2073 GtkListStore *data_store;
2074 GtkTreeIter iter;
2076 w = e_builder_get_widget (editor->priv->builder, "im-dyntable");
2077 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
2079 /* Clear */
2081 e_contact_editor_dyntable_clear_data (dyntable);
2083 /* Fill in */
2085 data_store = e_contact_editor_dyntable_extract_data (dyntable);
2087 im_attr_list = e_contact_get_attributes_set (
2088 editor->priv->contact,
2089 im_service_fetch_set,
2090 G_N_ELEMENTS (im_service_fetch_set)
2093 for (l = im_attr_list; l; l = g_list_next(l)) {
2094 EVCardAttribute *attr = l->data;
2095 gchar *im_name;
2096 gint service_type;
2097 gint slot;
2099 im_name = e_vcard_attribute_get_value (attr);
2100 service_type = eab_get_im_type_index (attr);
2102 slot = get_ui_slot (attr);
2103 if (slot < 0)
2104 slot = IM_SLOTS + 1; /* attach at the end */
2106 gtk_list_store_append (data_store, &iter);
2107 gtk_list_store_set (data_store, &iter,
2108 DYNTABLE_STORE_COLUMN_SORTORDER, slot,
2109 DYNTABLE_STORE_COLUMN_SELECTED_ITEM, service_type,
2110 DYNTABLE_STORE_COLUMN_ENTRY_STRING, im_name,
2111 -1);
2113 g_free (im_name);
2116 g_list_free_full (im_attr_list, (GDestroyNotify) e_vcard_attribute_free);
2118 e_contact_editor_dyntable_fill_in_data (dyntable);
2121 static void
2122 extract_im (EContactEditor *editor)
2124 GList *attr_list = NULL;
2125 GList *old_attr_list = NULL;
2126 GList *ll;
2127 gint ii;
2128 GtkWidget *w;
2129 EContactEditorDynTable *dyntable;
2130 GtkListStore *data_store;
2131 GtkTreeModel *tree_model;
2132 GtkTreeIter iter;
2133 gboolean valid;
2135 w = e_builder_get_widget (editor->priv->builder, "im-dyntable");
2136 dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
2137 data_store = e_contact_editor_dyntable_extract_data (dyntable);
2138 tree_model = GTK_TREE_MODEL (data_store);
2140 valid = gtk_tree_model_get_iter_first (tree_model, &iter);
2141 while (valid) {
2142 gint service_type;
2143 gint slot;
2144 gchar *im_name;
2145 EVCardAttribute *attr;
2146 const EABTypeLabel *im_service = eab_get_im_type_labels (&service_type);
2148 gtk_tree_model_get (tree_model,&iter,
2149 DYNTABLE_STORE_COLUMN_SORTORDER, &slot,
2150 DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &service_type,
2151 DYNTABLE_STORE_COLUMN_ENTRY_STRING, &im_name,
2152 -1);
2154 attr = e_vcard_attribute_new ("",
2155 e_contact_vcard_attribute (
2156 im_service[service_type].field_id));
2158 /* older evolution versions (<=3.12) will crash if SLOT>4 is stored,
2159 * but if we don't store the slot we loose sortorder.
2160 * this works only for <=4 IM slots. for more, old evolution
2161 * will go through types (AIM, Jabber, ...) and stop after 4
2162 * no matter what x-evo-slot says.
2164 if (slot < 4)
2165 set_ui_slot (attr, slot + 1);
2167 e_vcard_attribute_add_value (attr, im_name);
2169 attr_list = g_list_prepend (attr_list, attr);
2171 valid = gtk_tree_model_iter_next (tree_model, &iter);
2173 attr_list = g_list_reverse (attr_list);
2175 /* Splice in the old attributes, minus the IM_SLOTS first */
2177 old_attr_list = e_contact_get_attributes_set (
2178 editor->priv->contact,
2179 im_service_fetch_set,
2180 G_N_ELEMENTS (im_service_fetch_set)
2182 for (ll = old_attr_list, ii = 0; ll && ii < IM_SLOTS; ii++) {
2183 e_vcard_attribute_free (ll->data);
2184 ll = g_list_delete_link (ll, ll);
2187 old_attr_list = ll;
2188 attr_list = g_list_concat (attr_list, old_attr_list);
2190 for (ii = 0; ii < G_N_ELEMENTS (im_service_fetch_set); ii++) {
2191 e_contact_set_attributes (editor->priv->contact, im_service_fetch_set[ii], NULL);
2194 for (ll = attr_list; ll; ll = ll->next) {
2195 EVCard *vcard;
2196 vcard = E_VCARD (editor->priv->contact);
2197 e_vcard_append_attribute (vcard, e_vcard_attribute_copy ((EVCardAttribute *) ll->data));
2200 g_list_free_full (attr_list, (GDestroyNotify) e_vcard_attribute_free);
2203 static void
2204 sensitize_im_types (EContactEditor *editor)
2206 GtkWidget *w;
2207 GtkListStore *list_store;
2208 GtkTreeModel *model;
2209 GtkTreeIter iter;
2210 gint i, n_elements;
2211 gboolean valid;
2212 const EABTypeLabel *im_service = eab_get_im_type_labels (&n_elements);
2214 w = e_builder_get_widget (editor->priv->builder, "im-dyntable");
2215 list_store = e_contact_editor_dyntable_get_combo_store (E_CONTACT_EDITOR_DYNTABLE (w));
2216 model = GTK_TREE_MODEL (list_store);
2218 valid = gtk_tree_model_get_iter_first (model, &iter);
2220 for (i = 0; i < n_elements; i++) {
2221 if (!valid) {
2222 g_warning (G_STRLOC ": Unexpected end of im items in combo box");
2223 return;
2226 gtk_list_store_set (
2227 GTK_LIST_STORE (model), &iter,
2228 DYNTABLE_COMBO_COLUMN_SENSITIVE,
2229 is_field_supported (editor, im_service[i].field_id),
2230 -1);
2232 valid = gtk_tree_model_iter_next (model, &iter);
2236 static void
2237 sensitize_im (EContactEditor *editor)
2239 gint i, n_elements;
2240 gboolean enabled;
2241 gboolean no_ims_supported;
2242 GtkWidget *w;
2243 const EABTypeLabel *im_service = eab_get_im_type_labels (&n_elements);
2245 enabled = editor->priv->target_editable;
2246 no_ims_supported = TRUE;
2248 for (i = 0; i < n_elements; i++)
2249 if (is_field_supported (editor, im_service[i].field_id)) {
2250 no_ims_supported = FALSE;
2251 break;
2254 if (no_ims_supported)
2255 enabled = FALSE;
2257 w = e_builder_get_widget (editor->priv->builder, "im-dyntable");
2258 gtk_widget_set_sensitive (w, enabled);
2260 sensitize_im_types (editor);
2263 static void
2264 init_address_textview (EContactEditor *editor,
2265 gint record)
2267 gchar *textview_name;
2268 GtkWidget *textview;
2269 GtkTextBuffer *text_buffer;
2271 textview_name = g_strdup_printf (
2272 "textview-%s-address", address_name[record]);
2273 textview = e_builder_get_widget (editor->priv->builder, textview_name);
2274 g_free (textview_name);
2276 text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
2278 g_signal_connect (
2279 text_buffer, "changed",
2280 G_CALLBACK (object_changed), editor);
2283 static void
2284 init_address_field (EContactEditor *editor,
2285 gint record,
2286 const gchar *widget_field_name)
2288 gchar *entry_name;
2289 GtkWidget *entry;
2291 entry_name = g_strdup_printf (
2292 "entry-%s-%s", address_name[record], widget_field_name);
2293 entry = e_builder_get_widget (editor->priv->builder, entry_name);
2294 g_free (entry_name);
2296 g_signal_connect (
2297 entry, "changed",
2298 G_CALLBACK (object_changed), editor);
2299 g_signal_connect_swapped (
2300 entry, "activate",
2301 G_CALLBACK (entry_activated), editor);
2304 static void
2305 init_address_record (EContactEditor *editor,
2306 gint record)
2308 init_address_textview (editor, record);
2309 init_address_field (editor, record, "city");
2310 init_address_field (editor, record, "state");
2311 init_address_field (editor, record, "zip");
2312 init_address_field (editor, record, "country");
2313 init_address_field (editor, record, "pobox");
2316 static void
2317 init_address (EContactEditor *editor)
2319 gint i;
2321 for (i = 0; i < ADDRESS_SLOTS; i++)
2322 init_address_record (editor, i);
2324 gtk_expander_set_expanded (
2325 GTK_EXPANDER (e_builder_get_widget (editor->priv->builder, "expander-address-other")),
2326 !editor->priv->compress_ui);
2329 static void
2330 fill_in_address_textview (EContactEditor *editor,
2331 gint record,
2332 EContactAddress *address)
2334 gchar *textview_name;
2335 GtkWidget *textview;
2336 GtkTextBuffer *text_buffer;
2337 GtkTextIter iter_end, iter_start;
2339 textview_name = g_strdup_printf ("textview-%s-address", address_name[record]);
2340 textview = e_builder_get_widget (editor->priv->builder, textview_name);
2341 g_free (textview_name);
2343 text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
2344 gtk_text_buffer_set_text (text_buffer, address->street ? address->street : "", -1);
2346 gtk_text_buffer_get_end_iter (text_buffer, &iter_end);
2347 if (address->ext && *address->ext) {
2348 gtk_text_buffer_insert (text_buffer, &iter_end, "\n", -1);
2349 gtk_text_buffer_insert (text_buffer, &iter_end, address->ext, -1);
2350 } else {
2351 gtk_text_buffer_insert (text_buffer, &iter_end, "", -1);
2353 gtk_text_buffer_get_iter_at_line (text_buffer, &iter_start, 0);
2354 gtk_text_buffer_place_cursor (text_buffer, &iter_start);
2357 static void
2358 fill_in_address_label_textview (EContactEditor *editor,
2359 gint record,
2360 const gchar *label)
2362 gchar *textview_name;
2363 GtkWidget *textview;
2364 GtkTextBuffer *text_buffer;
2366 textview_name = g_strdup_printf (
2367 "textview-%s-address", address_name[record]);
2368 textview = e_builder_get_widget (editor->priv->builder, textview_name);
2369 g_free (textview_name);
2371 text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
2372 gtk_text_buffer_set_text (text_buffer, label ? label : "", -1);
2375 static void
2376 fill_in_address_field (EContactEditor *editor,
2377 gint record,
2378 const gchar *widget_field_name,
2379 const gchar *string)
2381 gchar *entry_name;
2382 GtkWidget *entry;
2384 entry_name = g_strdup_printf (
2385 "entry-%s-%s", address_name[record], widget_field_name);
2386 entry = e_builder_get_widget (editor->priv->builder, entry_name);
2387 g_free (entry_name);
2389 set_entry_text (editor, GTK_ENTRY (entry), string);
2392 static void
2393 fill_in_address_record (EContactEditor *editor,
2394 gint record)
2396 EContactAddress *address;
2397 gchar *address_label;
2399 address = e_contact_get (editor->priv->contact, addresses[record]);
2400 address_label = e_contact_get (editor->priv->contact, address_labels[record]);
2402 if (address &&
2403 (!STRING_IS_EMPTY (address->street) ||
2404 !STRING_IS_EMPTY (address->ext) ||
2405 !STRING_IS_EMPTY (address->locality) ||
2406 !STRING_IS_EMPTY (address->region) ||
2407 !STRING_IS_EMPTY (address->code) ||
2408 !STRING_IS_EMPTY (address->po) ||
2409 !STRING_IS_EMPTY (address->country))) {
2410 fill_in_address_textview (editor, record, address);
2411 fill_in_address_field (editor, record, "city", address->locality);
2412 fill_in_address_field (editor, record, "state", address->region);
2413 fill_in_address_field (editor, record, "zip", address->code);
2414 fill_in_address_field (editor, record, "country", address->country);
2415 fill_in_address_field (editor, record, "pobox", address->po);
2416 } else if (!STRING_IS_EMPTY (address_label)) {
2417 fill_in_address_label_textview (editor, record, address_label);
2420 g_free (address_label);
2421 if (address)
2422 g_boxed_free (e_contact_address_get_type (), address);
2425 static void
2426 fill_in_address (EContactEditor *editor)
2428 gint i;
2430 for (i = 0; i < ADDRESS_SLOTS; i++)
2431 fill_in_address_record (editor, i);
2434 static gchar *
2435 append_to_address_label (gchar *address_label,
2436 const gchar *part,
2437 gboolean newline)
2439 gchar *new_address_label;
2441 if (STRING_IS_EMPTY (part))
2442 return address_label;
2444 if (address_label)
2445 new_address_label = g_strjoin (
2446 newline ? "\n" : ", ",
2447 address_label, part, NULL);
2448 else
2449 new_address_label = g_strdup (part);
2451 g_free (address_label);
2452 return new_address_label;
2455 static void
2456 set_address_label (EContact *contact,
2457 EContactField field,
2458 EContactAddress *address)
2460 gchar *address_label = NULL;
2461 gboolean format_address;
2462 GSettings *settings;
2464 if (!address) {
2465 e_contact_set (contact, field, NULL);
2466 return;
2469 settings = e_util_ref_settings ("org.gnome.evolution.addressbook");
2470 format_address = g_settings_get_boolean (settings, "address-formatting");
2471 g_object_unref (settings);
2473 if (format_address) {
2474 address_label = eab_format_address (
2475 contact,
2476 (field == E_CONTACT_ADDRESS_LABEL_WORK) ?
2477 E_CONTACT_ADDRESS_WORK :
2478 E_CONTACT_ADDRESS_HOME);
2481 if (!format_address || !address_label) {
2482 address_label = append_to_address_label (
2483 address_label, address->street, TRUE);
2484 address_label = append_to_address_label (
2485 address_label, address->ext, TRUE);
2486 address_label = append_to_address_label (
2487 address_label, address->locality, TRUE);
2488 address_label = append_to_address_label (
2489 address_label, address->region, FALSE);
2490 address_label = append_to_address_label (
2491 address_label, address->code, TRUE);
2492 address_label = append_to_address_label (
2493 address_label, address->po, TRUE);
2494 address_label = append_to_address_label (
2495 address_label, address->country, TRUE);
2498 e_contact_set (contact, field, address_label);
2499 g_free (address_label);
2502 static void
2503 extract_address_record (EContactEditor *editor,
2504 gint record)
2506 EContactAddress *address;
2508 address = g_new0 (EContactAddress, 1);
2510 extract_address_from_gui (editor, address, record);
2511 if (!STRING_IS_EMPTY (address->street) ||
2512 !STRING_IS_EMPTY (address->ext) ||
2513 !STRING_IS_EMPTY (address->locality) ||
2514 !STRING_IS_EMPTY (address->region) ||
2515 !STRING_IS_EMPTY (address->code) ||
2516 !STRING_IS_EMPTY (address->po) ||
2517 !STRING_IS_EMPTY (address->country)) {
2518 e_contact_set (editor->priv->contact, addresses[record], address);
2519 set_address_label (editor->priv->contact, address_labels[record], address);
2521 else {
2522 e_contact_set (editor->priv->contact, addresses[record], NULL);
2523 set_address_label (editor->priv->contact, address_labels[record], NULL);
2526 g_boxed_free (e_contact_address_get_type (), address);
2529 static void
2530 extract_address (EContactEditor *editor)
2532 gint i;
2534 for (i = 0; i < ADDRESS_SLOTS; i++)
2535 extract_address_record (editor, i);
2538 static void
2539 sensitize_address_textview (EContactEditor *editor,
2540 gint record,
2541 gboolean enabled)
2543 gchar *widget_name;
2544 GtkWidget *textview;
2545 GtkWidget *label;
2547 widget_name = g_strdup_printf ("textview-%s-address", address_name[record]);
2548 textview = e_builder_get_widget (editor->priv->builder, widget_name);
2549 g_free (widget_name);
2551 widget_name = g_strdup_printf ("label-%s-address", address_name[record]);
2552 label = e_builder_get_widget (editor->priv->builder, widget_name);
2553 g_free (widget_name);
2555 gtk_text_view_set_editable (GTK_TEXT_VIEW (textview), enabled);
2556 gtk_widget_set_sensitive (label, enabled);
2559 static void
2560 sensitize_address_field (EContactEditor *editor,
2561 gint record,
2562 const gchar *widget_field_name,
2563 gboolean enabled)
2565 gchar *widget_name;
2566 GtkWidget *entry;
2567 GtkWidget *label;
2569 widget_name = g_strdup_printf (
2570 "entry-%s-%s", address_name[record], widget_field_name);
2571 entry = e_builder_get_widget (editor->priv->builder, widget_name);
2572 g_free (widget_name);
2574 widget_name = g_strdup_printf (
2575 "label-%s-%s", address_name[record], widget_field_name);
2576 label = e_builder_get_widget (editor->priv->builder, widget_name);
2577 g_free (widget_name);
2579 gtk_editable_set_editable (GTK_EDITABLE (entry), enabled);
2580 gtk_widget_set_sensitive (label, enabled);
2583 static void
2584 sensitize_address_record (EContactEditor *editor,
2585 gint record,
2586 gboolean enabled)
2588 sensitize_address_textview (editor, record, enabled);
2589 sensitize_address_field (editor, record, "city", enabled);
2590 sensitize_address_field (editor, record, "state", enabled);
2591 sensitize_address_field (editor, record, "zip", enabled);
2592 sensitize_address_field (editor, record, "country", enabled);
2593 sensitize_address_field (editor, record, "pobox", enabled);
2596 static void
2597 sensitize_address (EContactEditor *editor)
2599 gint i;
2601 for (i = 0; i < ADDRESS_SLOTS; i++) {
2602 gboolean enabled = TRUE;
2604 if (!editor->priv->target_editable ||
2605 !(is_field_supported (editor, addresses[i]) ||
2606 is_field_supported (editor, address_labels[i])))
2607 enabled = FALSE;
2609 sensitize_address_record (editor, i, enabled);
2613 typedef struct {
2614 const gchar *widget_name;
2615 gint field_id; /* EContactField or -1 */
2616 gboolean process_data; /* If we should extract/fill in contents */
2617 gboolean desensitize_for_read_only;
2619 FieldMapping;
2621 /* Table of widgets that interact with simple fields. This table is used to:
2623 * - Fill in data.
2624 * - Extract data.
2625 * - Set sensitivity based on backend capabilities.
2626 * - Set sensitivity based on book writeability. */
2628 static FieldMapping simple_field_map[] = {
2629 { "entry-homepage", E_CONTACT_HOMEPAGE_URL, TRUE, TRUE },
2630 { "accellabel-homepage", E_CONTACT_HOMEPAGE_URL, FALSE, TRUE },
2632 { "entry-jobtitle", E_CONTACT_TITLE, TRUE, TRUE },
2633 { "label-jobtitle", E_CONTACT_TITLE, FALSE, TRUE },
2635 { "entry-company", E_CONTACT_ORG, TRUE, TRUE },
2636 { "label-company", E_CONTACT_ORG, FALSE, TRUE },
2638 { "entry-department", E_CONTACT_ORG_UNIT, TRUE, TRUE },
2639 { "label-department", E_CONTACT_ORG_UNIT, FALSE, TRUE },
2641 { "entry-profession", E_CONTACT_ROLE, TRUE, TRUE },
2642 { "label-profession", E_CONTACT_ROLE, FALSE, TRUE },
2644 { "entry-manager", E_CONTACT_MANAGER, TRUE, TRUE },
2645 { "label-manager", E_CONTACT_MANAGER, FALSE, TRUE },
2647 { "entry-assistant", E_CONTACT_ASSISTANT, TRUE, TRUE },
2648 { "label-assistant", E_CONTACT_ASSISTANT, FALSE, TRUE },
2650 { "entry-nickname", E_CONTACT_NICKNAME, TRUE, TRUE },
2651 { "label-nickname", E_CONTACT_NICKNAME, FALSE, TRUE },
2653 { "dateedit-birthday", E_CONTACT_BIRTH_DATE, TRUE, TRUE },
2654 { "label-birthday", E_CONTACT_BIRTH_DATE, FALSE, TRUE },
2656 { "dateedit-anniversary", E_CONTACT_ANNIVERSARY, TRUE, TRUE },
2657 { "label-anniversary", E_CONTACT_ANNIVERSARY, FALSE, TRUE },
2659 { "entry-spouse", E_CONTACT_SPOUSE, TRUE, TRUE },
2660 { "label-spouse", E_CONTACT_SPOUSE, FALSE, TRUE },
2662 { "entry-office", E_CONTACT_OFFICE, TRUE, TRUE },
2663 { "label-office", E_CONTACT_OFFICE, FALSE, TRUE },
2665 { "text-comments", E_CONTACT_NOTE, TRUE, TRUE },
2667 { "entry-fullname", E_CONTACT_FULL_NAME, TRUE, TRUE },
2668 { "button-fullname", E_CONTACT_FULL_NAME, FALSE, TRUE },
2670 { "entry-categories", E_CONTACT_CATEGORIES, TRUE, TRUE },
2671 { "button-categories", E_CONTACT_CATEGORIES, FALSE, TRUE },
2673 { "entry-weblog", E_CONTACT_BLOG_URL, TRUE, TRUE },
2674 { "label-weblog", E_CONTACT_BLOG_URL, FALSE, TRUE },
2676 { "entry-caluri", E_CONTACT_CALENDAR_URI, TRUE, TRUE },
2677 { "label-caluri", E_CONTACT_CALENDAR_URI, FALSE, TRUE },
2679 { "entry-fburl", E_CONTACT_FREEBUSY_URL, TRUE, TRUE },
2680 { "label-fburl", E_CONTACT_FREEBUSY_URL, FALSE, TRUE },
2682 { "entry-videourl", E_CONTACT_VIDEO_URL, TRUE, TRUE },
2683 { "label-videourl", E_CONTACT_VIDEO_URL, FALSE, TRUE },
2685 { "checkbutton-htmlmail", E_CONTACT_WANTS_HTML, TRUE, TRUE },
2687 { "image-chooser", E_CONTACT_PHOTO, TRUE, TRUE },
2688 { "button-image", E_CONTACT_PHOTO, FALSE, TRUE },
2690 { "combo-file-as", E_CONTACT_FILE_AS, TRUE, TRUE },
2691 { "accellabel-fileas", E_CONTACT_FILE_AS, FALSE, TRUE },
2694 static void
2695 init_simple_field (EContactEditor *editor,
2696 GtkWidget *widget)
2698 GObject *changed_object = NULL;
2700 if (GTK_IS_ENTRY (widget)) {
2701 changed_object = G_OBJECT (widget);
2702 g_signal_connect_swapped (
2703 widget, "activate",
2704 G_CALLBACK (entry_activated), editor);
2706 } else if (GTK_IS_COMBO_BOX (widget)) {
2707 changed_object = G_OBJECT (widget);
2708 g_signal_connect_swapped (
2709 gtk_bin_get_child (GTK_BIN (widget)), "activate",
2710 G_CALLBACK (entry_activated), editor);
2712 } else if (GTK_IS_TEXT_VIEW (widget)) {
2713 changed_object = G_OBJECT (
2714 gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)));
2716 } else if (E_IS_URL_ENTRY (widget)) {
2717 changed_object = G_OBJECT (widget);
2718 g_signal_connect_swapped (
2719 changed_object, "activate",
2720 G_CALLBACK (entry_activated), editor);
2722 } else if (E_IS_DATE_EDIT (widget)) {
2723 changed_object = G_OBJECT (widget);
2725 } else if (E_IS_IMAGE_CHOOSER (widget)) {
2726 changed_object = G_OBJECT (widget);
2727 g_signal_connect (
2728 widget, "changed",
2729 G_CALLBACK (image_chooser_changed), editor);
2731 } else if (GTK_IS_TOGGLE_BUTTON (widget)) {
2732 g_signal_connect (
2733 widget, "toggled",
2734 G_CALLBACK (object_changed), editor);
2737 if (changed_object)
2738 g_signal_connect (
2739 changed_object, "changed",
2740 G_CALLBACK (object_changed), editor);
2743 static void
2744 fill_in_simple_field (EContactEditor *editor,
2745 GtkWidget *widget,
2746 gint field_id)
2748 EContact *contact;
2750 contact = editor->priv->contact;
2752 g_signal_handlers_block_matched (
2753 widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
2755 if (GTK_IS_ENTRY (widget)) {
2756 gchar *text = e_contact_get (contact, field_id);
2757 gtk_entry_set_text (GTK_ENTRY (widget), STRING_MAKE_NON_NULL (text));
2758 g_free (text);
2760 } else if (GTK_IS_COMBO_BOX (widget)) {
2761 gchar *text = e_contact_get (contact, field_id);
2762 gtk_entry_set_text (
2763 GTK_ENTRY (gtk_bin_get_child (GTK_BIN (widget))),
2764 STRING_MAKE_NON_NULL (text));
2765 g_free (text);
2767 } else if (GTK_IS_TEXT_VIEW (widget)) {
2768 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
2769 gchar *text = e_contact_get (contact, field_id);
2770 gtk_text_buffer_set_text (buffer, STRING_MAKE_NON_NULL (text), -1);
2771 g_free (text);
2773 } else if (E_IS_URL_ENTRY (widget)) {
2774 gchar *text = e_contact_get (contact, field_id);
2775 gtk_entry_set_text (
2776 GTK_ENTRY (widget), STRING_MAKE_NON_NULL (text));
2777 g_free (text);
2779 } else if (E_IS_DATE_EDIT (widget)) {
2780 EContactDate *date = e_contact_get (contact, field_id);
2781 if (date)
2782 e_date_edit_set_date (
2783 E_DATE_EDIT (widget),
2784 date->year, date->month, date->day);
2785 else
2786 e_date_edit_set_time (E_DATE_EDIT (widget), -1);
2788 e_contact_date_free (date);
2790 } else if (E_IS_IMAGE_CHOOSER (widget)) {
2791 EContactPhoto *photo = e_contact_get (contact, field_id);
2792 editor->priv->image_set = FALSE;
2793 if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
2794 e_image_chooser_set_image_data (
2795 E_IMAGE_CHOOSER (widget),
2796 (gchar *) photo->data.inlined.data,
2797 photo->data.inlined.length);
2798 editor->priv->image_set = TRUE;
2799 } else if (photo && photo->type == E_CONTACT_PHOTO_TYPE_URI) {
2800 gchar *file_name = g_filename_from_uri (photo->data.uri, NULL, NULL);
2801 if (file_name) {
2802 e_image_chooser_set_from_file (
2803 E_IMAGE_CHOOSER (widget),
2804 file_name);
2805 editor->priv->image_set = TRUE;
2806 g_free (file_name);
2810 if (!editor->priv->image_set) {
2811 gchar *file_name;
2813 file_name = e_icon_factory_get_icon_filename (
2814 "avatar-default", GTK_ICON_SIZE_DIALOG);
2815 e_image_chooser_set_from_file (
2816 E_IMAGE_CHOOSER (widget), file_name);
2817 editor->priv->image_set = FALSE;
2818 g_free (file_name);
2821 editor->priv->image_changed = FALSE;
2822 e_contact_photo_free (photo);
2824 } else if (GTK_IS_TOGGLE_BUTTON (widget)) {
2825 gboolean val = e_contact_get (contact, field_id) != NULL;
2827 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), val);
2829 } else {
2830 g_warning (G_STRLOC ": Unhandled widget class in mappings!");
2833 g_signal_handlers_unblock_matched (
2834 widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
2837 static void
2838 extract_simple_field (EContactEditor *editor,
2839 GtkWidget *widget,
2840 gint field_id)
2842 EContact *contact;
2844 contact = editor->priv->contact;
2846 if (GTK_IS_ENTRY (widget)) {
2847 const gchar *text = gtk_entry_get_text (GTK_ENTRY (widget));
2848 e_contact_set (contact, field_id, (gchar *) text);
2850 } else if (GTK_IS_COMBO_BOX_TEXT (widget)) {
2851 gchar *text = NULL;
2853 if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (widget))) {
2854 GtkWidget *entry = gtk_bin_get_child (GTK_BIN (widget));
2856 text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
2859 if (!text)
2860 text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (widget));
2862 e_contact_set (contact, field_id, text);
2864 g_free (text);
2865 } else if (GTK_IS_COMBO_BOX (widget)) {
2866 GtkTreeIter iter;
2867 gchar *text = NULL;
2869 if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (widget))) {
2870 GtkWidget *entry = gtk_bin_get_child (GTK_BIN (widget));
2872 text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
2875 if (!text && gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter)) {
2876 GtkListStore *store;
2878 store = GTK_LIST_STORE (
2879 gtk_combo_box_get_model (
2880 GTK_COMBO_BOX (widget)));
2882 gtk_tree_model_get (
2883 GTK_TREE_MODEL (store), &iter,
2884 0, &text,
2885 -1);
2888 e_contact_set (contact, field_id, text);
2890 g_free (text);
2892 } else if (GTK_IS_TEXT_VIEW (widget)) {
2893 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
2894 GtkTextIter start, end;
2895 gchar *text;
2897 gtk_text_buffer_get_start_iter (buffer, &start);
2898 gtk_text_buffer_get_end_iter (buffer, &end);
2899 text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
2900 e_contact_set (contact, field_id, text);
2901 g_free (text);
2903 } else if (E_IS_URL_ENTRY (widget)) {
2904 const gchar *text = gtk_entry_get_text (GTK_ENTRY (widget));
2905 e_contact_set (contact, field_id, (gchar *) text);
2907 } else if (E_IS_DATE_EDIT (widget)) {
2908 EContactDate date;
2909 if (e_date_edit_get_date (
2910 E_DATE_EDIT (widget),
2911 (gint *) &date.year,
2912 (gint *) &date.month,
2913 (gint *) &date.day))
2914 e_contact_set (contact, field_id, &date);
2915 else
2916 e_contact_set (contact, field_id, NULL);
2918 } else if (E_IS_IMAGE_CHOOSER (widget)) {
2919 EContactPhoto photo;
2920 photo.type = E_CONTACT_PHOTO_TYPE_INLINED;
2921 photo.data.inlined.mime_type = NULL;
2922 if (editor->priv->image_changed) {
2923 gchar *img_buff = NULL;
2924 if (editor->priv->image_set &&
2925 e_image_chooser_get_image_data (
2926 E_IMAGE_CHOOSER (widget),
2927 &img_buff, &photo.data.inlined.length)) {
2928 GdkPixbuf *pixbuf, *new;
2929 GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
2931 photo.data.inlined.data = (guchar *) img_buff;
2932 img_buff = NULL;
2933 gdk_pixbuf_loader_write (
2934 loader,
2935 photo.data.inlined.data,
2936 photo.data.inlined.length, NULL);
2937 gdk_pixbuf_loader_close (loader, NULL);
2939 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
2940 if (pixbuf) {
2941 gint width, height, prompt_response;
2943 g_object_ref (pixbuf);
2945 height = gdk_pixbuf_get_height (pixbuf);
2946 width = gdk_pixbuf_get_width (pixbuf);
2947 if ((height > 96 || width > 96)) {
2949 prompt_response =
2950 e_alert_run_dialog_for_args
2951 (GTK_WINDOW (editor->priv->app),
2952 "addressbook:prompt-resize",
2953 NULL);
2955 if (prompt_response == GTK_RESPONSE_YES) {
2956 if (width > height) {
2957 height = height * 96 / width;
2958 width = 96;
2959 } else {
2960 width = width *96 / height;
2961 height = 96;
2964 new = e_icon_factory_pixbuf_scale (pixbuf, width, height);
2965 if (new) {
2966 GdkPixbufFormat *format =
2967 gdk_pixbuf_loader_get_format (loader);
2968 gchar *format_name =
2969 gdk_pixbuf_format_get_name (format);
2970 g_free (photo.data.inlined.data);
2971 gdk_pixbuf_save_to_buffer (
2972 new, &img_buff,
2973 &photo.data.inlined.length,
2974 format_name, NULL, NULL);
2975 photo.data.inlined.data = (guchar *) img_buff;
2976 img_buff = NULL;
2977 g_free (format_name);
2978 g_object_unref (new);
2980 } else if (prompt_response == GTK_RESPONSE_CANCEL) {
2981 g_object_unref (pixbuf);
2982 g_object_unref (loader);
2983 return;
2986 g_object_unref (pixbuf);
2988 editor->priv->image_changed = FALSE;
2989 g_object_unref (loader);
2991 e_contact_set (contact, field_id, &photo);
2993 g_free (photo.data.inlined.data);
2995 } else {
2996 editor->priv->image_changed = FALSE;
2997 e_contact_set (contact, E_CONTACT_PHOTO, NULL);
3001 } else if (GTK_IS_TOGGLE_BUTTON (widget)) {
3002 gboolean val;
3004 val = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
3005 e_contact_set (contact, field_id, val ? (gpointer) 1 : NULL);
3007 } else {
3008 g_warning (G_STRLOC ": Unhandled widget class in mappings!");
3012 static void
3013 sensitize_simple_field (GtkWidget *widget,
3014 gboolean enabled)
3016 if (GTK_IS_ENTRY (widget))
3017 gtk_editable_set_editable (GTK_EDITABLE (widget), enabled);
3018 else if (GTK_IS_TEXT_VIEW (widget))
3019 gtk_text_view_set_editable (GTK_TEXT_VIEW (widget), enabled);
3020 else if (E_IS_DATE_EDIT (widget))
3021 e_date_edit_set_editable (E_DATE_EDIT (widget), enabled);
3022 else
3023 gtk_widget_set_sensitive (widget, enabled);
3026 static void
3027 init_simple (EContactEditor *editor)
3029 GtkWidget *widget;
3030 gint i;
3032 for (i = 0; i < G_N_ELEMENTS (simple_field_map); i++) {
3033 widget = e_builder_get_widget (
3034 editor->priv->builder, simple_field_map[i].widget_name);
3035 if (!widget)
3036 continue;
3038 init_simple_field (editor, widget);
3041 /* --- Special cases --- */
3043 /* Update file_as */
3045 widget = e_builder_get_widget (editor->priv->builder, "entry-fullname");
3046 g_signal_connect (
3047 widget, "changed",
3048 G_CALLBACK (name_entry_changed), editor);
3050 widget = e_builder_get_widget (editor->priv->builder, "combo-file-as");
3051 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (widget), 0);
3052 g_signal_connect (
3053 widget, "changed",
3054 G_CALLBACK (file_as_combo_changed), editor);
3056 widget = e_builder_get_widget (editor->priv->builder, "entry-company");
3057 g_signal_connect (
3058 widget, "changed",
3059 G_CALLBACK (company_entry_changed), editor);
3062 static void
3063 fill_in_simple (EContactEditor *editor)
3065 EContactName *name;
3066 gchar *filename;
3067 gint i;
3069 for (i = 0; i < G_N_ELEMENTS (simple_field_map); i++) {
3070 GtkWidget *widget;
3072 if (simple_field_map[i].field_id < 0 ||
3073 !simple_field_map[i].process_data)
3074 continue;
3076 widget = e_builder_get_widget (
3077 editor->priv->builder, simple_field_map[i].widget_name);
3078 if (!widget)
3079 continue;
3081 fill_in_simple_field (
3082 editor, widget, simple_field_map[i].field_id);
3085 /* --- Special cases --- */
3087 /* Update broken-up name */
3089 g_object_get (editor->priv->contact, "name", &name, NULL);
3091 if (editor->priv->name)
3092 e_contact_name_free (editor->priv->name);
3094 editor->priv->name = name;
3096 /* Update the contact editor title */
3098 filename = (gchar *) e_contact_get (editor->priv->contact, E_CONTACT_FILE_AS);
3100 if (filename) {
3101 gchar *title;
3102 title = g_strdup_printf (_("Contact Editor — %s"), filename);
3103 gtk_window_set_title (GTK_WINDOW (editor->priv->app), title);
3104 g_free (title);
3105 g_free (filename);
3106 } else
3107 gtk_window_set_title (
3108 GTK_WINDOW (editor->priv->app), _("Contact Editor"));
3110 /* Update file_as combo options */
3112 update_file_as_combo (editor);
3115 static void
3116 extract_simple (EContactEditor *editor)
3118 gint i;
3120 for (i = 0; i < G_N_ELEMENTS (simple_field_map); i++) {
3121 GtkWidget *widget;
3123 if (simple_field_map[i].field_id < 0 ||
3124 !simple_field_map[i].process_data)
3125 continue;
3127 widget = e_builder_get_widget (
3128 editor->priv->builder, simple_field_map[i].widget_name);
3129 if (!widget)
3130 continue;
3132 extract_simple_field (
3133 editor, widget, simple_field_map[i].field_id);
3136 /* Special cases */
3138 e_contact_set (editor->priv->contact, E_CONTACT_NAME, editor->priv->name);
3141 static void
3142 sensitize_simple (EContactEditor *editor)
3144 gint i;
3146 for (i = 0; i < G_N_ELEMENTS (simple_field_map); i++) {
3147 GtkWidget *widget;
3148 gboolean enabled = TRUE;
3150 widget = e_builder_get_widget (
3151 editor->priv->builder, simple_field_map[i].widget_name);
3152 if (!widget)
3153 continue;
3155 if (simple_field_map[i].field_id >= 0 &&
3156 !is_field_supported (editor, simple_field_map[i].field_id))
3157 enabled = FALSE;
3159 if (simple_field_map[i].desensitize_for_read_only &&
3160 !editor->priv->target_editable)
3161 enabled = FALSE;
3163 sensitize_simple_field (widget, enabled);
3167 enum CertKind {
3168 CERT_KIND_X509,
3169 CERT_KIND_PGP
3172 enum CertColumns {
3173 CERT_COLUMN_SUBJECT_STRING,
3174 CERT_COLUMN_KIND_STRING,
3175 CERT_COLUMN_KIND_INT,
3176 CERT_COLUMN_DATA_ECONTACTCERT,
3177 CERT_COLUMN_CERT_GCRCERTIFICATE,
3178 N_CERT_COLUMNS
3181 static void
3182 cert_tab_selection_changed_cb (GtkTreeSelection *selection,
3183 EContactEditor *editor)
3185 GtkWidget *widget;
3186 GtkTreeModel *model;
3187 GtkTreeIter iter;
3188 gboolean has_selected;
3190 g_return_if_fail (E_IS_CONTACT_EDITOR (editor));
3192 has_selected = gtk_tree_selection_get_selected (selection, &model, &iter);
3194 widget = e_builder_get_widget (editor->priv->builder, "cert-remove-btn");
3195 gtk_widget_set_sensitive (widget, has_selected);
3197 widget = e_builder_get_widget (editor->priv->builder, "cert-load-pgp-btn");
3198 gtk_widget_set_sensitive (widget, has_selected && is_field_supported (editor, E_CONTACT_PGP_CERT));
3200 widget = e_builder_get_widget (editor->priv->builder, "cert-load-x509-btn");
3201 gtk_widget_set_sensitive (widget, has_selected && is_field_supported (editor, E_CONTACT_X509_CERT));
3203 widget = e_builder_get_widget (editor->priv->builder, "cert-save-btn");
3204 gtk_widget_set_sensitive (widget, has_selected);
3206 widget = e_builder_get_widget (editor->priv->builder, "cert-preview-scw");
3207 widget = gtk_bin_get_child (GTK_BIN (widget));
3209 if (GTK_IS_VIEWPORT (widget))
3210 widget = gtk_bin_get_child (GTK_BIN (widget));
3212 g_return_if_fail (GCR_IS_CERTIFICATE_WIDGET (widget));
3214 if (has_selected) {
3215 GcrCertificate *cert = NULL;
3217 gtk_tree_model_get (model, &iter, CERT_COLUMN_CERT_GCRCERTIFICATE, &cert, -1);
3219 gcr_certificate_widget_set_certificate (GCR_CERTIFICATE_WIDGET (widget), cert);
3221 g_clear_object (&cert);
3222 } else {
3223 gcr_certificate_widget_set_certificate (GCR_CERTIFICATE_WIDGET (widget), NULL);
3227 static void
3228 cert_add_filters_for_kind (GtkFileChooser *file_chooser,
3229 enum CertKind kind)
3231 GtkFileFilter *filter;
3233 g_return_if_fail (GTK_IS_FILE_CHOOSER (file_chooser));
3234 g_return_if_fail (kind == CERT_KIND_PGP || kind == CERT_KIND_X509);
3236 if (kind == CERT_KIND_X509) {
3237 filter = gtk_file_filter_new ();
3238 gtk_file_filter_set_name (filter, _("X.509 certificates"));
3239 gtk_file_filter_add_mime_type (filter, "application/x-x509-user-cert");
3240 gtk_file_chooser_add_filter (file_chooser, filter);
3241 } else {
3242 filter = gtk_file_filter_new ();
3243 gtk_file_filter_set_name (filter, _("PGP keys"));
3244 gtk_file_filter_add_mime_type (filter, "application/pgp-keys");
3245 gtk_file_chooser_add_filter (file_chooser, filter);
3248 filter = gtk_file_filter_new ();
3249 gtk_file_filter_set_name (filter, _("All files"));
3250 gtk_file_filter_add_pattern (filter, "*");
3251 gtk_file_chooser_add_filter (file_chooser, filter);
3254 static EContactCert *
3255 cert_load_for_kind (EContactEditor *editor,
3256 enum CertKind kind)
3258 EContactCert *cert = NULL;
3259 GtkWindow *parent;
3260 GtkWidget *dialog;
3261 GtkFileChooser *file_chooser;
3262 GError *error = NULL;
3264 g_return_val_if_fail (E_IS_CONTACT_EDITOR (editor), NULL);
3265 g_return_val_if_fail (kind == CERT_KIND_PGP || kind == CERT_KIND_X509, NULL);
3267 parent = eab_editor_get_window (EAB_EDITOR (editor));
3268 dialog = gtk_file_chooser_dialog_new (
3269 kind == CERT_KIND_PGP ? _("Open PGP key") : _("Open X.509 certificate"), parent,
3270 GTK_FILE_CHOOSER_ACTION_OPEN,
3271 _("_Cancel"), GTK_RESPONSE_CANCEL,
3272 _("_Open"), GTK_RESPONSE_OK,
3273 NULL);
3275 file_chooser = GTK_FILE_CHOOSER (dialog);
3276 gtk_file_chooser_set_local_only (file_chooser, TRUE);
3277 gtk_file_chooser_set_select_multiple (file_chooser, FALSE);
3278 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
3280 cert_add_filters_for_kind (file_chooser, kind);
3282 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
3283 gchar *filename;
3284 gchar *content = NULL;
3285 gsize length = 0;
3287 filename = gtk_file_chooser_get_filename (file_chooser);
3288 if (!filename) {
3289 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Chosen file is not a local file."));
3290 } else if (g_file_get_contents (filename, &content, &length, &error) && length > 0) {
3291 cert = e_contact_cert_new ();
3292 cert->length = length;
3293 cert->data = content;
3296 g_free (filename);
3299 gtk_widget_destroy (dialog);
3301 if (error) {
3302 e_notice (parent, GTK_MESSAGE_ERROR, _("Failed to load certificate: %s"), error->message);
3303 g_clear_error (&error);
3306 return cert;
3309 static void
3310 cert_update_row_with_cert (GtkListStore *list_store,
3311 GtkTreeIter *iter,
3312 EContactCert *cert,
3313 enum CertKind kind)
3315 GcrCertificate *gcr_cert = NULL;
3316 gchar *subject = NULL;
3318 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
3319 g_return_if_fail (iter != NULL);
3320 g_return_if_fail (cert != NULL);
3321 g_return_if_fail (kind == CERT_KIND_PGP || kind == CERT_KIND_X509);
3323 if (kind == CERT_KIND_X509) {
3324 gcr_cert = gcr_simple_certificate_new ((const guchar *) cert->data, cert->length);
3325 if (gcr_cert)
3326 subject = gcr_certificate_get_subject_name (gcr_cert);
3329 gtk_list_store_set (list_store, iter,
3330 CERT_COLUMN_SUBJECT_STRING, subject,
3331 CERT_COLUMN_KIND_STRING, kind == CERT_KIND_X509 ? C_("cert-kind", "X.509") : C_("cert-kind", "PGP"),
3332 CERT_COLUMN_KIND_INT, kind,
3333 CERT_COLUMN_DATA_ECONTACTCERT, cert,
3334 CERT_COLUMN_CERT_GCRCERTIFICATE, gcr_cert,
3335 -1);
3337 g_clear_object (&gcr_cert);
3338 g_free (subject);
3341 static void
3342 cert_add_kind (EContactEditor *editor,
3343 enum CertKind kind)
3345 GtkTreeView *tree_view;
3346 GtkTreeSelection *selection;
3347 GtkTreeModel *model;
3348 GtkTreeIter iter;
3349 EContactCert *cert;
3351 g_return_if_fail (E_IS_CONTACT_EDITOR (editor));
3352 g_return_if_fail (kind == CERT_KIND_PGP || kind == CERT_KIND_X509);
3354 tree_view = GTK_TREE_VIEW (e_builder_get_widget (editor->priv->builder, "certs-treeview"));
3355 g_return_if_fail (tree_view != NULL);
3357 model = gtk_tree_view_get_model (tree_view);
3358 selection = gtk_tree_view_get_selection (tree_view);
3360 cert = cert_load_for_kind (editor, kind);
3361 if (cert) {
3362 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
3363 cert_update_row_with_cert (GTK_LIST_STORE (model), &iter, cert, kind);
3364 e_contact_cert_free (cert);
3366 gtk_tree_selection_select_iter (selection, &iter);
3368 object_changed (G_OBJECT (tree_view), editor);
3372 static void
3373 cert_add_pgp_btn_clicked_cb (GtkWidget *button,
3374 EContactEditor *editor)
3376 g_return_if_fail (E_IS_CONTACT_EDITOR (editor));
3378 cert_add_kind (editor, CERT_KIND_PGP);
3381 static void
3382 cert_add_x509_btn_clicked_cb (GtkWidget *button,
3383 EContactEditor *editor)
3385 g_return_if_fail (E_IS_CONTACT_EDITOR (editor));
3387 cert_add_kind (editor, CERT_KIND_X509);
3390 static void
3391 cert_remove_btn_clicked_cb (GtkWidget *button,
3392 EContactEditor *editor)
3394 GtkTreeView *tree_view;
3395 GtkTreeSelection *selection;
3396 GtkTreeModel *model;
3397 GtkTreeIter iter, select;
3398 gboolean have_select;
3400 g_return_if_fail (E_IS_CONTACT_EDITOR (editor));
3402 tree_view = GTK_TREE_VIEW (e_builder_get_widget (editor->priv->builder, "certs-treeview"));
3403 g_return_if_fail (tree_view != NULL);
3405 selection = gtk_tree_view_get_selection (tree_view);
3406 g_return_if_fail (gtk_tree_selection_get_selected (selection, &model, &iter));
3408 select = iter;
3409 have_select = gtk_tree_model_iter_next (model, &select);
3410 if (!have_select) {
3411 select = iter;
3412 have_select = gtk_tree_model_iter_previous (model, &select);
3415 if (have_select)
3416 gtk_tree_selection_select_iter (selection, &select);
3418 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
3420 object_changed (G_OBJECT (tree_view), editor);
3423 static void
3424 cert_load_kind (EContactEditor *editor,
3425 enum CertKind kind)
3427 GtkTreeView *tree_view;
3428 GtkTreeSelection *selection;
3429 GtkTreeModel *model;
3430 GtkTreeIter iter;
3431 EContactCert *cert;
3433 g_return_if_fail (E_IS_CONTACT_EDITOR (editor));
3434 g_return_if_fail (kind == CERT_KIND_PGP || kind == CERT_KIND_X509);
3436 tree_view = GTK_TREE_VIEW (e_builder_get_widget (editor->priv->builder, "certs-treeview"));
3437 g_return_if_fail (tree_view != NULL);
3439 selection = gtk_tree_view_get_selection (tree_view);
3440 g_return_if_fail (gtk_tree_selection_get_selected (selection, &model, &iter));
3442 cert = cert_load_for_kind (editor, kind);
3443 if (cert) {
3444 cert_update_row_with_cert (GTK_LIST_STORE (model), &iter, cert, kind);
3445 e_contact_cert_free (cert);
3447 object_changed (G_OBJECT (tree_view), editor);
3451 static void
3452 cert_load_pgp_btn_clicked_cb (GtkWidget *button,
3453 EContactEditor *editor)
3455 g_return_if_fail (E_IS_CONTACT_EDITOR (editor));
3457 cert_load_kind (editor, CERT_KIND_PGP);
3460 static void
3461 cert_load_x509_btn_clicked_cb (GtkWidget *button,
3462 EContactEditor *editor)
3464 g_return_if_fail (E_IS_CONTACT_EDITOR (editor));
3466 cert_load_kind (editor, CERT_KIND_X509);
3469 static void
3470 cert_save_btn_clicked_cb (GtkWidget *button,
3471 EContactEditor *editor)
3473 GtkTreeView *tree_view;
3474 GtkTreeSelection *selection;
3475 GtkTreeModel *model;
3476 GtkTreeIter iter;
3477 EContactCert *cert = NULL;
3478 gint kind = -1;
3479 GtkWindow *parent;
3480 GtkWidget *dialog;
3481 GtkFileChooser *file_chooser;
3482 GError *error = NULL;
3484 g_return_if_fail (E_IS_CONTACT_EDITOR (editor));
3486 tree_view = GTK_TREE_VIEW (e_builder_get_widget (editor->priv->builder, "certs-treeview"));
3487 g_return_if_fail (tree_view != NULL);
3489 selection = gtk_tree_view_get_selection (tree_view);
3490 g_return_if_fail (gtk_tree_selection_get_selected (selection, &model, &iter));
3492 gtk_tree_model_get (model, &iter,
3493 CERT_COLUMN_KIND_INT, &kind,
3494 CERT_COLUMN_DATA_ECONTACTCERT, &cert,
3495 -1);
3497 g_return_if_fail (kind == CERT_KIND_X509 || kind == CERT_KIND_PGP);
3498 g_return_if_fail (cert != NULL);
3500 parent = eab_editor_get_window (EAB_EDITOR (editor));
3501 dialog = gtk_file_chooser_dialog_new (
3502 kind == CERT_KIND_PGP ? _("Save PGP key") : _("Save X.509 certificate"), parent,
3503 GTK_FILE_CHOOSER_ACTION_SAVE,
3504 _("_Cancel"), GTK_RESPONSE_CANCEL,
3505 _("_Save"), GTK_RESPONSE_OK,
3506 NULL);
3508 file_chooser = GTK_FILE_CHOOSER (dialog);
3509 gtk_file_chooser_set_local_only (file_chooser, TRUE);
3510 gtk_file_chooser_set_select_multiple (file_chooser, FALSE);
3511 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
3513 cert_add_filters_for_kind (file_chooser, kind);
3515 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
3516 gchar *filename;
3518 filename = gtk_file_chooser_get_filename (file_chooser);
3519 if (!filename) {
3520 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Chosen file is not a local file."));
3521 } else {
3522 g_file_set_contents (filename, cert->data, cert->length, &error);
3525 g_free (filename);
3528 gtk_widget_destroy (dialog);
3529 e_contact_cert_free (cert);
3531 if (error) {
3532 e_notice (parent, GTK_MESSAGE_ERROR, _("Failed to save certificate: %s"), error->message);
3533 g_clear_error (&error);
3537 static void
3538 init_certs (EContactEditor *editor)
3540 GtkListStore *list_store;
3541 GtkTreeView *tree_view;
3542 GtkTreeViewColumn *column;
3543 GtkTreeSelection *selection;
3544 GtkCellRenderer *renderer;
3545 GcrCertificateWidget *certificate_widget;
3546 GtkWidget *widget;
3548 tree_view = GTK_TREE_VIEW (e_builder_get_widget (editor->priv->builder, "certs-treeview"));
3549 g_return_if_fail (tree_view != NULL);
3551 gtk_tree_view_set_headers_visible (tree_view, FALSE);
3553 column = gtk_tree_view_column_new ();
3554 gtk_tree_view_append_column (tree_view, column);
3556 renderer = gtk_cell_renderer_text_new ();
3557 gtk_tree_view_column_pack_start (column, renderer, FALSE);
3558 gtk_tree_view_column_add_attribute (column, renderer, "text", CERT_COLUMN_KIND_STRING);
3560 column = gtk_tree_view_column_new ();
3561 gtk_tree_view_column_set_expand (column, TRUE);
3562 gtk_tree_view_append_column (tree_view, column);
3564 renderer = gtk_cell_renderer_text_new ();
3565 gtk_tree_view_column_pack_start (column, renderer, FALSE);
3566 gtk_tree_view_column_add_attribute (column, renderer, "text", CERT_COLUMN_SUBJECT_STRING);
3568 list_store = gtk_list_store_new (N_CERT_COLUMNS,
3569 G_TYPE_STRING, /* CERT_COLUMN_SUBJECT_STRING */
3570 G_TYPE_STRING, /* CERT_COLUMN_KIND_STRING */
3571 G_TYPE_INT, /* CERT_COLUMN_KIND_INT */
3572 E_TYPE_CONTACT_CERT, /* CERT_COLUMN_DATA_ECONTACTCERT */
3573 GCR_TYPE_CERTIFICATE); /* CERT_COLUMN_CERT_GCRCERTIFICATE */
3575 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (list_store));
3577 certificate_widget = gcr_certificate_widget_new (NULL);
3578 gtk_widget_show (GTK_WIDGET (certificate_widget));
3579 widget = e_builder_get_widget (editor->priv->builder, "cert-preview-scw");
3580 gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (certificate_widget));
3582 selection = gtk_tree_view_get_selection (tree_view);
3583 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3584 g_signal_connect (selection, "changed", G_CALLBACK (cert_tab_selection_changed_cb), editor);
3586 widget = e_builder_get_widget (editor->priv->builder, "cert-add-pgp-btn");
3587 g_signal_connect (widget, "clicked", G_CALLBACK (cert_add_pgp_btn_clicked_cb), editor);
3589 widget = e_builder_get_widget (editor->priv->builder, "cert-add-x509-btn");
3590 g_signal_connect (widget, "clicked", G_CALLBACK (cert_add_x509_btn_clicked_cb), editor);
3592 widget = e_builder_get_widget (editor->priv->builder, "cert-remove-btn");
3593 g_signal_connect (widget, "clicked", G_CALLBACK (cert_remove_btn_clicked_cb), editor);
3595 widget = e_builder_get_widget (editor->priv->builder, "cert-load-pgp-btn");
3596 g_signal_connect (widget, "clicked", G_CALLBACK (cert_load_pgp_btn_clicked_cb), editor);
3598 widget = e_builder_get_widget (editor->priv->builder, "cert-load-x509-btn");
3599 g_signal_connect (widget, "clicked", G_CALLBACK (cert_load_x509_btn_clicked_cb), editor);
3601 widget = e_builder_get_widget (editor->priv->builder, "cert-save-btn");
3602 g_signal_connect (widget, "clicked", G_CALLBACK (cert_save_btn_clicked_cb), editor);
3605 static void
3606 fill_in_certs (EContactEditor *editor)
3608 GtkTreeModel *model;
3609 GtkListStore *list_store;
3610 GtkWidget *widget;
3611 GList *attrs, *link;
3612 GtkTreeIter iter;
3613 enum CertKind kind;
3615 widget = e_builder_get_widget (editor->priv->builder, "certs-treeview");
3616 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3617 list_store = GTK_LIST_STORE (model);
3619 /* Clear */
3621 gtk_list_store_clear (list_store);
3623 /* Fill in */
3625 attrs = e_vcard_get_attributes (E_VCARD (editor->priv->contact));
3626 for (link = attrs; link; link = g_list_next (link)) {
3627 EVCardAttribute *attr = link->data;
3628 EContactCert *cert;
3629 GString *value;
3630 GtkTreeIter iter;
3632 if (e_vcard_attribute_has_type (attr, "X509"))
3633 kind = CERT_KIND_X509;
3634 else if (e_vcard_attribute_has_type (attr, "PGP"))
3635 kind = CERT_KIND_PGP;
3636 else
3637 continue;
3639 value = e_vcard_attribute_get_value_decoded (attr);
3640 if (!value || !value->len) {
3641 if (value)
3642 g_string_free (value, TRUE);
3643 continue;
3646 cert = e_contact_cert_new ();
3647 cert->length = value->len;
3648 cert->data = g_malloc (cert->length);
3649 memcpy (cert->data, value->str, cert->length);
3651 gtk_list_store_append (list_store, &iter);
3653 cert_update_row_with_cert (list_store, &iter, cert, kind);
3655 e_contact_cert_free (cert);
3656 g_string_free (value, TRUE);
3659 if (gtk_tree_model_get_iter_first (model, &iter)) {
3660 GtkTreeSelection *selection;
3662 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3663 gtk_tree_selection_select_iter (selection, &iter);
3667 static void
3668 extract_certs_for_kind (EContactEditor *editor,
3669 enum CertKind kind,
3670 EContactField field,
3671 GtkTreeModel *model)
3673 GtkTreeIter iter;
3674 gboolean valid;
3675 EVCard *vcard;
3676 GList *attrs = NULL, *link;
3678 if (is_field_supported (editor, field)) {
3679 valid = gtk_tree_model_get_iter_first (model, &iter);
3680 while (valid) {
3681 EContactCert *cert = NULL;
3682 gint set_kind = -1;
3684 gtk_tree_model_get (model, &iter,
3685 CERT_COLUMN_KIND_INT, &set_kind,
3686 CERT_COLUMN_DATA_ECONTACTCERT, &cert,
3687 -1);
3689 if (cert && set_kind == kind) {
3690 EVCardAttribute *attr;
3692 attr = e_vcard_attribute_new ("", e_contact_vcard_attribute (field));
3693 e_vcard_attribute_add_param_with_value (
3694 attr, e_vcard_attribute_param_new (EVC_TYPE),
3695 field == E_CONTACT_X509_CERT ? "X509" : "PGP");
3696 e_vcard_attribute_add_param_with_value (
3697 attr,
3698 e_vcard_attribute_param_new (EVC_ENCODING),
3699 "b");
3701 e_vcard_attribute_add_value_decoded (attr, cert->data, cert->length);
3703 attrs = g_list_prepend (attrs, attr);
3706 e_contact_cert_free (cert);
3708 valid = gtk_tree_model_iter_next (model, &iter);
3712 attrs = g_list_reverse (attrs);
3714 vcard = E_VCARD (editor->priv->contact);
3716 for (link = attrs; link; link = g_list_next (link)) {
3717 /* takes ownership of the attribute */
3718 e_vcard_append_attribute (vcard, link->data);
3721 g_list_free (attrs);
3724 static void
3725 extract_certs (EContactEditor *editor)
3727 GtkWidget *widget;
3728 GtkTreeModel *model;
3729 GList *attrs, *link;
3730 EVCard *vcard;
3732 widget = e_builder_get_widget (editor->priv->builder, "certs-treeview");
3733 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3735 vcard = E_VCARD (editor->priv->contact);
3736 attrs = g_list_copy (e_vcard_get_attributes (vcard));
3738 for (link = attrs; link; link = g_list_next (link)) {
3739 EVCardAttribute *attr = link->data;
3741 /* Remove only those types the editor can work with. */
3742 if ((!e_vcard_attribute_get_name (attr) ||
3743 g_ascii_strcasecmp (EVC_KEY, e_vcard_attribute_get_name (attr)) == 0) &&
3744 (e_vcard_attribute_has_type (attr, "X509") ||
3745 e_vcard_attribute_has_type (attr, "PGP"))) {
3746 e_vcard_remove_attribute (vcard, attr);
3750 g_list_free (attrs);
3752 /* The saved order will always be X.509 first, then PGP */
3753 extract_certs_for_kind (editor, CERT_KIND_X509, E_CONTACT_X509_CERT, model);
3754 extract_certs_for_kind (editor, CERT_KIND_PGP, E_CONTACT_PGP_CERT, model);
3757 static void
3758 sensitize_certs (EContactEditor *editor)
3760 GtkWidget *widget;
3762 widget = e_builder_get_widget (editor->priv->builder, "certs-grid");
3764 gtk_widget_set_sensitive (widget, editor->priv->target_editable && (
3765 is_field_supported (editor, E_CONTACT_X509_CERT) ||
3766 is_field_supported (editor, E_CONTACT_PGP_CERT)));
3768 widget = e_builder_get_widget (editor->priv->builder, "cert-add-pgp-btn");
3769 gtk_widget_set_sensitive (widget, is_field_supported (editor, E_CONTACT_PGP_CERT));
3771 widget = e_builder_get_widget (editor->priv->builder, "cert-add-x509-btn");
3772 gtk_widget_set_sensitive (widget, is_field_supported (editor, E_CONTACT_X509_CERT));
3774 widget = e_builder_get_widget (editor->priv->builder, "certs-treeview");
3775 cert_tab_selection_changed_cb (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)), editor);
3778 static void
3779 fill_in_all (EContactEditor *editor)
3781 GtkWidget *focused_widget;
3782 gpointer weak_pointer;
3784 /* Widget changes can cause focus widget change, thus remember the current
3785 widget and restore it after the fill is done; some fill operations
3786 can delete widgets, like the dyntable, thus do the weak_pointer as well.
3788 focused_widget = gtk_window_get_focus (eab_editor_get_window (EAB_EDITOR (editor)));
3789 weak_pointer = focused_widget;
3790 if (focused_widget)
3791 g_object_add_weak_pointer (G_OBJECT (focused_widget), &weak_pointer);
3793 fill_in_source_field (editor);
3794 fill_in_simple (editor);
3795 fill_in_email (editor);
3796 fill_in_phone (editor);
3797 fill_in_sip (editor);
3798 fill_in_im (editor);
3799 fill_in_address (editor);
3800 fill_in_certs (editor);
3802 /* Visibility of sections and status of menuitems in the config-menu depend on data
3803 * they have to be initialized here instead of init_all() and sensitize_all()
3805 configure_visibility (editor);
3806 config_sensitize_cb (NULL, editor);
3808 if (weak_pointer) {
3809 g_object_remove_weak_pointer (G_OBJECT (focused_widget), &weak_pointer);
3810 gtk_widget_grab_focus (focused_widget);
3814 static void
3815 extract_all (EContactEditor *editor)
3817 extract_simple (editor);
3818 extract_email (editor);
3819 extract_phone (editor);
3820 extract_sip (editor);
3821 extract_im (editor);
3822 extract_address (editor);
3823 extract_certs (editor);
3826 static void
3827 sensitize_all (EContactEditor *editor)
3829 GtkWidget *focused_widget;
3830 gpointer weak_pointer;
3832 /* Widget changes can cause focus widget change, thus remember the current
3833 widget and restore it after the fill is done; some fill operations
3834 can delete widgets, like the dyntable, thus do the weak_pointer as well.
3836 focused_widget = gtk_window_get_focus (eab_editor_get_window (EAB_EDITOR (editor)));
3837 weak_pointer = focused_widget;
3838 if (focused_widget)
3839 g_object_add_weak_pointer (G_OBJECT (focused_widget), &weak_pointer);
3841 sensitize_ok (editor);
3842 sensitize_simple (editor);
3843 sensitize_email (editor);
3844 sensitize_phone (editor);
3845 sensitize_sip (editor);
3846 sensitize_im (editor);
3847 sensitize_address (editor);
3848 sensitize_certs (editor);
3850 if (weak_pointer) {
3851 g_object_remove_weak_pointer (G_OBJECT (focused_widget), &weak_pointer);
3852 gtk_widget_grab_focus (focused_widget);
3856 static void
3857 init_personal (EContactEditor *editor)
3859 gtk_expander_set_expanded (
3860 GTK_EXPANDER (e_builder_get_widget (editor->priv->builder, "expander-personal-web")),
3861 !editor->priv->compress_ui);
3862 gtk_expander_set_expanded (
3863 GTK_EXPANDER (e_builder_get_widget (editor->priv->builder, "expander-personal-job")),
3864 !editor->priv->compress_ui);
3865 gtk_expander_set_expanded (
3866 GTK_EXPANDER (e_builder_get_widget (editor->priv->builder, "expander-personal-misc")),
3867 !editor->priv->compress_ui);
3870 static void
3871 init_all (EContactEditor *editor)
3873 const gchar *contents[] = { "viewport1", "viewport2", "viewport3", "text-comments" };
3874 gint ii;
3875 GtkRequisition tab_req, requisition;
3876 GtkWidget *widget;
3878 init_simple (editor);
3879 init_email (editor);
3880 init_phone (editor);
3881 init_sip (editor);
3882 init_im (editor);
3883 init_personal (editor);
3884 init_address (editor);
3885 init_certs (editor);
3886 init_config (editor);
3888 /* with so many scrolled windows, we need to
3889 * do some manual sizing */
3890 requisition.width = -1;
3891 requisition.height = -1;
3893 for (ii = 0; ii < G_N_ELEMENTS (contents); ii++) {
3894 widget = e_builder_get_widget (editor->priv->builder, contents[ii]);
3896 gtk_widget_get_preferred_size (widget, NULL, &tab_req);
3898 if (tab_req.width > requisition.width)
3899 requisition.width = tab_req.width;
3900 if (tab_req.height > requisition.height)
3901 requisition.height = tab_req.height;
3904 if (requisition.width > 0 && requisition.height > 0) {
3905 GtkWidget *window;
3906 GdkScreen *screen;
3907 GdkRectangle monitor_area;
3908 gint x = 0, y = 0, monitor, width, height;
3910 window = e_builder_get_widget (
3911 editor->priv->builder, "contact editor");
3913 gtk_widget_get_preferred_size (window, &tab_req, NULL);
3914 width = tab_req.width - 320 + 24;
3915 height = tab_req.height - 240 + 24;
3917 screen = gtk_window_get_screen (GTK_WINDOW (window));
3918 gtk_window_get_position (GTK_WINDOW (window), &x, &y);
3920 monitor = gdk_screen_get_monitor_at_point (screen, x, y);
3922 if (monitor < 0)
3923 monitor = 0;
3925 if (monitor >= gdk_screen_get_n_monitors (screen))
3926 monitor = 0;
3928 gdk_screen_get_monitor_workarea (screen, monitor, &monitor_area);
3930 if (requisition.width > monitor_area.width - width)
3931 requisition.width = monitor_area.width - width;
3933 if (requisition.height > monitor_area.height - height)
3934 requisition.height = monitor_area.height - height;
3936 if (requisition.width > 0 && requisition.height > 0)
3937 gtk_window_set_default_size (
3938 GTK_WINDOW (window),
3939 width + requisition.width,
3940 height + requisition.height);
3943 widget = e_builder_get_widget (editor->priv->builder, "text-comments");
3944 if (widget)
3945 e_spell_text_view_attach (GTK_TEXT_VIEW (widget));
3948 static void
3949 contact_editor_get_client_cb (GObject *source_object,
3950 GAsyncResult *result,
3951 gpointer user_data)
3953 EClientComboBox *combo_box;
3954 ConnectClosure *closure = user_data;
3955 EClient *client;
3956 GError *error = NULL;
3958 combo_box = E_CLIENT_COMBO_BOX (source_object);
3960 client = e_client_combo_box_get_client_finish (
3961 combo_box, result, &error);
3963 /* Sanity check. */
3964 g_return_if_fail (
3965 ((client != NULL) && (error == NULL)) ||
3966 ((client == NULL) && (error != NULL)));
3968 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
3969 g_warn_if_fail (client == NULL);
3970 g_error_free (error);
3971 goto exit;
3973 } else if (error != NULL) {
3974 GtkWindow *parent;
3976 parent = eab_editor_get_window (EAB_EDITOR (closure->editor));
3978 eab_load_error_dialog (
3979 GTK_WIDGET (parent), NULL,
3980 closure->source, error);
3982 e_source_combo_box_set_active (
3983 E_SOURCE_COMBO_BOX (combo_box),
3984 e_client_get_source (E_CLIENT (closure->editor->priv->target_client)));
3986 g_error_free (error);
3987 goto exit;
3990 /* FIXME Write a private contact_editor_set_target_client(). */
3991 g_object_set (closure->editor, "target_client", client, NULL);
3993 g_object_unref (client);
3995 exit:
3996 connect_closure_free (closure);
3999 static void
4000 source_changed (EClientComboBox *combo_box,
4001 EContactEditor *editor)
4003 ConnectClosure *closure;
4004 ESource *target_source;
4005 ESource *source_source;
4006 ESource *source;
4008 source = e_source_combo_box_ref_active (
4009 E_SOURCE_COMBO_BOX (combo_box));
4010 g_return_if_fail (source != NULL);
4012 if (editor->priv->cancellable != NULL) {
4013 g_cancellable_cancel (editor->priv->cancellable);
4014 g_object_unref (editor->priv->cancellable);
4015 editor->priv->cancellable = NULL;
4018 target_source = e_client_get_source (E_CLIENT (editor->priv->target_client));
4019 source_source = e_client_get_source (E_CLIENT (editor->priv->source_client));
4021 if (e_source_equal (target_source, source))
4022 goto exit;
4024 if (e_source_equal (source_source, source)) {
4025 g_object_set (
4026 editor, "target_client",
4027 editor->priv->source_client, NULL);
4028 goto exit;
4031 editor->priv->cancellable = g_cancellable_new ();
4033 closure = g_slice_new0 (ConnectClosure);
4034 closure->editor = g_object_ref (editor);
4035 closure->source = g_object_ref (source);
4037 e_client_combo_box_get_client (
4038 combo_box, source,
4039 editor->priv->cancellable,
4040 contact_editor_get_client_cb,
4041 closure);
4043 exit:
4044 g_object_unref (source);
4047 static void
4048 full_name_editor_closed_cb (GtkWidget *widget,
4049 gpointer data)
4051 if (GTK_IS_WIDGET (widget))
4052 gtk_widget_destroy (widget);
4055 static void
4056 full_name_response (GtkDialog *dialog,
4057 gint response,
4058 EContactEditor *editor)
4060 EContactName *name;
4061 GtkWidget *fname_widget;
4062 gint style = 0;
4063 gboolean editable = FALSE;
4065 g_object_get (dialog, "editable", &editable, NULL);
4067 if (editable && response == GTK_RESPONSE_OK) {
4068 g_object_get (dialog, "name", &name, NULL);
4070 style = file_as_get_style (editor);
4072 fname_widget = e_builder_get_widget (
4073 editor->priv->builder, "entry-fullname");
4075 if (GTK_IS_ENTRY (fname_widget)) {
4076 GtkEntry *entry;
4077 gchar *full_name = e_contact_name_to_string (name);
4078 const gchar *old_full_name;
4080 entry = GTK_ENTRY (fname_widget);
4081 old_full_name = gtk_entry_get_text (entry);
4083 if (strcmp (full_name, old_full_name))
4084 gtk_entry_set_text (entry, full_name);
4085 g_free (full_name);
4088 e_contact_name_free (editor->priv->name);
4089 editor->priv->name = name;
4091 file_as_set_style (editor, style);
4094 g_signal_handlers_disconnect_by_func (editor, G_CALLBACK (full_name_editor_closed_cb), dialog);
4096 gtk_widget_destroy (GTK_WIDGET (dialog));
4097 editor->priv->fullname_dialog = NULL;
4100 static void
4101 full_name_clicked (GtkWidget *button,
4102 EContactEditor *editor)
4104 GtkDialog *dialog;
4105 GtkWindow *parent;
4106 gboolean fullname_supported;
4108 if (editor->priv->fullname_dialog) {
4109 gtk_window_present (GTK_WINDOW (editor->priv->fullname_dialog));
4110 return;
4113 parent = eab_editor_get_window (EAB_EDITOR (editor));
4114 dialog = GTK_DIALOG (e_contact_editor_fullname_new (parent, editor->priv->name));
4115 fullname_supported = is_field_supported (editor, E_CONTACT_FULL_NAME);
4117 g_object_set (
4118 dialog, "editable",
4119 fullname_supported & editor->priv->target_editable, NULL);
4121 g_signal_connect (
4122 dialog, "response",
4123 G_CALLBACK (full_name_response), editor);
4125 /* Close the fullname dialog if the editor is closed */
4126 g_signal_connect_swapped (
4127 editor, "editor_closed",
4128 G_CALLBACK (full_name_editor_closed_cb), dialog);
4130 gtk_widget_show (GTK_WIDGET (dialog));
4131 editor->priv->fullname_dialog = GTK_WIDGET (dialog);
4134 static void
4135 categories_response (GtkDialog *dialog,
4136 gint response,
4137 EContactEditor *editor)
4139 gchar *categories;
4140 GtkWidget *entry;
4142 entry = e_builder_get_widget (editor->priv->builder, "entry-categories");
4144 if (response == GTK_RESPONSE_OK) {
4145 categories = e_categories_dialog_get_categories (
4146 E_CATEGORIES_DIALOG (dialog));
4147 if (GTK_IS_ENTRY (entry))
4148 gtk_entry_set_text (
4149 GTK_ENTRY (entry), categories);
4150 else
4151 e_contact_set (
4152 editor->priv->contact,
4153 E_CONTACT_CATEGORIES,
4154 categories);
4155 g_free (categories);
4158 gtk_widget_destroy (GTK_WIDGET (dialog));
4159 editor->priv->categories_dialog = NULL;
4162 static void
4163 categories_clicked (GtkWidget *button,
4164 EContactEditor *editor)
4166 gchar *categories = NULL;
4167 GtkDialog *dialog;
4168 GtkWindow *window;
4169 GtkWidget *entry = e_builder_get_widget (editor->priv->builder, "entry-categories");
4171 if (entry && GTK_IS_ENTRY (entry))
4172 categories = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
4173 else if (editor->priv->contact)
4174 categories = e_contact_get (editor->priv->contact, E_CONTACT_CATEGORIES);
4176 if (editor->priv->categories_dialog != NULL) {
4177 gtk_window_present (GTK_WINDOW (editor->priv->categories_dialog));
4178 g_free (categories);
4179 return;
4180 }else if (!(dialog = GTK_DIALOG (e_categories_dialog_new (categories)))) {
4181 e_alert_run_dialog_for_args (
4182 GTK_WINDOW (editor->priv->app),
4183 "addressbook:edit-categories", NULL);
4184 g_free (categories);
4185 return;
4188 g_signal_connect (
4189 dialog, "response",
4190 G_CALLBACK (categories_response), editor);
4192 window = GTK_WINDOW (dialog);
4194 /* Close the category dialog if the editor is closed */
4195 gtk_window_set_destroy_with_parent (window, TRUE);
4196 gtk_window_set_modal (window, FALSE);
4197 gtk_window_set_transient_for (window, eab_editor_get_window (EAB_EDITOR (editor)));
4199 gtk_widget_show (GTK_WIDGET (dialog));
4200 g_free (categories);
4202 editor->priv->categories_dialog = GTK_WIDGET (dialog);
4205 static void
4206 image_selected (EContactEditor *editor)
4208 gchar *file_name;
4209 GtkWidget *image_chooser;
4211 file_name = gtk_file_chooser_get_filename (
4212 GTK_FILE_CHOOSER (editor->priv->file_selector));
4214 if (!file_name)
4215 return;
4217 image_chooser = e_builder_get_widget (editor->priv->builder, "image-chooser");
4219 g_signal_handlers_block_by_func (
4220 image_chooser, image_chooser_changed, editor);
4221 e_image_chooser_set_from_file (
4222 E_IMAGE_CHOOSER (image_chooser), file_name);
4223 g_signal_handlers_unblock_by_func (
4224 image_chooser, image_chooser_changed, editor);
4226 editor->priv->image_set = TRUE;
4227 editor->priv->image_changed = TRUE;
4228 object_changed (G_OBJECT (image_chooser), editor);
4231 static void
4232 image_cleared (EContactEditor *editor)
4234 GtkWidget *image_chooser;
4235 gchar *file_name;
4237 image_chooser = e_builder_get_widget (
4238 editor->priv->builder, "image-chooser");
4240 file_name = e_icon_factory_get_icon_filename (
4241 "avatar-default", GTK_ICON_SIZE_DIALOG);
4243 g_signal_handlers_block_by_func (
4244 image_chooser, image_chooser_changed, editor);
4245 e_image_chooser_set_from_file (
4246 E_IMAGE_CHOOSER (image_chooser), file_name);
4247 g_signal_handlers_unblock_by_func (
4248 image_chooser, image_chooser_changed, editor);
4250 g_free (file_name);
4252 editor->priv->image_set = FALSE;
4253 editor->priv->image_changed = TRUE;
4254 object_changed (G_OBJECT (image_chooser), editor);
4257 static void
4258 file_chooser_response (GtkWidget *widget,
4259 gint response,
4260 EContactEditor *editor)
4262 if (response == GTK_RESPONSE_ACCEPT)
4263 image_selected (editor);
4264 else if (response == GTK_RESPONSE_NO)
4265 image_cleared (editor);
4267 gtk_widget_hide (editor->priv->file_selector);
4270 static gboolean
4271 file_selector_deleted (GtkWidget *widget)
4273 gtk_widget_hide (widget);
4274 return TRUE;
4277 static void
4278 update_preview_cb (GtkFileChooser *file_chooser,
4279 gpointer data)
4281 GtkWidget *preview;
4282 gchar *filename = NULL;
4283 GdkPixbuf *pixbuf;
4285 gtk_file_chooser_set_preview_widget_active (file_chooser, TRUE);
4286 preview = GTK_WIDGET (data);
4287 filename = gtk_file_chooser_get_preview_filename (file_chooser);
4288 if (filename == NULL)
4289 return;
4291 pixbuf = gdk_pixbuf_new_from_file_at_size (filename, 128, 128, NULL);
4292 if (!pixbuf) {
4293 gchar *alternate_file;
4294 alternate_file = e_icon_factory_get_icon_filename (
4295 "avatar-default", GTK_ICON_SIZE_DIALOG);
4296 if (alternate_file) {
4297 pixbuf = gdk_pixbuf_new_from_file_at_size (
4298 alternate_file, 128, 128, NULL);
4299 g_free (alternate_file);
4302 g_free (filename);
4304 gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf);
4305 if (pixbuf)
4306 g_object_unref (pixbuf);
4309 static void
4310 image_clicked (GtkWidget *button,
4311 EContactEditor *editor)
4313 if (!editor->priv->file_selector) {
4314 GtkImage *preview;
4315 GtkFileFilter *filter;
4317 editor->priv->file_selector = gtk_file_chooser_dialog_new (
4318 _("Please select an image for this contact"),
4319 GTK_WINDOW (editor->priv->app),
4320 GTK_FILE_CHOOSER_ACTION_OPEN,
4321 _("_Cancel"), GTK_RESPONSE_CANCEL,
4322 _("_Open"), GTK_RESPONSE_ACCEPT,
4323 _("_No image"), GTK_RESPONSE_NO,
4324 NULL);
4326 filter = gtk_file_filter_new ();
4327 gtk_file_filter_add_mime_type (filter, "image/*");
4328 gtk_file_chooser_set_filter (
4329 GTK_FILE_CHOOSER (editor->priv->file_selector),
4330 filter);
4332 preview = GTK_IMAGE (gtk_image_new ());
4333 gtk_file_chooser_set_preview_widget (
4334 GTK_FILE_CHOOSER (editor->priv->file_selector),
4335 GTK_WIDGET (preview));
4336 g_signal_connect (
4337 editor->priv->file_selector, "update-preview",
4338 G_CALLBACK (update_preview_cb), preview);
4340 gtk_dialog_set_default_response (
4341 GTK_DIALOG (editor->priv->file_selector),
4342 GTK_RESPONSE_ACCEPT);
4344 g_signal_connect (
4345 editor->priv->file_selector, "response",
4346 G_CALLBACK (file_chooser_response), editor);
4348 g_signal_connect_after (
4349 editor->priv->file_selector, "delete-event",
4350 G_CALLBACK (file_selector_deleted),
4351 editor->priv->file_selector);
4354 /* Display the dialog */
4356 gtk_window_set_modal (GTK_WINDOW (editor->priv->file_selector), TRUE);
4357 gtk_window_present (GTK_WINDOW (editor->priv->file_selector));
4360 typedef struct {
4361 EContactEditor *ce;
4362 gboolean should_close;
4363 gchar *new_id;
4364 } EditorCloseStruct;
4366 static void
4367 contact_removed_cb (GObject *source_object,
4368 GAsyncResult *result,
4369 gpointer user_data)
4371 EBookClient *book_client = E_BOOK_CLIENT (source_object);
4372 EditorCloseStruct *ecs = user_data;
4373 EContactEditor *ce = ecs->ce;
4374 gboolean should_close = ecs->should_close;
4375 GError *error = NULL;
4377 e_book_client_remove_contact_finish (book_client, result, &error);
4379 gtk_widget_set_sensitive (ce->priv->app, TRUE);
4380 ce->priv->in_async_call = FALSE;
4382 e_contact_set (ce->priv->contact, E_CONTACT_UID, ecs->new_id);
4384 eab_editor_contact_deleted (EAB_EDITOR (ce), error, ce->priv->contact);
4386 ce->priv->is_new_contact = FALSE;
4388 if (should_close) {
4389 eab_editor_close (EAB_EDITOR (ce));
4390 } else {
4391 ce->priv->changed = FALSE;
4393 g_object_ref (ce->priv->target_client);
4394 g_object_unref (ce->priv->source_client);
4395 ce->priv->source_client = ce->priv->target_client;
4397 sensitize_all (ce);
4400 if (error != NULL)
4401 g_error_free (error);
4403 g_object_unref (ce);
4404 g_free (ecs->new_id);
4405 g_free (ecs);
4408 static void
4409 contact_added_cb (EBookClient *book_client,
4410 const GError *error,
4411 const gchar *id,
4412 gpointer closure)
4414 EditorCloseStruct *ecs = closure;
4415 EContactEditor *ce = ecs->ce;
4416 gboolean should_close = ecs->should_close;
4418 if (ce->priv->source_client != ce->priv->target_client && !e_client_is_readonly (E_CLIENT (ce->priv->source_client)) &&
4419 !error && ce->priv->is_new_contact == FALSE) {
4420 ecs->new_id = g_strdup (id);
4421 e_book_client_remove_contact (
4422 ce->priv->source_client, ce->priv->contact, NULL, contact_removed_cb, ecs);
4423 return;
4426 gtk_widget_set_sensitive (ce->priv->app, TRUE);
4427 ce->priv->in_async_call = FALSE;
4429 e_contact_set (ce->priv->contact, E_CONTACT_UID, id);
4431 eab_editor_contact_added (EAB_EDITOR (ce), error, ce->priv->contact);
4433 if (!error) {
4434 ce->priv->is_new_contact = FALSE;
4436 if (should_close) {
4437 eab_editor_close (EAB_EDITOR (ce));
4438 } else {
4439 ce->priv->changed = FALSE;
4440 sensitize_all (ce);
4444 g_object_unref (ce);
4445 g_free (ecs);
4448 static void
4449 contact_modified_cb (EBookClient *book_client,
4450 const GError *error,
4451 gpointer closure)
4453 EditorCloseStruct *ecs = closure;
4454 EContactEditor *ce = ecs->ce;
4455 gboolean should_close = ecs->should_close;
4457 gtk_widget_set_sensitive (ce->priv->app, TRUE);
4458 ce->priv->in_async_call = FALSE;
4460 eab_editor_contact_modified (EAB_EDITOR (ce), error, ce->priv->contact);
4462 if (!error) {
4463 if (should_close) {
4464 eab_editor_close (EAB_EDITOR (ce));
4466 else {
4467 ce->priv->changed = FALSE;
4468 sensitize_all (ce);
4472 g_object_unref (ce);
4473 g_free (ecs);
4476 static void
4477 contact_modified_ready_cb (GObject *source_object,
4478 GAsyncResult *result,
4479 gpointer user_data)
4481 EBookClient *book_client = E_BOOK_CLIENT (source_object);
4482 GError *error = NULL;
4484 e_book_client_modify_contact_finish (book_client, result, &error);
4486 contact_modified_cb (book_client, error, user_data);
4488 if (error != NULL)
4489 g_error_free (error);
4492 /* Emits the signal to request saving a contact */
4493 static void
4494 real_save_contact (EContactEditor *ce,
4495 gboolean should_close)
4497 EShell *shell;
4498 EditorCloseStruct *ecs;
4499 ESourceRegistry *registry;
4501 shell = eab_editor_get_shell (EAB_EDITOR (ce));
4502 registry = e_shell_get_registry (shell);
4504 ecs = g_new0 (EditorCloseStruct, 1);
4505 ecs->ce = ce;
4506 g_object_ref (ecs->ce);
4508 ecs->should_close = should_close;
4510 gtk_widget_set_sensitive (ce->priv->app, FALSE);
4511 ce->priv->in_async_call = TRUE;
4513 if (ce->priv->source_client != ce->priv->target_client) {
4514 /* Two-step move; add to target, then remove from source */
4515 eab_merging_book_add_contact (
4516 registry, ce->priv->target_client,
4517 ce->priv->contact, contact_added_cb, ecs);
4518 } else {
4519 if (ce->priv->is_new_contact)
4520 eab_merging_book_add_contact (
4521 registry, ce->priv->target_client,
4522 ce->priv->contact, contact_added_cb, ecs);
4523 else if (ce->priv->check_merge)
4524 eab_merging_book_modify_contact (
4525 registry, ce->priv->target_client,
4526 ce->priv->contact, contact_modified_cb, ecs);
4527 else
4528 e_book_client_modify_contact (
4529 ce->priv->target_client, ce->priv->contact, NULL,
4530 contact_modified_ready_cb, ecs);
4534 static void
4535 save_contact (EContactEditor *ce,
4536 gboolean should_close)
4538 gchar *uid;
4539 const gchar *name_entry_string;
4540 const gchar *file_as_entry_string;
4541 const gchar *company_name_string;
4542 GtkWidget *entry_fullname, *entry_file_as, *company_name, *client_combo_box;
4543 ESource *active_source;
4545 if (!ce->priv->target_client)
4546 return;
4548 client_combo_box = e_builder_get_widget (ce->priv->builder, "client-combo-box");
4549 active_source = e_source_combo_box_ref_active (E_SOURCE_COMBO_BOX (client_combo_box));
4550 g_return_if_fail (active_source != NULL);
4552 if (!e_source_equal (e_client_get_source (E_CLIENT (ce->priv->target_client)), active_source)) {
4553 e_alert_run_dialog_for_args (
4554 GTK_WINDOW (ce->priv->app),
4555 "addressbook:error-still-opening",
4556 e_source_get_display_name (active_source),
4557 NULL);
4558 g_object_unref (active_source);
4559 return;
4562 g_object_unref (active_source);
4564 if (ce->priv->target_editable && e_client_is_readonly (E_CLIENT (ce->priv->source_client))) {
4565 if (e_alert_run_dialog_for_args (
4566 GTK_WINDOW (ce->priv->app),
4567 "addressbook:prompt-move",
4568 NULL) == GTK_RESPONSE_NO)
4569 return;
4572 entry_fullname = e_builder_get_widget (ce->priv->builder, "entry-fullname");
4573 entry_file_as = gtk_bin_get_child (
4574 GTK_BIN (e_builder_get_widget (ce->priv->builder, "combo-file-as")));
4575 company_name = e_builder_get_widget (ce->priv->builder, "entry-company");
4576 name_entry_string = gtk_entry_get_text (GTK_ENTRY (entry_fullname));
4577 file_as_entry_string = gtk_entry_get_text (GTK_ENTRY (entry_file_as));
4578 company_name_string = gtk_entry_get_text (GTK_ENTRY (company_name));
4580 if (strcmp (company_name_string , "")) {
4581 if (!strcmp (name_entry_string, ""))
4582 gtk_entry_set_text (
4583 GTK_ENTRY (entry_fullname),
4584 company_name_string);
4585 if (!strcmp (file_as_entry_string, ""))
4586 gtk_entry_set_text (
4587 GTK_ENTRY (entry_file_as),
4588 company_name_string);
4591 extract_all (ce);
4593 if (!e_contact_editor_is_valid (EAB_EDITOR (ce))) {
4594 uid = e_contact_get (ce->priv->contact, E_CONTACT_UID);
4595 g_object_unref (ce->priv->contact);
4596 ce->priv->contact = e_contact_new ();
4597 if (uid) {
4598 e_contact_set (ce->priv->contact, E_CONTACT_UID, uid);
4599 g_free (uid);
4601 return;
4604 real_save_contact (ce, should_close);
4607 static void
4608 e_contact_editor_save_contact (EABEditor *editor,
4609 gboolean should_close)
4611 save_contact (E_CONTACT_EDITOR (editor), should_close);
4614 /* Closes the dialog box and emits the appropriate signals */
4615 static void
4616 e_contact_editor_close (EABEditor *editor)
4618 EContactEditor *ce = E_CONTACT_EDITOR (editor);
4620 if (ce->priv->app != NULL) {
4621 gtk_widget_destroy (ce->priv->app);
4622 ce->priv->app = NULL;
4623 eab_editor_closed (editor);
4627 static const EContactField non_string_fields[] = {
4628 E_CONTACT_FULL_NAME,
4629 E_CONTACT_ADDRESS,
4630 E_CONTACT_ADDRESS_HOME,
4631 E_CONTACT_ADDRESS_WORK,
4632 E_CONTACT_ADDRESS_OTHER,
4633 E_CONTACT_EMAIL,
4634 E_CONTACT_IM_AIM,
4635 E_CONTACT_IM_GROUPWISE,
4636 E_CONTACT_IM_JABBER,
4637 E_CONTACT_IM_YAHOO,
4638 E_CONTACT_IM_GADUGADU,
4639 E_CONTACT_IM_MSN,
4640 E_CONTACT_IM_ICQ,
4641 E_CONTACT_IM_SKYPE,
4642 E_CONTACT_IM_TWITTER,
4643 E_CONTACT_PHOTO,
4644 E_CONTACT_LOGO,
4645 E_CONTACT_X509_CERT,
4646 E_CONTACT_CATEGORY_LIST,
4647 E_CONTACT_BIRTH_DATE,
4648 E_CONTACT_ANNIVERSARY
4652 static gboolean
4653 is_non_string_field (EContactField id)
4655 gint count = sizeof (non_string_fields) / sizeof (EContactField);
4656 gint i;
4657 for (i = 0; i < count; i++)
4658 if (id == non_string_fields[i])
4659 return TRUE;
4660 return FALSE;
4664 /* insert checks here (date format, for instance, etc.) */
4665 static gboolean
4666 e_contact_editor_is_valid (EABEditor *editor)
4668 EContactEditor *ce = E_CONTACT_EDITOR (editor);
4669 GtkWidget *widget;
4670 gboolean validation_error = FALSE;
4671 GSList *iter;
4672 GString *errmsg = g_string_new (_("The contact data is invalid:\n\n"));
4673 time_t bday, now = time (NULL);
4675 widget = e_builder_get_widget (ce->priv->builder, "dateedit-birthday");
4676 if (!(e_date_edit_date_is_valid (E_DATE_EDIT (widget)))) {
4677 g_string_append_printf (
4678 errmsg, _("“%s” has an invalid format"),
4679 e_contact_pretty_name (E_CONTACT_BIRTH_DATE));
4680 validation_error = TRUE;
4682 /* If valid, see if the birthday is a future date */
4683 bday = e_date_edit_get_time (E_DATE_EDIT (widget));
4684 if (bday > now) {
4685 g_string_append_printf (
4686 errmsg, _("“%s” cannot be a future date"),
4687 e_contact_pretty_name (E_CONTACT_BIRTH_DATE));
4688 validation_error = TRUE;
4691 widget = e_builder_get_widget (ce->priv->builder, "dateedit-anniversary");
4692 if (!(e_date_edit_date_is_valid (E_DATE_EDIT (widget)))) {
4693 g_string_append_printf (
4694 errmsg, _("%s“%s” has an invalid format"),
4695 validation_error ? ",\n" : "",
4696 e_contact_pretty_name (E_CONTACT_ANNIVERSARY));
4697 validation_error = TRUE;
4700 for (iter = ce->priv->required_fields; iter; iter = iter->next) {
4701 const gchar *field_name = iter->data;
4702 EContactField field_id = e_contact_field_id (field_name);
4704 if (is_non_string_field (field_id)) {
4705 if (e_contact_get_const (ce->priv->contact, field_id) == NULL) {
4706 g_string_append_printf (
4707 errmsg, _("%s“%s” is empty"),
4708 validation_error ? ",\n" : "",
4709 e_contact_pretty_name (field_id));
4710 validation_error = TRUE;
4711 break;
4714 } else {
4715 const gchar *text;
4717 text = e_contact_get_const (ce->priv->contact, field_id);
4719 if (STRING_IS_EMPTY (text)) {
4720 g_string_append_printf (
4721 errmsg, _("%s“%s” is empty"),
4722 validation_error ? ",\n" : "",
4723 e_contact_pretty_name (field_id));
4724 validation_error = TRUE;
4725 break;
4731 if (validation_error) {
4732 g_string_append (errmsg, ".");
4733 e_alert_run_dialog_for_args (
4734 GTK_WINDOW (ce->priv->app),
4735 "addressbook:generic-error",
4736 _("Invalid contact."), errmsg->str, NULL);
4737 g_string_free (errmsg, TRUE);
4738 return FALSE;
4740 else {
4741 g_string_free (errmsg, TRUE);
4742 return TRUE;
4746 static gboolean
4747 e_contact_editor_is_changed (EABEditor *editor)
4749 return E_CONTACT_EDITOR (editor)->priv->changed;
4752 static GtkWindow *
4753 e_contact_editor_get_window (EABEditor *editor)
4755 return GTK_WINDOW (E_CONTACT_EDITOR (editor)->priv->app);
4758 static void
4759 file_save_and_close_cb (GtkWidget *widget,
4760 EContactEditor *ce)
4762 save_contact (ce, TRUE);
4765 static void
4766 file_cancel_cb (GtkWidget *widget,
4767 EContactEditor *ce)
4769 eab_editor_close (EAB_EDITOR (ce));
4772 /* Callback used when the dialog box is destroyed */
4773 static gint
4774 app_delete_event_cb (GtkWidget *widget,
4775 GdkEvent *event,
4776 gpointer data)
4778 EContactEditor *ce;
4780 ce = E_CONTACT_EDITOR (data);
4782 /* if we're saving, don't allow the dialog to close */
4783 if (ce->priv->in_async_call)
4784 return TRUE;
4786 if (ce->priv->changed) {
4787 switch (eab_prompt_save_dialog (GTK_WINDOW (ce->priv->app))) {
4788 case GTK_RESPONSE_YES:
4789 eab_editor_save_contact (EAB_EDITOR (ce), TRUE);
4790 return TRUE;
4792 case GTK_RESPONSE_NO:
4793 break;
4795 case GTK_RESPONSE_CANCEL:
4796 default:
4797 return TRUE;
4802 eab_editor_close (EAB_EDITOR (ce));
4803 return TRUE;
4806 static void
4807 show_help_cb (GtkWidget *widget,
4808 gpointer data)
4810 /* FIXME Pass a proper parent window. */
4811 e_display_help (NULL, "contacts-usage-add-contact");
4814 static GList *
4815 add_to_tab_order (GList *list,
4816 GtkBuilder *builder,
4817 const gchar *name)
4819 GtkWidget *widget = e_builder_get_widget (builder, name);
4820 return g_list_prepend (list, widget);
4823 static void
4824 setup_tab_order (GtkBuilder *builder)
4826 GtkWidget *container;
4827 GList *list = NULL;
4829 container = e_builder_get_widget (builder, "table-contact-editor-general");
4831 if (container) {
4832 list = add_to_tab_order (list, builder, "entry-fullname");
4833 list = add_to_tab_order (list, builder, "entry-jobtitle");
4834 list = add_to_tab_order (list, builder, "entry-company");
4835 list = add_to_tab_order (list, builder, "combo-file-as");
4836 list = add_to_tab_order (list, builder, "entry-phone-1");
4837 list = add_to_tab_order (list, builder, "entry-phone-2");
4838 list = add_to_tab_order (list, builder, "entry-phone-3");
4839 list = add_to_tab_order (list, builder, "entry-phone-4");
4841 list = add_to_tab_order (list, builder, "entry-email1");
4842 list = add_to_tab_order (list, builder, "alignment-htmlmail");
4843 list = add_to_tab_order (list, builder, "entry-web");
4844 list = add_to_tab_order (list, builder, "entry-homepage");
4845 list = add_to_tab_order (list, builder, "button-fulladdr");
4846 list = add_to_tab_order (list, builder, "text-address");
4847 list = g_list_reverse (list);
4848 e_container_change_tab_order (GTK_CONTAINER (container), list);
4849 g_list_free (list);
4853 container = e_builder_get_widget (builder, "table-home-address");
4854 gtk_container_get_focus_chain (GTK_CONTAINER (container), &list);
4856 list = add_to_tab_order (list, builder, "scrolledwindow-home-address");
4857 list = add_to_tab_order (list, builder, "entry-home-city");
4858 list = add_to_tab_order (list, builder, "entry-home-zip");
4859 list = add_to_tab_order (list, builder, "entry-home-state");
4860 list = add_to_tab_order (list, builder, "entry-home-pobox");
4861 list = add_to_tab_order (list, builder, "entry-home-country");
4862 list = g_list_reverse (list);
4864 gtk_container_set_focus_chain (GTK_CONTAINER (container), list);
4865 g_list_free (list);
4867 container = e_builder_get_widget (builder, "table-work-address");
4868 gtk_container_get_focus_chain (GTK_CONTAINER (container), &list);
4870 list = add_to_tab_order (list, builder, "scrolledwindow-work-address");
4871 list = add_to_tab_order (list, builder, "entry-work-city");
4872 list = add_to_tab_order (list, builder, "entry-work-zip");
4873 list = add_to_tab_order (list, builder, "entry-work-state");
4874 list = add_to_tab_order (list, builder, "entry-work-pobox");
4875 list = add_to_tab_order (list, builder, "entry-work-country");
4876 list = g_list_reverse (list);
4878 gtk_container_set_focus_chain (GTK_CONTAINER (container), list);
4879 g_list_free (list);
4881 container = e_builder_get_widget (builder, "table-other-address");
4882 gtk_container_get_focus_chain (GTK_CONTAINER (container), &list);
4884 list = add_to_tab_order (list, builder, "scrolledwindow-other-address");
4885 list = add_to_tab_order (list, builder, "entry-other-city");
4886 list = add_to_tab_order (list, builder, "entry-other-zip");
4887 list = add_to_tab_order (list, builder, "entry-other-state");
4888 list = add_to_tab_order (list, builder, "entry-other-pobox");
4889 list = add_to_tab_order (list, builder, "entry-other-country");
4890 list = g_list_reverse (list);
4892 gtk_container_set_focus_chain (GTK_CONTAINER (container), list);
4893 g_list_free (list);
4896 static void
4897 expand_dyntable (GtkExpander *expander,
4898 EContactEditorDynTable *dyntable,
4899 gint max_slots)
4901 if (gtk_expander_get_expanded (expander)) {
4902 e_contact_editor_dyntable_set_show_max (dyntable, max_slots);
4903 } else {
4904 e_contact_editor_dyntable_set_show_max (dyntable,
4905 SLOTS_IN_COLLAPSED_STATE);
4909 static void
4910 expander_contact_mail_cb (GObject *object,
4911 GParamSpec *param_spec,
4912 gpointer user_data)
4914 expand_dyntable (GTK_EXPANDER (object),
4915 E_CONTACT_EDITOR_DYNTABLE (user_data),
4916 EMAIL_SLOTS);
4919 static void
4920 expander_contact_phone_cb (GObject *object,
4921 GParamSpec *param_spec,
4922 gpointer user_data)
4924 expand_dyntable (GTK_EXPANDER (object),
4925 E_CONTACT_EDITOR_DYNTABLE (user_data),
4926 PHONE_SLOTS);
4929 static void
4930 expander_contact_sip_cb (GObject *object,
4931 GParamSpec *param_spec,
4932 gpointer user_data)
4934 expand_dyntable (GTK_EXPANDER (object),
4935 E_CONTACT_EDITOR_DYNTABLE (user_data),
4936 SIP_SLOTS);
4939 static void
4940 expander_contact_im_cb (GObject *object,
4941 GParamSpec *param_spec,
4942 gpointer user_data)
4944 expand_dyntable (GTK_EXPANDER (object),
4945 E_CONTACT_EDITOR_DYNTABLE (user_data),
4946 IM_SLOTS);
4949 static void
4950 contact_editor_focus_widget_changed_cb (EFocusTracker *focus_tracker,
4951 GParamSpec *param,
4952 EContactEditor *editor)
4954 GtkWidget *widget;
4956 widget = e_focus_tracker_get_focus (focus_tracker);
4958 /* there is no problem to call the attach multiple times */
4959 if (widget)
4960 e_widget_undo_attach (widget, focus_tracker);
4963 static void
4964 e_contact_editor_init (EContactEditor *e_contact_editor)
4966 GtkBuilder *builder;
4967 EShell *shell;
4968 EClientCache *client_cache;
4969 GtkWidget *container;
4970 GtkWidget *widget, *label, *dyntable;
4971 GtkEntryCompletion *completion;
4973 e_contact_editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (
4974 e_contact_editor, E_TYPE_CONTACT_EDITOR, EContactEditorPrivate);
4976 /* FIXME The shell should be obtained
4977 * through a constructor property. */
4978 shell = e_shell_get_default ();
4979 client_cache = e_shell_get_client_cache (shell);
4981 e_contact_editor->priv->name = e_contact_name_new ();
4983 e_contact_editor->priv->contact = NULL;
4984 e_contact_editor->priv->changed = FALSE;
4985 e_contact_editor->priv->check_merge = FALSE;
4986 e_contact_editor->priv->image_set = FALSE;
4987 e_contact_editor->priv->image_changed = FALSE;
4988 e_contact_editor->priv->in_async_call = FALSE;
4989 e_contact_editor->priv->target_editable = TRUE;
4990 e_contact_editor->priv->fullname_dialog = NULL;
4991 e_contact_editor->priv->categories_dialog = NULL;
4992 e_contact_editor->priv->compress_ui = e_shell_get_express_mode (shell);
4994 /* Make sure custom widget types are available */
4995 g_type_ensure (E_TYPE_IMAGE_CHOOSER);
4996 g_type_ensure (E_TYPE_CLIENT_COMBO_BOX);
4997 g_type_ensure (E_TYPE_CONTACT_EDITOR_DYNTABLE);
4998 g_type_ensure (E_TYPE_URL_ENTRY);
4999 g_type_ensure (E_TYPE_DATE_EDIT);
5001 builder = gtk_builder_new ();
5002 e_load_ui_builder_definition (builder, "contact-editor.ui");
5004 e_contact_editor->priv->builder = builder;
5006 setup_tab_order (builder);
5008 e_contact_editor->priv->app =
5009 e_builder_get_widget (builder, "contact editor");
5010 widget = e_contact_editor->priv->app;
5012 gtk_window_set_type_hint (
5013 GTK_WINDOW (widget), GDK_WINDOW_TYPE_HINT_NORMAL);
5014 container = gtk_dialog_get_action_area (GTK_DIALOG (widget));
5015 gtk_container_set_border_width (GTK_CONTAINER (container), 12);
5016 container = gtk_dialog_get_content_area (GTK_DIALOG (widget));
5017 gtk_container_set_border_width (GTK_CONTAINER (container), 0);
5019 init_all (e_contact_editor);
5021 widget = e_builder_get_widget (
5022 e_contact_editor->priv->builder, "button-image");
5023 g_signal_connect (
5024 widget, "clicked",
5025 G_CALLBACK (image_clicked), e_contact_editor);
5026 widget = e_builder_get_widget (
5027 e_contact_editor->priv->builder, "button-fullname");
5028 g_signal_connect (
5029 widget, "clicked",
5030 G_CALLBACK (full_name_clicked), e_contact_editor);
5031 widget = e_builder_get_widget (
5032 e_contact_editor->priv->builder, "button-categories");
5033 g_signal_connect (
5034 widget, "clicked",
5035 G_CALLBACK (categories_clicked), e_contact_editor);
5036 widget = e_builder_get_widget (
5037 e_contact_editor->priv->builder, "client-combo-box");
5038 e_client_combo_box_set_client_cache (
5039 E_CLIENT_COMBO_BOX (widget), client_cache);
5040 g_signal_connect (
5041 widget, "changed",
5042 G_CALLBACK (source_changed), e_contact_editor);
5043 label = e_builder_get_widget (
5044 e_contact_editor->priv->builder, "where-label");
5045 gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
5046 widget = e_builder_get_widget (
5047 e_contact_editor->priv->builder, "button-ok");
5048 g_signal_connect (
5049 widget, "clicked",
5050 G_CALLBACK (file_save_and_close_cb), e_contact_editor);
5051 widget = e_builder_get_widget (
5052 e_contact_editor->priv->builder, "button-cancel");
5053 g_signal_connect (
5054 widget, "clicked",
5055 G_CALLBACK (file_cancel_cb), e_contact_editor);
5056 widget = e_builder_get_widget (
5057 e_contact_editor->priv->builder, "button-help");
5058 g_signal_connect (
5059 widget, "clicked",
5060 G_CALLBACK (show_help_cb), e_contact_editor);
5062 widget = e_builder_get_widget (
5063 e_contact_editor->priv->builder, "expander-contact-phone");
5064 dyntable = e_builder_get_widget (
5065 e_contact_editor->priv->builder, "phone-dyntable");
5066 g_signal_connect (widget, "notify::expanded",
5067 G_CALLBACK (expander_contact_phone_cb), dyntable);
5069 widget = e_builder_get_widget (
5070 e_contact_editor->priv->builder, "expander-contact-sip");
5071 dyntable = e_builder_get_widget (
5072 e_contact_editor->priv->builder, "sip-dyntable");
5073 g_signal_connect (widget, "notify::expanded",
5074 G_CALLBACK (expander_contact_sip_cb), dyntable);
5076 widget = e_builder_get_widget (
5077 e_contact_editor->priv->builder, "expander-contact-im");
5078 dyntable = e_builder_get_widget (
5079 e_contact_editor->priv->builder, "im-dyntable");
5080 g_signal_connect (widget, "notify::expanded",
5081 G_CALLBACK (expander_contact_im_cb), dyntable);
5083 widget = e_builder_get_widget (
5084 e_contact_editor->priv->builder, "expander-contact-email");
5085 dyntable = e_builder_get_widget (
5086 e_contact_editor->priv->builder, "mail-dyntable");
5087 g_signal_connect (widget, "notify::expanded",
5088 G_CALLBACK (expander_contact_mail_cb), dyntable);
5090 widget = e_builder_get_widget (
5091 e_contact_editor->priv->builder, "entry-fullname");
5092 if (widget)
5093 gtk_widget_grab_focus (widget);
5095 widget = e_builder_get_widget (
5096 e_contact_editor->priv->builder, "entry-categories");
5097 completion = e_category_completion_new ();
5098 gtk_entry_set_completion (GTK_ENTRY (widget), completion);
5099 g_object_unref (completion);
5101 /* Connect to the deletion of the dialog */
5103 g_signal_connect (
5104 e_contact_editor->priv->app, "delete_event",
5105 G_CALLBACK (app_delete_event_cb), e_contact_editor);
5107 /* set the icon */
5108 gtk_window_set_icon_name (
5109 GTK_WINDOW (e_contact_editor->priv->app), "contact-editor");
5111 gtk_application_add_window (
5112 GTK_APPLICATION (shell),
5113 GTK_WINDOW (e_contact_editor->priv->app));
5116 static void
5117 e_contact_editor_constructed (GObject *object)
5119 const gchar *ui =
5120 "<ui>"
5121 " <menubar name='undo-menubar'>"
5122 " <menu action='undo-menu'>"
5123 " <menuitem action='undo'/>"
5124 " <menuitem action='redo'/>"
5125 " </menu>"
5126 " </menubar>"
5127 "</ui>";
5128 EContactEditor *editor = E_CONTACT_EDITOR (object);
5129 GtkActionGroup *action_group;
5130 GtkAction *action;
5131 GError *error = NULL;
5133 /* Chain up to parent's method. */
5134 G_OBJECT_CLASS (parent_class)->constructed (object);
5136 editor->priv->focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor->priv->app));
5137 editor->priv->ui_manager = gtk_ui_manager_new ();
5139 gtk_window_add_accel_group (
5140 GTK_WINDOW (editor->priv->app),
5141 gtk_ui_manager_get_accel_group (editor->priv->ui_manager));
5143 e_signal_connect_notify (
5144 editor->priv->focus_tracker, "notify::focus",
5145 G_CALLBACK (contact_editor_focus_widget_changed_cb), editor);
5147 action_group = gtk_action_group_new ("undo");
5148 gtk_action_group_set_translation_domain (
5149 action_group, GETTEXT_PACKAGE);
5150 gtk_action_group_add_actions (
5151 action_group, undo_entries,
5152 G_N_ELEMENTS (undo_entries), editor);
5153 gtk_ui_manager_insert_action_group (
5154 editor->priv->ui_manager, action_group, 0);
5156 action = gtk_action_group_get_action (action_group, "undo");
5157 e_focus_tracker_set_undo_action (editor->priv->focus_tracker, action);
5159 action = gtk_action_group_get_action (action_group, "redo");
5160 e_focus_tracker_set_redo_action (editor->priv->focus_tracker, action);
5162 g_object_unref (action_group);
5164 gtk_ui_manager_add_ui_from_string (editor->priv->ui_manager, ui, -1, &error);
5165 if (error != NULL) {
5166 g_warning ("%s: %s", G_STRFUNC, error->message);
5167 g_error_free (error);
5171 static void
5172 e_contact_editor_dispose (GObject *object)
5174 EContactEditor *e_contact_editor = E_CONTACT_EDITOR (object);
5176 if (e_contact_editor->priv->file_selector != NULL) {
5177 gtk_widget_destroy (e_contact_editor->priv->file_selector);
5178 e_contact_editor->priv->file_selector = NULL;
5181 g_slist_free_full (
5182 e_contact_editor->priv->writable_fields,
5183 (GDestroyNotify) g_free);
5184 e_contact_editor->priv->writable_fields = NULL;
5186 g_slist_free_full (
5187 e_contact_editor->priv->required_fields,
5188 (GDestroyNotify) g_free);
5189 e_contact_editor->priv->required_fields = NULL;
5191 if (e_contact_editor->priv->target_client) {
5192 g_signal_handler_disconnect (
5193 e_contact_editor->priv->target_client,
5194 e_contact_editor->priv->target_editable_id);
5197 if (e_contact_editor->priv->name) {
5198 e_contact_name_free (e_contact_editor->priv->name);
5199 e_contact_editor->priv->name = NULL;
5202 if (e_contact_editor->priv->focus_tracker) {
5203 g_signal_handlers_disconnect_by_data (
5204 e_contact_editor->priv->focus_tracker,
5205 e_contact_editor);
5208 g_clear_object (&e_contact_editor->priv->contact);
5209 g_clear_object (&e_contact_editor->priv->source_client);
5210 g_clear_object (&e_contact_editor->priv->target_client);
5211 g_clear_object (&e_contact_editor->priv->builder);
5212 g_clear_object (&e_contact_editor->priv->ui_manager);
5213 g_clear_object (&e_contact_editor->priv->cancellable);
5214 g_clear_object (&e_contact_editor->priv->focus_tracker);
5216 /* Chain up to parent's dispose() method. */
5217 G_OBJECT_CLASS (parent_class)->dispose (object);
5220 static void
5221 supported_fields_cb (GObject *source_object,
5222 GAsyncResult *result,
5223 gpointer user_data)
5225 EBookClient *book_client = E_BOOK_CLIENT (source_object);
5226 EContactEditor *ce = user_data;
5227 gchar *prop_value = NULL;
5228 GSList *fields;
5229 gboolean success;
5230 GError *error = NULL;
5232 success = e_client_get_backend_property_finish (
5233 E_CLIENT (book_client), result, &prop_value, &error);
5235 if (!success)
5236 prop_value = NULL;
5238 if (error != NULL) {
5239 g_warning (
5240 "%s: Failed to get supported fields: %s",
5241 G_STRFUNC, error->message);
5242 g_error_free (error);
5245 if (!g_slist_find (eab_editor_get_all_editors (), ce)) {
5246 g_warning (
5247 "supported_fields_cb called for book that's still "
5248 "around, but contact editor that's been destroyed.");
5249 g_free (prop_value);
5250 return;
5253 fields = e_client_util_parse_comma_strings (prop_value);
5255 g_object_set (ce, "writable_fields", fields, NULL);
5257 g_slist_free_full (fields, (GDestroyNotify) g_free);
5258 g_free (prop_value);
5260 eab_editor_show (EAB_EDITOR (ce));
5262 sensitize_all (ce);
5265 static void
5266 required_fields_cb (GObject *source_object,
5267 GAsyncResult *result,
5268 gpointer user_data)
5270 EBookClient *book_client = E_BOOK_CLIENT (source_object);
5271 EContactEditor *ce = user_data;
5272 gchar *prop_value = NULL;
5273 GSList *fields;
5274 gboolean success;
5275 GError *error = NULL;
5277 success = e_client_get_backend_property_finish (
5278 E_CLIENT (book_client), result, &prop_value, &error);
5280 if (!success)
5281 prop_value = NULL;
5283 if (error != NULL) {
5284 g_warning (
5285 "%s: Failed to get supported fields: %s",
5286 G_STRFUNC, error->message);
5287 g_error_free (error);
5290 if (!g_slist_find (eab_editor_get_all_editors (), ce)) {
5291 g_warning (
5292 "supported_fields_cb called for book that's still "
5293 "around, but contact editor that's been destroyed.");
5294 g_free (prop_value);
5295 return;
5298 fields = e_client_util_parse_comma_strings (prop_value);
5300 g_object_set (ce, "required_fields", fields, NULL);
5302 g_slist_free_full (fields, (GDestroyNotify) g_free);
5303 g_free (prop_value);
5306 EABEditor *
5307 e_contact_editor_new (EShell *shell,
5308 EBookClient *book_client,
5309 EContact *contact,
5310 gboolean is_new_contact,
5311 gboolean editable)
5313 EABEditor *editor;
5315 g_return_val_if_fail (E_IS_SHELL (shell), NULL);
5316 g_return_val_if_fail (E_IS_BOOK_CLIENT (book_client), NULL);
5317 g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
5319 editor = g_object_new (E_TYPE_CONTACT_EDITOR, "shell", shell, NULL);
5321 g_object_set (
5322 editor,
5323 "source_client", book_client,
5324 "contact", contact,
5325 "is_new_contact", is_new_contact,
5326 "editable", editable,
5327 NULL);
5329 return editor;
5332 static void
5333 notify_readonly_cb (EBookClient *book_client,
5334 GParamSpec *pspec,
5335 EContactEditor *ce)
5337 EClient *client;
5338 gint new_target_editable;
5339 gboolean changed = FALSE;
5341 client = E_CLIENT (ce->priv->target_client);
5342 new_target_editable = !e_client_is_readonly (client);
5344 if (ce->priv->target_editable != new_target_editable)
5345 changed = TRUE;
5347 ce->priv->target_editable = new_target_editable;
5349 if (changed)
5350 sensitize_all (ce);
5353 static void
5354 e_contact_editor_set_property (GObject *object,
5355 guint property_id,
5356 const GValue *value,
5357 GParamSpec *pspec)
5359 EContactEditor *editor;
5361 editor = E_CONTACT_EDITOR (object);
5363 switch (property_id) {
5364 case PROP_SOURCE_CLIENT: {
5365 gboolean writable;
5366 gboolean changed = FALSE;
5367 EBookClient *source_client;
5369 source_client = E_BOOK_CLIENT (g_value_get_object (value));
5371 if (source_client == editor->priv->source_client)
5372 break;
5374 if (editor->priv->source_client)
5375 g_object_unref (editor->priv->source_client);
5377 editor->priv->source_client = source_client;
5378 g_object_ref (editor->priv->source_client);
5380 if (!editor->priv->target_client) {
5381 editor->priv->target_client = editor->priv->source_client;
5382 g_object_ref (editor->priv->target_client);
5384 editor->priv->target_editable_id = e_signal_connect_notify (
5385 editor->priv->target_client, "notify::readonly",
5386 G_CALLBACK (notify_readonly_cb), editor);
5388 e_client_get_backend_property (
5389 E_CLIENT (editor->priv->target_client),
5390 BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS,
5391 NULL, supported_fields_cb, editor);
5393 e_client_get_backend_property (
5394 E_CLIENT (editor->priv->target_client),
5395 BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS,
5396 NULL, required_fields_cb, editor);
5399 writable = !e_client_is_readonly (E_CLIENT (editor->priv->target_client));
5400 if (writable != editor->priv->target_editable) {
5401 editor->priv->target_editable = writable;
5402 changed = TRUE;
5405 if (changed)
5406 sensitize_all (editor);
5408 break;
5411 case PROP_TARGET_CLIENT: {
5412 gboolean writable;
5413 gboolean changed = FALSE;
5414 EBookClient *target_client;
5416 target_client = E_BOOK_CLIENT (g_value_get_object (value));
5418 if (target_client == editor->priv->target_client)
5419 break;
5421 if (editor->priv->target_client) {
5422 g_signal_handler_disconnect (
5423 editor->priv->target_client,
5424 editor->priv->target_editable_id);
5425 g_object_unref (editor->priv->target_client);
5428 editor->priv->target_client = target_client;
5429 g_object_ref (editor->priv->target_client);
5431 editor->priv->target_editable_id = e_signal_connect_notify (
5432 editor->priv->target_client, "notify::readonly",
5433 G_CALLBACK (notify_readonly_cb), editor);
5435 e_client_get_backend_property (
5436 E_CLIENT (editor->priv->target_client),
5437 BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS,
5438 NULL, supported_fields_cb, editor);
5440 e_client_get_backend_property (
5441 E_CLIENT (editor->priv->target_client),
5442 BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS,
5443 NULL, required_fields_cb, editor);
5445 if (!editor->priv->is_new_contact)
5446 editor->priv->changed = TRUE;
5448 writable = !e_client_is_readonly (E_CLIENT (editor->priv->target_client));
5450 if (writable != editor->priv->target_editable) {
5451 editor->priv->target_editable = writable;
5452 changed = TRUE;
5455 if (changed)
5456 sensitize_all (editor);
5458 break;
5461 case PROP_CONTACT:
5462 if (editor->priv->contact)
5463 g_object_unref (editor->priv->contact);
5464 editor->priv->contact = e_contact_duplicate (
5465 E_CONTACT (g_value_get_object (value)));
5466 fill_in_all (editor);
5467 editor->priv->changed = FALSE;
5468 break;
5470 case PROP_IS_NEW_CONTACT:
5471 editor->priv->is_new_contact = g_value_get_boolean (value);
5472 break;
5474 case PROP_EDITABLE: {
5475 gboolean new_value = g_value_get_boolean (value);
5476 gboolean changed = (editor->priv->target_editable != new_value);
5478 editor->priv->target_editable = new_value;
5480 if (changed)
5481 sensitize_all (editor);
5482 break;
5485 case PROP_CHANGED: {
5486 gboolean new_value = g_value_get_boolean (value);
5487 gboolean changed = (editor->priv->changed != new_value);
5489 editor->priv->changed = new_value;
5491 if (changed)
5492 sensitize_ok (editor);
5493 break;
5495 case PROP_WRITABLE_FIELDS:
5496 g_slist_free_full (
5497 editor->priv->writable_fields,
5498 (GDestroyNotify) g_free);
5499 editor->priv->writable_fields = g_slist_copy_deep (
5500 g_value_get_pointer (value),
5501 (GCopyFunc) g_strdup, NULL);
5503 sensitize_all (editor);
5504 break;
5505 case PROP_REQUIRED_FIELDS:
5506 g_slist_free_full (
5507 editor->priv->required_fields,
5508 (GDestroyNotify) g_free);
5509 editor->priv->required_fields = g_slist_copy_deep (
5510 g_value_get_pointer (value),
5511 (GCopyFunc) g_strdup, NULL);
5512 break;
5513 default:
5514 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
5515 break;
5519 static void
5520 e_contact_editor_get_property (GObject *object,
5521 guint property_id,
5522 GValue *value,
5523 GParamSpec *pspec)
5525 EContactEditor *e_contact_editor;
5527 e_contact_editor = E_CONTACT_EDITOR (object);
5529 switch (property_id) {
5530 case PROP_SOURCE_CLIENT:
5531 g_value_set_object (value, e_contact_editor->priv->source_client);
5532 break;
5534 case PROP_TARGET_CLIENT:
5535 g_value_set_object (value, e_contact_editor->priv->target_client);
5536 break;
5538 case PROP_CONTACT:
5539 extract_all (e_contact_editor);
5540 g_value_set_object (value, e_contact_editor->priv->contact);
5541 break;
5543 case PROP_IS_NEW_CONTACT:
5544 g_value_set_boolean (
5545 value, e_contact_editor->priv->is_new_contact);
5546 break;
5548 case PROP_EDITABLE:
5549 g_value_set_boolean (
5550 value, e_contact_editor->priv->target_editable);
5551 break;
5553 case PROP_CHANGED:
5554 g_value_set_boolean (
5555 value, e_contact_editor->priv->changed);
5556 break;
5558 case PROP_WRITABLE_FIELDS:
5559 g_value_set_pointer (value, e_contact_editor->priv->writable_fields);
5560 break;
5561 case PROP_REQUIRED_FIELDS:
5562 g_value_set_pointer (value, e_contact_editor->priv->required_fields);
5563 break;
5564 default:
5565 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
5566 break;
5571 * e_contact_editor_raise:
5572 * @config: The %EContactEditor object.
5574 * Raises the dialog associated with this %EContactEditor object.
5576 static void
5577 e_contact_editor_raise (EABEditor *editor)
5579 EContactEditor *ce = E_CONTACT_EDITOR (editor);
5580 GdkWindow *window;
5582 window = gtk_widget_get_window (ce->priv->app);
5584 if (window != NULL)
5585 gdk_window_raise (window);
5589 * e_contact_editor_show:
5590 * @ce: The %EContactEditor object.
5592 * Shows the dialog associated with this %EContactEditor object.
5594 static void
5595 e_contact_editor_show (EABEditor *editor)
5597 EContactEditor *ce = E_CONTACT_EDITOR (editor);
5598 gtk_widget_show (ce->priv->app);