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









                                                                           
                          













                                       
                                
                              

                            














                                                                       






                                                  







                                                                









                                                                                          


































                                                                                   
                      

























































































                                                                              


                                                                                   


                                                                                  




                                                                                    

                                                                                             







                                                                       
                 










                                                                     


                                                                   


                                                                 



                                                 
                      






                                                                        


                                       








                                                 
                                    











                                                          




































                                                                                            




































                                                                                          




































                                                                                          





























                                                                                                  
                                              





                                                                 
                              

 
    


                                                                                    
                    














                                                                                       




                                                       





                                                                               
                    














                                                                                       




                                                       


    
                                                                             
 
                             
                    
 

                                                     
 



                                                                                
                                   
 


                                                                                       
 
                                   




                                                       
 
 



                                                                                      
                      
                    



                                                     
                                                  
                            
                                                                             

                                                   
                       


                    
 






                                                                                       






                                                       







                                                                           
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Configuration component listener
 *
 * Author:
 *   Rodrigo Moya <rodrigo@ximian.com>
 *
 * Copyright 2002, Ximian, Inc.
 */

#include <gtk/gtksignal.h>
#include <gtk/gtktypeutils.h>
#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-event-source.h>
#include <bonobo/bonobo-moniker-util.h>
#include <bonobo/bonobo-object.h>
#include "e-config-listener.h"

#define PARENT_TYPE GTK_TYPE_OBJECT

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

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

static void e_config_listener_class_init (EConfigListenerClass *klass);
static void e_config_listener_init       (EConfigListener *cl);
static void e_config_listener_destroy    (GtkObject *object);

static GtkObjectClass *parent_class = NULL;

enum {
    KEY_CHANGED,
    LAST_SIGNAL
};

static guint config_listener_signals[LAST_SIGNAL];

static void
e_config_listener_class_init (EConfigListenerClass *klass)
{
    GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);

    parent_class = gtk_type_class (PARENT_TYPE);

    object_class->destroy = e_config_listener_destroy;
    klass->key_changed = NULL;

    config_listener_signals[KEY_CHANGED] =
        gtk_signal_new ("key_changed",
                GTK_RUN_FIRST,
                object_class->type,
                GTK_SIGNAL_OFFSET (EConfigListenerClass, key_changed),
                gtk_marshal_NONE__STRING,
                GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
    gtk_object_class_add_signals (object_class, config_listener_signals, LAST_SIGNAL);
}

static void
e_config_listener_init (EConfigListener *cl)
{
    CORBA_Environment ev;

    /* allocate internal structure */
    cl->priv = g_new0 (EConfigListenerPrivate, 1);

    cl->priv->keys = g_hash_table_new (g_str_hash, g_str_equal);

    /* activate the configuration database */
    CORBA_exception_init (&ev);
    cl->priv->db = bonobo_get_object ("wombat:", "Bonobo/ConfigDatabase", &ev);

    if (BONOBO_EX (&ev) || cl->priv->db == CORBA_OBJECT_NIL) {
        CORBA_exception_free (&ev);
        cl->priv->db = CORBA_OBJECT_NIL;
    }
}

static void
free_key_hash (gpointer key, gpointer value, gpointer user_data)
{
    KeyData *kd = (KeyData *) value;

    g_return_if_fail (kd != NULL);

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

    g_free (kd);
}

static void
e_config_listener_destroy (GtkObject *object)
{
    EConfigListener *cl = (EConfigListener *) object;

    g_return_if_fail (E_IS_CONFIG_LISTENER (cl));

    if (cl->priv->db != CORBA_OBJECT_NIL) {
        bonobo_object_release_unref (cl->priv->db, NULL);
        cl->priv->db = CORBA_OBJECT_NIL;
    }

    g_hash_table_foreach (cl->priv->keys, (GHFunc) free_key_hash, NULL);
    g_hash_table_destroy (cl->priv->keys);
    cl->priv->keys = NULL;

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

    if (GTK_OBJECT_CLASS (parent_class)->destroy)
        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

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

    if (!type) {
        static const GtkTypeInfo info = {
            "EConfigListener",
            sizeof (EConfigListener),
            sizeof (EConfigListenerClass),
            (GtkClassInitFunc) e_config_listener_class_init,
            (GtkObjectInitFunc) e_config_listener_init,
            NULL, /* reserved_1 */
            NULL, /* reserved_2 */
            (GtkClassInitFunc) NULL
        };

        type = gtk_type_unique (PARENT_TYPE, &info);
    }

    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 = gtk_type_new (E_CONFIG_LISTENER_TYPE);
    return cl;
}

