aboutsummaryrefslogblamecommitdiffstats
path: root/addressbook/gui/widgets/e-addressbook-table-adapter.c
blob: ee1ba7c75b03cf69598272b03d5028b6d29d2a24 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                                    
                                                                             






                                                        
 
                    
                   

      
                    
                       
 
                              
 

                                        
                          
                                
                         


                             
 



                                                                                   
                                         
                                 
 
                                                                                       

                           

  




                                                                          

                                    



                                                              
 
           
                                                


                                                              



                                                                           
 


                                    

                                   
                                     




                           
                                      
 
                                          
 
                                                       
 
                               
 
                                                     
 

                                                                                     


                                                                     
           

                                        
                                    


                                                                  
           

                                        
                                                                              

                                                              
                                                               

 

































                                                                              
                                                                               
               


                                       
 
                                                                              
                                                              
                          
                           
 



                                                                   

                            

                                                                    
 

                                                                    
                                                                       




                                                                                      
                                                         













                                                                                  
                                              



                                                                            
                                              

                                         
 
                  
                                                                          
 
 
           



                                            
 
                                                                              
                                                              
 
                                                             
                                           
                                          
                                         

                                  



                                                                           
                               
 
                                                                             
                             

                               
                                               
 


                                               
                                                                                    





                                                                              





                                                                           
                                                             
                                                 
                                              
                                                           
 

                                          
                                         
 

                                                                     
                                                           




                                                                  


                                               
 
                     

 
                                                      
               


                                                 
 
                                



                                                 


                                         
 
                       

 
               

                                               
 
                             


               


                                                
 
                                            

 
              


                                                 
 
                                


           
                                                                             
 
                                   
 

                                                                 
 

                                                      
 
 















                                                                              


           
                                                                    
 
                                                                          

 
           
                                         


                                                  
 
                                                           
                                                                            


           
                                          

                                                  
 
                                          
                                  
 

                                                        
 
                                                           
                       


                                                             

                                                                


           
                                         

                                                  
 


                                                        
                                                           
                                                                   


           
                                        
                                                 
 


                                                        
                                                           


                                                        
    

                                                                         



                                                              

                                   



















                                                       


             
                                                          


                                     
                                                                   
 
                                                          
 
                                  
 
/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Authors:
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gtk/gtk.h>
#include <glib/gi18n.h>

#include <libebook/libebook.h>

#include "e-addressbook-model.h"
#include "e-addressbook-table-adapter.h"
#include "eab-book-util.h"
#include "eab-contact-merging.h"
#include "eab-gui-util.h"
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>

#define E_ADDRESSBOOK_TABLE_ADAPTER_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE \
    ((obj), E_TYPE_ADDRESSBOOK_TABLE_ADAPTER, EAddressbookTableAdapterPrivate))

struct _EAddressbookTableAdapterPrivate {
    EAddressbookModel *model;

    gint create_contact_id, remove_contact_id, modify_contact_id, model_changed_id;

    GHashTable *emails;
};

/* Forward Declarations */
static void e_addressbook_table_adapter_table_model_init
                    (ETableModelInterface *interface);

G_DEFINE_TYPE_WITH_CODE (
    EAddressbookTableAdapter,
    e_addressbook_table_adapter,
    G_TYPE_OBJECT,
    G_IMPLEMENT_INTERFACE (
        E_TYPE_TABLE_MODEL,
        e_addressbook_table_adapter_table_model_init))

static void
unlink_model (EAddressbookTableAdapter *adapter)
{
    EAddressbookTableAdapterPrivate *priv = adapter->priv;

    g_signal_handler_disconnect (priv->model, priv->create_contact_id);
    g_signal_handler_disconnect (priv->model, priv->remove_contact_id);
    g_signal_handler_disconnect (priv->model, priv->modify_contact_id);
    g_signal_handler_disconnect (priv->model, priv->model_changed_id);

    priv->create_contact_id = 0;
    priv->remove_contact_id = 0;
    priv->modify_contact_id = 0;
    priv->model_changed_id = 0;

    g_object_unref (priv->model);

    priv->model = NULL;
}

static void
addressbook_finalize (GObject *object)
{
    EAddressbookTableAdapter *adapter;

    adapter = E_ADDRESSBOOK_TABLE_ADAPTER (object);

    unlink_model (adapter);

    g_hash_table_destroy (adapter->priv->emails);

    /* Chain up to parent's finalize() method. */
    G_OBJECT_CLASS (e_addressbook_table_adapter_parent_class)->finalize (object);
}

/* This function returns the number of columns in our ETableModel. */
static gint
addressbook_col_count (ETableModel *etc)
{
    return E_CONTACT_FIELD_LAST;
}

/* This function returns the number of rows in our ETableModel. */
static gint
addressbook_row_count (ETableModel *etc)
{
    EAddressbookTableAdapter *adapter = E_ADDRESSBOOK_TABLE_ADAPTER (etc);
    EAddressbookTableAdapterPrivate *priv = adapter->priv;

    return e_addressbook_model_contact_count (priv->model);
}

