aboutsummaryrefslogblamecommitdiffstats
path: root/e-util/e-config-listener.c
blob: 7c1c6eb091e24a8c94cb41889d45fa59eaa186c4 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14


                                   










                                                                    
                                                                             



                                                 
  
                                                        
  

   
                   
                               

                              
                                 


                            
                  
                  
                            
               
                                
                              

                            




                                
                        



                                                                       

                                                                                            
 
                                         
 






                                                  


                                                          
                                                            
 
                                                        
 
                                                            


                                              










                                                                                  


           
                           
 

                                      
                                                               
 

                           
                                 


                                         
                      





                    












                                                                         
                                            




                                                         


                                              


                                                         

         


                          

                                                                     

 
     

                                 
                              

                    











                                                                                         



















                                                                       
                                                         



                  


                                        



                                            
                                         


                                      
                                           


                                         











                                                                                   

                       
 
                                                                                            


                
                                                                   


                                               






                                           
                               

                                                                     
                                

                                                                   
                              

                                                                 
                                 
                                                                  

                      
                      




                                          
                                                                                      


                                                                                      


                                                          


                  
        





                                                                                 




                                                                   
                               

                       




                                                                

                                                       
                                                                               




                                                                                
 








                                                                             
                
                                                   









                                                                 
     





                                                                             




                                                                 
                               

                    




                                                             

                                                       
                                                                               










                                                                                 
 


                                                     
                
                                                    









                                                                 
    





                                                                          




                                                                
                               

                    




                                                             

                                                       
                                                                               




                                                                               
 








                                                                            
                
                                                  









                                                                 
      





                                                                               




                                                                  
                               
                  
                    




                                                               

                                                       
                                                                               

                                                                        
                                                                             

                                                                                          

                                                 

                                                      
                                             




                                                                                         
                
                                                     
                                                         





                                                                 
                   

 
    

                                                                                    
                    
                           







                                                                                   




                                                                           




                                                               




                                                                               
                    
                           







                                                                                 




                                                                            




                                                               


    
                                                                             
 
                    
                           
 

                                                     
 



                                                                                




                                                                           




                                                               
 
 


                                                                                      
                      
                    
                           



                                                     
                                                  
                            
                                                                             

                                                   
                       


                    
 




                                                                           






                                                               
 

    

                                                                     


                                                     
                                                  



                                                     


                                                                   
                         






                                                                   
 






                                                             
/*
 * Configuration component listener
 *
 * 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:
 *      Rodrigo Moya <rodrigo@ximian.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#include <string.h>
#include <gconf/gconf-client.h>
#include "e-config-listener.h"

#define PARENT_TYPE G_TYPE_OBJECT

typedef struct {
    EConfigListener *cl;
    guint lid;
    char *key;
    GConfValueType type;
    union {
        gboolean v_bool;
        float v_float;
        long v_long;
        char *v_str;
    } value;
    gboolean used_default;
} KeyData;

struct _EConfigListenerPrivate {
    GConfClient *db;
    GHashTable *keys;
};

static void e_config_listener_class_init (EConfigListenerClass *klass);
static void e_config_listener_init       (EConfigListener *cl, EConfigListenerClass *klass);
static void e_config_listener_finalize   (GObject *object);

static GObjectClass *parent_class = NULL;

enum {
    KEY_CHANGED,
    LAST_SIGNAL
};

static guint config_listener_signals[LAST_SIGNAL];

static void
e_config_listener_class_init (EConfigListenerClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    parent_class = g_type_class_peek_parent (klass);

    object_class->finalize = e_config_listener_finalize;
    klass->key_changed = NULL;

    config_listener_signals[KEY_CHANGED] =
        g_signal_new ("key_changed",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (EConfigListenerClass, key_changed),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__STRING,
                  G_TYPE_NONE, 1, G_TYPE_STRING);

    /* make sure GConf is initialized */
    if (!gconf_is_initialized ())
        gconf_init (0, NULL, NULL);
}

static void
free_key_data (KeyData *kd)
{
    g_return_if_fail (kd != NULL);

    gconf_client_notify_remove (kd->cl->priv->db, kd->lid);

    g_free (kd->key);
    switch (kd->type) {
    case GCONF_VALUE_STRING :
        g_free (kd->value.v_str);
        break;
    default :
        break;
    }

    g_free (kd);
}

static void
e_config_listener_init (EConfigListener *cl, EConfigListenerClass *klass)
{
    /* allocate internal structure */
    cl->priv = g_new0 (EConfigListenerPrivate, 1);

    cl->priv->keys = g_hash_table_new_full (
        g_str_hash, g_str_equal,
        (GDestroyNotify) NULL,
        (GDestroyNotify) free_key_data);
    cl->priv->db = gconf_client_get_default ();
}