static void
property_change_cb (BonoboListener *listener,
            char *event_name,
            CORBA_any *any,
            CORBA_Environment *ev,
            gpointer user_data)
{
    KeyData *kd = (KeyData *) user_data;

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

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

    /* set new value */
    if (bonobo_arg_type_is_equal (any->_type, BONOBO_ARG_BOOLEAN, NULL)) {
        kd->type = GTK_TYPE_BOOL;
        kd->value.v_bool = BONOBO_ARG_GET_BOOLEAN (any);
    } else if (bonobo_arg_type_is_equal (any->_type, BONOBO_ARG_FLOAT, NULL)) {
        kd->type = GTK_TYPE_FLOAT;
        kd->value.v_float = BONOBO_ARG_GET_FLOAT (any);
    } else if (bonobo_arg_type_is_equal (any->_type, BONOBO_ARG_LONG, NULL)) {
        kd->type = GTK_TYPE_LONG;
        kd->value.v_long = BONOBO_ARG_GET_LONG (any);
    } else if (bonobo_arg_type_is_equal (any->_type, BONOBO_ARG_STRING, NULL)) {
        kd->type = GTK_TYPE_STRING;
        kd->value.v_str = g_strdup (BONOBO_ARG_GET_STRING (any));
    } else
        return;

    gtk_signal_emit (GTK_OBJECT (kd->cl), config_listener_signals[KEY_CHANGED], kd->key);
}

static KeyData *
add_key (EConfigListener *cl, const char *key, GtkFundamentalType type,
     gpointer value, gboolean used_default)
{
    KeyData *kd;
    char *event_name;
    char *ch;
    CORBA_Environment ev;

    /* 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 GTK_TYPE_BOOL :
        memcpy (&kd->value.v_bool, value, sizeof (gboolean));
        break;
    case GTK_TYPE_FLOAT :
        memcpy (&kd->value.v_float, value, sizeof (float));
        break;
    case GTK_TYPE_LONG :
        memcpy (&kd->value.v_long, value, sizeof (long));
        break;
    case GTK_TYPE_STRING :
        kd->value.v_str = (char *) value;
        break;
    default :
        break;
    }

    kd->used_default = used_default;

    /* add the listener for changes */
    event_name = g_strdup_printf ("=Bonobo/ConfigDatabase:change%s",
                      kd->key);
    ch = strrchr (event_name, '/');
    if (ch)
        *ch = ':';

    CORBA_exception_init (&ev);
    bonobo_event_source_client_add_listener (
        cl->priv->db,
        property_change_cb,
        event_name,
        &ev, kd);
    if (BONOBO_EX (&ev)) {
        CORBA_exception_free (&ev);
        g_free (event_name);
        free_key_hash (kd->key, kd, NULL);
        return NULL;
    }

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

    CORBA_exception_free (&ev);
    g_free (event_name);

    return kd;
}

