9a04f8b6a43fae2cfa1aa9e0af5dea5ab641f0f5
[claws.git] / src / editaddress.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <gtk/gtkwindow.h>
30 #include <gtk/gtksignal.h>
31 #include <gtk/gtklabel.h>
32 #include <gtk/gtkvbox.h>
33 #include <gtk/gtkentry.h>
34 #include <gtk/gtktable.h>
35
36 #include "mgutils.h"
37 #include "addressbook.h"
38 #include "addressitem.h"
39 #include "addritem.h"
40 #include "addrbook.h"
41 #include "manage_window.h"
42 #include "gtkutils.h"
43 #include "codeconv.h"
44 #include "editaddress.h"
45
46 #include "prefs_common.h"
47
48 static struct _PersonEdit_dlg {
49         GtkWidget *container;
50         GtkWidget *notebook;
51         GtkWidget *ok_btn;
52         GtkWidget *cancel_btn;
53         GtkWidget *statusbar;   /* used when prefs_common.addressbook_use_editaddress_dialog is TRUE */
54         GtkWidget *title;       /* used when prefs_common.addressbook_use_editaddress_dialog is FALSE */
55         gint status_cid;
56
57         /* User data tab */
58         GtkWidget *entry_name;
59         GtkWidget *entry_first;
60         GtkWidget *entry_last;
61         GtkWidget *entry_nick;
62
63         /* EMail data tab */
64         GtkWidget *entry_email;
65         GtkWidget *entry_alias;
66         GtkWidget *entry_remarks;
67         GtkWidget *clist_email;
68         GtkWidget *email_up;
69         GtkWidget *email_down;
70         GtkWidget *email_del;
71         GtkWidget *email_mod;
72         GtkWidget *email_add;
73
74         /* Attribute data tab */
75         GtkWidget *entry_atname;
76         GtkWidget *entry_atvalue;
77         GtkWidget *clist_attrib;
78         GtkWidget *attrib_add;
79         GtkWidget *attrib_del;
80         GtkWidget *attrib_mod;
81
82         gint rowIndEMail;
83         gint rowIndAttrib;
84         gboolean editNew;
85         gboolean read_only;
86 } personeditdlg;
87
88 /* transient data */
89 static AddressBookFile *current_abf = NULL;
90 static ItemPerson *current_person = NULL;
91 static ItemFolder *current_parent_folder = NULL;
92 static EditAddressPostUpdateCallback edit_person_close_post_update_cb = NULL;
93
94 typedef enum {
95         EMAIL_COL_EMAIL   = 0,
96         EMAIL_COL_ALIAS   = 1,
97         EMAIL_COL_REMARKS = 2
98 } PersonEditEMailColumnPos;
99
100 typedef enum {
101         ATTRIB_COL_NAME    = 0,
102         ATTRIB_COL_VALUE   = 1
103 } PersonEditAttribColumnPos;
104
105 #define EDITPERSON_WIDTH      520
106 #define EDITPERSON_HEIGHT     320
107
108 #define EMAIL_N_COLS          3
109 #define EMAIL_COL_WIDTH_EMAIL 180
110 #define EMAIL_COL_WIDTH_ALIAS 80
111
112 #define ATTRIB_N_COLS          2
113 #define ATTRIB_COL_WIDTH_NAME  120
114 #define ATTRIB_COL_WIDTH_VALUE 180
115
116 #define PAGE_BASIC             0
117 #define PAGE_EMAIL             1
118 #define PAGE_ATTRIBUTES        2
119
120 static void edit_person_status_show( gchar *msg ) {
121         if( personeditdlg.statusbar != NULL ) {
122                 gtk_statusbar_pop( GTK_STATUSBAR(personeditdlg.statusbar), personeditdlg.status_cid );
123                 if( msg ) {
124                         gtk_statusbar_push( GTK_STATUSBAR(personeditdlg.statusbar), personeditdlg.status_cid, msg );
125                 }
126         }
127 }
128
129 static void edit_person_ok(GtkWidget *widget, gboolean *cancelled) {
130         *cancelled = FALSE;
131         if (prefs_common.addressbook_use_editaddress_dialog)
132                 gtk_main_quit();
133         else
134                 addressbook_edit_person_close( *cancelled );
135 }
136
137 static void edit_person_cancel(GtkWidget *widget, gboolean *cancelled) {
138         *cancelled = TRUE;
139         if (prefs_common.addressbook_use_editaddress_dialog)
140                 gtk_main_quit();
141         else
142                 addressbook_edit_person_close( *cancelled );
143 }
144
145 static gint edit_person_delete_event(GtkWidget *widget, GdkEventAny *event, gboolean *cancelled) {
146         *cancelled = TRUE;
147         if (prefs_common.addressbook_use_editaddress_dialog)
148                 gtk_main_quit();
149         else
150                 addressbook_edit_person_close( *cancelled );
151         return TRUE;
152 }
153
154 static gboolean edit_person_key_pressed(GtkWidget *widget, GdkEventKey *event, gboolean *cancelled) {
155         if (prefs_common.addressbook_use_editaddress_dialog) {
156         if (event && event->keyval == GDK_Escape) {
157                 *cancelled = TRUE;
158                 gtk_main_quit();
159         }
160         }
161         return FALSE;
162 }
163
164 static gchar *_title_new_ = NULL;
165 static gchar *_title_edit_ = NULL;
166
167 static void edit_person_set_widgets_title( gchar *text )
168 {
169         gchar *label = NULL;
170
171         g_return_if_fail( text != NULL );
172
173         gtk_label_set_text(GTK_LABEL(personeditdlg.title), "");
174         label = g_markup_printf_escaped("<b>%s</b>", text);
175         gtk_label_set_markup(GTK_LABEL(personeditdlg.title), label);
176         g_free(label);
177 }
178
179 static void edit_person_set_window_title( gint pageNum ) {
180         gchar *sTitle;
181
182         if( _title_new_ == NULL ) {
183                 _title_new_ = g_strdup( _("Add New Person") );
184                 _title_edit_ = g_strdup( _("Edit Person Details") );
185         }
186
187         if( pageNum == PAGE_BASIC ) {
188                 if( personeditdlg.editNew ) {
189                         if (prefs_common.addressbook_use_editaddress_dialog)
190                                 gtk_window_set_title( GTK_WINDOW(personeditdlg.container), _title_new_ );
191                         else
192                                 edit_person_set_widgets_title( _title_new_ );
193                 }
194                 else {
195                         if (prefs_common.addressbook_use_editaddress_dialog)
196                                 gtk_window_set_title( GTK_WINDOW(personeditdlg.container), _title_edit_ );
197                         else
198                                 edit_person_set_widgets_title( _title_edit_ );
199                 }
200         }
201         else {
202                 if( personeditdlg.entry_name == NULL ) {
203                         sTitle = g_strdup( _title_edit_ );
204                 }
205                 else {
206                         gchar *name;
207                         name = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_name), 0, -1 );
208                         sTitle = g_strdup_printf( "%s - %s", _title_edit_, name );
209                         g_free( name );
210                 }
211                 if (prefs_common.addressbook_use_editaddress_dialog)
212                         gtk_window_set_title( GTK_WINDOW(personeditdlg.container), sTitle );
213                 else
214                         edit_person_set_widgets_title( sTitle );
215                 g_free( sTitle );
216         }
217 }
218
219 static void edit_person_email_clear( gpointer data ) {
220         gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_email), "" );
221         gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_alias), "" );
222         gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_remarks), "" );
223 }
224
225 static void edit_person_attrib_clear( gpointer data ) {
226         gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atname), "" );
227         gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atvalue), "" );
228 }
229
230 static void edit_person_switch_page( GtkNotebook *notebook, GtkNotebookPage *page,
231                                         gint pageNum, gpointer user_data)
232 {
233         edit_person_set_window_title( pageNum );
234         edit_person_status_show( "" );
235 }
236
237 /*
238 * Load clist with a copy of person's email addresses.
239 */
240 static void edit_person_load_email( ItemPerson *person ) {
241         GList *node = person->listEMail;
242         GtkCList *clist = GTK_CLIST(personeditdlg.clist_email);
243         gchar *text[ EMAIL_N_COLS ];
244         while( node ) {
245                 ItemEMail *emorig = ( ItemEMail * ) node->data;
246                 ItemEMail *email = addritem_copyfull_item_email( emorig );
247                 gint row;
248                 text[ EMAIL_COL_EMAIL   ] = email->address;
249                 text[ EMAIL_COL_ALIAS   ] = email->obj.name;
250                 text[ EMAIL_COL_REMARKS ] = email->remarks;
251
252                 row = gtk_clist_append( clist, text );
253                 gtk_clist_set_row_data( clist, row, email );
254                 node = g_list_next( node );
255         }
256 }
257
258 static void edit_person_email_list_selected( GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data ) {
259         ItemEMail *email = gtk_clist_get_row_data( clist, row );
260         if( email ) {
261                 if( email->address )
262                         gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_email), email->address );
263                 if( ADDRITEM_NAME(email) )
264                         gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_alias), ADDRITEM_NAME(email) );
265                 if( email->remarks )
266                         gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_remarks), email->remarks );
267                 if (!personeditdlg.read_only) {
268                         gtk_widget_set_sensitive(personeditdlg.email_del, TRUE);
269                         gtk_widget_set_sensitive(personeditdlg.email_up, row > 0);
270                         gtk_widget_set_sensitive(personeditdlg.email_down, gtk_clist_get_row_data(clist, row + 1) != NULL);
271                 }
272         } else {
273                 gtk_widget_set_sensitive(personeditdlg.email_del, FALSE);
274                 gtk_widget_set_sensitive(personeditdlg.email_up, FALSE);
275                 gtk_widget_set_sensitive(personeditdlg.email_down, FALSE);
276         }
277         personeditdlg.rowIndEMail = row;
278         edit_person_status_show( NULL );
279 }
280
281 static void edit_person_email_move( gint dir ) {
282         GtkCList *clist = GTK_CLIST(personeditdlg.clist_email);
283         gint row = personeditdlg.rowIndEMail + dir;
284         ItemEMail *email = gtk_clist_get_row_data( clist, row );
285         if( email ) {
286                 gtk_clist_row_move( clist, personeditdlg.rowIndEMail, row );
287                 personeditdlg.rowIndEMail = row;
288                 if (!personeditdlg.read_only) {
289                         gtk_widget_set_sensitive(personeditdlg.email_up, row > 0);
290                         gtk_widget_set_sensitive(personeditdlg.email_down, gtk_clist_get_row_data(clist, row + 1) != NULL);
291                 }
292         } else {
293                 gtk_widget_set_sensitive(personeditdlg.email_up, FALSE);
294                 gtk_widget_set_sensitive(personeditdlg.email_down, FALSE);
295         }
296         edit_person_email_clear( NULL );
297         edit_person_status_show( NULL );
298 }
299
300 static void edit_person_email_move_up( gpointer data ) {
301         edit_person_email_move( -1 );
302 }
303
304 static void edit_person_email_move_down( gpointer data ) {
305         edit_person_email_move( +1 );
306 }
307
308 static void edit_person_email_delete( gpointer data ) {
309         GtkCList *clist = GTK_CLIST(personeditdlg.clist_email);
310         gint row = personeditdlg.rowIndEMail;
311         ItemEMail *email = gtk_clist_get_row_data( clist, row );
312         edit_person_email_clear( NULL );
313         if( email ) {
314                 /* Remove list entry */
315                 gtk_clist_remove( clist, row );
316                 addritem_free_item_email( email );
317                 email = NULL;
318         }
319
320         /* Position hilite bar */
321         email = gtk_clist_get_row_data( clist, row );
322         if( ! email ) {
323                 personeditdlg.rowIndEMail = -1 + row;
324         }
325         if (!personeditdlg.read_only) {
326                 gtk_widget_set_sensitive(personeditdlg.email_del, gtk_clist_get_row_data(clist, 0) != NULL);
327                 gtk_widget_set_sensitive(personeditdlg.email_up, gtk_clist_get_row_data(clist, personeditdlg.rowIndEMail + 1) != NULL);
328                 gtk_widget_set_sensitive(personeditdlg.email_down, gtk_clist_get_row_data(clist, personeditdlg.rowIndEMail - 1) != NULL);
329         }
330         edit_person_status_show( NULL );
331 }
332
333 static ItemEMail *edit_person_email_edit( gboolean *error, ItemEMail *email ) {
334         ItemEMail *retVal = NULL;
335         gchar *sEmail, *sAlias, *sRemarks, *sEmail_;
336
337         *error = TRUE;
338         sEmail_ = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_email), 0, -1 );
339         sAlias = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_alias), 0, -1 );
340         sRemarks = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_remarks), 0, -1 );
341         sEmail = mgu_email_check_empty( sEmail_ );
342         g_free( sEmail_ );
343
344         if( sEmail ) {
345                 if( email == NULL ) {
346                         email = addritem_create_item_email();
347                 }
348                 addritem_email_set_address( email, sEmail );
349                 addritem_email_set_alias( email, sAlias );
350                 addritem_email_set_remarks( email, sRemarks );
351                 retVal = email;
352                 *error = FALSE;
353         }
354         else {
355                 edit_person_status_show( _( "An Email address must be supplied." ) );
356         }
357
358         g_free( sEmail );
359         g_free( sAlias );
360         g_free( sRemarks );
361
362         return retVal;
363 }
364
365 static void edit_person_email_modify( gpointer data ) {
366         gboolean errFlg = FALSE;
367         GtkCList *clist = GTK_CLIST(personeditdlg.clist_email);
368         gint row = personeditdlg.rowIndEMail;
369         ItemEMail *email = gtk_clist_get_row_data( clist, row );
370         if( email ) {
371                 edit_person_email_edit( &errFlg, email );
372                 if( ! errFlg ) {
373                         gtk_clist_set_text( clist, row, EMAIL_COL_EMAIL, email->address );
374                         gtk_clist_set_text( clist, row, EMAIL_COL_ALIAS, email->obj.name );
375                         gtk_clist_set_text( clist, row, EMAIL_COL_REMARKS, email->remarks );
376                         edit_person_email_clear( NULL );
377                 }
378         }
379 }
380
381 static void edit_person_email_add( gpointer data ) {
382         GtkCList *clist = GTK_CLIST(personeditdlg.clist_email);
383         gboolean errFlg = FALSE;
384         ItemEMail *email = NULL;
385         gint row = personeditdlg.rowIndEMail;
386         if( gtk_clist_get_row_data( clist, row ) == NULL ) row = 0;
387
388         email = edit_person_email_edit( &errFlg, NULL );
389         if( ! errFlg ) {
390                 gchar *text[ EMAIL_N_COLS ];
391                 text[ EMAIL_COL_EMAIL   ] = email->address;
392                 text[ EMAIL_COL_ALIAS   ] = email->obj.name;
393                 text[ EMAIL_COL_REMARKS ] = email->remarks;
394
395                 row = gtk_clist_insert( clist, 1 + row, text );
396                 gtk_clist_set_row_data( clist, row, email );
397                 gtk_clist_select_row( clist, row, 0 );
398                 edit_person_email_clear( NULL );
399         }
400 }
401
402 /*
403 * Comparison using cell contents (text in first column). Used for sort
404 * address index widget.
405 */
406 static gint edit_person_attrib_compare_func(
407         GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
408 {
409         GtkCell *cell1 = ((GtkCListRow *)ptr1)->cell;
410         GtkCell *cell2 = ((GtkCListRow *)ptr2)->cell;
411         gchar *name1 = NULL, *name2 = NULL;
412
413         if( cell1 ) name1 = cell1->u.text;
414         if( cell2 ) name2 = cell2->u.text;
415         if( ! name1 ) return ( name2 != NULL );
416         if( ! name2 ) return -1;
417         return g_utf8_collate( name1, name2 );
418 }
419
420 static gboolean list_find_attribute(const gchar *attr)
421 {
422         GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib);
423         UserAttribute *attrib;
424         gint row = 0;
425         while( (attrib = gtk_clist_get_row_data( clist, row )) ) {
426                 if (!g_ascii_strcasecmp(attrib->name, attr)) {
427                         gtk_clist_select_row(clist, row, 0);
428                         return TRUE;
429                 }
430                 row++;
431         }
432         return FALSE;
433 }
434
435 static gboolean list_find_email(const gchar *addr)
436 {
437         GtkCList *clist = GTK_CLIST(personeditdlg.clist_email);
438         ItemEMail *email;
439         gint row = 0;
440         while( (email = gtk_clist_get_row_data( clist, row )) ) {
441                 if (!g_ascii_strcasecmp(email->address, addr)) {
442                         gtk_clist_select_row(clist, row, 0);
443                         return TRUE;
444                 }
445                 row++;
446         }
447         return FALSE;
448 }
449
450 /*
451 * Load clist with a copy of person's email addresses.
452 */
453 static void edit_person_load_attrib( ItemPerson *person ) {
454         GList *node = person->listAttrib;
455         GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib);
456         gchar *text[ ATTRIB_N_COLS ];
457         while( node ) {
458                 UserAttribute *atorig = ( UserAttribute * ) node->data;
459                 UserAttribute *attrib = addritem_copy_attribute( atorig );
460                 gint row;
461                 text[ ATTRIB_COL_NAME  ] = attrib->name;
462                 text[ ATTRIB_COL_VALUE ] = attrib->value;
463
464                 row = gtk_clist_append( clist, text );
465                 gtk_clist_set_row_data( clist, row, attrib );
466                 node = g_list_next( node );
467         }
468 }
469
470 static void edit_person_attrib_list_selected( GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data ) {
471         UserAttribute *attrib = gtk_clist_get_row_data( clist, row );
472         if( attrib && !personeditdlg.read_only) {
473                 gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atname), attrib->name );
474                 gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atvalue), attrib->value );
475                 gtk_widget_set_sensitive(personeditdlg.attrib_del, TRUE);
476         } else {
477                 gtk_widget_set_sensitive(personeditdlg.attrib_del, FALSE);
478         }
479         personeditdlg.rowIndAttrib = row;
480         edit_person_status_show( NULL );
481 }
482
483 static void edit_person_attrib_delete( gpointer data ) {
484         GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib);
485         gint row = personeditdlg.rowIndAttrib;
486         UserAttribute *attrib = gtk_clist_get_row_data( clist, row );
487         edit_person_attrib_clear( NULL );
488         if( attrib ) {
489                 /* Remove list entry */
490                 gtk_clist_remove( clist, row );
491                 addritem_free_attribute( attrib );
492                 attrib = NULL;
493         }
494
495         /* Position hilite bar */
496         attrib = gtk_clist_get_row_data( clist, row );
497         if( ! attrib ) {
498                 personeditdlg.rowIndAttrib = -1 + row;
499         } 
500         
501         if (!personeditdlg.read_only)
502                 gtk_widget_set_sensitive(personeditdlg.attrib_del, gtk_clist_get_row_data(clist, 0) != NULL);
503         
504         edit_person_status_show( NULL );
505 }
506
507 static UserAttribute *edit_person_attrib_edit( gboolean *error, UserAttribute *attrib ) {
508         UserAttribute *retVal = NULL;
509         gchar *sName, *sValue, *sName_, *sValue_;
510
511         *error = TRUE;
512         sName_ = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_atname), 0, -1 );
513         sValue_ = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_atvalue), 0, -1 );
514         sName = mgu_email_check_empty( sName_ );
515         sValue = mgu_email_check_empty( sValue_ );
516         g_free( sName_ );
517         g_free( sValue_ );
518
519         if( sName && sValue ) {
520                 if( attrib == NULL ) {
521                         attrib = addritem_create_attribute();
522                 }
523                 addritem_attrib_set_name( attrib, sName );
524                 addritem_attrib_set_value( attrib, sValue );
525                 retVal = attrib;
526                 *error = FALSE;
527         }
528         else {
529                 edit_person_status_show( _( "A Name and Value must be supplied." ) );
530         }
531
532         g_free( sName );
533         g_free( sValue );
534
535         return retVal;
536 }
537
538 static void edit_person_attrib_modify( gpointer data ) {
539         gboolean errFlg = FALSE;
540         GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib);
541         gint row = personeditdlg.rowIndAttrib;
542         UserAttribute *attrib = gtk_clist_get_row_data( clist, row );
543         if( attrib ) {
544                 edit_person_attrib_edit( &errFlg, attrib );
545                 if( ! errFlg ) {
546                         gtk_clist_set_text( clist, row, ATTRIB_COL_NAME, attrib->name );
547                         gtk_clist_set_text( clist, row, ATTRIB_COL_VALUE, attrib->value );
548                         edit_person_attrib_clear( NULL );
549                 }
550         }
551 }
552
553 static void edit_person_attrib_add( gpointer data ) {
554         GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib);
555         gboolean errFlg = FALSE;
556         UserAttribute *attrib = NULL;
557         gint row = personeditdlg.rowIndAttrib;
558         if( gtk_clist_get_row_data( clist, row ) == NULL ) row = 0;
559
560         attrib = edit_person_attrib_edit( &errFlg, NULL );
561         if( ! errFlg ) {
562                 gchar *text[ EMAIL_N_COLS ];
563                 text[ ATTRIB_COL_NAME  ] = attrib->name;
564                 text[ ATTRIB_COL_VALUE ] = attrib->value;
565
566                 row = gtk_clist_insert( clist, 1 + row, text );
567                 gtk_clist_set_row_data( clist, row, attrib );
568                 gtk_clist_select_row( clist, row, 0 );
569                 edit_person_attrib_clear( NULL );
570         }
571 }
572
573 /*!
574  *\brief        Save Gtk object size to prefs dataset
575  */
576 static void edit_person_size_allocate_cb(GtkWidget *widget,
577                                          GtkAllocation *allocation)
578 {
579         g_return_if_fail(allocation != NULL);
580
581         prefs_common.addressbookeditpersonwin_width = allocation->width;
582         prefs_common.addressbookeditpersonwin_height = allocation->height;
583 }
584
585 /* build edit person widgets, return a pointer to the main container of the widgetset (a vbox) */
586 static GtkWidget* addressbook_edit_person_widgets_create( GtkWidget* container, gboolean *cancelled )
587 {
588         GtkWidget *vbox;
589         GtkWidget *vnbox;
590         GtkWidget *notebook;
591         GtkWidget *hbbox;
592         GtkWidget *ok_btn;
593         GtkWidget *cancel_btn;
594
595         vbox = gtk_vbox_new(FALSE, 4);
596         /* gtk_container_set_border_width(GTK_CONTAINER(vbox), BORDER_WIDTH); */
597         gtk_widget_show(vbox);
598         gtk_container_add(GTK_CONTAINER(container), vbox);
599
600         vnbox = gtk_vbox_new(FALSE, 4);
601         gtk_container_set_border_width(GTK_CONTAINER(vnbox), 4);
602         gtk_widget_show(vnbox);
603         gtk_box_pack_start(GTK_BOX(vbox), vnbox, TRUE, TRUE, 0);
604
605         /* Notebook */
606         notebook = gtk_notebook_new();
607         gtk_widget_show(notebook);
608         gtk_box_pack_start(GTK_BOX(vnbox), notebook, TRUE, TRUE, 0);
609         gtk_container_set_border_width(GTK_CONTAINER(notebook), 6);
610
611         /* Button panel */
612         if (prefs_common.addressbook_use_editaddress_dialog)
613         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
614                                       &ok_btn, GTK_STOCK_OK,
615                                       NULL, NULL);
616         else
617                 gtkut_stock_with_text_button_set_create(&hbbox,
618                                           &cancel_btn, GTK_STOCK_CANCEL, _("Discard"),
619                                       &ok_btn, GTK_STOCK_OK, _("Apply"),
620                                       NULL, NULL, NULL);
621         gtk_box_pack_end(GTK_BOX(vnbox), hbbox, FALSE, FALSE, 0);
622         gtk_widget_grab_default(ok_btn);
623
624         g_signal_connect(G_OBJECT(ok_btn), "clicked",
625                          G_CALLBACK(edit_person_ok), cancelled);
626         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
627                          G_CALLBACK(edit_person_cancel), cancelled);
628         g_signal_connect(G_OBJECT(notebook), "switch_page",
629                          G_CALLBACK(edit_person_switch_page), NULL );
630
631         gtk_widget_show_all(vbox);
632
633         personeditdlg.notebook   = notebook;
634         personeditdlg.ok_btn     = ok_btn;
635         personeditdlg.cancel_btn = cancel_btn;
636
637         return vbox;
638 }
639
640 static void addressbook_edit_person_dialog_create( gboolean *cancelled ) {
641         GtkWidget *window;
642         GtkWidget *hsbox;
643         GtkWidget *vbox;
644         GtkWidget *statusbar;
645         static GdkGeometry geometry;
646
647         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
648         /* gtk_container_set_border_width(GTK_CONTAINER(window), 0); */
649         gtk_window_set_title(GTK_WINDOW(window), _("Edit Person Data"));
650         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
651         gtk_window_set_modal(GTK_WINDOW(window), TRUE); 
652         g_signal_connect(G_OBJECT(window), "delete_event",
653                          G_CALLBACK(edit_person_delete_event),
654                          cancelled);
655         g_signal_connect(G_OBJECT(window), "size_allocate",
656                          G_CALLBACK(edit_person_size_allocate_cb),
657                         cancelled);
658         g_signal_connect(G_OBJECT(window), "key_press_event",
659                          G_CALLBACK(edit_person_key_pressed),
660                          cancelled);
661
662         vbox = addressbook_edit_person_widgets_create(window, cancelled);
663
664         /* Status line */
665         hsbox = gtk_hbox_new(FALSE, 0);
666         gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
667         statusbar = gtk_statusbar_new();
668         gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
669
670         if (!geometry.min_height) {
671                 geometry.min_width = EDITPERSON_WIDTH;
672                 geometry.min_height = EDITPERSON_HEIGHT;
673         }
674
675         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
676                                       GDK_HINT_MIN_SIZE);
677         gtk_widget_set_size_request(window, prefs_common.addressbookeditpersonwin_width,
678                                     prefs_common.addressbookeditpersonwin_height);
679
680         personeditdlg.container  = window;
681         personeditdlg.statusbar  = statusbar;
682         personeditdlg.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit Person Dialog" );
683
684 }
685
686 /* parent must be a box */
687 static void addressbook_edit_person_widgetset_create( GtkWidget *parent, gboolean *cancelled )
688 {
689         GtkWidget *vbox;
690         GtkWidget *label;
691
692         if ( parent == NULL )
693                 g_warning("addressbook_edit_person_widgetset_create: parent is NULL");
694
695         vbox = gtk_vbox_new(FALSE, 0);
696         gtk_box_pack_end(GTK_BOX(parent), vbox, TRUE, TRUE, 0);
697
698         label = gtk_label_new(_("Edit Person Data"));
699         gtk_label_set_justify( GTK_LABEL(label), GTK_JUSTIFY_CENTER);
700         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
701
702         addressbook_edit_person_widgets_create(vbox, cancelled);
703
704         gtk_widget_set_size_request(vbox, EDITPERSON_WIDTH, EDITPERSON_HEIGHT);
705
706         personeditdlg.container = vbox;
707         personeditdlg.title = label;
708         personeditdlg.statusbar  = NULL;
709         personeditdlg.status_cid = 0;
710 }
711
712 void addressbook_edit_person_widgetset_hide( void )
713 {
714         if ( personeditdlg.container )
715                 gtk_widget_hide( personeditdlg.container );
716 }
717
718 static void addressbook_edit_person_page_basic( gint pageNum, gchar *pageLbl ) {
719         GtkWidget *vbox;
720         GtkWidget *table;
721         GtkWidget *label;
722         GtkWidget *entry_name;
723         GtkWidget *entry_fn;
724         GtkWidget *entry_ln;
725         GtkWidget *entry_nn;
726         const gchar *locale;
727         gint top = 0;
728
729         vbox = gtk_vbox_new( FALSE, 8 );
730         gtk_widget_show( vbox );
731         gtk_container_add( GTK_CONTAINER( personeditdlg.notebook ), vbox );
732         gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH );
733
734         label = gtk_label_new_with_mnemonic( pageLbl );
735         gtk_widget_show( label );
736         gtk_notebook_set_tab_label(
737                 GTK_NOTEBOOK( personeditdlg.notebook ),
738                 gtk_notebook_get_nth_page( GTK_NOTEBOOK( personeditdlg.notebook ), pageNum ), label );
739
740         table = gtk_table_new( 4, 3, FALSE);
741         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
742         gtk_container_set_border_width( GTK_CONTAINER(table), 8 );
743         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
744         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
745
746 #define ATTACH_ROW(text, entry) \
747 { \
748         label = gtk_label_new(text); \
749         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
750                          GTK_FILL, 0, 0, 0); \
751         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
752  \
753         entry = gtk_entry_new(); \
754         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
755                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
756         top++; \
757 }
758
759         ATTACH_ROW(_("Display Name"), entry_name);
760         locale = conv_get_current_locale();
761         if (locale &&
762             (!g_ascii_strncasecmp(locale, "ja", 2) ||
763              !g_ascii_strncasecmp(locale, "ko", 2) ||
764              !g_ascii_strncasecmp(locale, "zh", 2))) {
765                 ATTACH_ROW(_("Last Name"), entry_ln);
766                 ATTACH_ROW(_("First Name"), entry_fn);
767         } else {
768                 ATTACH_ROW(_("First Name"), entry_fn);
769                 ATTACH_ROW(_("Last Name"), entry_ln);
770         }
771         ATTACH_ROW(_("Nickname"), entry_nn);
772
773 #undef ATTACH_ROW
774
775         gtk_widget_show_all(vbox);
776
777         personeditdlg.entry_name  = entry_name;
778         personeditdlg.entry_first = entry_fn;
779         personeditdlg.entry_last  = entry_ln;
780         personeditdlg.entry_nick  = entry_nn;
781 }
782
783 static gboolean email_adding = FALSE, email_saving = FALSE;
784
785 static void edit_person_entry_email_changed (GtkWidget *entry, gpointer data)
786 {
787         gboolean non_empty = gtk_clist_get_row_data(GTK_CLIST(personeditdlg.clist_email), 0) != NULL;
788
789         if (personeditdlg.read_only)
790                 return;
791
792         if (gtk_entry_get_text(GTK_ENTRY(personeditdlg.entry_email)) == NULL
793         ||  strlen(gtk_entry_get_text(GTK_ENTRY(personeditdlg.entry_email))) == 0) {
794                 gtk_widget_set_sensitive(personeditdlg.email_add,FALSE);
795                 gtk_widget_set_sensitive(personeditdlg.email_mod,FALSE);
796                 email_adding = FALSE;
797                 email_saving = FALSE;
798         } else if (list_find_email(gtk_entry_get_text(GTK_ENTRY(personeditdlg.entry_email)))) {
799                 gtk_widget_set_sensitive(personeditdlg.email_add,FALSE);
800                 gtk_widget_set_sensitive(personeditdlg.email_mod,non_empty);
801                 email_adding = FALSE;
802                 email_saving = non_empty;
803         } else {
804                 gtk_widget_set_sensitive(personeditdlg.email_add,TRUE);
805                 gtk_widget_set_sensitive(personeditdlg.email_mod,non_empty);
806                 email_adding = TRUE;
807                 email_saving = non_empty;
808         }
809 }
810
811 static gboolean edit_person_entry_email_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
812 {
813         if (event && event->keyval == GDK_Return) {
814                 if (email_saving)
815                         edit_person_email_modify(NULL);         
816                 else if (email_adding)
817                         edit_person_email_add(NULL);
818         }
819         return FALSE;
820 }
821
822
823 static void addressbook_edit_person_page_email( gint pageNum, gchar *pageLbl ) {
824         GtkWidget *vbox;
825         GtkWidget *hbox;
826         GtkWidget *vboxl;
827         GtkWidget *vboxb;
828         GtkWidget *vbuttonbox;
829         GtkWidget *buttonUp;
830         GtkWidget *buttonDown;
831         GtkWidget *buttonDel;
832         GtkWidget *buttonMod;
833         GtkWidget *buttonAdd;
834
835         GtkWidget *table;
836         GtkWidget *label;
837         GtkWidget *clist_swin;
838         GtkWidget *clist;
839         GtkWidget *entry_email;
840         GtkWidget *entry_alias;
841         GtkWidget *entry_remarks;
842         gint top;
843
844         gchar *titles[ EMAIL_N_COLS ];
845         gint i;
846
847         titles[ EMAIL_COL_EMAIL   ] = _("Email Address");
848         titles[ EMAIL_COL_ALIAS   ] = _("Alias");
849         titles[ EMAIL_COL_REMARKS ] = _("Remarks");
850
851         vbox = gtk_vbox_new( FALSE, 8 );
852         gtk_widget_show( vbox );
853         gtk_container_add( GTK_CONTAINER( personeditdlg.notebook ), vbox );
854         gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH );
855
856         label = gtk_label_new_with_mnemonic( pageLbl );
857         gtk_widget_show( label );
858         gtk_notebook_set_tab_label(
859                 GTK_NOTEBOOK( personeditdlg.notebook ),
860                 gtk_notebook_get_nth_page( GTK_NOTEBOOK( personeditdlg.notebook ), pageNum ), label );
861
862         /* Split into two areas */
863         hbox = gtk_hbox_new( FALSE, 0 );
864         gtk_container_add( GTK_CONTAINER( vbox ), hbox );
865
866         /* Address list */
867         vboxl = gtk_vbox_new( FALSE, 4 );
868         gtk_container_add( GTK_CONTAINER( hbox ), vboxl );
869         gtk_container_set_border_width( GTK_CONTAINER(vboxl), 4 );
870
871         clist_swin = gtk_scrolled_window_new( NULL, NULL );
872         gtk_container_add( GTK_CONTAINER(vboxl), clist_swin );
873         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
874                                        GTK_POLICY_AUTOMATIC,
875                                        GTK_POLICY_AUTOMATIC);
876
877         clist = gtk_clist_new_with_titles( EMAIL_N_COLS, titles );
878         gtk_container_add( GTK_CONTAINER(clist_swin), clist );
879         gtk_clist_set_selection_mode( GTK_CLIST(clist), GTK_SELECTION_BROWSE );
880         gtk_clist_set_column_width( GTK_CLIST(clist), EMAIL_COL_EMAIL, EMAIL_COL_WIDTH_EMAIL );
881         gtk_clist_set_column_width( GTK_CLIST(clist), EMAIL_COL_ALIAS, EMAIL_COL_WIDTH_ALIAS );
882
883         for( i = 0; i < EMAIL_N_COLS; i++ )
884                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button, GTK_CAN_FOCUS);
885
886         /* Data entry area */
887         table = gtk_table_new( 4, 2, FALSE);
888         gtk_box_pack_start(GTK_BOX(vboxl), table, FALSE, FALSE, 0);
889         gtk_container_set_border_width( GTK_CONTAINER(table), 4 );
890         gtk_table_set_row_spacings(GTK_TABLE(table), 4);
891         gtk_table_set_col_spacings(GTK_TABLE(table), 4);
892
893         /* First row */
894         top = 0;
895         label = gtk_label_new(_("Email Address"));
896         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
897         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
898
899         entry_email = gtk_entry_new();
900         gtk_table_attach(GTK_TABLE(table), entry_email, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
901
902         /* Next row */
903         ++top;
904         label = gtk_label_new(_("Alias"));
905         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
906         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
907
908         entry_alias = gtk_entry_new();
909         gtk_table_attach(GTK_TABLE(table), entry_alias, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
910
911         /* Next row */
912         ++top;
913         label = gtk_label_new(_("Remarks"));
914         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
915         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
916
917         entry_remarks = gtk_entry_new();
918         gtk_table_attach(GTK_TABLE(table), entry_remarks, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
919
920         /* Button box */
921         vboxb = gtk_vbox_new( FALSE, 4 );
922         gtk_box_pack_start(GTK_BOX(hbox), vboxb, FALSE, FALSE, 2);
923
924         vbuttonbox = gtk_vbutton_box_new();
925         gtk_button_box_set_layout( GTK_BUTTON_BOX(vbuttonbox), GTK_BUTTONBOX_START );
926         gtk_box_set_spacing( GTK_BOX(vbuttonbox), 8 );
927         gtk_container_set_border_width( GTK_CONTAINER(vbuttonbox), 4 );
928         gtk_container_add( GTK_CONTAINER(vboxb), vbuttonbox );
929
930         /* Buttons */
931         buttonUp = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
932         gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonUp );
933
934         buttonDown = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
935         gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonDown );
936
937         buttonDel = gtk_button_new_from_stock(GTK_STOCK_DELETE);
938         gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonDel );
939
940         buttonMod = gtk_button_new_from_stock(GTK_STOCK_SAVE);
941         gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonMod );
942
943         buttonAdd = gtk_button_new_from_stock(GTK_STOCK_ADD);
944         gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonAdd );
945
946         gtk_widget_show_all(vbox);
947
948         /* Event handlers */
949         g_signal_connect( G_OBJECT(clist), "select_row",
950                           G_CALLBACK( edit_person_email_list_selected), NULL );
951         g_signal_connect( G_OBJECT(buttonUp), "clicked",
952                           G_CALLBACK( edit_person_email_move_up ), NULL );
953         g_signal_connect( G_OBJECT(buttonDown), "clicked",
954                           G_CALLBACK( edit_person_email_move_down ), NULL );
955         g_signal_connect( G_OBJECT(buttonDel), "clicked",
956                           G_CALLBACK( edit_person_email_delete ), NULL );
957         g_signal_connect( G_OBJECT(buttonMod), "clicked",
958                           G_CALLBACK( edit_person_email_modify ), NULL );
959         g_signal_connect( G_OBJECT(buttonAdd), "clicked",
960                           G_CALLBACK( edit_person_email_add ), NULL );
961         g_signal_connect(G_OBJECT(entry_email), "changed",
962                          G_CALLBACK(edit_person_entry_email_changed), NULL);
963         g_signal_connect(G_OBJECT(entry_email), "key_press_event",
964                          G_CALLBACK(edit_person_entry_email_pressed), NULL);
965         g_signal_connect(G_OBJECT(entry_alias), "key_press_event",
966                          G_CALLBACK(edit_person_entry_email_pressed), NULL);
967         g_signal_connect(G_OBJECT(entry_remarks), "key_press_event",
968                          G_CALLBACK(edit_person_entry_email_pressed), NULL);
969
970         personeditdlg.clist_email   = clist;
971         personeditdlg.entry_email   = entry_email;
972         personeditdlg.entry_alias   = entry_alias;
973         personeditdlg.entry_remarks = entry_remarks;
974         personeditdlg.email_up = buttonUp;
975         personeditdlg.email_down = buttonDown;
976         personeditdlg.email_del = buttonDel;
977         personeditdlg.email_mod = buttonMod;
978         personeditdlg.email_add = buttonAdd;
979 }
980
981 static gboolean attrib_adding = FALSE, attrib_saving = FALSE;
982
983 static void edit_person_entry_att_changed (GtkWidget *entry, gpointer data)
984 {
985         gboolean non_empty = gtk_clist_get_row_data(GTK_CLIST(personeditdlg.clist_attrib), 0) != NULL;
986
987         if (personeditdlg.read_only)
988                 return;
989
990         if (gtk_entry_get_text(GTK_ENTRY(personeditdlg.entry_atname)) == NULL
991         ||  strlen(gtk_entry_get_text(GTK_ENTRY(personeditdlg.entry_atname))) == 0) {
992                 gtk_widget_set_sensitive(personeditdlg.attrib_add,FALSE);
993                 gtk_widget_set_sensitive(personeditdlg.attrib_mod,FALSE);
994                 attrib_adding = FALSE;
995                 attrib_saving = FALSE;
996         } else if (list_find_attribute(gtk_entry_get_text(GTK_ENTRY(personeditdlg.entry_atname)))) {
997                 gtk_widget_set_sensitive(personeditdlg.attrib_add,FALSE);
998                 gtk_widget_set_sensitive(personeditdlg.attrib_mod,non_empty);
999                 attrib_adding = FALSE;
1000                 attrib_saving = non_empty;
1001         } else {
1002                 gtk_widget_set_sensitive(personeditdlg.attrib_add,TRUE);
1003                 gtk_widget_set_sensitive(personeditdlg.attrib_mod,non_empty);
1004                 attrib_adding = TRUE;
1005                 attrib_saving = non_empty;
1006         }
1007 }
1008
1009 static gboolean edit_person_entry_att_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
1010 {
1011         if (event && event->keyval == GDK_Return) {
1012                 if (attrib_saving)
1013                         edit_person_attrib_modify(NULL);
1014                 else if (attrib_adding)
1015                         edit_person_attrib_add(NULL);
1016         }
1017         return FALSE;
1018 }
1019
1020 static void addressbook_edit_person_page_attrib( gint pageNum, gchar *pageLbl ) {
1021         GtkWidget *vbox;
1022         GtkWidget *hbox;
1023         GtkWidget *vboxl;
1024         GtkWidget *vboxb;
1025         GtkWidget *vbuttonbox;
1026         GtkWidget *buttonDel;
1027         GtkWidget *buttonMod;
1028         GtkWidget *buttonAdd;
1029
1030         GtkWidget *table;
1031         GtkWidget *label;
1032         GtkWidget *clist_swin;
1033         GtkWidget *clist;
1034         GtkWidget *entry_name;
1035         GtkWidget *entry_value;
1036         gint top;
1037
1038         gchar *titles[ ATTRIB_N_COLS ];
1039         gint i;
1040
1041         titles[ ATTRIB_COL_NAME  ] = _("Name");
1042         titles[ ATTRIB_COL_VALUE ] = _("Value");
1043
1044         vbox = gtk_vbox_new( FALSE, 8 );
1045         gtk_widget_show( vbox );
1046         gtk_container_add( GTK_CONTAINER( personeditdlg.notebook ), vbox );
1047         gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH );
1048
1049         label = gtk_label_new_with_mnemonic( pageLbl );
1050         gtk_widget_show( label );
1051         gtk_notebook_set_tab_label(
1052                 GTK_NOTEBOOK( personeditdlg.notebook ),
1053                 gtk_notebook_get_nth_page( GTK_NOTEBOOK( personeditdlg.notebook ), pageNum ), label );
1054
1055         /* Split into two areas */
1056         hbox = gtk_hbox_new( FALSE, 0 );
1057         gtk_container_add( GTK_CONTAINER( vbox ), hbox );
1058
1059         /* Attribute list */
1060         vboxl = gtk_vbox_new( FALSE, 4 );
1061         gtk_container_add( GTK_CONTAINER( hbox ), vboxl );
1062         gtk_container_set_border_width( GTK_CONTAINER(vboxl), 4 );
1063
1064         clist_swin = gtk_scrolled_window_new( NULL, NULL );
1065         gtk_container_add( GTK_CONTAINER(vboxl), clist_swin );
1066         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
1067                                        GTK_POLICY_AUTOMATIC,
1068                                        GTK_POLICY_AUTOMATIC);
1069
1070         clist = gtk_clist_new_with_titles( ATTRIB_N_COLS, titles );
1071         gtk_container_add( GTK_CONTAINER(clist_swin), clist );
1072         gtk_clist_set_selection_mode( GTK_CLIST(clist), GTK_SELECTION_BROWSE );
1073         gtk_clist_set_column_width( GTK_CLIST(clist), ATTRIB_COL_NAME, ATTRIB_COL_WIDTH_NAME );
1074         gtk_clist_set_column_width( GTK_CLIST(clist), ATTRIB_COL_VALUE, ATTRIB_COL_WIDTH_VALUE );
1075         gtk_clist_set_compare_func( GTK_CLIST(clist), edit_person_attrib_compare_func );
1076         gtk_clist_set_auto_sort( GTK_CLIST(clist), TRUE );
1077
1078         for( i = 0; i < ATTRIB_N_COLS; i++ )
1079                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button, GTK_CAN_FOCUS);
1080
1081         /* Data entry area */
1082         table = gtk_table_new( 4, 2, FALSE);
1083         gtk_box_pack_start(GTK_BOX(vboxl), table, FALSE, FALSE, 0);
1084         gtk_container_set_border_width( GTK_CONTAINER(table), 4 );
1085         gtk_table_set_row_spacings(GTK_TABLE(table), 4);
1086         gtk_table_set_col_spacings(GTK_TABLE(table), 4);
1087
1088         /* First row */
1089         top = 0;
1090         label = gtk_label_new(_("Name"));
1091         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
1092         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1093
1094         entry_name = gtk_entry_new();
1095         gtk_table_attach(GTK_TABLE(table), entry_name, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
1096
1097         /* Next row */
1098         ++top;
1099         label = gtk_label_new(_("Value"));
1100         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
1101         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1102
1103         entry_value = gtk_entry_new();
1104         gtk_table_attach(GTK_TABLE(table), entry_value, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
1105
1106         /* Button box */
1107         vboxb = gtk_vbox_new( FALSE, 4 );
1108         gtk_box_pack_start(GTK_BOX(hbox), vboxb, FALSE, FALSE, 2);
1109
1110         vbuttonbox = gtk_vbutton_box_new();
1111         gtk_button_box_set_layout( GTK_BUTTON_BOX(vbuttonbox), GTK_BUTTONBOX_START );
1112         gtk_box_set_spacing( GTK_BOX(vbuttonbox), 8 );
1113         gtk_container_set_border_width( GTK_CONTAINER(vbuttonbox), 4 );
1114         gtk_container_add( GTK_CONTAINER(vboxb), vbuttonbox );
1115
1116         /* Buttons */
1117         buttonDel = gtk_button_new_from_stock(GTK_STOCK_DELETE);
1118         gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonDel );
1119
1120         buttonMod = gtk_button_new_from_stock(GTK_STOCK_SAVE);
1121         gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonMod );
1122
1123         buttonAdd = gtk_button_new_from_stock(GTK_STOCK_ADD);
1124         gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonAdd );
1125         
1126         gtk_widget_set_sensitive(buttonDel,FALSE);
1127         gtk_widget_set_sensitive(buttonMod,FALSE);
1128         gtk_widget_set_sensitive(buttonAdd,FALSE);
1129
1130         gtk_widget_show_all(vbox);
1131
1132         /* Event handlers */
1133         g_signal_connect( G_OBJECT(clist), "select_row",
1134                           G_CALLBACK( edit_person_attrib_list_selected), NULL );
1135         g_signal_connect( G_OBJECT(buttonDel), "clicked",
1136                           G_CALLBACK( edit_person_attrib_delete ), NULL );
1137         g_signal_connect( G_OBJECT(buttonMod), "clicked",
1138                           G_CALLBACK( edit_person_attrib_modify ), NULL );
1139         g_signal_connect( G_OBJECT(buttonAdd), "clicked",
1140                           G_CALLBACK( edit_person_attrib_add ), NULL );
1141         g_signal_connect(G_OBJECT(entry_name), "changed",
1142                          G_CALLBACK(edit_person_entry_att_changed), NULL);
1143         g_signal_connect(G_OBJECT(entry_name), "key_press_event",
1144                          G_CALLBACK(edit_person_entry_att_pressed), NULL);
1145         g_signal_connect(G_OBJECT(entry_value), "key_press_event",
1146                          G_CALLBACK(edit_person_entry_att_pressed), NULL);
1147
1148         personeditdlg.clist_attrib  = clist;
1149         personeditdlg.entry_atname  = entry_name;
1150         personeditdlg.entry_atvalue = entry_value;
1151         personeditdlg.attrib_add = buttonAdd;
1152         personeditdlg.attrib_del = buttonDel;
1153         personeditdlg.attrib_mod = buttonMod;
1154 }
1155
1156 static void addressbook_edit_person_create( GtkWidget *parent, gboolean *cancelled ) {
1157         if (prefs_common.addressbook_use_editaddress_dialog)
1158         addressbook_edit_person_dialog_create( cancelled );
1159         else
1160                 addressbook_edit_person_widgetset_create( parent, cancelled );
1161         addressbook_edit_person_page_basic( PAGE_BASIC, _( "_User Data" ) );
1162         addressbook_edit_person_page_email( PAGE_EMAIL, _( "_Email Addresses" ) );
1163         addressbook_edit_person_page_attrib( PAGE_ATTRIBUTES, _( "O_ther Attributes" ) );
1164         gtk_widget_show_all( personeditdlg.container );
1165 }
1166
1167 /*
1168 * Return list of email items.
1169 */
1170 static GList *edit_person_build_email_list() {
1171         GtkCList *clist = GTK_CLIST(personeditdlg.clist_email);
1172         GList *listEMail = NULL;
1173         ItemEMail *email;
1174         gint row = 0;
1175         while( (email = gtk_clist_get_row_data( clist, row )) ) {
1176                 listEMail = g_list_append( listEMail, email );
1177                 row++;
1178         }
1179         return listEMail;
1180 }
1181
1182 /*
1183 * Return list of attributes.
1184 */
1185 static GList *edit_person_build_attrib_list() {
1186         GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib);
1187         GList *listAttrib = NULL;
1188         UserAttribute *attrib;
1189         gint row = 0;
1190         while( (attrib = gtk_clist_get_row_data( clist, row )) ) {
1191                 listAttrib = g_list_append( listAttrib, attrib );
1192                 row++;
1193         }
1194         return listAttrib;
1195 }
1196
1197 static void update_sensitivity(void)
1198 {
1199         gtk_widget_set_sensitive(personeditdlg.entry_name,    !personeditdlg.read_only);
1200         gtk_widget_set_sensitive(personeditdlg.entry_first,   !personeditdlg.read_only);
1201         gtk_widget_set_sensitive(personeditdlg.entry_last,    !personeditdlg.read_only);
1202         gtk_widget_set_sensitive(personeditdlg.entry_nick,    !personeditdlg.read_only);
1203         gtk_widget_set_sensitive(personeditdlg.entry_email,   !personeditdlg.read_only);
1204         gtk_widget_set_sensitive(personeditdlg.entry_alias,   !personeditdlg.read_only);
1205         gtk_widget_set_sensitive(personeditdlg.entry_remarks, !personeditdlg.read_only);
1206         gtk_widget_set_sensitive(personeditdlg.email_up,      !personeditdlg.read_only);
1207         gtk_widget_set_sensitive(personeditdlg.email_down,    !personeditdlg.read_only);
1208         gtk_widget_set_sensitive(personeditdlg.email_del,     !personeditdlg.read_only);
1209         gtk_widget_set_sensitive(personeditdlg.email_mod,     !personeditdlg.read_only);
1210         gtk_widget_set_sensitive(personeditdlg.email_add,     !personeditdlg.read_only);
1211         gtk_widget_set_sensitive(personeditdlg.entry_atname,  !personeditdlg.read_only);
1212         gtk_widget_set_sensitive(personeditdlg.entry_atvalue, !personeditdlg.read_only);
1213         gtk_widget_set_sensitive(personeditdlg.attrib_add,    !personeditdlg.read_only);
1214         gtk_widget_set_sensitive(personeditdlg.attrib_del,    !personeditdlg.read_only);
1215         gtk_widget_set_sensitive(personeditdlg.attrib_mod,    !personeditdlg.read_only);
1216 }
1217
1218 static void addressbook_edit_person_flush_transient( void )
1219 {
1220         ItemPerson *person = current_person;
1221         EditAddressPostUpdateCallback callback = edit_person_close_post_update_cb;
1222
1223         /* reset transient data */
1224         current_abf = NULL;
1225         current_person = NULL;
1226         current_parent_folder = NULL;
1227         edit_person_close_post_update_cb = NULL;
1228
1229         /* post action to perform on addressbook side */
1230         if (callback)
1231                 callback( person );
1232 }
1233
1234 void addressbook_edit_person_invalidate( AddressBookFile *abf, ItemFolder *parent_folder,
1235                                                                                  ItemPerson *person )
1236 {
1237         if (current_abf == NULL &&
1238                 current_person == NULL &&
1239                 current_parent_folder == NULL)
1240                 /* edit address form is already hidden */
1241                 return;
1242
1243         /* unconditional invalidation or invalidating the currently edited item */
1244         if ( ( abf == NULL && person == NULL && parent_folder == NULL )
1245                 || (current_abf == abf ||
1246                         current_person == person ||
1247                         current_parent_folder == parent_folder))
1248                 addressbook_edit_person_close( TRUE );
1249 }
1250
1251 gboolean addressbook_edit_person_close( gboolean cancelled )
1252 {
1253         GList *listEMail = NULL;
1254         GList *listAttrib = NULL;
1255         gchar *cn = NULL;
1256
1257         listEMail = edit_person_build_email_list();
1258         listAttrib = edit_person_build_attrib_list();
1259         if( cancelled ) {
1260                 addritem_free_list_email( listEMail );
1261                 addritem_free_list_attribute( listAttrib );
1262                 gtk_clist_clear( GTK_CLIST(personeditdlg.clist_email) );
1263                 gtk_clist_clear( GTK_CLIST(personeditdlg.clist_attrib) );
1264
1265                 if (!prefs_common.addressbook_use_editaddress_dialog)
1266                         gtk_widget_hide( personeditdlg.container );
1267
1268                 /* no callback, as we're discarding the form */
1269                 edit_person_close_post_update_cb = NULL;
1270                 addressbook_edit_person_flush_transient();
1271
1272                 /* set focus to the address list (this is done by the post_update_cb usually) */
1273                 addressbook_address_list_set_focus();
1274                 return FALSE;
1275         }
1276
1277         cn = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_name), 0, -1 );
1278         if( current_person && current_abf ) {
1279                 /* Update email/attribute list for existing current_person */
1280                 addrbook_update_address_list( current_abf, current_person, listEMail );
1281                 addrbook_update_attrib_list( current_abf, current_person, listAttrib );
1282         }
1283         else {
1284                 /* Create new current_person and email/attribute list */
1285                 if( cn == NULL || *cn == '\0' ) {
1286                         /* Wasting our time */
1287                         if( listEMail == NULL && listAttrib == NULL ) cancelled = TRUE;
1288                 }
1289                 if( ! cancelled && current_abf ) {
1290                         current_person = addrbook_add_address_list( current_abf, current_parent_folder, listEMail );
1291                         addrbook_add_attrib_list( current_abf, current_person, listAttrib );
1292                 }
1293         }
1294         listEMail = NULL;
1295         listAttrib = NULL;
1296
1297         if( ! cancelled ) {
1298                 /* Set current_person stuff */
1299                 gchar *name;
1300                 addritem_person_set_common_name( current_person, cn );
1301                 name = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_first), 0, -1 );
1302                 addritem_person_set_first_name( current_person, name );
1303                 g_free( name );
1304                 name = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_last), 0, -1 );
1305                 addritem_person_set_last_name( current_person, name );
1306                 g_free( name );
1307                 name = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_nick), 0, -1 );
1308                 addritem_person_set_nick_name( current_person, name );
1309                 g_free( name );
1310         }
1311         g_free( cn );
1312
1313         gtk_clist_clear( GTK_CLIST(personeditdlg.clist_email) );
1314         gtk_clist_clear( GTK_CLIST(personeditdlg.clist_attrib) );
1315
1316         if (!prefs_common.addressbook_use_editaddress_dialog)
1317                 gtk_widget_hide( personeditdlg.container );
1318
1319         addressbook_edit_person_flush_transient();
1320
1321         return TRUE;
1322 }
1323  
1324 /*
1325 * Edit person.
1326 * Enter: abf    Address book.
1327 *        parent Parent folder for person (or NULL if adding to root folder). Argument is
1328 *               only required for new objects).
1329 *        person Person to edit, or NULL for a new person object.
1330 *        pgMail If TRUE, E-Mail page will be activated.
1331 * Return: Edited object, or NULL if cancelled.
1332 */
1333 ItemPerson *addressbook_edit_person( AddressBookFile *abf, ItemFolder *parent_folder, ItemPerson *person,
1334                                                                          gboolean pgMail, GtkWidget *parent_container,
1335                                                                          void (*post_update_cb) (ItemPerson *person),
1336                                                                          gboolean get_focus) {
1337         static gboolean cancelled;
1338
1339         /* set transient data */
1340         current_abf = abf;
1341         current_person = person;
1342         current_parent_folder = parent_folder;
1343         edit_person_close_post_update_cb = post_update_cb;
1344
1345         if( !personeditdlg.container )
1346                 addressbook_edit_person_create(parent_container, &cancelled);
1347
1348         /* typically, get focus when dialog mode is enabled, or when editing a new address */
1349         if( get_focus ) {
1350                 gtk_widget_grab_focus(personeditdlg.ok_btn);
1351                 gtk_widget_grab_focus(personeditdlg.entry_name);
1352         }
1353         
1354         personeditdlg.read_only = (current_abf == NULL);
1355         update_sensitivity();
1356
1357         gtk_widget_show(personeditdlg.container);
1358         if (prefs_common.addressbook_use_editaddress_dialog)
1359                 manage_window_set_transient(GTK_WINDOW(personeditdlg.container));
1360
1361         /* Clear all fields */
1362         personeditdlg.rowIndEMail = -1;
1363         personeditdlg.rowIndAttrib = -1;
1364         edit_person_status_show( "" );
1365         gtk_clist_clear( GTK_CLIST(personeditdlg.clist_email) );
1366         gtk_clist_clear( GTK_CLIST(personeditdlg.clist_attrib) );
1367         gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_name), "" );
1368         gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_first), "" );
1369         gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_last), "" );
1370         gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_nick), "" );
1371
1372         personeditdlg.editNew = FALSE;
1373         if( person ) {
1374                 if( ADDRITEM_NAME(person) )
1375                         gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_name), ADDRITEM_NAME(person) );
1376                 if( person->firstName )
1377                         gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_first), person->firstName );
1378                 if( person->lastName )
1379                         gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_last), person->lastName );
1380                 if( person->nickName )
1381                         gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_nick), person->nickName );
1382                 edit_person_load_email( person );
1383                 edit_person_load_attrib( person );
1384         }
1385         else {
1386                 personeditdlg.editNew = TRUE;
1387         }
1388
1389         /* Select appropriate start page */
1390         if( pgMail ) {
1391                 gtk_notebook_set_current_page( GTK_NOTEBOOK(personeditdlg.notebook), PAGE_EMAIL );
1392         }
1393         else {
1394                 gtk_notebook_set_current_page( GTK_NOTEBOOK(personeditdlg.notebook), PAGE_BASIC );
1395         }
1396
1397         gtk_clist_select_row( GTK_CLIST(personeditdlg.clist_email), 0, 0 );
1398         gtk_clist_select_row( GTK_CLIST(personeditdlg.clist_attrib), 0, 0 );
1399         edit_person_email_clear( NULL );
1400         if (person)
1401                 edit_person_email_list_selected(GTK_CLIST(personeditdlg.clist_email), 0, 0, NULL, NULL);
1402
1403         edit_person_attrib_clear( NULL );
1404
1405         if (prefs_common.addressbook_use_editaddress_dialog) {
1406
1407         gtk_main();
1408                 gtk_widget_hide( personeditdlg.container );
1409
1410                 if (!addressbook_edit_person_close( cancelled ))
1411                         return NULL;
1412         }
1413
1414         return person;
1415 }
1416
1417 /*
1418 * End of Source.
1419 */
1420