static void
e_config_listener_finalize (GObject *object)
{
    EConfigListener *cl = (EConfigListener *) object;

    g_return_if_fail (E_IS_CONFIG_LISTENER (cl));

    g_hash_table_destroy (cl->priv->keys);
    cl->priv->keys = NULL;

    if (cl->priv->db != NULL) {
        g_object_unref (G_OBJECT (cl->priv->db));
        cl->priv->db = NULL;
    }

    g_free (cl->priv);
    cl->priv = NULL;

    if (G_OBJECT_CLASS (parent_class)->finalize)
        (* G_OBJECT_CLASS (parent_class)->finalize) (object);
}

GType
e_config_listener_get_type (void)
{
    static GType type = 0;

    if (!type) {
        static const GTypeInfo info = {
                        sizeof (EConfigListenerClass),
                        (GBaseInitFunc) NULL,
                        (GBaseFinalizeFunc) NULL,
                        (GClassInitFunc) e_config_listener_class_init,
                        NULL,
                        NULL,
                        sizeof (EConfigListener),
                        0,
                        (GInstanceInitFunc) e_config_listener_init
                };
                type = g_type_register_static (PARENT_TYPE, "EConfigListener", &info, 0);
    }

    return type;
}

/**
 * e_config_listener_new
 *
 * Create a new configuration listener, which is an object which
 * allows to listen for changes in the configuration database. It keeps
 * an updated copy of all requested configuration entries, so that
 * access is much quicker and instantaneous.
 *
 * Returns: the newly created listener.
 */
EConfigListener *
e_config_listener_new (void)
{
    EConfigListener *cl;

    cl = g_object_new (E_CONFIG_LISTENER_TYPE, NULL);
    return cl;
}

static void
property_change_cb (GConfEngine *engine,
            guint cnxn_id,
            GConfEntry *entry,
            gpointer user_data)
{
    KeyData *kd = (KeyData *) user_data;

    g_return_if_fail (entry != NULL);
    g_return_if_fail (kd != NULL);

    /* free previous value */
    if (kd->type == GCONF_VALUE_STRING)
        g_free (kd->value.v_str);

    /* set new value */
    if (entry->value->type == GCONF_VALUE_BOOL) {
        kd->type = GCONF_VALUE_BOOL;
        kd->value.v_bool = gconf_value_get_bool (entry->value);
    } else if (entry->value->type == GCONF_VALUE_FLOAT) {
        kd->type = GCONF_VALUE_FLOAT;
        kd->value.v_float = gconf_value_get_float (entry->value);
    } else if (entry->value->type == GCONF_VALUE_INT) {
        kd->type = GCONF_VALUE_INT;
        kd->value.v_long = gconf_value_get_int (entry->value);
    } else if (entry->value->type == GCONF_VALUE_STRING) {
        kd->type = GCONF_VALUE_STRING;
        kd->value.v_str = g_strdup (gconf_value_get_string (entry->value));
    } else
        return;

    g_signal_emit (G_OBJECT (kd->cl), config_listener_signals[KEY_CHANGED], 0, kd->key);
}

