aboutsummaryrefslogblamecommitdiffstats
path: root/addressbook/gui/contact-editor/e-contact-editor.c
blob: 2b9c22a249618371a4e33c737d5ff6fbd7a12eb7 (plain) (tree)
1
2
3
4
5


                                                                           

                                          
















                                                                     
                   
 




                                 
                                        

                                

                                       

                                           
                                     

                                    


                                                   



                                                          
                                    
 

                           
                             
                                     
                                      
                              
 

                


                      



                      



                                                                                    
                                                         
 
     
                                                                                                                                                          
      


                                                                                                       
                                                           
                                                 

                                                 
                                               
                                                                  
                                                                          
                                                       
                                              
 


                                                 
 


                           
                 
                 
                        
                     
                           

  





                            


















                                                       
                                                                                    




                             

















                                                                



                                                        

                                         
 
                                                  
 

                                                                    

                                                                    

                                                                        

                                                                               

                                                                     
 

                                       

                                             



                                                                              
 

                                          

                                             



                                                                                 
 

                                         

                                             



                                                                                
 








                                                                                   


                                                   
                                                   

 
           
                                                                                            
 

                                                                  
                          
                          
                                              
                                                                           






                                                                                           

 

                                        
 



                                                                                    
                                                                                       
                                                                                    


           








                                                              




                                               


           
                                                               



                                            
                          










                                                                                   
                                                  


                                                                                        
     
                             
      
                                 
                           




                                               


           
                                                               



                                            
                                                  
 
                                                                              

                        




                                               
 
 


                                                                
                                                     

                                

                                         
 
                                             


                                                                       

                                                                                   




                                               

 














                                                                     
                                                                                                                      
















                                                              





                                                              




                                                   





                                                              








                                                  





                                                              



















                                                                                          
                         




                                       


                                                

                                                        




                                                                    
                                           



                                  
                            


                  
           
                                                    

                     

                              
                                                                                          

                          
 


                                                
                          

                                                            

         

                                                                    

                                                                          




                                                                             


                 





                                                              


                                                                             
                                                           






                                                              
                      
                     
 
                                          
        
                                       



                                                               
        
                                         




                                               




                                                                 
                      
 
                                          

                                
        
                                                                       
        
                                         




                                               


           


                                                                  


                                                                 




                                                 
                          



                                                               
                                                                   



                                                                 
                                                                   
                                            


                                                                 
                                                                     



                                                                 
                                                                    



                                                                  






                                                                                        

                                            
                                                     
                              
                                            
                                           
 

                                
                                        
                              
 


                                                  
 

                                                  


                                                                                   
                                                                                      

                                          


                                                      

                                                 

                                             


           








                                                                                             
                                            
                                                     
                              




                                                  
                                          



                                                       
 








                                                                                                   
                 


                                                                                                        
                                                          




                                             

                                                             
                                

                            
                                                                                 
                                    
                                         
                                                                         



                                                         
                                                            
                                                      

                                                                                
                                    
                             
                                             

                                            
                            



                                                         
                                                 
                                                                                



                                                                




                                               


































                                                                                           








































                                                                                       

                                               
                                                     
 
                          
                                             
 




                                                                     
 




                                                                                                                     






















                                                                                 
                              











                                                






                                                  



                                             



                                                 





























                                                            
        
                                                  







                                                                                                

                                                








                                                    






                                                                               

                                            
                                                     




                                           
 
                                                                   

                                  
                                                 
                

                                                                                                  
         


                                             



                                                 
                                                               










                                                                   













                                                                        

                                     
                                                                           



                                     
                             

 

                         


                                                                               

                                                                       

                                                                 
                                                                                

                                                                                          

                          

  
           
                              
 
                                                     
                                    
 
                                                          

                                                              













                                                                       































                                                                              
                                                                         

                                                                             
                                  


         
           

                                                        
                      
                          
                              
                              
                                     
 








                                               
                                                   
                                                 
        
                                           



                                                                                
                                             

                                                  

                                                           

                                      
                                          



                                                                               

                             
                                                                             
 


                                                                        
 
                                           
                                     
                                                    




                                                                                         





                                                                                




                                                                                



                                                                                  
 




                                                                                

                               
                                                                                   












                                                                                         
                                                                                  



                                                           

                                        
                                               
                                                                                       


                                                                   


                                                                                                


                                                                               
 
                                     




                                                                                     




                                                                    



                                                                                































                                                                                     


                                                                       
 


                                                                     


                                                         

                                           
                                                            

 





























                                                                                            
                

                                  
                                           
                                        
 




                                                                     
                                     
                                     
                                                   
                                             
                              
 


                                                                                                 
                  




                                                                  
                               
 
                                      

                        






                                                                   
                      






                                                                                 
                                        
                      



                                                                           
 
                            
                                                                          
                                                                   
 
                                             
 



                                                       

                      
                                 

                                                                              
                                                                   



                                                                               

                                                










                                                                       



                                                                             
                      
                                                                  

                                                                             
                      




                                                                                     

                                                                                  







                                                                                                        
                

                                             

         







                                             
                               




                                                     



                                                            














                                               
           






                                                                                                                                                                                       
     


                                                                                                      
      


                                                                                           
                                                                                             

                                                     
     
                 
      
         
                         





                                                                
                                                                                                                                














                                              




                      




























































































                                                                                                      
     




                                                                         
 

                                                                                       
        





                                                                    



                                                                                            
                                                                                                         


                                      



















                                                                                                                                       
        






































                                                                                                
      







                                                                                        
                   













                                                                                    
                                                 
        









                                                                                                      




                                                                                                                                                                   
                                                                                           
                                                                                                       
         








                                                                                        
                   
 

                                                 









                                                                                                      
                                                                                                                                                                              



                                              


                                                                                                    

                                                                                                                        
         





                                                                                          
                   
 

                                                   









                                                                                                        
                                                                                                                                                                                       

                           
                                                  


                                                                                                     

                                                                                                                   
         


           
                                              
 
                                                           

                            
                                      

                                                         


           
                                                          
 
                                                                


           

                                  
                         
 


                                                                                                         
 


                                                                                                         
 


                                                                                                         
 


                                                                                                         
        


                                                                                                           






                                                     
















                                                                            




                                                                                                




                                                












































                                                                                                            


























                                                                  




                                                                                        










                                                                            
 

           











                                                                    




                                                                                                   



           







                                                                       
                                           
                             
                                                       

                                                      
                                                              

                                                        
                                                           

                                                     

                                                               


                                                             
                                                                   

                                                       
                                                             

                                                         
                                                               

                                                         
                                                               

                                                             
                                                                   

                                                           
                                                                 

                                                       
                                                             

                                                             
                                                                      

                                                                 
                                                                          

                                                       
                                                            

                                                             



                                                                      









                                                                                                         

                            




                          

                                          







                                                                    
                                                                                                                        



                                                                   
                                                                                                                        



                                                                   
                                                                                                                          










































                                                                                                     
                                                                                                                                      
                                                                                                            
                                                                                                                        

                                                                                                                    
                                                                                                                                               
                                                                                                             

                                                                                                                           

                                               
                                                                                                                                                 



                                                                                                                 
                                                                                                                             








                                                                       
                                                                                              



                                                                                                                                   


                                              

                                              


           
                                     






                                                                                   
                                                                                                                                








                                                                                        
                                                                                                 









                                                                                  
                                                                                         
                                                                           
                                                                                                          






                                                                                
                                                                                         


           
                                    
 
                                   
                   
                              
                                       
                                
                                             
                                      

                                  
                            
                                                    
 
                                                






                                                                     
                                     



                                                                                                    
 



                                                                                








                                                                                                      




                                                                                                               
 
                                                                                   









                                                                         

                 
                                                                                









                                                                    
                 
 





                                                                                                                    
                                   



           




                                                                                

                                                                              







                                                        

                                           



           





                                                                    
                                                                              










                                                            
                                           



           



                                    

                                      

                                  
                            



                                                                            

                                                                                      



                                                                 

                                                   
                 
 


                                                                                               
 



                                                                                














                                                                                                                    




                                                            
                                                                                   












                                                                                                             
                 
 
                                                                                












                                                                                        
                 

         











                                                                 
 











                                                                
















                                                                
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* 
 * e-contact-editor.c
 * Copyright (C) 2000  Ximian, Inc.
 * Author: Chris Lahey <clahey@ximian.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <config.h>

#include <time.h>
#include <gtk/gtkcheckbutton.h>
#include <gtk/gtkcheckmenuitem.h>
#include <gtk/gtkcombo.h>
#include <gtk/gtktext.h>
#include <libgnomeui/gnome-popup-menu.h>
#include <libgnome/gnome-i18n.h>

#include <bonobo/bonobo-ui-container.h>
#include <bonobo/bonobo-ui-util.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gdk-pixbuf/gnome-canvas-pixbuf.h>
#include <gal/widgets/e-categories.h>
#include <gal/widgets/e-gui-utils.h>
#include <gal/widgets/e-unicode.h>
#include <gal/e-text/e-entry.h>

#include <e-util/e-categories-master-list-wombat.h>

#include "addressbook/printing/e-contact-print.h"
#include "addressbook/printing/e-contact-print-envelope.h"
#include "e-util/e-gui-utils.h"
#include "widgets/misc/e-dateedit.h"

#include "e-card-merging.h"

#include "e-contact-editor.h"
#include "e-contact-editor-address.h"
#include "e-contact-editor-fullname.h"
#include "e-contact-save-as.h"

/* Signal IDs */
enum {
    CARD_ADDED,
    CARD_MODIFIED,
    CARD_DELETED,
    EDITOR_CLOSED,
    LAST_SIGNAL
};

static void e_contact_editor_init       (EContactEditor      *card);
static void e_contact_editor_class_init (EContactEditorClass     *klass);
static void e_contact_editor_set_arg (GtkObject *o, GtkArg *arg, guint arg_id);
static void e_contact_editor_get_arg (GtkObject *object, GtkArg *arg, guint arg_id);
static void e_contact_editor_destroy (GtkObject *object);

#if 0
static GtkWidget *e_contact_editor_build_dialog(EContactEditor *editor, gchar *entry_id, gchar *label_id, gchar *title, GList **list, GnomeUIInfo **info);
#endif
static void _email_arrow_pressed (GtkWidget *widget, GdkEventButton *button, EContactEditor *editor);
static void _phone_arrow_pressed (GtkWidget *widget, GdkEventButton *button, EContactEditor *editor);
static void _address_arrow_pressed (GtkWidget *widget, GdkEventButton *button, EContactEditor *editor);
static void enable_writable_fields(EContactEditor *editor);
static void set_editable(EContactEditor *editor);
static void fill_in_info(EContactEditor *editor);
static void extract_info(EContactEditor *editor);
static void set_fields(EContactEditor *editor);
static void set_address_field(EContactEditor *editor, int result);
static void add_field_callback(GtkWidget *widget, EContactEditor *editor);
static void command_state_changed (EContactEditor *ce);
static void close_dialog (EContactEditor *ce);