static void
addressbook_append_row (ETableModel *etm,
                        ETableModel *source,
                        gint row)
{
    EAddressbookTableAdapter *adapter = E_ADDRESSBOOK_TABLE_ADAPTER (etm);
    EAddressbookTableAdapterPrivate *priv = adapter->priv;
    EClientCache *client_cache;
    ESourceRegistry *registry;
    EBookClient *book_client;
    EContact *contact;
    gint col;

    contact = e_contact_new ();

    for (col = 1; col < E_CONTACT_LAST_SIMPLE_STRING; col++) {
        gconstpointer val = e_table_model_value_at (source, col, row);
        e_contact_set (contact, col, (gpointer) val);
    }

    client_cache =
        e_addressbook_model_get_client_cache (priv->model);
    book_client = e_addressbook_model_get_client (priv->model);

    registry = e_client_cache_ref_registry (client_cache);

    eab_merging_book_add_contact (
        registry, book_client, contact, NULL, NULL);

    g_object_unref (registry);

    g_object_unref (contact);
}

/* This function returns the value at a particular point in our ETableModel. */
static gpointer
addressbook_value_at (ETableModel *etc,
                      gint col,
                      gint row)
{
    EAddressbookTableAdapter *adapter = E_ADDRESSBOOK_TABLE_ADAPTER (etc);
    EAddressbookTableAdapterPrivate *priv = adapter->priv;
    EContact *contact;
    const gchar *value;

    if (col >= E_CONTACT_FIELD_LAST)
        return NULL;

    if (row >= e_addressbook_model_contact_count (priv->model))
        return NULL;

    contact = e_addressbook_model_contact_at (priv->model, row);
    value = e_contact_get_const (contact, col);

    if (value && *value && (col == E_CONTACT_EMAIL_1 ||
        col == E_CONTACT_EMAIL_2 || col == E_CONTACT_EMAIL_3)) {
        gchar *val = g_hash_table_lookup (priv->emails, value);

        if (val) {
            /* we have this already cached, so use value from the cache */
            value = val;
        } else {
            gchar *name = NULL, *mail = NULL;

            if (eab_parse_qp_email (value, &name, &mail))
                val = g_strdup_printf ("%s <%s>", name, mail);
            else
                val = g_strdup (value);

            g_free (name);
            g_free (mail);

            g_hash_table_insert (priv->emails, g_strdup (value), val);
            value = val;
        }
    }

    return (gpointer)(value ? value : "");
}

/* This function sets the value at a particular point in our ETableModel. */
static void
contact_modified_cb (EBookClient *book_client,
                     const GError *error,
                     gpointer user_data)
{
    if (error)
        eab_error_dialog (NULL, _("Error modifying card"), error);
}

static void
addressbook_set_value_at (ETableModel *etc,
                          gint col,
                          gint row,
                          gconstpointer val)
{
    EAddressbookTableAdapter *adapter = E_ADDRESSBOOK_TABLE_ADAPTER (etc);
    EAddressbookTableAdapterPrivate *priv = adapter->priv;

    if (e_addressbook_model_get_editable (priv->model)) {
        EClientCache *client_cache;
        ESourceRegistry *registry;
        EBookClient *book_client;
        EContact *contact;

        if (col >= E_CONTACT_FIELD_LAST)
            return;

        if (row >= e_addressbook_model_contact_count (priv->model))
            return;

        contact = e_addressbook_model_get_contact (priv->model, row);
        if (!contact)
            return;

        e_table_model_pre_change (etc);

        if (col == E_CONTACT_EMAIL_1 ||
            col == E_CONTACT_EMAIL_2 ||
            col == E_CONTACT_EMAIL_3) {
            const gchar *old_value = e_contact_get_const (contact, col);

            /* remove old value from cache and use new one */
            if (old_value && *old_value)
                g_hash_table_remove (priv->emails, old_value);
        }

        client_cache =
            e_addressbook_model_get_client_cache (priv->model);
        book_client = e_addressbook_model_get_client (priv->model);

        registry = e_client_cache_ref_registry (client_cache);

        e_contact_set (contact, col, (gpointer) val);
        eab_merging_book_modify_contact (
            registry, book_client,
            contact, contact_modified_cb, etc);

        g_object_unref (registry);

        g_object_unref (contact);

        /* XXX Do we need this?  Shouldn't the commit_contact
         *     generate a changed signal? */
        e_table_model_cell_changed (etc, col, row);
    }
}

/* This function returns whether a particular cell is editable. */
static gboolean
addressbook_is_cell_editable (ETableModel *etc,
                              gint col,
                              gint row)
{
    return FALSE;
}