gboolean
e_config_listener_get_boolean_with_default (EConfigListener *cl,
                        const char *key,
                        gboolean def,
                        gboolean *used_default)
{
    gboolean value;
    KeyData *kd;
    gboolean d;
    gpointer orig_key, orig_value;

    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 */
    if (!g_hash_table_lookup_extended (cl->priv->keys, key, &orig_key, &orig_value)) {
        /* not found, so retrieve it from the configuration database */
        value = bonobo_config_get_boolean_with_default (cl->priv->db, key, def, &d);
        kd = add_key (cl, key, GTK_TYPE_BOOL, &value, d);

        if (used_default != NULL)
            *used_default = d;
    } else {
        kd = (KeyData *) orig_value;
        g_assert (kd != NULL);

        if (kd->type == GTK_TYPE_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_with_default (EConfigListener *cl,
                      const char *key,
                      float def,
                      gboolean *used_default)
{
    float value;
    KeyData *kd;
    gboolean d;
    gpointer orig_key, orig_value;

    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 */
    if (!g_hash_table_lookup_extended (cl->priv->keys, key, &orig_key, &orig_value)) {
        /* not found, so retrieve it from the configuration database */
        value = bonobo_config_get_float_with_default (cl->priv->db, key, def, &d);
        kd = add_key (cl, key, GTK_TYPE_FLOAT, &value, d);

        if (used_default != NULL)
            *used_default = d;
    } else {
        kd = (KeyData *) orig_value;
        g_assert (kd != NULL);

        if (kd->type == GTK_TYPE_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_with_default (EConfigListener *cl,
                     const char *key,
                     long def,
                     gboolean *used_default)
{
    long value;
    KeyData *kd;
    gboolean d;
    gpointer orig_key, orig_value;

    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 */
    if (!g_hash_table_lookup_extended (cl->priv->keys, key, &orig_key, &orig_value)) {
        /* not found, so retrieve it from the configuration database */
        value = bonobo_config_get_long_with_default (cl->priv->db, key, def, &d);
        kd = add_key (cl, key, GTK_TYPE_LONG, &value, d);

        if (used_default != NULL)
            *used_default = d;
    } else {
        kd = (KeyData *) orig_value;
        g_assert (kd != NULL);

        if (kd->type == GTK_TYPE_LONG) {
            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_with_default (EConfigListener *cl,
                       const char *key,
                       const char *def,
                       gboolean *used_default)
{
    char *str;
    KeyData *kd;
    gboolean d;
    gpointer orig_key, orig_value;

    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 */
    if (!g_hash_table_lookup_extended (cl->priv->keys, key, &orig_key, &orig_value)) {
        /* not found, so retrieve it from the configuration database */
        str = bonobo_config_get_string_with_default (cl->priv->db, key, (char *) def, &d);
        if (str) {
            kd = add_key (cl, key, GTK_TYPE_STRING, (gpointer) str, d);

            if (used_default != NULL)
                *used_default = d;
        } else
            return NULL;
    } else {
        kd = (KeyData *) orig_value;
        g_assert (kd != NULL);

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

    return g_strdup (str);
}

void
e_config_listener_set_boolean (EConfigListener *cl, const char *key, gboolean value)
{
    CORBA_Environment ev;
    KeyData *kd;

    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;

    CORBA_exception_init (&ev);

    bonobo_config_set_boolean (cl->priv->db, key, value, &ev);
    if (BONOBO_EX (&ev))
        g_warning ("Cannot save config key %s -- %s", key, BONOBO_EX_ID (&ev));

    CORBA_exception_free (&ev);

    /* 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)
{
    CORBA_Environment ev;
    KeyData *kd;

    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;

    CORBA_exception_init (&ev);

    bonobo_config_set_float (cl->priv->db, key, value, &ev);
    if (BONOBO_EX (&ev))
        g_warning ("Cannot save config key %s -- %s", key, BONOBO_EX_ID (&ev));

    CORBA_exception_free (&ev);

    /* 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)
{
    CORBA_Environment ev;
    KeyData *kd;

    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;

    CORBA_exception_init (&ev);

    bonobo_config_set_long (cl->priv->db, key, value, &ev);
    if (BONOBO_EX (&ev))
        g_warning ("Cannot save config key %s -- %s", key, BONOBO_EX_ID (&ev));

    CORBA_exception_free (&ev);

    /* 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)
{
    CORBA_Environment ev;
    char *s1, *s2;
    KeyData *kd;

    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);

    CORBA_exception_init (&ev);

    bonobo_config_set_string (cl->priv->db, key, value, &ev);
    if (BONOBO_EX (&ev))
        g_warning ("Cannot save config key %s -- %s", key, BONOBO_EX_ID (&ev));

    CORBA_exception_free (&ev);

    /* 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);
    }
}

Bonobo_ConfigDatabase
e_config_listener_get_db (EConfigListener *cl)
{
    g_return_val_if_fail (E_IS_CONFIG_LISTENER (cl), CORBA_OBJECT_NIL);
    return cl->priv->db;
}