static GtkObjectClass *parent_class = NULL;

static guint contact_editor_signals[LAST_SIGNAL];

/* The arguments we take */
enum {
    ARG_0,
    ARG_BOOK,
    ARG_CARD,
    ARG_IS_NEW_CARD,
    ARG_EDITABLE,
    ARG_WRITABLE_FIELDS
};

enum {
    DYNAMIC_LIST_EMAIL,
    DYNAMIC_LIST_PHONE,
    DYNAMIC_LIST_ADDRESS
};

GtkType
e_contact_editor_get_type (void)
{
  static GtkType contact_editor_type = 0;

  if (!contact_editor_type)
    {
      static const GtkTypeInfo contact_editor_info =
      {
        "EContactEditor",
        sizeof (EContactEditor),
        sizeof (EContactEditorClass),
        (GtkClassInitFunc) e_contact_editor_class_init,
        (GtkObjectInitFunc) e_contact_editor_init,
        /* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL,
      };

      contact_editor_type = gtk_type_unique (GTK_TYPE_OBJECT, &contact_editor_info);
    }

  return contact_editor_type;
}

typedef void (*GtkSignal_NONE__INT_OBJECT) (GtkObject * object,
                        gint arg1,
                        GtkObject *arg2,
                        gpointer user_data);

static void
e_marshal_NONE__INT_OBJECT (GtkObject * object,
                GtkSignalFunc func,
                gpointer func_data, GtkArg * args)
{
    GtkSignal_NONE__INT_OBJECT rfunc;
    rfunc = (GtkSignal_NONE__INT_OBJECT) func;
    (*rfunc) (object,
          GTK_VALUE_INT (args[0]),
          GTK_VALUE_OBJECT (args[1]),
          func_data);
}

static void
e_contact_editor_class_init (EContactEditorClass *klass)
{
  GtkObjectClass *object_class;

  object_class = (GtkObjectClass*) klass;

  parent_class = gtk_type_class (GTK_TYPE_OBJECT);

  gtk_object_add_arg_type ("EContactEditor::book", GTK_TYPE_OBJECT, 
               GTK_ARG_READWRITE, ARG_BOOK);
  gtk_object_add_arg_type ("EContactEditor::card", GTK_TYPE_OBJECT, 
               GTK_ARG_READWRITE, ARG_CARD);
  gtk_object_add_arg_type ("EContactEditor::is_new_card", GTK_TYPE_BOOL,
               GTK_ARG_READWRITE, ARG_IS_NEW_CARD);
  gtk_object_add_arg_type ("EContactEditor::writable_fields", GTK_TYPE_POINTER,
               GTK_ARG_READWRITE, ARG_WRITABLE_FIELDS);
  gtk_object_add_arg_type ("EContactEditor::editable", GTK_TYPE_BOOL,
               GTK_ARG_READWRITE, ARG_EDITABLE);

  contact_editor_signals[CARD_ADDED] =
      gtk_signal_new ("card_added",
              GTK_RUN_FIRST,
              object_class->type,
              GTK_SIGNAL_OFFSET (EContactEditorClass, card_added),
              e_marshal_NONE__INT_OBJECT,
              GTK_TYPE_NONE, 2,
              GTK_TYPE_INT, GTK_TYPE_OBJECT);

  contact_editor_signals[CARD_MODIFIED] =
      gtk_signal_new ("card_modified",
              GTK_RUN_FIRST,
              object_class->type,
              GTK_SIGNAL_OFFSET (EContactEditorClass, card_modified),
              e_marshal_NONE__INT_OBJECT,
              GTK_TYPE_NONE, 2,
              GTK_TYPE_INT, GTK_TYPE_OBJECT);

  contact_editor_signals[CARD_DELETED] =
      gtk_signal_new ("card_deleted",
              GTK_RUN_FIRST,
              object_class->type,
              GTK_SIGNAL_OFFSET (EContactEditorClass, card_deleted),
              e_marshal_NONE__INT_OBJECT,
              GTK_TYPE_NONE, 2,
              GTK_TYPE_INT, GTK_TYPE_OBJECT);

  contact_editor_signals[EDITOR_CLOSED] =
      gtk_signal_new ("editor_closed",
              GTK_RUN_FIRST,
              object_class->type,
              GTK_SIGNAL_OFFSET (EContactEditorClass, editor_closed),
              gtk_marshal_NONE__NONE,
              GTK_TYPE_NONE, 0);

  gtk_object_class_add_signals (object_class, contact_editor_signals, LAST_SIGNAL);
 
  object_class->set_arg = e_contact_editor_set_arg;
  object_class->get_arg = e_contact_editor_get_arg;
  object_class->destroy = e_contact_editor_destroy;
}

static void
_replace_button(EContactEditor *editor, gchar *button_xml, gchar *image, GtkSignalFunc func)
{
    GladeXML *gui = editor->gui;
    GtkWidget *button = glade_xml_get_widget(gui, button_xml);
    GtkWidget *pixmap;
    gchar *image_temp;
    if (button && GTK_IS_BUTTON(button)) {
        image_temp = g_strdup_printf("%s/%s", EVOLUTIONDIR, image);
        pixmap = e_create_image_widget(NULL, image_temp, NULL, 0, 0);
        gtk_container_add(GTK_CONTAINER(button),
                  pixmap);
        g_free(image_temp);
        gtk_widget_show(pixmap);
        gtk_signal_connect(GTK_OBJECT(button), "button_press_event", func, editor);
    }
}

static void
_replace_buttons(EContactEditor *editor)
{
    _replace_button(editor, "button-phone1", "arrow.png", _phone_arrow_pressed);
    _replace_button(editor, "button-phone2", "arrow.png", _phone_arrow_pressed);
    _replace_button(editor, "button-phone3", "arrow.png", _phone_arrow_pressed);
    _replace_button(editor, "button-phone4", "arrow.png", _phone_arrow_pressed);
    _replace_button(editor, "button-address", "arrow.png", _address_arrow_pressed);
    _replace_button(editor, "button-email1", "arrow.png", _email_arrow_pressed);
}

static void
wants_html_changed (GtkWidget *widget, EContactEditor *editor)
{
    gboolean wants_html;
    gtk_object_get(GTK_OBJECT(widget),
               "active", &wants_html,
               NULL);
    gtk_object_set(GTK_OBJECT(editor->card),
               "wants_html", wants_html,
               NULL);

    if (!editor->changed) {
        editor->changed = TRUE;
        command_state_changed (editor);
    }
}

static void
phone_entry_changed (GtkWidget *widget, EContactEditor *editor)
{
    int which;
    gchar *string;
    GtkEntry *entry = GTK_ENTRY(widget);
    ECardPhone *phone;

    if ( widget == glade_xml_get_widget(editor->gui, "entry-phone1") ) {
        which = 1;
    } else if ( widget == glade_xml_get_widget(editor->gui, "entry-phone2") ) {
        which = 2;
    } else if ( widget == glade_xml_get_widget(editor->gui, "entry-phone3") ) {
        which = 3;
    } else if ( widget == glade_xml_get_widget(editor->gui, "entry-phone4") ) {
        which = 4;
    } else
        return;
    string = e_utf8_gtk_entry_get_text(entry);
    phone = e_card_phone_new();
    phone->number = string;
    e_card_simple_set_phone(editor->simple, editor->phone_choice[which - 1], phone);
#if 0
    phone->number = NULL;
#endif
    e_card_phone_free(phone);
    set_fields(editor);

    if (!editor->changed) {
        editor->changed = TRUE;
        command_state_changed (editor);
    }
}

static void
email_entry_changed (GtkWidget *widget, EContactEditor *editor)
{
    gchar *string;
    GtkEntry *entry = GTK_ENTRY(widget);

    string = e_utf8_gtk_entry_get_text(entry);

    e_card_simple_set_email(editor->simple, editor->email_choice, string);

    g_free (string);

    if (!editor->changed) {
        editor->changed = TRUE;
        command_state_changed (editor);
    }
}

static void
address_text_changed (GtkWidget *widget, EContactEditor *editor)
{
    GtkEditable *editable = GTK_EDITABLE(widget);
    ECardAddrLabel *address;

    if (editor->address_choice == -1)
        return;

    address = e_card_address_label_new();

    address->data = e_utf8_gtk_editable_get_chars(editable, 0, -1);

    e_card_simple_set_address(editor->simple, editor->address_choice, address);
    e_card_address_label_free(address);

    if (!editor->changed) {
        editor->changed = TRUE;
        command_state_changed (editor);
    }
}

/* This function tells you whether name_to_style will make sense.  */
static gboolean
style_makes_sense(const ECardName *name, char *company, int style)
{
    switch (style) {
    case 0: /* Fall Through */
    case 1:
        return TRUE;
    case 2:
        if (company && *company)
            return TRUE;
        else
            return FALSE;
    case 3: /* Fall Through */
    case 4:
        if (company && *company && name && ((name->given && *name->given) || (name->family && *name->family)))
            return TRUE;
        else
            return FALSE;
    default:
        return FALSE;
    }
}

static char *
name_to_style(const ECardName *name, char *company, int style)
{
    char *string;
    char *strings[4], **stringptr;
    char *substring;
    switch (style) {
    case 0:
        stringptr = strings;
        if (name) {
            if (name->family && *name->family)
                *(stringptr++) = name->family;
            if (name->given && *name->given)
                *(stringptr++) = name->given;
        }
        *stringptr = NULL;
        string = g_strjoinv(", ", strings);
        break;
    case 1:
        stringptr = strings;
        if (name) {
            if (name->given && *name->given)
                *(stringptr++) = name->given;
            if (name->family && *name->family)
                *(stringptr++) = name->family;
        }
        *stringptr = NULL;
        string = g_strjoinv(" ", strings);
        break;
    case 2:
        string = g_strdup(company);
        break;
    case 3: /* Fall Through */
    case 4:
        stringptr = strings;
        if (name) {
            if (name->family && *name->family)
                *(stringptr++) = name->family;
            if (name->given && *name->given)
                *(stringptr++) = name->given;
        }
        *stringptr = NULL;
        substring = g_strjoinv(", ", strings);
        if (!(company && *company))
            company = "";
        if (style == 3)
            string = g_strdup_printf("%s (%s)", substring, company);
        else
            string = g_strdup_printf("%s (%s)", company, substring);
        g_free(substring);
        break;
    default:
        string = g_strdup("");
    }
    return string;
}

static int
file_as_get_style (EContactEditor *editor)
{
    GtkEntry *file_as = GTK_ENTRY(glade_xml_get_widget(editor->gui, "entry-file-as"));
    char *filestring;
    char *trystring;
    ECardName *name = editor->name;
    int i;
    int style;

    if (!(file_as && GTK_IS_ENTRY(file_as)))
        return -1;

    filestring = e_utf8_gtk_entry_get_text(file_as);

    style = -1;
    for (i = 0; i < 5; i++) {
        trystring = name_to_style(name, editor->company, i);
        if (!strcmp(trystring, filestring)) {
            g_free(trystring);
            g_free(filestring);
            return i;
        }
        g_free(trystring);
    }
    g_free (filestring);
    return -1;
}

static void
file_as_set_style(EContactEditor *editor, int style)
{
    char *string;
    int i;
    GList *strings = NULL;
    GtkEntry *file_as = GTK_ENTRY(glade_xml_get_widget(editor->gui, "entry-file-as"));
    GtkWidget *widget;
        

    if (!(file_as && GTK_IS_ENTRY(file_as)))
        return;

    if (style == -1) {
        string = e_utf8_gtk_entry_get_text(file_as);
        strings = g_list_append(strings, string);
    }

    widget = glade_xml_get_widget(editor->gui, "combo-file-as");

    for (i = 0; i < 5; i++) {
        if (style_makes_sense(editor->name, editor->company, i)) {
            char *u;
            u = name_to_style(editor->name, editor->company, i);
            string = e_utf8_to_gtk_string (widget, u);
            g_free (u);
            if (string) strings = g_list_append(strings, string);
        }
    }

    if (widget && GTK_IS_COMBO(widget)) {
        GtkCombo *combo = GTK_COMBO(widget);
        gtk_combo_set_popdown_strings(combo, strings);
        g_list_foreach(strings, (GFunc) g_free, NULL);
        g_list_free(strings);
    }

    if (style != -1) {
        string = name_to_style(editor->name, editor->company, style);
        e_utf8_gtk_entry_set_text(file_as, string);
        g_free(string);
    }
}

static void
name_entry_changed (GtkWidget *widget, EContactEditor *editor)
{
    int style = 0;
    char *string;

    style = file_as_get_style(editor);
    
    e_card_name_free(editor->name);

    string = e_utf8_gtk_entry_get_text (GTK_ENTRY(widget));
    editor->name = e_card_name_from_string(string);
    g_free (string);
    
    file_as_set_style(editor, style);

    if (!editor->changed) {
        editor->changed = TRUE;
        command_state_changed (editor);
    }
}

static void
company_entry_changed (GtkWidget *widget, EContactEditor *editor)
{
    int style = 0;

    style = file_as_get_style(editor);
    
    g_free(editor->company);
    
    editor->company = e_utf8_gtk_entry_get_text(GTK_ENTRY(widget));
    
    file_as_set_style(editor, style);

    if (!editor->changed) {
        editor->changed = TRUE;
        command_state_changed (editor);
    }
}

static void
set_entry_changed_signal_phone(EContactEditor *editor, char *id)
{
    GtkWidget *widget = glade_xml_get_widget(editor->gui, id);
    if (widget && GTK_IS_ENTRY(widget))
        gtk_signal_connect(GTK_OBJECT(widget), "changed",
                   phone_entry_changed, editor);
}

static void
set_entry_changed_signals(EContactEditor *editor)
{
    GtkWidget *widget;
    set_entry_changed_signal_phone(editor, "entry-phone1");
    set_entry_changed_signal_phone(editor, "entry-phone2");
    set_entry_changed_signal_phone(editor, "entry-phone3");
    set_entry_changed_signal_phone(editor, "entry-phone4");
    widget = glade_xml_get_widget(editor->gui, "entry-email1");
    if (widget && GTK_IS_ENTRY(widget)) {
        gtk_signal_connect(GTK_OBJECT(widget), "changed",
                   email_entry_changed, editor);
    }
    widget = glade_xml_get_widget(editor->gui, "text-address");
    if (widget && GTK_IS_TEXT(widget)) {
        gtk_signal_connect(GTK_OBJECT(widget), "changed",
                   address_text_changed, editor);
    }
    widget = glade_xml_get_widget(editor->gui, "entry-fullname");
    if (widget && GTK_IS_ENTRY(widget)) {
        gtk_signal_connect(GTK_OBJECT(widget), "changed",
                   name_entry_changed, editor);
    }
    widget = glade_xml_get_widget(editor->gui, "entry-company");
    if (widget && GTK_IS_ENTRY(widget)) {
        gtk_signal_connect(GTK_OBJECT(widget), "changed",
                   company_entry_changed, editor);
    }
}

static void
full_name_clicked(GtkWidget *button, EContactEditor *editor)
{
    GnomeDialog *dialog = GNOME_DIALOG(e_contact_editor_fullname_new(editor->name));
    int result;

    gtk_object_set (GTK_OBJECT (dialog),
            "editable", editor->editable,
            NULL);
    gtk_widget_show(GTK_WIDGET(dialog));
    result = gnome_dialog_run (dialog);

    if (result == 0) {
        ECardName *name;
        GtkWidget *fname_widget;
        int style = 0;

        gtk_object_get(GTK_OBJECT(dialog),
                   "name", &name,
                   NULL);

        style = file_as_get_style(editor);

        fname_widget = glade_xml_get_widget(editor->gui, "entry-fullname");
        if (fname_widget && GTK_IS_ENTRY(fname_widget)) {
            char *full_name = e_card_name_to_string(name);
            e_utf8_gtk_entry_set_text(GTK_ENTRY(fname_widget), full_name);
            g_free(full_name);
        }

        e_card_name_free(editor->name);
        editor->name = e_card_name_copy(name);

        file_as_set_style(editor, style);
    }
    gtk_object_unref(GTK_OBJECT(dialog));
}

static void
full_addr_clicked(GtkWidget *button, EContactEditor *editor)
{
    GnomeDialog *dialog;
    int result;
    const ECardDeliveryAddress *address;

    address = e_card_simple_get_delivery_address(editor->simple, editor->address_choice);

    dialog = GNOME_DIALOG(e_contact_editor_address_new(address));
    gtk_object_set (GTK_OBJECT (dialog),
            "editable", editor->editable,
            NULL);
    gtk_widget_show(GTK_WIDGET(dialog));

    result = gnome_dialog_run (dialog);
    if (result == 0) {
        ECardDeliveryAddress *new_address;
        GtkWidget *address_widget;

        gtk_object_get(GTK_OBJECT(dialog),
                   "address", &new_address,
                   NULL);

        address_widget = glade_xml_get_widget(editor->gui, "text-address");
        if (address_widget && GTK_IS_EDITABLE(address_widget)) {
            char *string = e_card_delivery_address_to_string(new_address);
            e_utf8_gtk_editable_set_text(GTK_EDITABLE(address_widget), string);
            g_free(string);
        } else {
            ECardAddrLabel *address = e_card_delivery_address_to_label(new_address);
            e_card_simple_set_address(editor->simple, editor->address_choice, address);
            e_card_address_label_free(address);
        }

        e_card_simple_set_delivery_address(editor->simple, editor->address_choice, new_address);

        e_card_delivery_address_free(new_address);
    }
    gtk_object_unref(GTK_OBJECT(dialog));
}

static void
categories_clicked(GtkWidget *button, EContactEditor *editor)
{
    char *categories = NULL;
    GnomeDialog *dialog;
    int result;
    GtkWidget *entry = glade_xml_get_widget(editor->gui, "entry-categories");
    ECategoriesMasterList *ecml;
    if (entry && GTK_IS_ENTRY(entry))
        categories = e_utf8_gtk_entry_get_text(GTK_ENTRY(entry));
    else if (editor->card)
        gtk_object_get(GTK_OBJECT(editor->card),
                   "categories", &categories,
                   NULL);
    dialog = GNOME_DIALOG(e_categories_new(categories));
    ecml = e_categories_master_list_wombat_new ();
    gtk_object_set(GTK_OBJECT(dialog),
               "header", _("This contact belongs to these categories:"),
               "ecml", ecml,
               NULL);
    gtk_object_unref (GTK_OBJECT (ecml));
    gtk_widget_show(GTK_WIDGET(dialog));
    result = gnome_dialog_run (dialog);
    g_free (categories);
    if (result == 0) {
        gtk_object_get(GTK_OBJECT(dialog),
                   "categories", &categories,
                   NULL);
        if (entry && GTK_IS_ENTRY(entry))
            e_utf8_gtk_entry_set_text(GTK_ENTRY(entry), categories);
        else
            gtk_object_set(GTK_OBJECT(editor->card),
                       "categories", categories,
                       NULL);
        g_free(categories);
    }
    gtk_object_destroy(GTK_OBJECT(dialog));
}

static void
ensure_select_names_contact (EContactEditor *editor)
{
    if (editor->select_names_contacts == NULL) {
        editor->select_names_contacts = e_select_names_manager_new ();
        e_select_names_manager_add_section (editor->select_names_contacts,
                            "contacts",
                            "Related Contacts");
    }
}

static void
contacts_clicked (GtkWidget *button, EContactEditor *editor)
{
    ensure_select_names_contact (editor);
    e_select_names_manager_activate_dialog (editor->select_names_contacts,
                        "contacts");
}

static void
add_lists (EContactEditor *editor)
{
    GtkWidget *table = glade_xml_get_widget (editor->gui, "table-contacts");
    if (table && GTK_IS_TABLE (table)) {
        GtkWidget *entry;

        ensure_select_names_contact (editor);
        entry = e_select_names_manager_create_entry (editor->select_names_contacts,
                                 "contacts");
        gtk_table_attach_defaults (GTK_TABLE (table), entry, 0, 1, 0, 1);
        gtk_widget_show (entry);
    }
}


typedef struct {
    EContactEditor *ce;
    gboolean should_close;
} EditorCloseStruct;

static void
card_added_cb (EBook *book, EBookStatus status, const char *id, EditorCloseStruct *ecs)
{
    EContactEditor *ce = ecs->ce;
    gboolean should_close = ecs->should_close;

    g_free (ecs);

    gtk_signal_emit (GTK_OBJECT (ce), contact_editor_signals[CARD_ADDED],
             status, ce->card);

    if (status == E_BOOK_STATUS_SUCCESS) {
        ce->is_new_card = FALSE;

        if (should_close)
            close_dialog (ce);
    }
}

static void
card_modified_cb (EBook *book, EBookStatus status, EditorCloseStruct *ecs)
{
    EContactEditor *ce = ecs->ce;
    gboolean should_close = ecs->should_close;

    g_free (ecs);

    gtk_signal_emit (GTK_OBJECT (ce), contact_editor_signals[CARD_MODIFIED],
             status, ce->card);

    if (status == E_BOOK_STATUS_SUCCESS) {
        if (should_close)
            close_dialog (ce);
    }
}

/* Emits the signal to request saving a card */
static void
save_card (EContactEditor *ce, gboolean should_close)
{
    extract_info (ce);
    e_card_simple_sync_card (ce->simple);

    if (ce->book) {
        EditorCloseStruct *ecs = g_new(EditorCloseStruct, 1);
        
        ecs->ce = ce;
        ecs->should_close = should_close;

        if (ce->is_new_card)
            e_card_merging_book_add_card (ce->book, ce->card, GTK_SIGNAL_FUNC(card_added_cb), ecs);
        else
            e_card_merging_book_commit_card (ce->book, ce->card, GTK_SIGNAL_FUNC(card_modified_cb), ecs);
    }
}

/* Closes the dialog box and emits the appropriate signals */
static void
close_dialog (EContactEditor *ce)
{
    g_assert (ce->app != NULL);

    gtk_widget_destroy (ce->app);
    ce->app = NULL;

    gtk_signal_emit (GTK_OBJECT (ce), contact_editor_signals[EDITOR_CLOSED]);
}

/* Menu callbacks */

/* File/Save callback */
static void
file_save_cb (GtkWidget *widget, gpointer data)
{
    EContactEditor *ce;

    ce = E_CONTACT_EDITOR (data);
    save_card (ce, FALSE);
}

/* File/Close callback */
static void
file_close_cb (GtkWidget *widget, gpointer data)
{
    EContactEditor *ce;

    ce = E_CONTACT_EDITOR (data);
    close_dialog (ce);
}

static void
file_save_as_cb (GtkWidget *widget, gpointer data)
{
    EContactEditor *ce;
    ECard *card;

    ce = E_CONTACT_EDITOR (data);

    extract_info (ce);
    e_card_simple_sync_card (ce->simple);

    card = ce->card;
    e_contact_save_as("Save as VCard", card);
}

static void
file_send_as_cb (GtkWidget *widget, gpointer data)
{
    EContactEditor *ce;
    ECard *card;

    ce = E_CONTACT_EDITOR (data);

    extract_info (ce);
    e_card_simple_sync_card (ce->simple);

    card = ce->card;
    e_card_send(card, E_CARD_DISPOSITION_AS_ATTACHMENT);
}

static void
file_send_to_cb (GtkWidget *widget, gpointer data)
{
    EContactEditor *ce;
    ECard *card;

    ce = E_CONTACT_EDITOR (data);

    extract_info (ce);
    e_card_simple_sync_card (ce->simple);

    card = ce->card;
    e_card_send(card, E_CARD_DISPOSITION_AS_TO);
}

gboolean
e_contact_editor_confirm_delete(GtkWindow *parent)
{
    GnomeDialog *dialog;
    GladeXML *gui;
    int result;

    gui = glade_xml_new (EVOLUTION_GLADEDIR "/e-contact-editor-confirm-delete.glade", NULL);

    dialog = GNOME_DIALOG(glade_xml_get_widget(gui, "confirm-dialog"));

    gnome_dialog_set_parent(dialog, parent);
    
    result = gnome_dialog_run_and_close(dialog);

    gtk_object_unref(GTK_OBJECT(gui));

    return !result;
}

static void
card_deleted_cb (EBook *book, EBookStatus status, EContactEditor *ce)
{
    gtk_signal_emit (GTK_OBJECT (ce), contact_editor_signals[CARD_DELETED],
             status, ce->card);
}

static void
delete_cb (GtkWidget *widget, gpointer data)
{
    EContactEditor *ce = E_CONTACT_EDITOR (data);
    ECard *card = ce->card;
    ECardSimple *simple = ce->simple;

    gtk_object_ref(GTK_OBJECT(card));
    gtk_object_ref(GTK_OBJECT(simple));

    if (e_contact_editor_confirm_delete(GTK_WINDOW(ce->app))) {

        extract_info (ce);
        e_card_simple_sync_card (simple);
        
        if (!ce->is_new_card && ce->book)
            e_book_remove_card (ce->book, card, GTK_SIGNAL_FUNC(card_deleted_cb), ce);
    }

    gtk_object_unref(GTK_OBJECT(card));
    gtk_object_unref(GTK_OBJECT(simple));
}

/* Emits the signal to request printing a card */
static void
print_cb (BonoboUIComponent *uih, void *data, const char *path)
{
    EContactEditor *ce;

    ce = E_CONTACT_EDITOR (data);

    extract_info (ce);
    e_card_simple_sync_card (ce->simple);

    gtk_widget_show(e_contact_print_card_dialog_new(ce->card));
}

/* Emits the signal to request printing a card */
static void
print_envelope_cb (BonoboUIComponent *uih, void *data, const char *path)
{
    EContactEditor *ce;

    ce = E_CONTACT_EDITOR (data);

    extract_info (ce);
    e_card_simple_sync_card (ce->simple);

    gtk_widget_show(e_contact_print_envelope_dialog_new(ce->card));
}

/* Toolbar/Save and Close callback */
static void
tb_save_and_close_cb (BonoboUIComponent *uih, void *data, const char *path)
{
    EContactEditor *ce;

    ce = E_CONTACT_EDITOR (data);
    save_card (ce, TRUE);
}

static
BonoboUIVerb verbs [] = {
    BONOBO_UI_UNSAFE_VERB ("ContactEditorSave", file_save_cb),
    BONOBO_UI_UNSAFE_VERB ("ContactEditorSaveAs", file_save_as_cb),
    BONOBO_UI_UNSAFE_VERB ("ContactEditorSaveClose", tb_save_and_close_cb),
    BONOBO_UI_UNSAFE_VERB ("ContactEditorSendAs", file_send_as_cb),
    BONOBO_UI_UNSAFE_VERB ("ContactEditorSendTo", file_send_to_cb),
    BONOBO_UI_UNSAFE_VERB ("ContactEditorDelete", delete_cb),
    BONOBO_UI_UNSAFE_VERB ("ContactEditorPrint", print_cb),
    BONOBO_UI_UNSAFE_VERB ("ContactEditorPrintEnvelope", print_envelope_cb),
    /*  BONOBO_UI_UNSAFE_VERB ("ContactEditorPageSetup", file_page_setup_menu), */
    BONOBO_UI_UNSAFE_VERB ("ContactEditorClose", file_close_cb),
    
    BONOBO_UI_VERB_END
};

static void
create_ui (EContactEditor *ce)
{
    bonobo_ui_component_add_verb_list_with_data (
        ce->uic, verbs, ce);

    bonobo_ui_util_set_ui (ce->uic, EVOLUTION_DATADIR,
                   "evolution-contact-editor.xml",
                   "evolution-contact-editor");
}

/* Callback used when the dialog box is destroyed */
static gint
app_delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
{
    EContactEditor *ce;

    ce = E_CONTACT_EDITOR (data);

    close_dialog (ce);
    return TRUE;
}

static GList *
add_to_tab_order(GList *list, GladeXML *gui, char *name)
{
    GtkWidget *widget = glade_xml_get_widget(gui, name);
    return g_list_prepend(list, widget);
}

static void
setup_tab_order(GladeXML *gui)
{
    GtkWidget *container;
    GList *list = NULL;

    container = glade_xml_get_widget(gui, "table-contact-editor-general");

    if (container) {
        list = add_to_tab_order(list, gui, "entry-fullname");
        list = add_to_tab_order(list, gui, "entry-jobtitle");
        list = add_to_tab_order(list, gui, "entry-company");
        list = add_to_tab_order(list, gui, "combo-file-as");
        list = add_to_tab_order(list, gui, "entry-phone1");
        list = add_to_tab_order(list, gui, "entry-phone2");
        list = add_to_tab_order(list, gui, "entry-phone3");
        list = add_to_tab_order(list, gui, "entry-phone4");
        list = g_list_reverse(list);
        e_container_change_tab_order(GTK_CONTAINER(container), list);
        g_list_free(list);

        list = NULL;
        list = add_to_tab_order(list, gui, "entry-email1");
        list = add_to_tab_order(list, gui, "entry-web");
        list = add_to_tab_order(list, gui, "text-address");
        list = add_to_tab_order(list, gui, "alignment-contacts");
        list = g_list_reverse(list);
        e_container_change_tab_order(GTK_CONTAINER(container), list);
        g_list_free(list);
    }
}

static void
e_contact_editor_init (EContactEditor *e_contact_editor)
{
    GladeXML *gui;
    GtkWidget *widget;
    GtkWidget *bonobo_win;
    GtkWidget *wants_html;
    BonoboUIContainer *container;

    e_contact_editor->email_info = NULL;
    e_contact_editor->phone_info = NULL;
    e_contact_editor->address_info = NULL;
    e_contact_editor->email_popup = NULL;
    e_contact_editor->phone_popup = NULL;
    e_contact_editor->address_popup = NULL;
    e_contact_editor->email_list = NULL;
    e_contact_editor->phone_list = NULL;
    e_contact_editor->address_list = NULL;
    e_contact_editor->name = e_card_name_new();
    e_contact_editor->company = g_strdup("");
    
    e_contact_editor->email_choice = 0;
    e_contact_editor->phone_choice[0] = E_CARD_SIMPLE_PHONE_ID_BUSINESS;
    e_contact_editor->phone_choice[1] = E_CARD_SIMPLE_PHONE_ID_HOME;
    e_contact_editor->phone_choice[2] = E_CARD_SIMPLE_PHONE_ID_BUSINESS_FAX;
    e_contact_editor->phone_choice[3] = E_CARD_SIMPLE_PHONE_ID_MOBILE;
    e_contact_editor->address_choice = 0;

    e_contact_editor->arbitrary_fields = NULL;
    
    e_contact_editor->simple = e_card_simple_new(NULL);

    e_contact_editor->card = NULL;
    e_contact_editor->changed = FALSE;

    gui = glade_xml_new (EVOLUTION_GLADEDIR "/contact-editor.glade", NULL);
    e_contact_editor->gui = gui;

    setup_tab_order(gui);

    e_contact_editor->app = glade_xml_get_widget (gui, "contact editor");

    e_container_foreach_leaf (GTK_CONTAINER (e_contact_editor->app),
                  (GtkCallback) add_field_callback,
                  e_contact_editor);

    _replace_buttons(e_contact_editor);
    add_lists (e_contact_editor);
    set_entry_changed_signals(e_contact_editor);

    wants_html = glade_xml_get_widget(e_contact_editor->gui, "checkbutton-htmlmail");
    if (wants_html && GTK_IS_TOGGLE_BUTTON(wants_html))
        gtk_signal_connect(GTK_OBJECT(wants_html), "toggled",
                   wants_html_changed, e_contact_editor);
    
    widget = glade_xml_get_widget(e_contact_editor->gui, "button-fullname");
    if (widget && GTK_IS_BUTTON(widget))
        gtk_signal_connect(GTK_OBJECT(widget), "clicked",
                   full_name_clicked, e_contact_editor);

    widget = glade_xml_get_widget(e_contact_editor->gui, "button-fulladdr");
    if (widget && GTK_IS_BUTTON(widget))
        gtk_signal_connect(GTK_OBJECT(widget), "clicked",
                   full_addr_clicked, e_contact_editor);

    widget = glade_xml_get_widget(e_contact_editor->gui, "button-categories");
    if (widget && GTK_IS_BUTTON(widget))
        gtk_signal_connect(GTK_OBJECT(widget), "clicked",
                   categories_clicked, e_contact_editor);

    widget = glade_xml_get_widget(e_contact_editor->gui, "button-contacts");
    if (widget && GTK_IS_BUTTON(widget))
        gtk_signal_connect(GTK_OBJECT(widget), "clicked",
                   contacts_clicked, e_contact_editor);


    /* Construct the app */
    bonobo_win = bonobo_window_new ("contact-editor-dialog", "Contact Editor");

    /* FIXME: The sucking bit */
    {
        GtkWidget *contents;

        contents = gnome_dock_get_client_area (
            GNOME_DOCK (GNOME_APP (e_contact_editor->app)->dock));
        if (!contents) {
            g_message ("contact_editor_construct(): Could not get contents");
            return;
        }
        gtk_widget_ref (contents);
        gtk_container_remove (GTK_CONTAINER (contents->parent), contents);
        bonobo_window_set_contents (BONOBO_WINDOW (bonobo_win), contents);
        gtk_widget_destroy (e_contact_editor->app);
        e_contact_editor->app = bonobo_win;
    }

    /* Build the menu and toolbar */

    container = bonobo_ui_container_new ();
    bonobo_ui_container_set_win (container, BONOBO_WINDOW (e_contact_editor->app));

    e_contact_editor->uic = bonobo_ui_component_new_default ();
    if (!e_contact_editor->uic) {
        g_message ("e_contact_editor_init(): eeeeek, could not create the UI handler!");
        return;
    }
    bonobo_ui_component_set_container (e_contact_editor->uic,
                       bonobo_object_corba_objref (
                           BONOBO_OBJECT (container)));

    create_ui (e_contact_editor);

    /* Connect to the deletion of the dialog */

    gtk_signal_connect (GTK_OBJECT (e_contact_editor->app), "delete_event",
                GTK_SIGNAL_FUNC (app_delete_event_cb), e_contact_editor);
}

void
e_contact_editor_destroy (GtkObject *object) {
    EContactEditor *e_contact_editor = E_CONTACT_EDITOR(object);

    if (e_contact_editor->writable_fields) {
        gtk_object_unref(GTK_OBJECT(e_contact_editor->writable_fields));
    }
    if (e_contact_editor->email_list) {
        g_list_foreach(e_contact_editor->email_list, (GFunc) g_free, NULL);
        g_list_free(e_contact_editor->email_list);
    }
    if (e_contact_editor->email_info) {
        g_free(e_contact_editor->email_info);
    }
    if (e_contact_editor->email_popup) {
        gtk_widget_unref(e_contact_editor->email_popup);
    }
    
    if (e_contact_editor->phone_list) {
        g_list_foreach(e_contact_editor->phone_list, (GFunc) g_free, NULL);
        g_list_free(e_contact_editor->phone_list);
    }
    if (e_contact_editor->phone_info) {
        g_free(e_contact_editor->phone_info);
    }
    if (e_contact_editor->phone_popup) {
        gtk_widget_unref(e_contact_editor->phone_popup);
    }
    
    if (e_contact_editor->address_list) {
        g_list_foreach(e_contact_editor->address_list, (GFunc) g_free, NULL);
        g_list_free(e_contact_editor->address_list);
    }
    if (e_contact_editor->address_info) {
        g_free(e_contact_editor->address_info);
    }
    if (e_contact_editor->address_popup) {
        gtk_widget_unref(e_contact_editor->address_popup);
    }
    
    if (e_contact_editor->simple)
        gtk_object_unref(GTK_OBJECT(e_contact_editor->simple));

    if (e_contact_editor->book)
        gtk_object_unref(GTK_OBJECT(e_contact_editor->book));

    if (e_contact_editor->name)
        e_card_name_free(e_contact_editor->name);

    g_free (e_contact_editor->company);

    gtk_object_unref(GTK_OBJECT(e_contact_editor->gui));
}

static void
command_state_changed (EContactEditor *ce)
{
    bonobo_ui_component_set_prop (ce->uic,
                      "/commands/ContactEditorSaveClose",
                      "sensitive",
                      ce->changed ? "1" : "0", NULL);
    bonobo_ui_component_set_prop (ce->uic,
                      "/commands/ContactEditorSave",
                      "sensitive",
                      ce->changed ? "1" : "0", NULL);
    bonobo_ui_component_set_prop (ce->uic,
                      "/commands/ContactEditorDelete",
                      "sensitive",
                      (ce->editable && !ce->is_new_card) ? "1" : "0", NULL);
}

static void
supported_fields_cb (EBook *book, EBookStatus status,
             EList *fields, EContactEditor *ce)
{
    gtk_object_set (GTK_OBJECT (ce),
            "writable_fields", fields,
            NULL);

    e_contact_editor_show (ce);

    command_state_changed (ce);
}

EContactEditor *
e_contact_editor_new (EBook *book,
              ECard *card,
              gboolean is_new_card,
              gboolean editable)
{
    EContactEditor *ce;

    ce = E_CONTACT_EDITOR (gtk_type_new (E_CONTACT_EDITOR_TYPE));

    gtk_object_set (GTK_OBJECT (ce),
            "book", book,
            "card", card,
            "is_new_card", is_new_card,
            "editable", editable,
            NULL);

    if (book)
        e_book_get_supported_fields (book, (EBookFieldsCallback)supported_fields_cb, ce);

    return ce;
}

static void
e_contact_editor_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
{
    EContactEditor *editor;

    editor = E_CONTACT_EDITOR (o);
    
    switch (arg_id){
    case ARG_BOOK:
        if (editor->book)
            gtk_object_unref(GTK_OBJECT(editor->book));
        editor->book = E_BOOK(GTK_VALUE_OBJECT (*arg));
        gtk_object_ref (GTK_OBJECT (editor->book));
        /* XXX more here about editable/etc. */
        break;
    case ARG_CARD:
        if (editor->card)
            gtk_object_unref(GTK_OBJECT(editor->card));
        editor->card = e_card_duplicate(E_CARD(GTK_VALUE_OBJECT (*arg)));
        gtk_object_set(GTK_OBJECT(editor->simple),
                   "card", editor->card,
                   NULL);
        fill_in_info(editor);
        editor->changed = FALSE;
        break;

    case ARG_IS_NEW_CARD:
        editor->is_new_card = GTK_VALUE_BOOL (*arg) ? TRUE : FALSE;
        break;

    case ARG_EDITABLE: {
        gboolean new_value = GTK_VALUE_BOOL (*arg) ? TRUE : FALSE;
        gboolean changed = (editor->editable != new_value);

        editor->editable = new_value;

        if (changed) {
            set_editable (editor);
            command_state_changed (editor);
        }
        break;
    }
    case ARG_WRITABLE_FIELDS:
        if (editor->writable_fields)
            gtk_object_unref(GTK_OBJECT(editor->writable_fields));
        editor->writable_fields = GTK_VALUE_POINTER (*arg);
        if (editor->writable_fields)
            gtk_object_ref (GTK_OBJECT (editor->writable_fields));
        else
            editor->writable_fields = e_list_new(NULL, NULL, NULL);
        enable_writable_fields (editor);
        break;
    }
}

static void
e_contact_editor_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
    EContactEditor *e_contact_editor;

    e_contact_editor = E_CONTACT_EDITOR (object);

    switch (arg_id) {
    case ARG_BOOK:
        GTK_VALUE_OBJECT (*arg) = GTK_OBJECT(e_contact_editor->book);
        break;

    case ARG_CARD:
        e_card_simple_sync_card(e_contact_editor->simple);
        extract_info(e_contact_editor);
        GTK_VALUE_OBJECT (*arg) = GTK_OBJECT(e_contact_editor->card);
        break;

    case ARG_IS_NEW_CARD:
        GTK_VALUE_BOOL (*arg) = e_contact_editor->is_new_card ? TRUE : FALSE;
        break;

    case ARG_EDITABLE:
        GTK_VALUE_BOOL (*arg) = e_contact_editor->editable ? TRUE : FALSE;
        break;

    case ARG_WRITABLE_FIELDS:
        if (e_contact_editor->writable_fields)
            GTK_VALUE_POINTER (*arg) = e_list_duplicate (e_contact_editor->writable_fields);
        else
            GTK_VALUE_POINTER (*arg) = NULL;
        break;
    default:
        arg->type = GTK_TYPE_INVALID;
        break;
    }
}

static void
_popup_position(GtkMenu *menu,
        gint *x,
        gint *y,
        gpointer data)
{
    GtkWidget *button = GTK_WIDGET(data);
    GtkRequisition request;
    int mh, mw;
    gdk_window_get_origin (button->window, x, y);
    *x += button->allocation.width;
    *y += button->allocation.height;

    gtk_widget_size_request(GTK_WIDGET(menu), &request);

    mh = request.height;
    mw = request.width;

    *x -= mw;
    if (*x < 0)
        *x = 0;
    
    if (*y < 0)
        *y = 0;
    
    if ((*x + mw) > gdk_screen_width ())
        *x = gdk_screen_width () - mw;
    
    if ((*y + mh) > gdk_screen_height ())
        *y = gdk_screen_height () - mh;
}

static gint
_arrow_pressed (GtkWidget *widget, GdkEventButton *button, EContactEditor *editor, GtkWidget *popup, GList **list, GnomeUIInfo **info, gchar *label, gchar *entry, gchar *dialog_title)
{
    gint menu_item;
    gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "button_press_event");
    gtk_widget_realize(popup);
    menu_item = gnome_popup_menu_do_popup_modal(popup, _popup_position, widget, button, editor);
    if ( menu_item != -1 ) {
#if 0
        if (menu_item == g_list_length (*list)) {
            e_contact_editor_build_dialog(editor, entry, label, dialog_title, list, info);
        } else {
#endif
            GtkWidget *label_widget = glade_xml_get_widget(editor->gui, label);
            if (label_widget && GTK_IS_LABEL(label_widget)) {
                gtk_object_set(GTK_OBJECT(label_widget),
                           "label", _(g_list_nth_data(*list, menu_item)),
                           NULL);
            }
#if 0
        }
#endif
    }
    return menu_item;
}

static void
e_contact_editor_build_ui_info(GList *list, GnomeUIInfo **infop)
{
    GnomeUIInfo *info;
    GnomeUIInfo singleton = { GNOME_APP_UI_TOGGLEITEM, NULL, NULL, NULL, NULL, NULL, GNOME_APP_PIXMAP_NONE, 0, 0, 0, NULL };
    GnomeUIInfo end = GNOMEUIINFO_END;
    int length;
    int i;

    info = *infop;

    if ( info )
        g_free(info);
    length = g_list_length( list );
    info = g_new(GnomeUIInfo, length + 2);
    for (i = 0; i < length; i++) {
        info[i] = singleton;
        info[i].label = _(list->data);
        list = list->next;
    }
    info[i] = end;

    *infop = info;
}

static void
e_contact_editor_build_phone_ui (EContactEditor *editor)
{
    int i;

    if (editor->phone_list == NULL) {
        static char *info[] = {
            N_("Assistant"),
            N_("Business"),
            N_("Business 2"),
            N_("Business Fax"),
            N_("Callback"),
            N_("Car"),
            N_("Company"),
            N_("Home"),
            N_("Home 2"),
            N_("Home Fax"),
            N_("ISDN"),
            N_("Mobile"),
            N_("Other"),
            N_("Other Fax"),
            N_("Pager"),
            N_("Primary"),
            N_("Radio"),
            N_("Telex"),
            N_("TTY/TDD")
        };
        
        for (i = 0; i < sizeof(info) / sizeof(info[0]); i++) {
            editor->phone_list = g_list_append(editor->phone_list, g_strdup(info[i]));
        }
    }
    if (editor->phone_info == NULL) {
        e_contact_editor_build_ui_info(editor->phone_list, &editor->phone_info);
        
        if ( editor->phone_popup )
            gtk_widget_unref(editor->phone_popup);
        
        editor->phone_popup = gnome_popup_menu_new(editor->phone_info);
    }
}

static void
e_contact_editor_build_email_ui (EContactEditor *editor)
{
    int i;

    if (editor->email_list == NULL) {
        static char *info[] = {
            N_("Primary Email"),
            N_("Email 2"),
            N_("Email 3")
        };
        
        for (i = 0; i < sizeof(info) / sizeof(info[0]); i++) {
            editor->email_list = g_list_append(editor->email_list, g_strdup(info[i]));
        }
    }
    if (editor->email_info == NULL) {
        e_contact_editor_build_ui_info(editor->email_list, &editor->email_info);

        if ( editor->email_popup )
            gtk_widget_unref(editor->email_popup);
        
        editor->email_popup = gnome_popup_menu_new(editor->email_info);
    }
}

static void
e_contact_editor_build_address_ui (EContactEditor *editor)
{
    int i;

    if (editor->address_list == NULL) {
        static char *info[] = {
            N_("Business"),
            N_("Home"),
            N_("Other")
        };
        
        for (i = 0; i < sizeof(info) / sizeof(info[0]); i++) {
            editor->address_list = g_list_append(editor->address_list, g_strdup(info[i]));
        }
    }
    if (editor->address_info == NULL) {
        e_contact_editor_build_ui_info(editor->address_list, &editor->address_info);

        if ( editor->address_popup )
            gtk_widget_unref(editor->address_popup);
        
        editor->address_popup = gnome_popup_menu_new(editor->address_info);
    }
}
#if 0
static void
_dialog_clicked(GtkWidget *dialog, gint button, EContactEditor *editor)
{
    GtkWidget *label = gtk_object_get_data(GTK_OBJECT(dialog),
                           "e_contact_editor_label");

    GtkWidget *dialog_entry = gtk_object_get_data(GTK_OBJECT(dialog),
                              "e_contact_editor_dialog_entry");
    
    GList **list = gtk_object_get_data(GTK_OBJECT(dialog),
                       "e_contact_editor_list");
    GList **info = gtk_object_get_data(GTK_OBJECT(dialog),
                       "e_contact_editor_info");
    switch (button) {
    case 0:
        if (label && GTK_IS_LABEL(label)) {
            gtk_object_set(GTK_OBJECT(label),
                       "label", gtk_entry_get_text(GTK_ENTRY(dialog_entry)),
                       NULL);
            *list = g_list_append(*list, e_utf8_gtk_entry_get_text(GTK_ENTRY(dialog_entry)));
            g_free(*info);
            *info = NULL;
        }
        break;
    }
    gnome_dialog_close(GNOME_DIALOG(dialog));
}

static void
_dialog_destroy(EContactEditor *editor, GtkWidget *dialog)
{
    gnome_dialog_close(GNOME_DIALOG(dialog));
}

static GtkWidget *
e_contact_editor_build_dialog(EContactEditor *editor, gchar *entry_id, gchar *label_id, gchar *title, GList **list, GnomeUIInfo **info)
{
    GtkWidget *dialog_entry = gtk_entry_new();
    GtkWidget *entry = glade_xml_get_widget(editor->gui, entry_id);
    GtkWidget *label = glade_xml_get_widget(editor->gui, label_id);
    
    GtkWidget *dialog = gnome_dialog_new(title,
                         NULL);
    
    gtk_container_add(GTK_CONTAINER(GNOME_DIALOG(dialog)->vbox),
              gtk_widget_new (gtk_frame_get_type(),
                      "border_width", 4,
                      "label", title,
                      "child", gtk_widget_new(gtk_alignment_get_type(),
                                  "child", dialog_entry,
                                  "xalign", .5,
                                  "yalign", .5,
                                  "xscale", 1.0,
                                  "yscale", 1.0,
                                  "border_width", 9,
                                  NULL),
                      NULL));

    gnome_dialog_append_button_with_pixmap(GNOME_DIALOG(dialog),
                           "Add",
                           GNOME_STOCK_PIXMAP_ADD);
    gnome_dialog_append_button(GNOME_DIALOG(dialog), GNOME_STOCK_BUTTON_CANCEL);
    gnome_dialog_set_default(GNOME_DIALOG(dialog), 0);
    
    gtk_signal_connect(GTK_OBJECT(dialog), "clicked",
               _dialog_clicked, editor);
    gtk_signal_connect_while_alive(GTK_OBJECT(editor), "destroy",
                       _dialog_destroy, GTK_OBJECT(dialog), GTK_OBJECT(dialog));
    
    gtk_object_set_data(GTK_OBJECT(dialog),
                "e_contact_editor_entry", entry);
    gtk_object_set_data(GTK_OBJECT(dialog),
                "e_contact_editor_label", label);
    gtk_object_set_data(GTK_OBJECT(dialog),
                "e_contact_editor_dialog_entry", dialog_entry);
    gtk_object_set_data(GTK_OBJECT(dialog),
                "e_contact_editor_list", list);
    gtk_object_set_data(GTK_OBJECT(dialog),
                "e_contact_editor_info", info);

    gtk_widget_show_all(dialog);
    return dialog;
}
#endif

static void
_phone_arrow_pressed (GtkWidget *widget, GdkEventButton *button, EContactEditor *editor)
{
    int which;
    int i;
    gchar *label;
    gchar *entry;
    int result;
    if ( widget == glade_xml_get_widget(editor->gui, "button-phone1") ) {
        which = 1;
    } else if ( widget == glade_xml_get_widget(editor->gui, "button-phone2") ) {
        which = 2;
    } else if ( widget == glade_xml_get_widget(editor->gui, "button-phone3") ) {
        which = 3;
    } else if ( widget == glade_xml_get_widget(editor->gui, "button-phone4") ) {
        which = 4;
    } else
        return;
    
    label = g_strdup_printf("label-phone%d", which);
    entry = g_strdup_printf("entry-phone%d", which);

    e_contact_editor_build_phone_ui (editor);
    
    for(i = 0; i < E_CARD_SIMPLE_PHONE_ID_LAST; i++) {
        const ECardPhone *phone = e_card_simple_get_phone(editor->simple, i);
        gboolean checked;
        checked = phone && phone->number && *phone->number;
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(editor->phone_info[i].widget),
                           checked);
        gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(editor->phone_info[i].widget),
                            TRUE);
    }
    
    result = _arrow_pressed (widget, button, editor, editor->phone_popup, &editor->phone_list, &editor->phone_info, label, entry, "Add new phone number type");
    
    if (result != -1) {
        editor->phone_choice[which - 1] = result;
        set_fields(editor);
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, label), TRUE);
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, entry), editor->editable);
    }

    g_free(label);
    g_free(entry);
}