static KeyData *
add_key (EConfigListener *cl, const char *key, GConfValueType type,
     gpointer value, gboolean used_default)
{
    KeyData *kd;

    /* add the key to our hash table */
    kd = g_new0 (KeyData, 1);
    kd->cl = cl;
    kd->key = g_strdup (key);
    kd->type = type;
    switch (type) {
    case GCONF_VALUE_BOOL :
        memcpy (&kd->value.v_bool, value, sizeof (gboolean));
        break;
    case GCONF_VALUE_FLOAT :
        memcpy (&kd->value.v_float, value, sizeof (float));
        break;
    case GCONF_VALUE_INT :
        memcpy (&kd->value.v_long, value, sizeof (long));
        break;
    case GCONF_VALUE_STRING :
        kd->value.v_str = g_strdup ((const char *) value);
        break;
    default :
        break;
    }

    kd->used_default = used_default;

    /* add the listener for changes */
    gconf_client_add_dir (cl->priv->db, key, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
    kd->lid = gconf_client_notify_add (cl->priv->db, key,
                       (GConfClientNotifyFunc) property_change_cb,
                       kd, NULL, NULL);

    g_hash_table_insert (cl->priv->keys, kd->key, kd);

    return kd;
}

gboolean
e_config_listener_get_boolean (EConfigListener *cl, const char *key)
{
    return e_config_listener_get_boolean_with_default (cl, key, FALSE, NULL);
}

gboolean
e_config_listener_get_boolean_with_default (EConfigListener *cl,
                        const char *key,
                        gboolean def,
                        gboolean *used_default)
{
    GConfValue *conf_value;
    gboolean value;
    KeyData *kd;

    g_return_val_if_fail (E_IS_CONFIG_LISTENER (cl), FALSE);
    g_return_val_if_fail (key != NULL, FALSE);

    /* search for the key in our hash table */
    kd = g_hash_table_lookup (cl->priv->keys, key);
    if (kd == NULL) {
        /* not found, so retrieve it from the configuration database */
        conf_value = gconf_client_get (cl->priv->db, key, NULL);
        if (conf_value) {
            value = gconf_value_get_bool (conf_value);
            kd = add_key (cl, key, GCONF_VALUE_BOOL, &value, FALSE);
            gconf_value_free (conf_value);

            if (used_default != NULL)
                *used_default = FALSE;
        } else {
            value = def;
            kd = add_key (cl, key, GCONF_VALUE_BOOL, &def, TRUE);

            if (used_default != NULL)
                *used_default = TRUE;
        }
    } else {
        if (kd->type == GCONF_VALUE_BOOL) {
            value = kd->value.v_bool;
            if (used_default != NULL)
                *used_default = kd->used_default;
        } else
            return FALSE;
    }

    return value;
}

float
e_config_listener_get_float (EConfigListener *cl, const char *key)
{
    return e_config_listener_get_float_with_default (cl, key, 0.0, NULL);
}

float
e_config_listener_get_float_with_default (EConfigListener *cl,
                      const char *key,
                      float def,
                      gboolean *used_default)
{
    GConfValue *conf_value;
    float value;
    KeyData *kd;

    g_return_val_if_fail (E_IS_CONFIG_LISTENER (cl), -1);
    g_return_val_if_fail (key != NULL, -1);

    /* search for the key in our hash table */
    kd = g_hash_table_lookup (cl->priv->keys, key);
    if (kd == NULL) {
        /* not found, so retrieve it from the configuration database */
        conf_value = gconf_client_get (cl->priv->db, key, NULL);
        if (conf_value) {
            value = gconf_value_get_float (conf_value);
            kd = add_key (cl, key, GCONF_VALUE_FLOAT, &value, FALSE);
            gconf_value_free (conf_value);

            if (used_default != NULL)
                *used_default = FALSE;
        } else {
            value = def;
            kd = add_key (cl, key, GCONF_VALUE_FLOAT, &def, TRUE);

            if (used_default != NULL)
                *used_default = TRUE;
        }
    } else {
        if (kd->type == GCONF_VALUE_FLOAT) {
            value = kd->value.v_float;
            if (used_default != NULL)
                *used_default = kd->used_default;
        } else
            return -1;
    }

    return value;
}

long
e_config_listener_get_long (EConfigListener *cl, const char *key)
{
    return e_config_listener_get_long_with_default (cl, key, 0, NULL);
}

long
e_config_listener_get_long_with_default (EConfigListener *cl,
                     const char *key,
                     long def,
                     gboolean *used_default)
{
    GConfValue *conf_value;
    long value;
    KeyData *kd;

    g_return_val_if_fail (E_IS_CONFIG_LISTENER (cl), -1);
    g_return_val_if_fail (key != NULL, -1);

    /* search for the key in our hash table */
    kd = g_hash_table_lookup (cl->priv->keys, key);
    if (kd == NULL) {
        /* not found, so retrieve it from the configuration database */
        conf_value = gconf_client_get (cl->priv->db, key, NULL);
        if (conf_value) {
            value = gconf_value_get_int (conf_value);
            kd = add_key (cl, key, GCONF_VALUE_INT, &value, FALSE);
            gconf_value_free (conf_value);

            if (used_default != NULL)
                *used_default = FALSE;
        } else {
            value = def;
            kd = add_key (cl, key, GCONF_VALUE_INT, &def, TRUE);

            if (used_default != NULL)
                *used_default = TRUE;
        }
    } else {
        if (kd->type == GCONF_VALUE_INT) {
            value = kd->value.v_long;
            if (used_default != NULL)
                *used_default = kd->used_default;
        } else
            return -1;
    }

    return value;
}

char *
e_config_listener_get_string (EConfigListener *cl, const char *key)
{
    return e_config_listener_get_string_with_default (cl, key, NULL, NULL);
}

char *
e_config_listener_get_string_with_default (EConfigListener *cl,
                       const char *key,
                       const char *def,
                       gboolean *used_default)
{
    GConfValue *conf_value;
    char *str;
    KeyData *kd;

    g_return_val_if_fail (E_IS_CONFIG_LISTENER (cl), NULL);
    g_return_val_if_fail (key != NULL, NULL);

    /* search for the key in our hash table */
    kd = g_hash_table_lookup (cl->priv->keys, key);
    if (kd == NULL) {
        /* not found, so retrieve it from the configuration database */
        conf_value = gconf_client_get (cl->priv->db, key, NULL);
        if (conf_value) {
            str = g_strdup (gconf_value_get_string (conf_value));
            kd = add_key (cl, key, GCONF_VALUE_STRING, (gpointer) str, FALSE);
            gconf_value_free (conf_value);

            if (used_default != NULL)
                *used_default = FALSE;
        } else {
            str = g_strdup (def);
            kd = add_key (cl, key, GCONF_VALUE_STRING, (gpointer) str, TRUE);

            if (used_default != NULL)
                *used_default = TRUE;
        }
    } else {
        if (kd->type == GCONF_VALUE_STRING) {
            str = g_strdup (kd->value.v_str);
            if (used_default != NULL)
                *used_default = kd->used_default;
        } else
            return NULL;
    }

    return str;
}

void
e_config_listener_set_boolean (EConfigListener *cl, const char *key, gboolean value)
{
    KeyData *kd;
    GError *err = NULL;

    g_return_if_fail (E_IS_CONFIG_LISTENER (cl));
    g_return_if_fail (key != NULL);

    /* check that the value is not the same */
    if (value == e_config_listener_get_boolean_with_default (cl, key, 0, NULL))
        return;

    gconf_client_set_bool (cl->priv->db, key, value, &err);
    if (err) {
        g_warning ("e_config_listener_set_bool: %s", err->message);
        g_error_free (err);
    } else {
        /* update the internal copy */
        kd = g_hash_table_lookup (cl->priv->keys, key);
        if (kd)
            kd->value.v_bool = value;
    }
}

void
e_config_listener_set_float (EConfigListener *cl, const char *key, float value)
{
    KeyData *kd;
    GError *err = NULL;

    g_return_if_fail (E_IS_CONFIG_LISTENER (cl));
    g_return_if_fail (key != NULL);

    /* check that the value is not the same */
    if (value == e_config_listener_get_float_with_default (cl, key, 0, NULL))
        return;

    gconf_client_set_float (cl->priv->db, key, value, &err);
    if (err) {
        g_warning ("e_config_listener_set_float: %s", err->message);
        g_error_free (err);
    } else {
        /* update the internal copy */
        kd = g_hash_table_lookup (cl->priv->keys, key);
        if (kd)
            kd->value.v_float = value;
    }
}

void
e_config_listener_set_long (EConfigListener *cl, const char *key, long value)
{
    KeyData *kd;
    GError *err = NULL;

    g_return_if_fail (E_IS_CONFIG_LISTENER (cl));
    g_return_if_fail (key != NULL);

    /* check that the value is not the same */
    if (value == e_config_listener_get_long_with_default (cl, key, 0, NULL))
        return;

    gconf_client_set_int (cl->priv->db, key, value, &err);
    if (err) {
        g_warning ("e_config_listener_set_long: %s", err->message);
        g_error_free (err);
    } else {
        /* update the internal copy */
        kd = g_hash_table_lookup (cl->priv->keys, key);
        if (kd)
            kd->value.v_long = value;
    }
}

void
e_config_listener_set_string (EConfigListener *cl, const char *key, const char *value)
{
    char *s1, *s2;
    KeyData *kd;
    GError *err = NULL;

    g_return_if_fail (E_IS_CONFIG_LISTENER (cl));
    g_return_if_fail (key != NULL);

    /* check that the value is not the same */
    s1 = (char *) value;
    s2 = e_config_listener_get_string_with_default (cl, key, NULL, NULL);
    if (!strcmp (s1 ? s1 : "", s2 ? s2 : "")) {
        g_free (s2);
        return;
    }

    g_free (s2);

    gconf_client_set_string (cl->priv->db, key, value, &err);
    if (err) {
        g_warning ("e_config_listener_set_bool: %s", err->message);
        g_error_free (err);
    } else {
        /* update the internal copy */
        kd = g_hash_table_lookup (cl->priv->keys, key);
        if (kd) {
            g_free (kd->value.v_str);
            kd->value.v_str = g_strdup (value);
        }
    }
}

void
e_config_listener_remove_value (EConfigListener *cl, const char *key)
{
    g_return_if_fail (E_IS_CONFIG_LISTENER (cl));
    g_return_if_fail (key != NULL);

    g_hash_table_remove (cl->priv->keys, key);
    gconf_client_unset (cl->priv->db, key, NULL);
}

void
e_config_listener_remove_dir (EConfigListener *cl, const char *dir)
{
    GSList *slist, *iter;
    const gchar *key;

    g_return_if_fail (E_IS_CONFIG_LISTENER (cl));
    g_return_if_fail (dir != NULL);

    slist = gconf_client_all_entries (cl->priv->db, dir, NULL);
        for (iter = slist; iter != NULL; iter = iter->next) {
                GConfEntry *entry = iter->data;

                key = gconf_entry_get_key (entry);
                gconf_client_unset (cl->priv->db, key, NULL);
                gconf_entry_free (entry);
        }

        g_slist_free (slist);
}