/* This function duplicates the value passed to it. */
static gpointer
addressbook_duplicate_value (ETableModel *etc,
                             gint col,
                             gconstpointer value)
{
    return g_strdup (value);
}

/* This function frees the value passed to it. */
static void
addressbook_free_value (ETableModel *etc,
                        gint col,
                        gpointer value)
{
    g_free (value);
}

static gpointer
addressbook_initialize_value (ETableModel *etc,
                              gint col)
{
    return g_strdup ("");
}

static gboolean
addressbook_value_is_empty (ETableModel *etc,
                            gint col,
                            gconstpointer value)
{
    return !(value && *(gchar *) value);
}

static gchar *
addressbook_value_to_string (ETableModel *etc,
                             gint col,
                             gconstpointer value)
{
    return g_strdup (value);
}

static void
e_addressbook_table_adapter_class_init (EAddressbookTableAdapterClass *class)
{
    GObjectClass *object_class;

    g_type_class_add_private (
        class, sizeof (EAddressbookTableAdapterPrivate));

    object_class = G_OBJECT_CLASS (class);
    object_class->finalize = addressbook_finalize;
}

static void
e_addressbook_table_adapter_table_model_init (ETableModelInterface *interface)
{
    interface->column_count = addressbook_col_count;
    interface->row_count = addressbook_row_count;
    interface->append_row = addressbook_append_row;

    interface->value_at = addressbook_value_at;
    interface->set_value_at = addressbook_set_value_at;
    interface->is_cell_editable = addressbook_is_cell_editable;

    interface->duplicate_value = addressbook_duplicate_value;
    interface->free_value = addressbook_free_value;
    interface->initialize_value = addressbook_initialize_value;
    interface->value_is_empty = addressbook_value_is_empty;
    interface->value_to_string = addressbook_value_to_string;
}

static void
e_addressbook_table_adapter_init (EAddressbookTableAdapter *adapter)
{
    adapter->priv = E_ADDRESSBOOK_TABLE_ADAPTER_GET_PRIVATE (adapter);
}

static void
create_contact (EAddressbookModel *model,
                gint index,
                gint count,
                EAddressbookTableAdapter *adapter)
{
    e_table_model_pre_change (E_TABLE_MODEL (adapter));
    e_table_model_rows_inserted (E_TABLE_MODEL (adapter), index, count);
}

static void
remove_contacts (EAddressbookModel *model,
                gpointer data,
                EAddressbookTableAdapter *adapter)
{
    GArray *indices = (GArray *) data;
    gint count = indices->len;

    /* clear whole cache */
    g_hash_table_remove_all (adapter->priv->emails);

    e_table_model_pre_change (E_TABLE_MODEL (adapter));
    if (count == 1)
        e_table_model_rows_deleted (
            E_TABLE_MODEL (adapter),
            g_array_index (indices, gint, 0), 1);
    else
        e_table_model_changed (E_TABLE_MODEL (adapter));
}

static void
modify_contact (EAddressbookModel *model,
                gint index,
                EAddressbookTableAdapter *adapter)
{
    /* clear whole cache */
    g_hash_table_remove_all (adapter->priv->emails);

    e_table_model_pre_change (E_TABLE_MODEL (adapter));
    e_table_model_row_changed (E_TABLE_MODEL (adapter), index);
}

static void
model_changed (EAddressbookModel *model,
               EAddressbookTableAdapter *adapter)
{
    /* clear whole cache */
    g_hash_table_remove_all (adapter->priv->emails);

    e_table_model_pre_change (E_TABLE_MODEL (adapter));
    e_table_model_changed (E_TABLE_MODEL (adapter));
}

void
e_addressbook_table_adapter_construct (EAddressbookTableAdapter *adapter,
                                       EAddressbookModel *model)
{
    EAddressbookTableAdapterPrivate *priv = adapter->priv;

    priv->model = model;
    g_object_ref (priv->model);

    priv->create_contact_id = g_signal_connect (
        priv->model, "contact_added",
        G_CALLBACK (create_contact), adapter);

    priv->remove_contact_id = g_signal_connect (
        priv->model, "contacts_removed",
        G_CALLBACK (remove_contacts), adapter);

    priv->modify_contact_id = g_signal_connect (
        priv->model, "contact_changed",
        G_CALLBACK (modify_contact), adapter);

    priv->model_changed_id = g_signal_connect (
        priv->model, "model_changed",
        G_CALLBACK (model_changed), adapter);

    priv->emails = g_hash_table_new_full (
        g_str_hash, g_str_equal,
        (GDestroyNotify) g_free,
        (GDestroyNotify) g_free);
}

ETableModel *
e_addressbook_table_adapter_new (EAddressbookModel *model)
{
    EAddressbookTableAdapter *et;

    et = g_object_new (E_TYPE_ADDRESSBOOK_TABLE_ADAPTER, NULL);

    e_addressbook_table_adapter_construct (et, model);

    return E_TABLE_MODEL (et);
}