static void
_email_arrow_pressed (GtkWidget *widget, GdkEventButton *button, EContactEditor *editor)
{
    int i;
    int result;

    e_contact_editor_build_email_ui (editor);

    for(i = 0; i < E_CARD_SIMPLE_EMAIL_ID_LAST; i++) {
        const char *string = e_card_simple_get_email(editor->simple, i);
        gboolean checked;
        checked = string && *string;
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(editor->email_info[i].widget),
                           checked);
        gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(editor->email_info[i].widget),
                            TRUE);
    }
    
    result = _arrow_pressed (widget, button, editor, editor->email_popup, &editor->email_list, &editor->email_info, "label-email1", "entry-email1", "Add new Email type");
    
    if (result != -1) {
        editor->email_choice = result;
        set_fields(editor);

        /* make sure the buttons/entry is/are sensitive */
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "label-email1"), TRUE);
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "entry-email1"), editor->editable);
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "checkbutton-htmlmail"), editor->editable);
    }
}

static void
_address_arrow_pressed (GtkWidget *widget, GdkEventButton *button, EContactEditor *editor)
{
    int i;
    int result;

    e_contact_editor_build_address_ui (editor);

    for(i = 0; i < E_CARD_SIMPLE_ADDRESS_ID_LAST; i++) {
        const ECardAddrLabel *address = e_card_simple_get_address(editor->simple, i);
        gboolean checked;
        checked = address && address->data && *address->data;
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(editor->address_info[i].widget),
                           checked);
        gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(editor->address_info[i].widget),
                            TRUE);
    }
    
    result = _arrow_pressed (widget, button, editor, editor->address_popup, &editor->address_list, &editor->address_info, "label-address", "text-address", "Add new Address type");

    if (result != -1) {
        set_address_field(editor, result);

        /* make sure the buttons/entry is/are sensitive */
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "label-address"), TRUE);
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "button-fulladdr"), editor->editable);
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "text-address"), editor->editable);
    }
}

static void
set_field(GtkEntry *entry, const char *string)
{
    char *oldstring = e_utf8_gtk_entry_get_text(entry);
    if (!string)
        string = "";
    if (strcmp(string, oldstring))
        e_utf8_gtk_entry_set_text(entry, string);
    g_free (oldstring);
}

static void
set_phone_field(GtkWidget *entry, const ECardPhone *phone)
{
    set_field(GTK_ENTRY(entry), phone ? phone->number : "");
}

static void
set_fields(EContactEditor *editor)
{
    GtkWidget *entry;

    entry = glade_xml_get_widget(editor->gui, "entry-phone1");
    if (entry && GTK_IS_ENTRY(entry))
        set_phone_field(entry, e_card_simple_get_phone(editor->simple, editor->phone_choice[0]));

    entry = glade_xml_get_widget(editor->gui, "entry-phone2");
    if (entry && GTK_IS_ENTRY(entry))
        set_phone_field(entry, e_card_simple_get_phone(editor->simple, editor->phone_choice[1]));

    entry = glade_xml_get_widget(editor->gui, "entry-phone3");
    if (entry && GTK_IS_ENTRY(entry))
        set_phone_field(entry, e_card_simple_get_phone(editor->simple, editor->phone_choice[2]));

    entry = glade_xml_get_widget(editor->gui, "entry-phone4");
    if (entry && GTK_IS_ENTRY(entry))
        set_phone_field(entry, e_card_simple_get_phone(editor->simple, editor->phone_choice[3]));
    
    entry = glade_xml_get_widget(editor->gui, "entry-email1");
    if (entry && GTK_IS_ENTRY(entry))
        set_field(GTK_ENTRY(entry), e_card_simple_get_email(editor->simple, editor->email_choice));
    
    set_address_field(editor, -1);
}

static void
set_address_field(EContactEditor *editor, int result)
{
    GtkWidget *widget;
    
    widget = glade_xml_get_widget(editor->gui, "text-address");

    if (widget && GTK_IS_TEXT(widget)) {
        int position;
        GtkEditable *editable;
        const ECardAddrLabel *address;

        if (result == -1)
            result = editor->address_choice;
        editor->address_choice = -1;

        position = 0;
        editable = GTK_EDITABLE(widget);
        gtk_editable_delete_text(editable, 0, -1);
        address = e_card_simple_get_address(editor->simple, result);
        if (address && address->data) {
            gchar *u = e_utf8_to_gtk_string ((GtkWidget *) editable, address->data);
            gtk_editable_insert_text(editable, u, strlen(u), &position);
            g_free (u);
        }

        editor->address_choice = result;
    }
}

static void
add_field_callback(GtkWidget *widget, EContactEditor *editor)
{
    const char *name;
    int i;
    static const char *builtins[] = {
        "entry-fullname",
        "entry-web",
        "entry-company",
        "entry-department",
        "entry-office",
        "entry-jobtitle",
        "entry-profession",
        "entry-manager",
        "entry-assistant",
        "entry-nickname",
        "entry-spouse",
        "text-comments",
        "entry-categories",
        "entry-contacts",
        "entry-file-as",
        "dateedit-anniversary",
        "dateedit-birthday",
        "entry-phone1",
        "entry-phone2",
        "entry-phone3",
        "entry-phone4",
        "entry-email1",
        "text-address",
        "checkbutton-mailingaddress",
        "checkbutton-htmlmail",
        NULL
    };
    name = glade_get_widget_name(widget);
    if (name) {
        for (i = 0; builtins[i]; i++) {
            if (!strcmp(name, builtins[i]))
                return;
        }
        if (GTK_IS_ENTRY(widget) || GTK_IS_TEXT(widget)) {
            editor->arbitrary_fields = g_list_prepend(editor->arbitrary_fields, g_strdup(name));
        }
    }
}

struct {
    char *id;
    char *key;
} field_mapping [] = {
    { "entry-fullname", "full_name" },
    { "entry-web", "url" },
    { "entry-company", "org" },
    { "entry-department", "org_unit" },
    { "entry-office", "office" },
    { "entry-jobtitle", "title" },
    { "entry-profession", "role" },
    { "entry-manager", "manager" },
    { "entry-assistant", "assistant" },
    { "entry-nickname", "nickname" },
    { "entry-spouse", "spouse" },
    { "text-comments", "note" },
    { "entry-categories", "categories" },
};

static void
fill_in_field(EContactEditor *editor, char *id, char *value)
{
    GtkWidget *widget = glade_xml_get_widget(editor->gui, id);
    if (widget && GTK_IS_EDITABLE(widget)) {
        int position = 0;
        GtkEditable *editable = GTK_EDITABLE(widget);
        gtk_editable_delete_text(editable, 0, -1);
        if (value) {
            gchar *u = e_utf8_to_gtk_string ((GtkWidget *) editable, value);
            gtk_editable_insert_text(editable, u, strlen(u), &position);
            g_free (u);
        }
    }
}

static void
fill_in_card_field(EContactEditor *editor, ECard *card, char *id, char *key)
{
    char *string;
    gtk_object_get(GTK_OBJECT(card),
               key, &string,
               NULL);
    fill_in_field(editor, id, string);
}

static void
fill_in_single_field(EContactEditor *editor, char *name)
{
    ECardSimple *simple = editor->simple;
    GtkWidget *widget = glade_xml_get_widget(editor->gui, name);
    if (widget && GTK_IS_EDITABLE(widget)) {
        int position = 0;
        GtkEditable *editable = GTK_EDITABLE(widget);
        const ECardArbitrary *arbitrary;

        gtk_editable_delete_text(editable, 0, -1);
        arbitrary = e_card_simple_get_arbitrary(simple,
                            name);
        if (arbitrary && arbitrary->value) {
            gchar *u = e_utf8_to_gtk_string ((GtkWidget *) editable, arbitrary->value);
            gtk_editable_insert_text(editable, u, strlen(u), &position);
            g_free (u);
        }
    }
}

static void
disable_widget_foreach (char *key, GtkWidget *widget, gpointer closure)
{
    gtk_widget_set_sensitive (widget, FALSE);
}

static struct {
    char *widget_name;
    ECardSimpleField field_id;
    gboolean desensitize_for_read_only;
} widget_field_mappings[] = {
    { "entry-web", E_CARD_SIMPLE_FIELD_URL, TRUE },
    { "accellabel-web", E_CARD_SIMPLE_FIELD_URL },

    { "entry-jobtitle", E_CARD_SIMPLE_FIELD_TITLE, TRUE },
    { "label-jobtitle", E_CARD_SIMPLE_FIELD_TITLE },

    { "entry-company", E_CARD_SIMPLE_FIELD_ORG, TRUE },
    { "label-company", E_CARD_SIMPLE_FIELD_ORG },

    { "combo-file-as", E_CARD_SIMPLE_FIELD_FILE_AS, TRUE },
    { "entry-file-as", E_CARD_SIMPLE_FIELD_FILE_AS, TRUE },
    { "accellabel-fileas", E_CARD_SIMPLE_FIELD_FILE_AS },

    { "label-department", E_CARD_SIMPLE_FIELD_ORG_UNIT },
    { "entry-department", E_CARD_SIMPLE_FIELD_ORG_UNIT, TRUE },

    { "label-office", E_CARD_SIMPLE_FIELD_OFFICE },
    { "entry-office", E_CARD_SIMPLE_FIELD_OFFICE, TRUE },

    { "label-profession", E_CARD_SIMPLE_FIELD_ROLE },
    { "entry-profession", E_CARD_SIMPLE_FIELD_ROLE, TRUE },

    { "label-manager", E_CARD_SIMPLE_FIELD_MANAGER },
    { "entry-manager", E_CARD_SIMPLE_FIELD_MANAGER, TRUE },

    { "label-assistant", E_CARD_SIMPLE_FIELD_ASSISTANT },
    { "entry-assistant", E_CARD_SIMPLE_FIELD_ASSISTANT, TRUE },

    { "label-nickname", E_CARD_SIMPLE_FIELD_NICKNAME },
    { "entry-nickname", E_CARD_SIMPLE_FIELD_NICKNAME, TRUE },

    { "label-spouse", E_CARD_SIMPLE_FIELD_SPOUSE },
    { "entry-spouse", E_CARD_SIMPLE_FIELD_SPOUSE, TRUE },

    { "label-birthday", E_CARD_SIMPLE_FIELD_BIRTH_DATE },
    { "dateedit-birthday", E_CARD_SIMPLE_FIELD_BIRTH_DATE, TRUE },

    { "label-anniversary", E_CARD_SIMPLE_FIELD_ANNIVERSARY },
    { "dateedit-anniversary", E_CARD_SIMPLE_FIELD_ANNIVERSARY, TRUE },

    { "label-comments", E_CARD_SIMPLE_FIELD_NOTE },
    { "text-comments", E_CARD_SIMPLE_FIELD_NOTE, TRUE },

    { "button-fullname", E_CARD_SIMPLE_FIELD_FULL_NAME },
    { "entry-fullname", E_CARD_SIMPLE_FIELD_FULL_NAME, TRUE },

    { "button-categories", E_CARD_SIMPLE_FIELD_CATEGORIES, TRUE },
    { "entry-categories", E_CARD_SIMPLE_FIELD_CATEGORIES, TRUE }
};
static int num_widget_field_mappings = sizeof(widget_field_mappings) / sizeof (widget_field_mappings[0]);

static void
enable_writable_fields(EContactEditor *editor)
{
    EList *fields = editor->writable_fields;
    EIterator *iter;
    GHashTable *dropdown_hash, *supported_hash;
    int i;
    ECardSimple *simple;
    ECard *card;
    char *widget_name;

    if (!fields)
        return;

    card = e_card_new ("");
    simple = e_card_simple_new (card);

    dropdown_hash = g_hash_table_new (g_str_hash, g_str_equal);
    supported_hash = g_hash_table_new (g_str_hash, g_str_equal);

    /* build our hashtable of the drop down menu items */
    e_contact_editor_build_phone_ui (editor);
    for (i = 0; i < E_CARD_SIMPLE_PHONE_ID_LAST; i ++)
        g_hash_table_insert (dropdown_hash,
                     (char*)e_card_simple_get_ecard_field(simple, e_card_simple_map_phone_to_field (i)),
                     editor->phone_info[i].widget);
    e_contact_editor_build_email_ui (editor);
    for (i = 0; i < E_CARD_SIMPLE_EMAIL_ID_LAST; i ++)
        g_hash_table_insert (dropdown_hash,
                     (char*)e_card_simple_get_ecard_field(simple, e_card_simple_map_email_to_field (i)),
                     editor->email_info[i].widget);
    e_contact_editor_build_address_ui (editor);
    for (i = 0; i < E_CARD_SIMPLE_ADDRESS_ID_LAST; i ++)
        g_hash_table_insert (dropdown_hash,
                     (char*)e_card_simple_get_ecard_field(simple, e_card_simple_map_address_to_field (i)),
                     editor->address_info[i].widget);

    /* then disable them all */
    g_hash_table_foreach (dropdown_hash, (GHFunc)disable_widget_foreach, NULL);

    /* disable the label widgets for the dropdowns (4 phone, 1
           email and the toggle button, and 1 address and one for
           the full adress button */
    for (i = 0; i < 4; i ++) {
        widget_name = g_strdup_printf ("label-phone%d", i+1);
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, widget_name), FALSE);
        g_free (widget_name);
        widget_name = g_strdup_printf ("entry-phone%d", i+1);
        gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, widget_name), FALSE);
        g_free (widget_name);
    }
    gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "label-email1"), FALSE);
    gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "entry-email1"), FALSE);
    gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "checkbutton-htmlmail"), FALSE);
    gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "label-address"), FALSE);
    gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "button-fulladdr"), FALSE);
    gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "text-address"), FALSE);

    /* enable widgets that map directly from a field to a widget (the drop down items) */
    iter = e_list_get_iterator (fields);
    for (; e_iterator_is_valid (iter); e_iterator_next (iter)) {
        char *field = (char*)e_iterator_get (iter);
        GtkWidget *widget = g_hash_table_lookup (dropdown_hash, field);

        if (widget) {
            gtk_widget_set_sensitive (widget, TRUE);
        }
        else {
            /* if it's not a field that's handled by the
                           dropdown items, add it to the has to be
                           used in the second step */
            g_hash_table_insert (supported_hash, field, field);
        }

        /* ugh - this is needed to make sure we don't have a
                   disabled label next to a drop down when the item in
                   the menu (the one reflected in the label) is
                   enabled. */
        if (!strcmp (field, e_card_simple_get_ecard_field (simple, e_card_simple_map_email_to_field(editor->email_choice)))) {
            gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "label-email1"), TRUE);
            gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "entry-email1"), editor->editable);
            gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "checkbutton-htmlmail"), TRUE);
        }
        else if (!strcmp (field, e_card_simple_get_ecard_field (simple, e_card_simple_map_address_to_field(editor->address_choice)))) {
            gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "label-address"), TRUE);
            gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "button-fulladdr"), editor->editable);
            gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, "text-address"), editor->editable);
        }
        else for (i = 0; i < 4; i ++) {
            if (!strcmp (field, e_card_simple_get_ecard_field (simple, e_card_simple_map_phone_to_field(editor->phone_choice[i])))) {
                widget_name = g_strdup_printf ("label-phone%d", i+1);
                gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, widget_name), TRUE);
                g_free (widget_name);
                widget_name = g_strdup_printf ("entry-phone%d", i+1);
                gtk_widget_set_sensitive (glade_xml_get_widget (editor->gui, widget_name), editor->editable);
                g_free (widget_name);
            }
        }
    }

    /* handle the label next to the dropdown widgets */

    for (i = 0; i < num_widget_field_mappings; i ++) {
        gboolean enabled = g_hash_table_lookup (supported_hash,
                            e_card_simple_get_ecard_field (simple,
                                               widget_field_mappings[i].field_id)) != NULL;
        gtk_widget_set_sensitive (glade_xml_get_widget(editor->gui,
                                   widget_field_mappings[i].widget_name), enabled);
    }

    g_hash_table_destroy (dropdown_hash);
    g_hash_table_destroy (supported_hash);
    gtk_object_unref (GTK_OBJECT(simple));
    gtk_object_unref (GTK_OBJECT(card));
}

static void
set_editable (EContactEditor *editor)
{
    int i;
    char *label, *entry;
    /* set the sensitivity of all the non-dropdown entry/texts/dateedits */
    for (i = 0; i < num_widget_field_mappings; i ++) {
        if (widget_field_mappings[i].desensitize_for_read_only)
            gtk_widget_set_sensitive (glade_xml_get_widget(editor->gui,
                                       widget_field_mappings[i].widget_name), editor->editable);
    }

    /* handle the phone dropdown entries */
    for (i = 0; i < 4; i ++) {
        label = g_strdup_printf ("label-phone%d", i+1);
        entry = g_strdup_printf ("entry-phone%d", i+1);

        if (GTK_WIDGET_IS_SENSITIVE (glade_xml_get_widget (editor->gui, label)))
            gtk_widget_set_sensitive (glade_xml_get_widget(editor->gui,
                                       entry), editor->editable);

        g_free (label);
        g_free (entry);
    }

    /* handle the email dropdown entry */
    label = "label-email1";
    entry = "entry-email1";
    if (GTK_WIDGET_IS_SENSITIVE (glade_xml_get_widget (editor->gui, label))) {
        gtk_widget_set_sensitive (glade_xml_get_widget(editor->gui,
                                   entry), editor->editable);
        gtk_widget_set_sensitive (glade_xml_get_widget(editor->gui,
                                   "checkbutton-htmlmail"), editor->editable);
    }

    /* handle the address dropdown entry */
    label = "label-address";
    entry = "text-address";
    if (GTK_WIDGET_IS_SENSITIVE (glade_xml_get_widget (editor->gui, label)))
        gtk_widget_set_sensitive (glade_xml_get_widget(editor->gui,
                                   entry), editor->editable);
}

static void
fill_in_info(EContactEditor *editor)
{
    ECard *card = editor->card;
    if (card) {
        char *file_as;
        char *related_contacts;
        ECardName *name;
        const ECardDate *anniversary;
        const ECardDate *bday;
        int i;
        GtkWidget *widget;
        GList *list;
        gboolean wants_html, wants_html_set;

        gtk_object_get(GTK_OBJECT(card),
                   "file_as",          &file_as,
                   "related_contacts", &related_contacts,
                   "name",             &name,
                   "anniversary",      &anniversary,
                   "birth_date",       &bday,
                   "wants_html_set",   &wants_html_set,
                   "wants_html",       &wants_html,
                   NULL);
    
        for (i = 0; i < sizeof(field_mapping) / sizeof(field_mapping[0]); i++) {
            fill_in_card_field(editor, card, field_mapping[i].id, field_mapping[i].key);
        }

        for (list = editor->arbitrary_fields; list; list = list->next) {
            fill_in_single_field(editor, list->data);
        }

        if (wants_html_set) {
            GtkWidget *widget = glade_xml_get_widget(editor->gui, "checkbutton-htmlmail");
            if (widget && GTK_IS_CHECK_BUTTON(widget)) {
                gtk_object_set(GTK_OBJECT(widget),
                           "active", wants_html,
                           NULL);
            }
        }

        /* File as has to come after company and name or else it'll get messed up when setting them. */
        fill_in_field(editor, "entry-file-as", file_as);
        
        e_card_name_free(editor->name);
        editor->name = e_card_name_copy(name);

        widget = glade_xml_get_widget(editor->gui, "dateedit-anniversary");
        if (widget && E_IS_DATE_EDIT(widget)) {
            EDateEdit *dateedit;
            dateedit = E_DATE_EDIT(widget);
            if (anniversary)
                e_date_edit_set_date (dateedit,
                              anniversary->year,
                              anniversary->month,
                              anniversary->day);
            else
                e_date_edit_set_time (dateedit, -1);
        }

        widget = glade_xml_get_widget(editor->gui, "dateedit-birthday");
        if (widget && E_IS_DATE_EDIT(widget)) {
            EDateEdit *dateedit;
            dateedit = E_DATE_EDIT(widget);
            if (bday)
                e_date_edit_set_date (dateedit,
                              bday->year,
                              bday->month,
                              bday->day);
            else
                e_date_edit_set_time (dateedit, -1);
        }

        if (editor->select_names_contacts && related_contacts && *related_contacts) {
            ESelectNamesModel *model = e_select_names_manager_get_source (editor->select_names_contacts,
                                              "contacts");
            e_select_names_model_import_destinationv (model, related_contacts);
        }

        set_fields(editor);
    }
}

static void
extract_field(EContactEditor *editor, ECard *card, char *editable_id, char *key)
{
    GtkWidget *widget = glade_xml_get_widget(editor->gui, editable_id);
    if (widget && GTK_IS_EDITABLE(widget)) {
        GtkEditable *editable = GTK_EDITABLE(widget);
        char *string = e_utf8_gtk_editable_get_chars(editable, 0, -1);

        if (string && *string)
            gtk_object_set(GTK_OBJECT(card),
                       key, string,
                       NULL);
        else
            gtk_object_set(GTK_OBJECT(card),
                       key, NULL,
                       NULL);

        if (string) g_free(string);
    }
}

static void
extract_single_field(EContactEditor *editor, char *name)
{
    GtkWidget *widget = glade_xml_get_widget(editor->gui, name);
    ECardSimple *simple = editor->simple;
    if (widget && GTK_IS_EDITABLE(widget)) {
        GtkEditable *editable = GTK_EDITABLE(widget);
        char *string = e_utf8_gtk_editable_get_chars(editable, 0, -1);

        if (string && *string)
            e_card_simple_set_arbitrary(simple,
                            name,
                            NULL,
                            string);
        else
            e_card_simple_set_arbitrary(simple,
                            name,
                            NULL,
                            NULL);
        if (string) g_free(string);
    }
}

static void
extract_info(EContactEditor *editor)
{
    ECard *card = editor->card;
    if (card) {
        ECardDate anniversary;
        ECardDate bday;
        int i;
        GtkWidget *widget;
        GList *list;

        widget = glade_xml_get_widget(editor->gui, "entry-file-as");
        if (widget && GTK_IS_EDITABLE(widget)) {
            GtkEditable *editable = GTK_EDITABLE(widget);
            char *string = e_utf8_gtk_editable_get_chars(editable, 0, -1);

            if (string && *string)
                gtk_object_set(GTK_OBJECT(card),
                           "file_as", string,
                           NULL);

            if (string) g_free(string);
        }

        for (i = 0; i < sizeof(field_mapping) / sizeof(field_mapping[0]); i++) {
            extract_field(editor, card, field_mapping[i].id, field_mapping[i].key);
        }

        for (list = editor->arbitrary_fields; list; list = list->next) {
            extract_single_field(editor, list->data);
        }

        if (editor->select_names_contacts) {
            ESelectNamesModel *model = e_select_names_manager_get_source (editor->select_names_contacts,
                                              "contacts");
            char *string = e_select_names_model_export_destinationv (model);
            if (string && *string)
                gtk_object_set (GTK_OBJECT (card),
                        "related_contacts", string,
                        NULL);
            else
                gtk_object_set (GTK_OBJECT (card),
                        "related_contacts", NULL,
                        NULL);
            g_free (string);
        }

        if (editor->name)
            gtk_object_set(GTK_OBJECT(card),
                       "name", editor->name,
                       NULL);

        widget = glade_xml_get_widget(editor->gui, "dateedit-anniversary");
        if (widget && E_IS_DATE_EDIT(widget)) {
            if (e_date_edit_get_date (E_DATE_EDIT (widget),
                          &anniversary.year,
                          &anniversary.month,
                          &anniversary.day)) {
                g_print ("%d %d %d\n", anniversary.year, anniversary.month, anniversary.day);
                gtk_object_set(GTK_OBJECT(card),
                           "anniversary", &anniversary,
                           NULL);
            } else
                gtk_object_set(GTK_OBJECT(card),
                           "anniversary", NULL,
                           NULL);
        }

        widget = glade_xml_get_widget(editor->gui, "dateedit-birthday");
        if (widget && E_IS_DATE_EDIT(widget)) {
            if (e_date_edit_get_date (E_DATE_EDIT (widget),
                          &bday.year,
                          &bday.month,
                          &bday.day)) {
                g_print ("%d %d %d\n", bday.year, bday.month, bday.day);
                gtk_object_set(GTK_OBJECT(card),
                           "birth_date", &bday,
                           NULL);
            } else
                gtk_object_set(GTK_OBJECT(card),
                           "birth_date", NULL,
                           NULL);
        }
    }
}

/**
 * e_contact_editor_raise:
 * @config: The %EContactEditor object.
 *
 * Raises the dialog associated with this %EContactEditor object.
 */
void
e_contact_editor_raise (EContactEditor *editor)
{
    gdk_window_raise (GTK_WIDGET (editor->app)->window);
}

/**
 * e_contact_editor_show:
 * @ce: The %EContactEditor object.
 *
 * Shows the dialog associated with this %EContactEditor object.
 */
void
e_contact_editor_show (EContactEditor *ce)
{
    gtk_widget_show (ce->app);
}

GtkWidget *
e_contact_editor_create_date(gchar *name,
                 gchar *string1, gchar *string2,
                 gint int1, gint int2);

GtkWidget *
e_contact_editor_create_date(gchar *name,
                 gchar *string1, gchar *string2,
                 gint int1, gint int2)
{
    GtkWidget *widget = e_date_edit_new ();
    e_date_edit_set_allow_no_date_set (E_DATE_EDIT (widget),
                       TRUE);
    e_date_edit_set_show_time (E_DATE_EDIT (widget), FALSE);
    e_date_edit_set_time (E_DATE_EDIT (widget), -1);
    return widget;
}