/**
 *
 * GConf libccs backend
 *
 * gconf.c
 *
 * Copyright (c) 2007 Danny Baumann <maniac@opencompositing.org>
 *
 * Parts of this code are taken from libberylsettings 
 * gconf backend, written by:
 *
 * Copyright (c) 2006 Robert Carr <racarr@opencompositing.org>
 * Copyright (c) 2007 Dennis Kasprzyk <onestone@opencompositing.org>
 *
 * This program 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.
 *
 **/

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>

#include <ccs.h>
#include <ccs-backend.h>

#include <X11/X.h>
#include <X11/Xlib.h>

#include <gconf/gconf.h>
#include <gconf/gconf-client.h>
#include <gconf/gconf-value.h>

#define CompAltMask        (1 << 16)
#define CompMetaMask       (1 << 17)
#define CompSuperMask      (1 << 18)
#define CompHyperMask      (1 << 19)
#define CompModeSwitchMask (1 << 20)
#define CompNumLockMask    (1 << 21)
#define CompScrollLockMask (1 << 22)

#define METACITY     "/apps/metacity"
#define COMPIZ       "/apps/compiz"
#define COMPIZCONFIG "/apps/compizconfig"
#define PROFILEPATH  COMPIZCONFIG "/profiles"
#define DEFAULTPROF "Default"
#define CORE_NAME   "core"

#define BUFSIZE 512

#define KEYNAME     char keyName[BUFSIZE]; \
                    if (setting->isScreen) \
                        snprintf (keyName, BUFSIZE, "screen%d", \
				  setting->screenNum); \
                    else \
                        snprintf (keyName, BUFSIZE, "allscreens");

#define PATHNAME    char pathName[BUFSIZE]; \
                    if (!setting->parent->name || \
			strcmp (setting->parent->name, "core") == 0) \
                        snprintf (pathName, BUFSIZE, \
				 "%s/general/%s/options/%s", COMPIZ, \
				 keyName, setting->name); \
                    else \
			snprintf(pathName, BUFSIZE, \
				 "%s/plugins/%s/%s/options/%s", COMPIZ, \
				 setting->parent->name, keyName, setting->name);

static const char* watchedGnomeDirectories[] = {
    METACITY,
    "/desktop/gnome/applications/terminal",
    "/apps/panel/applets/window_list/prefs"
};
#define NUM_WATCHED_DIRS 3

static GConfClient *client = NULL;
static GConfEngine *conf = NULL;
static guint compizNotifyId;
static guint gnomeNotifyIds[NUM_WATCHED_DIRS];
static char *currentProfile = NULL;

/* some forward declarations */
static Bool readInit (CCSContext * context);
static void readSetting (CCSContext * context, CCSSetting * setting);
static Bool readOption (CCSSetting * setting);
static Bool writeInit (CCSContext * context);
static void writeIntegratedOption (CCSContext *context, CCSSetting *setting,
				   int        index);

typedef enum {
    OptionInt,
    OptionBool,
    OptionKey,
    OptionString,
    OptionSpecial,
} SpecialOptionType;

typedef struct _SpecialOption {
    const char*       settingName;
    const char*       pluginName;
    Bool	      screen;
    const char*       gnomeName;
    SpecialOptionType type;
} SpecialOption;

const SpecialOption specialOptions[] = {
    {"run", "core", FALSE,
     METACITY "/global_keybindings/panel_run_dialog", OptionKey},
    {"main_menu", "core", FALSE,
     METACITY "/global_keybindings/panel_main_menu", OptionKey},
    {"run_command_screenshot", "core", FALSE,
     METACITY "/global_keybindings/run_command_screenshot", OptionKey},
    {"run_command_window_screenshot", "core", FALSE,
     METACITY "/global_keybindings/run_command_window_screenshot", OptionKey},
    {"run_command_terminal", "core", FALSE,
     METACITY "/global_keybindings/run_command_terminal", OptionKey},

    {"toggle_window_maximized", "core", FALSE,
     METACITY "/window_keybindings/toggle_maximized", OptionKey},
    {"minimize_window", "core", FALSE,
     METACITY "/window_keybindings/minimize", OptionKey},
    {"maximize_window", "core", FALSE,
     METACITY "/window_keybindings/maximize", OptionKey},
    {"unmaximize_window", "core", FALSE,
     METACITY "/window_keybindings/unmaximize", OptionKey},
    {"maximize_window_horizontally", "core", FALSE,
     METACITY "/window_keybindings/maximize_horizontally", OptionKey},
    {"maximize_window_vertically", "core", FALSE,
     METACITY "/window_keybindings/maximize_vertically", OptionKey},
    {"raise_window", "core", FALSE,
     METACITY "/window_keybindings/raise", OptionKey},
    {"lower_window", "core", FALSE,
     METACITY "/window_keybindings/lower", OptionKey},
    {"close_window", "core", FALSE,
     METACITY "/window_keybindings/close", OptionKey},
    {"toggle_window_shaded", "core", FALSE,
     METACITY "/window_keybindings/toggle_shaded", OptionKey},

    {"show_desktop", "core", FALSE,
     METACITY "/global_keybindings/show_desktop", OptionKey},

    {"initiate", "move", FALSE,
     METACITY "/window_keybindings/begin_move", OptionSpecial},
    {"initiate", "resize", FALSE,
     METACITY "/window_keybindings/begin_resize", OptionSpecial},
    {"window_menu", "core", FALSE,
     METACITY "/window_keybindings/activate_window_menu", OptionSpecial},
    /* this option does not exist in Compiz */
    {"mouse_button_modifier", NULL, FALSE,
     METACITY "/general/mouse_button_modifier", OptionSpecial},

    {"visual_bell", "fade", TRUE,
     METACITY "/general/visual_bell", OptionBool},
    {"fullscreen_visual_bell", "fade", TRUE,
     METACITY "/general/visual_bell_type", OptionSpecial},

    {"next", "switcher", FALSE,
     METACITY "/global_keybindings/switch_windows", OptionKey},
    {"prev", "switcher", FALSE,
     METACITY "/global_keybindings/switch_windows_backward", OptionKey},

    {"toggle_sticky", "extrawm", FALSE,
     METACITY "/window_keybindings/toggle_on_all_workspaces", OptionKey},
    {"toggle_fullscreen", "extrawm", FALSE,
     METACITY "/window_keybindings/toggle_fullscreen", OptionKey},

    {"command0", "core", FALSE,
     METACITY "/keybinding_commands/command_1", OptionString},
    {"command1", "core", FALSE,
     METACITY "/keybinding_commands/command_2", OptionString},
    {"command2", "core", FALSE,
     METACITY "/keybinding_commands/command_3", OptionString},
    {"command3", "core", FALSE,
     METACITY "/keybinding_commands/command_4", OptionString},
    {"command4", "core", FALSE,
     METACITY "/keybinding_commands/command_5", OptionString},
    {"command5", "core", FALSE,
     METACITY "/keybinding_commands/command_6", OptionString},
    {"command6", "core", FALSE,
     METACITY "/keybinding_commands/command_7", OptionString},
    {"command7", "core", FALSE,
     METACITY "/keybinding_commands/command_8", OptionString},
    {"command8", "core", FALSE,
     METACITY "/keybinding_commands/command_9", OptionString},
    {"command9", "core", FALSE,
     METACITY "/keybinding_commands/command_10", OptionString},
    {"command10", "core", FALSE,
     METACITY "/keybinding_commands/command_11", OptionString},
    {"command11", "core", FALSE,
     METACITY "/keybinding_commands/command_12", OptionString},

    {"run_command0", "core", FALSE,
     METACITY "/global_keybindings/run_command_1", OptionKey},
    {"run_command1", "core", FALSE,
     METACITY "/global_keybindings/run_command_2", OptionKey},
    {"run_command2", "core", FALSE,
     METACITY "/global_keybindings/run_command_3", OptionKey},
    {"run_command3", "core", FALSE,
     METACITY "/global_keybindings/run_command_4", OptionKey},
    {"run_command4", "core", FALSE,
     METACITY "/global_keybindings/run_command_5", OptionKey},
    {"run_command5", "core", FALSE,
     METACITY "/global_keybindings/run_command_6", OptionKey},
    {"run_command6", "core", FALSE,
     METACITY "/global_keybindings/run_command_7", OptionKey},
    {"run_command7", "core", FALSE,
     METACITY "/global_keybindings/run_command_8", OptionKey},
    {"run_command8", "core", FALSE,
     METACITY "/global_keybindings/run_command_9", OptionKey},
    {"run_command9", "core", FALSE,
     METACITY "/global_keybindings/run_command_10", OptionKey},
    {"run_command10", "core", FALSE,
     METACITY "/global_keybindings/run_command_11", OptionKey},
    {"run_command11", "core", FALSE,
     METACITY "/global_keybindings/run_command_12", OptionKey},

    {"rotate_to_1", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_1", OptionKey},
    {"rotate_to_2", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_2", OptionKey},
    {"rotate_to_3", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_3", OptionKey},
    {"rotate_to_4", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_4", OptionKey},
    {"rotate_to_5", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_5", OptionKey},
    {"rotate_to_6", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_6", OptionKey},
    {"rotate_to_7", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_7", OptionKey},
    {"rotate_to_8", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_8", OptionKey},
    {"rotate_to_9", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_9", OptionKey},
    {"rotate_to_10", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_10", OptionKey},
    {"rotate_to_11", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_11", OptionKey},
    {"rotate_to_12", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_12", OptionKey},

    {"rotate_left", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_left", OptionKey},
    {"rotate_right", "rotate", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_right", OptionKey},

    {"plane_to_1", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_1", OptionKey},
    {"plane_to_2", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_2", OptionKey},
    {"plane_to_3", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_3", OptionKey},
    {"plane_to_4", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_4", OptionKey},
    {"plane_to_5", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_5", OptionKey},
    {"plane_to_6", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_6", OptionKey},
    {"plane_to_7", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_7", OptionKey},
    {"plane_to_8", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_8", OptionKey},
    {"plane_to_9", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_9", OptionKey},
    {"plane_to_10", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_10", OptionKey},
    {"plane_to_11", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_11", OptionKey},
    {"plane_to_12", "plane", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_12", OptionKey},

    {"switch_to_1", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_1", OptionKey},
    {"switch_to_2", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_2", OptionKey},
    {"switch_to_3", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_3", OptionKey},
    {"switch_to_4", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_4", OptionKey},
    {"switch_to_5", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_5", OptionKey},
    {"switch_to_6", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_6", OptionKey},
    {"switch_to_7", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_7", OptionKey},
    {"switch_to_8", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_8", OptionKey},
    {"switch_to_9", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_9", OptionKey},
    {"switch_to_10", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_10", OptionKey},
    {"switch_to_11", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_11", OptionKey},
    {"switch_to_12", "vpswitch", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_12", OptionKey},

    {"up", "wall", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_up", OptionKey},
    {"down", "wall", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_down", OptionKey},
    {"left", "wall", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_left", OptionKey},
    {"right", "wall", FALSE,
     METACITY "/global_keybindings/switch_to_workspace_right", OptionKey},
    {"left_window", "wall", FALSE,
     METACITY "/window_keybindings/move_to_workspace_left", OptionKey},
    {"right_window", "wall", FALSE,
     METACITY "/window_keybindings/move_to_workspace_right", OptionKey},
    {"up_window", "wall", FALSE,
     METACITY "/window_keybindings/move_to_workspace_up", OptionKey},
    {"down_window", "wall", FALSE,
     METACITY "/window_keybindings/move_to_workspace_down", OptionKey},

    {"put_topleft", "put", FALSE,
     METACITY "/window_keybindings/move_to_corner_nw", OptionKey},
    {"put_topright", "put", FALSE,
     METACITY "/window_keybindings/move_to_corner_ne", OptionKey},
    {"put_bottomleft", "put", FALSE,
     METACITY "/window_keybindings/move_to_corner_sw", OptionKey},
    {"put_bottomright", "put", FALSE,
     METACITY "/window_keybindings/move_to_corner_se", OptionKey},
    {"put_left", "put", FALSE,
     METACITY "/window_keybindings/move_to_side_w", OptionKey},
    {"put_right", "put", FALSE,
     METACITY "/window_keybindings/move_to_side_e", OptionKey},
    {"put_top", "put", FALSE,
     METACITY "/window_keybindings/move_to_side_n", OptionKey},
    {"put_bottom", "put", FALSE,
     METACITY "/window_keybindings/move_to_side_s", OptionKey},

    {"rotate_to_1_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_1", OptionKey},
    {"rotate_to_2_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_2", OptionKey},
    {"rotate_to_3_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_3", OptionKey},
    {"rotate_to_4_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_4", OptionKey},
    {"rotate_to_5_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_5", OptionKey},
    {"rotate_to_6_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_6", OptionKey},
    {"rotate_to_7_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_7", OptionKey},
    {"rotate_to_8_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_8", OptionKey},
    {"rotate_to_9_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_9", OptionKey},
    {"rotate_to_10_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_10", OptionKey},
    {"rotate_to_11_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_11", OptionKey},
    {"rotate_to_12_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_12", OptionKey},

    {"rotate_left_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_left", OptionKey},
    {"rotate_right_window", "rotate", FALSE,
     METACITY "/window_keybindings/move_to_workspace_right", OptionKey},

    {"command_screenshot", "core", FALSE,
     METACITY "/keybinding_commands/command_screenshot", OptionString},
    {"command_window_screenshot", "core", FALSE,
     METACITY "/keybinding_commands/command_window_screenshot", OptionString},
    {"command_terminal", "core", FALSE,
     "/desktop/gnome/applications/terminal/exec", OptionString},

    {"current_viewport", "thumbnail", TRUE,
     "/apps/panel/applets/window_list/prefs/display_all_workspaces",
     OptionSpecial},

    {"autoraise", "core", FALSE,
     METACITY "/general/auto_raise", OptionBool},
    {"autoraise_delay", "core", FALSE,
     METACITY "/general/auto_raise_delay", OptionInt},
    {"raise_on_click", "core", FALSE,
     METACITY "/general/raise_on_click", OptionBool},
    {"click_to_focus", "core", FALSE,
     METACITY "/general/focus_mode", OptionSpecial},

    {"audible_bell", "core", FALSE,
     METACITY "/general/audible_bell", OptionBool},
    /*{"hsize", "core", TRUE,
     METACITY "/general/num_workspaces", OptionInt},*/
};

#define N_SOPTIONS (sizeof (specialOptions) / sizeof (struct _SpecialOption))

static CCSSetting *
findDisplaySettingForPlugin (CCSContext *context,
			     char       *plugin,
			     char       *setting)
{
    CCSPlugin  *p;
    CCSSetting *s;

    p = ccsFindPlugin (context, plugin);
    if (!p)
	return NULL;

    s = ccsFindSetting (p, setting, FALSE, 0);
    if (!s)
	return NULL;

    return s;
}

static Bool
isIntegratedOption (CCSSetting *setting,
		    int        *index)
{
    unsigned int i;

    for (i = 0; i < N_SOPTIONS; i++)
    {
	if ((strcmp (setting->name, specialOptions[i].settingName) == 0) &&
	    ((!setting->parent->name && !specialOptions[i].pluginName) ||
	     (setting->parent->name && specialOptions[i].pluginName &&
	      (strcmp (setting->parent->name,
		       specialOptions[i].pluginName) == 0))) &&
	    ((setting->isScreen && specialOptions[i].screen) ||
	     (!setting->isScreen && !specialOptions[i].screen)))
	{
	    if (index)
		*index = i;
	    return TRUE;
	}
    }
    return FALSE;
}

static void
valueChanged (GConfClient *client,
	      guint       cnxn_id,
	      GConfEntry  *entry,
	      gpointer    user_data)
{
    CCSContext   *context = (CCSContext *)user_data;
    char         *keyName = (char*) gconf_entry_get_key (entry);
    char         *pluginName;
    char         *token;
    int          index;
    Bool         isScreen;
    unsigned int screenNum;
    CCSPlugin    *plugin;
    CCSSetting   *setting;

    keyName += strlen (COMPIZ) + 1;

    token = strsep (&keyName, "/"); /* plugin */
    if (!token)
	return;

    if (strcmp (token, "general") == 0)
    {
	pluginName = "core";
    }
    else
    {
	token = strsep (&keyName, "/");
	if (!token)
	    return;
	pluginName = token;
    }

    plugin = ccsFindPlugin (context, pluginName);
    if (!plugin)
	return;

    token = strsep (&keyName, "/");
    if (!token)
	return;

    if (strcmp (token, "allscreens") == 0)
	isScreen = FALSE;
    else
    {
	isScreen = TRUE;
	sscanf (token, "screen%d", &screenNum);
    }

    token = strsep (&keyName, "/"); /* 'options' */
    if (!token)
	return;

    token = strsep (&keyName, "/");
    if (!token)
	return;

    setting = ccsFindSetting (plugin, token, isScreen, screenNum);
    if (!setting)
    {
	/* maybe it's an action which has a name_button/... naming scheme */
	const char *prefix[] = { "_key", "_button", "_edge", "_edgebutton", "_bell" };
	int        i;
	int        prefixLen, len = strlen (token);

	for (i = 0; i < sizeof (prefix) / sizeof (prefix[0]); i++)
	{
	    prefixLen = strlen (prefix[i]);
	    if (len < prefixLen)
		continue;

	    if (strcmp (token + len - prefixLen, prefix[i]) == 0)
	    {
		char *buffer = malloc (sizeof (char) * (len - prefixLen + 1));
		if (buffer)
		{
		    strncpy (buffer, token, len - prefixLen);
		    buffer[len - prefixLen] = 0;
		    setting = ccsFindSetting (plugin, buffer,
					      isScreen, screenNum);
		    free (buffer);
		}
		break;
	    }
	}
    }
    if (!setting)
	return;

    readInit (context);
    if (!readOption (setting))
	ccsResetToDefault (setting);

    if (ccsGetIntegrationEnabled (context) &&
	isIntegratedOption (setting, &index))
    {
	writeInit (context);
	writeIntegratedOption (context, setting, index);
    }
}

static void
gnomeValueChanged (GConfClient *client,
		   guint       cnxn_id,
		   GConfEntry  *entry,
		   gpointer    user_data)
{
    CCSContext *context = (CCSContext *)user_data;
    char       *keyName = (char*) gconf_entry_get_key (entry);
    int        i, last = 0, num = 0;
    Bool       needInit = TRUE;

    if (!ccsGetIntegrationEnabled (context))
	return;

    /* we have to loop multiple times here, because one Gnome
       option may be integrated with multiple Compiz options */

    while (1)
    {
	for (i = last, num = -1; i < N_SOPTIONS; i++)
	{
	    if (strcmp (specialOptions[i].gnomeName, keyName) == 0)
	    {
		num = i;
		last = i + 1;
		break;
	    }
	}

	if (num < 0)
	    break;

	if (strcmp (specialOptions[num].settingName,
		    "mouse_button_modifier") == 0)
	{
	    CCSSetting *s;

	    if (needInit)
	    {
		readInit (context);
		needInit = FALSE;
	    }

	    s = findDisplaySettingForPlugin (context, "core", "window_menu");
	    if (s)
		readSetting (context, s);

	    s = findDisplaySettingForPlugin (context, "move", "initiate");
	    if (s)
		readSetting (context, s);

	    s = findDisplaySettingForPlugin (context, "resize", "initiate");
	    if (s)
		readSetting (context, s);
	}
	else
	{
	    CCSPlugin     *plugin = NULL;
	    CCSSetting    *setting;
	    SpecialOption *opt = (SpecialOption *) &specialOptions[num];

	    plugin = ccsFindPlugin (context, (char*) opt->pluginName);
	    if (plugin)
	    {
		for (i = 0; i < context->numScreens; i++)
		{
		    unsigned int screen;

		    if (opt->screen)
			screen = context->screens[i];
		    else
			screen = 0;

		    setting = ccsFindSetting (plugin, (char*) opt->settingName,
					      opt->screen, screen);

		    if (setting)
		    {
			if (needInit)
			{
			    readInit (context);
			    needInit = FALSE;
			}
			readSetting (context, setting);
		    }

		    /* do not read display settings multiple
		       times for multiscreen environments */
		    if (!opt->screen)
			i = context->numScreens;
		}
	    }
	}
    }
}

static void
initClient (CCSContext *context)
{
    int i;

    client = gconf_client_get_for_engine (conf);

    compizNotifyId = gconf_client_notify_add (client, COMPIZ, valueChanged,
					      context, NULL, NULL);
    gconf_client_add_dir (client, COMPIZ, GCONF_CLIENT_PRELOAD_NONE, NULL);

    for (i = 0; i < NUM_WATCHED_DIRS; i++)
    {
	gnomeNotifyIds[i] = gconf_client_notify_add (client,
						     watchedGnomeDirectories[i],
						     gnomeValueChanged, context,
						     NULL, NULL);
	gconf_client_add_dir (client, watchedGnomeDirectories[i],
			      GCONF_CLIENT_PRELOAD_NONE, NULL);
    }
}

static void
finiClient (void)
{
    int i;

    if (compizNotifyId)
    {
	gconf_client_notify_remove (client, compizNotifyId);
	compizNotifyId = 0;
    }
    gconf_client_remove_dir (client, COMPIZ, NULL);

    for (i = 0; i < NUM_WATCHED_DIRS; i++)
    {
	if (gnomeNotifyIds[i])
	{
	    gconf_client_notify_remove (client, gnomeNotifyIds[0]);
	    gnomeNotifyIds[i] = 0;
	}
	gconf_client_remove_dir (client, watchedGnomeDirectories[i], NULL);
    }

    gconf_client_suggest_sync (client, NULL);

    g_object_unref (client);
    client = NULL;
}

static void
copyGconfValues (GConfEngine *conf,
		 const gchar *from,
		 const gchar *to,
		 Bool        associate,
		 const gchar *schemaPath)
{
    GSList *values, *tmp;
    GError *err = NULL;

    values = gconf_engine_all_entries (conf, from, &err);
    tmp = values;

    while (tmp)
    {
	GConfEntry *entry = tmp->data;
	GConfValue *value;
	const char *key = gconf_entry_get_key (entry);
	char       *name, *newKey, *newSchema = NULL;

	name = strrchr (key, '/');
	if (!name)
	    continue;

	if (to)
	{
	    asprintf (&newKey, "%s/%s", to, name + 1);

	    if (associate && schemaPath)
		asprintf (&newSchema, "%s/%s", schemaPath, name + 1);

	    value = gconf_engine_get_without_default (conf, key, NULL);
	    if (value && newKey)
	    {
		if (newSchema)
		    gconf_engine_associate_schema (conf, newKey,
						   newSchema, NULL);
		gconf_engine_set (conf, newKey, value, NULL);

		gconf_value_free (value);
	    }
	    if (newSchema)
		free (newSchema);
	    if (newKey)
		free (newKey);
	}
	else
	{
	    if (associate)
		gconf_engine_associate_schema (conf, key, NULL, NULL);
	    gconf_engine_unset (conf, key, NULL);
	}

	gconf_entry_unref (entry);
	tmp = g_slist_next (tmp);
    }

    if (values)
	g_slist_free (values);
}

static void
copyGconfRecursively (GConfEngine *conf,
		      GSList      *subdirs,
		      const gchar *to,
		      Bool        associate,
		      const gchar *schemaPath)
{
    GSList* tmp;

    tmp = subdirs;

    while (tmp)
    {
 	gchar *path = tmp->data;
	char  *newKey, *newSchema = NULL, *name;

	name = strrchr (path, '/');
	if (name)
	{
  	    if (to)
		asprintf (&newKey, "%s/%s", to, name + 1);
	    else
		newKey = NULL;

	    if (associate && schemaPath)
		asprintf (&newSchema, "%s/%s", schemaPath, name + 1);

	    copyGconfValues (conf, path, newKey, associate, newSchema);
	    copyGconfRecursively (conf,
				  gconf_engine_all_dirs (conf, path, NULL),
				  newKey, associate, newSchema);

	    if (newSchema)
		free (newSchema);

	    if (to)
	    {
		if (newKey)
		    free (newKey);
	    }
	    else
		gconf_engine_remove_dir (conf, path, NULL);
	}

	g_free (path);
	tmp = g_slist_next (tmp);
    }

    if (subdirs)
	g_slist_free (subdirs);
}

static void
copyGconfTree (CCSContext  *context,
	       const gchar *from,
	       const gchar *to,
	       Bool        associate,
	       const gchar *schemaPath)
{
    GSList* subdirs;

    /* we aren't allowed to have an open GConfClient object while
       using GConfEngine, so shut it down and open it again afterwards */
    finiClient ();

    subdirs = gconf_engine_all_dirs (conf, from, NULL);
    gconf_engine_suggest_sync (conf, NULL);

    copyGconfRecursively (conf, subdirs, to, associate, schemaPath);

    gconf_engine_suggest_sync (conf, NULL);

    initClient (context);
}

static Bool
readActionValue (CCSSetting *setting,
		 char       *pathName)
{
    char                  itemPath[BUFSIZE];
    GError                *err = NULL;
    Bool                  ret = FALSE;
    GConfValue            *gconfValue;
    CCSSettingActionValue action;

    memset (&action, 0, sizeof (CCSSettingActionValue));

    snprintf (itemPath, 512, "%s_bell", pathName);
    gconfValue = gconf_client_get (client, itemPath, &err);
    if (!err && gconfValue)
    {
	action.onBell = gconf_value_get_bool (gconfValue);
	ret = TRUE;
    }
    if (err)
    {
	g_error_free (err);
	err = NULL;
    }
    if (gconfValue)
	gconf_value_free (gconfValue);

    snprintf (itemPath, 512, "%s_edge", pathName);
    gconfValue = gconf_client_get (client, itemPath, &err);
    if (!err && gconfValue)
    {
	if ((gconfValue->type == GCONF_VALUE_LIST) &&
	    (gconf_value_get_list_type (gconfValue) == GCONF_VALUE_STRING))
	{
	    GSList        *list;
	    CCSStringList edgeList = NULL;

	    list = gconf_value_get_list (gconfValue);
	    for ( ; list; list = list->next)
	    {
		GConfValue *value = (GConfValue *) list->data;
		const char *edge = gconf_value_get_string (value);

		if (edge)
		    edgeList = ccsStringListAppend (edgeList, (char*) edge);
	    }

	    ccsStringListToEdges (edgeList, &action);

	    if (edgeList)
		ccsStringListFree (edgeList, FALSE);
	}
    }
    if (err)
    {
	g_error_free (err);
	err = NULL;
    }
    if (gconfValue)
	gconf_value_free (gconfValue);

    snprintf (itemPath, 512, "%s_edgebutton", pathName);
    gconfValue = gconf_client_get (client, itemPath, &err);
    if (!err && gconfValue)
    {
	action.edgeButton = gconf_value_get_int (gconfValue);
	ret = TRUE;
    }
    if (err)
    {
	g_error_free (err);
	err = NULL;
    }
    if (gconfValue)
	gconf_value_free (gconfValue);

    snprintf (itemPath, 512, "%s_key", pathName);
    gconfValue = gconf_client_get (client, itemPath, &err);
    if (!err && gconfValue)
    {
	const char* buffer;
	buffer = gconf_value_get_string (gconfValue);
	if (buffer)
	{
	    ccsStringToKeyBinding (buffer, &action);
	    ret = TRUE;
	}
    }
    if (err)
    {
	g_error_free (err);
	err = NULL;
    }
    if (gconfValue)
	gconf_value_free (gconfValue);

    snprintf (itemPath, 512, "%s_button", pathName);
    gconfValue = gconf_client_get (client, itemPath, &err);
    if (!err && gconfValue)
    {
	const char* buffer;
	buffer = gconf_value_get_string (gconfValue);
	if (buffer)
	{
	    ccsStringToButtonBinding (buffer, &action);
	    ret = TRUE;
	}
    }
    if (err)
    {
	g_error_free (err);
	err = NULL;
    }
    if (gconfValue)
	gconf_value_free (gconfValue);

    if (ret)
	ccsSetAction (setting, action);

    return ret;
}

static Bool
readListValue (CCSSetting *setting,
	       GConfValue *gconfValue)
{
    GConfValueType      valueType;
    unsigned int        nItems, i = 0;
    CCSSettingValueList list = NULL;
    GSList              *valueList = NULL;

    switch (setting->info.forList.listType)
    {
    case TypeString:
    case TypeMatch:
    case TypeColor:
	valueType = GCONF_VALUE_STRING;
	break;
    case TypeBool:
	valueType = GCONF_VALUE_BOOL;
	break;
    case TypeInt:
	valueType = GCONF_VALUE_INT;
	break;
    case TypeFloat:
	valueType = GCONF_VALUE_FLOAT;
	break;
    default:
	valueType = GCONF_VALUE_INVALID;
	break;
    }

    if (valueType == GCONF_VALUE_INVALID)
	return FALSE;

    if (valueType != gconf_value_get_list_type (gconfValue))
	return FALSE;

    valueList = gconf_value_get_list (gconfValue);
    if (!valueList)
    {
	ccsSetList (setting, NULL);
	return TRUE;
    }

    nItems = g_slist_length (valueList);

    switch (setting->info.forList.listType)
    {
    case TypeBool:
	{
	    Bool *array = malloc (nItems * sizeof (Bool));
	    if (!array)
		break;

	    for (; valueList; valueList = valueList->next, i++)
		array[i] =
		    gconf_value_get_bool (valueList->data) ? TRUE : FALSE;
	    list = ccsGetValueListFromBoolArray (array, nItems, setting);
	    free (array);
	}
	break;
    case TypeInt:
	{
	    int *array = malloc (nItems * sizeof (int));
	    if (!array)
		break;

	    for (; valueList; valueList = valueList->next, i++)
		array[i] = gconf_value_get_int (valueList->data);
	    list = ccsGetValueListFromIntArray (array, nItems, setting);
	    free (array);
	}
	break;
    case TypeFloat:
	{
	    float *array = malloc (nItems * sizeof (float));
	    if (!array)
		break;

	    for (; valueList; valueList = valueList->next, i++)
		array[i] = gconf_value_get_float (valueList->data);
	    list = ccsGetValueListFromFloatArray (array, nItems, setting);
	    free (array);
	}
	break;
    case TypeString:
    case TypeMatch:
	{
	    char **array = malloc (nItems * sizeof (char*));
	    if (!array)
		break;

	    for (; valueList; valueList = valueList->next, i++)
		array[i] = strdup (gconf_value_get_string (valueList->data));
	    list = ccsGetValueListFromStringArray (array, nItems, setting);
	    for (i = 0; i < nItems; i++)
		if (array[i])
		    free (array[i]);
	    free (array);
	}
	break;
    case TypeColor:
	{
	    CCSSettingColorValue *array;
	    array = malloc (nItems * sizeof (CCSSettingColorValue));
	    if (!array)
		break;

	    for (; valueList; valueList = valueList->next, i++)
    	    {
		memset (&array[i], 0, sizeof (CCSSettingColorValue));
		ccsStringToColor (gconf_value_get_string (valueList->data),
				  &array[i]);
	    }
	    list = ccsGetValueListFromColorArray (array, nItems, setting);
	    free (array);
	}
	break;
    default:
	break;
    }

    if (list)
    {
	ccsSetList (setting, list);
	ccsSettingValueListFree (list, TRUE);
	return TRUE;
    }

    return FALSE;
}

static unsigned int
getGnomeMouseButtonModifier(void)
{
    unsigned int modMask = 0;
    GError       *err = NULL;
    char         *value;

    value = gconf_client_get_string (client,
				     METACITY "/general/mouse_button_modifier",
				     &err);

    if (err)
    {
	g_error_free (err);
	return 0;
    }

    if (!value)
	return 0;

    modMask = ccsStringToModifiers (value);
    g_free (value);

    return modMask;
}

static Bool
readIntegratedOption (CCSContext *context,
		      CCSSetting *setting,
		      int        index)
{
    GError *err = NULL;
    Bool   ret = FALSE;

    switch (specialOptions[index].type)
    {
    case OptionInt:
	{
	    guint value;
	    value = gconf_client_get_int (client,
					  specialOptions[index].gnomeName,
					  &err);

	    if (!err)
	    {
		ccsSetInt (setting, value);
		ret = TRUE;
	    }
	}
	break;
    case OptionBool:
	{
	    gboolean value;
	    value = gconf_client_get_bool (client,
					   specialOptions[index].gnomeName,
					   &err);

	    if (!err)
	    {
		ccsSetBool (setting, value ? TRUE : FALSE);
		ret = TRUE;
	    }
	}
	break;
    case OptionString:
	{
	    char *value;
	    value = gconf_client_get_string (client,
					     specialOptions[index].gnomeName,
					     &err);

	    if (!err && value)
    	    {
		ccsSetString (setting, value);
		ret = TRUE;
		g_free (value);
	    }
	}
	break;
    case OptionKey:
	{
	    char *value;
	    value = gconf_client_get_string (client,
					     specialOptions[index].gnomeName,
					     &err);

	    if (!err && value)
    	    {
		CCSSettingActionValue action;
		memset (&action, 0, sizeof(CCSSettingActionValue));
		ccsGetAction (setting, &action);
		if (strcasecmp (value, "disabled") == 0)
		{
		    /* clear out keybinding */
		    action.keysym     = NoSymbol;
		    action.keyModMask = 0;
		    ccsSetAction (setting, action);
		    ret = TRUE;
		}
		else if (ccsStringToKeyBinding (value, &action))
		{
		    ccsSetAction (setting, action);
		    ret = TRUE;
		}
		g_free (value);
	    }
	}
	break;
    case OptionSpecial:
	{
	    const char *settingName = specialOptions[index].settingName;
	    const char *pluginName  = specialOptions[index].pluginName;

	    if (strcmp (settingName, "current_viewport") == 0)
	    {
		const char *name;
		gboolean   showAll;

		name = specialOptions[index].gnomeName;
		showAll = gconf_client_get_bool (client, name, &err);
		if (!err)
		{
		    ccsSetBool (setting, !showAll);
		    ret = TRUE;
		}
	    }
	    else if (strcmp (settingName, "fullscreen_visual_bell") == 0)
	    {
		const char *name;
		char       *value;

		name = specialOptions[index].gnomeName;
		value = gconf_client_get_string (client, name, &err);
		if (!err && value)
		{
		    Bool fullscreen;

		    fullscreen = strcmp (value, "fullscreen") == 0;
		    ccsSetBool (setting, fullscreen);
		    ret = TRUE;
		    g_free (value);
		}
	    }
	    else if (strcmp (settingName, "click_to_focus") == 0)
	    {
		char       *focusMode;
		const char *name;

		name = specialOptions[index].gnomeName;
		focusMode = gconf_client_get_string (client, name, &err);

		if (!err && focusMode)
		{
		    Bool clickToFocus = (strcmp (focusMode, "click") == 0);
		    ccsSetBool (setting, clickToFocus);
		    ret = TRUE;
		    g_free (focusMode);
		}
	    }
	    else if (((strcmp (settingName, "initiate") == 0) &&
		      ((strcmp (pluginName, "move") == 0) ||
		       (strcmp (pluginName, "resize") == 0))) ||
		     (strcmp (settingName, "window_menu") == 0))
	    {
		char       *value;
		const char *name;

		name = specialOptions[index].gnomeName;
		value = gconf_client_get_string (client, name, &err);

		if (!err && value)
		{
		    CCSSettingActionValue action;
		    memset (&action, 0, sizeof(CCSSettingActionValue));
		    ccsGetAction (setting, &action);

		    if (strcasecmp (value, "disabled") == 0)
		    {
			/* clear out keybinding */
			action.keysym     = NoSymbol;
			action.keyModMask = 0;
		    }
		    if ((strcasecmp (value, "disabled") == 0) ||
			ccsStringToKeyBinding (value, &action))
		    {
			action.buttonModMask = getGnomeMouseButtonModifier ();
			if (strcmp (settingName, "window_menu") == 0)
			    action.button = 3;
			else if (strcmp (pluginName, "resize") == 0)
			    action.button = 2;
			else
			    action.button = 1;

			ccsSetAction (setting, action);
			ret = TRUE;
		    }
		    g_free (value);
		}
	    }
	}
     	break;
    default:
	break;
    }

    if (err)
	g_error_free (err);

    return ret;
}

static Bool
readOption (CCSSetting * setting)
{
    GConfValue *gconfValue = NULL;
    GError     *err = NULL;
    Bool       ret = FALSE;

    KEYNAME;
    PATHNAME;

    /* first check if the key is set, but only if the setting
       type is not action - actions are in a subtree and handled
       separately */
    if (setting->type != TypeAction)
    {
	Bool valid = TRUE;
	gconfValue = gconf_client_get_without_default (client, pathName, &err);
	if (err)
	{
	    g_error_free (err);
	    return FALSE;
	}
	if (!gconfValue)
	    /* value is not set */
	    return FALSE;

	/* setting type sanity check */
	switch (setting->type)
	{
	case TypeString:
    	case TypeMatch:
	case TypeColor:
	    valid = (gconfValue->type == GCONF_VALUE_STRING);
	    break;
	case TypeInt:
	    valid = (gconfValue->type == GCONF_VALUE_INT);
	    break;
	case TypeBool:
	    valid = (gconfValue->type == GCONF_VALUE_BOOL);
	    break;
	case TypeFloat:
	    valid = (gconfValue->type == GCONF_VALUE_FLOAT);
	    break;
	case TypeList:
	    valid = (gconfValue->type == GCONF_VALUE_LIST);
	    break;
	default:
	    break;
	}
	if (!valid)
	{
	    printf ("GConf backend: There is an unsupported value at path %s. "
		    "Settings from this path won't be read. Try to remove "
		    "that value so that operation can continue properly.\n",
		    pathName);
	    return FALSE;
	}
    }

    switch (setting->type)
    {
    case TypeString:
	{
	    const char *value;
	    value = gconf_value_get_string (gconfValue);
	    if (value)
	    {
		ccsSetString (setting, value);
		ret = TRUE;
	    }
	}
	break;
    case TypeMatch:
	{
	    const char * value;
	    value = gconf_value_get_string (gconfValue);
	    if (value)
	    {
		ccsSetMatch (setting, value);
		ret = TRUE;
	    }
	}
	break;
    case TypeInt:
	{
	    int value;
	    value = gconf_value_get_int (gconfValue);

	    ccsSetInt (setting, value);
	    ret = TRUE;
	}
	break;
    case TypeBool:
	{
	    gboolean value;
	    value = gconf_value_get_bool (gconfValue);

	    ccsSetBool (setting, value ? TRUE : FALSE);
	    ret = TRUE;
	}
	break;
    case TypeFloat:
	{
	    double value;
	    value = gconf_value_get_float (gconfValue);

	    ccsSetFloat (setting, (float)value);
    	    ret = TRUE;
	}
	break;
    case TypeColor:
	{
	    const char           *value;
	    CCSSettingColorValue color;
	    value = gconf_value_get_string (gconfValue);

	    if (value && ccsStringToColor (value, &color))
	    {
		ccsSetColor (setting, color);
		ret = TRUE;
	    }
	}
	break;
    case TypeList:
	ret = readListValue (setting, gconfValue);
	break;
    case TypeAction:
	ret = readActionValue (setting, pathName);
	break;
    default:
	printf("GConf backend: attempt to read unsupported setting type %d!\n",
	       setting->type);
	break;
    }

    if (gconfValue)
	gconf_value_free (gconfValue);

    return ret;
}

static void
writeActionValue(CCSSettingActionValue *action,
		 char                  *pathName)
{
    char          *buffer;
    char          itemPath[BUFSIZE];
    CCSStringList edgeList, l;
    GSList        *list = NULL;

    snprintf (itemPath, BUFSIZE, "%s_edge", pathName);
    edgeList = ccsEdgesToStringList (action);
    for (l = edgeList; l; l = l->next)
	list = g_slist_append (list, l->data);

    gconf_client_set_list (client, itemPath, GCONF_VALUE_STRING, list, NULL);
    if (edgeList)
	ccsStringListFree (edgeList, TRUE);
    if (list)
	g_slist_free (list);

    snprintf (itemPath, BUFSIZE, "%s_bell", pathName);
    gconf_client_set_bool (client, itemPath, action->onBell, NULL);

    snprintf (itemPath, BUFSIZE, "%s_edgebutton", pathName);
    gconf_client_set_int (client, itemPath, action->edgeButton, NULL);

    snprintf (itemPath, BUFSIZE, "%s_button", pathName);
    buffer = ccsButtonBindingToString (action);
    if (buffer)
    {
	gconf_client_set_string (client, itemPath, buffer, NULL);
	free (buffer);
    }

    snprintf (itemPath, BUFSIZE, "%s_key", pathName);
    buffer = ccsKeyBindingToString (action);
    if (buffer)
    {
	gconf_client_set_string (client, itemPath, buffer, NULL);
	free (buffer);
    }
}

static void
writeListValue (CCSSetting *setting,
		char       *pathName)
{
    GSList              *valueList = NULL;
    GConfValueType      valueType;
    Bool                freeItems = FALSE;
    CCSSettingValueList list;
    gpointer            data;

    if (!ccsGetList (setting, &list))
	return;

    switch (setting->info.forList.listType)
    {
    case TypeBool:
	{
	    while (list)
	    {
		data = GINT_TO_POINTER (list->data->value.asBool);
		valueList = g_slist_append (valueList, data);
		list = list->next;
	    }
	    valueType = GCONF_VALUE_BOOL;
	}
	break;
    case TypeInt:
	{
	    while (list)
	    {
		data = GINT_TO_POINTER (list->data->value.asInt);
		valueList = g_slist_append(valueList, data);
		list = list->next;
    	    }
	    valueType = GCONF_VALUE_INT;
	}
	break;
    case TypeFloat:
	{
	    float *item;
	    while (list)
	    {
		item = malloc (sizeof (float));
		if (item)
		{
		    *item = list->data->value.asFloat;
		    valueList = g_slist_append (valueList, item);
		}
		list = list->next;
	    }
	    freeItems = TRUE;
	    valueType = GCONF_VALUE_FLOAT;
	}
	break;
    case TypeString:
	{
	    while (list)
	    {
		valueList = g_slist_append(valueList,
		   			   list->data->value.asString);
		list = list->next;
	    }
	    valueType = GCONF_VALUE_STRING;
	}
	break;
    case TypeMatch:
	{
	    while (list)
	    {
		valueList = g_slist_append(valueList,
		   			   list->data->value.asMatch);
		list = list->next;
	    }
	    valueType = GCONF_VALUE_STRING;
	}
	break;
    case TypeColor:
	{
	    char *item;
	    while (list)
	    {
		item = ccsColorToString (&list->data->value.asColor);
		valueList = g_slist_append (valueList, item);
		list = list->next;
	    }
	    freeItems = TRUE;
	    valueType = GCONF_VALUE_STRING;
	}
	break;
    default:
	printf("GConf backend: attempt to write unsupported list type %d!\n",
	       setting->info.forList.listType);
	valueType = GCONF_VALUE_INVALID;
	break;
    }

    if (valueType != GCONF_VALUE_INVALID)
    {
	gconf_client_set_list (client, pathName, valueType, valueList, NULL);

	if (freeItems)
	{
	    GSList *tmpList = valueList;
	    for (; tmpList; tmpList = tmpList->next)
		if (tmpList->data)
		    free (tmpList->data);
	}
    }
    if (valueList)
	g_slist_free (valueList);
}

static void
setGnomeMouseButtonModifier (unsigned int modMask)
{
    char   *modifiers, *currentValue;
    GError *err = NULL;

    modifiers = ccsModifiersToString (modMask);

    if (!modifiers)
	modifiers = strdup ("");
    if (!modifiers)
	return;

    currentValue =
	gconf_client_get_string(client,
				METACITY "/general/mouse_button_modifier",
				&err);
    if (err)
    {
	free (modifiers);
	g_error_free (err);
	return;
    }

    if (!currentValue || (strcmp (currentValue, modifiers) != 0))
	gconf_client_set_string (client,
				 METACITY "/general/mouse_button_modifier",
				 modifiers, NULL);
    if (currentValue)
	g_free (currentValue);

    free (modifiers);
}

static void
setButtonBindingForSetting (CCSContext   *context,
			    const char   *plugin,
			    const char   *setting,
			    unsigned int button,
			    unsigned int buttonModMask)
{
    CCSSetting            *s;
    CCSSettingActionValue action;

    s = findDisplaySettingForPlugin (context, (char*) plugin, (char*) setting);
    if (!s)
	return;

    if (s->type != TypeAction)
	return;

    action = s->value->value.asAction;

    if ((action.button != button) || (action.buttonModMask != buttonModMask))
    {
	action.button = button;
	action.buttonModMask = buttonModMask;

	ccsSetAction (s, action);
    }
}

static void
writeIntegratedOption (CCSContext *context,
		       CCSSetting *setting,
		       int        index)
{
    GError     *err = NULL;
    const char *optionName = specialOptions[index].gnomeName;

    switch (specialOptions[index].type)
    {
    case OptionInt:
	{
	    int newValue, currentValue;
	    if (!ccsGetInt (setting, &newValue))
		break;
	    currentValue = gconf_client_get_int (client, optionName, &err);

	    if (!err && (currentValue != newValue))
		gconf_client_set_int(client, specialOptions[index].gnomeName,
				     newValue, NULL);
	}
	break;
    case OptionBool:
	{
	    Bool     newValue;
	    gboolean currentValue;
	    if (!ccsGetBool (setting, &newValue))
		break;
    	    currentValue = gconf_client_get_bool (client, optionName, &err);

	    if (!err && ((currentValue && !newValue) ||
			 (!currentValue && newValue)))
		gconf_client_set_bool (client, specialOptions[index].gnomeName,
				       newValue, NULL);
	}
	break;
    case OptionString:
	{
	    char  *newValue;
	    gchar *currentValue;
	    if (!ccsGetString (setting, &newValue))
		break;
	    currentValue = gconf_client_get_string (client, optionName, &err);

    	    if (!err && currentValue)
	    {
		if (strcmp (currentValue, newValue) != 0)
		    gconf_client_set_string (client, optionName,
		     			     newValue, NULL);
		g_free (currentValue);
	    }
	}
	break;
    case OptionKey:
	{
	    char  *newValue;
	    gchar *currentValue;

	    newValue = ccsKeyBindingToString (&setting->value->value.asAction);
	    if (newValue && !strlen (newValue))
	    {
		free (newValue);
		newValue = NULL;
	    }
	    if (!newValue)
		newValue = strdup ("disabled");
	    if (newValue)
    	    {
		currentValue = gconf_client_get_string(client,
						       optionName, &err);

		if (!err && currentValue)
		{
		    if (strcmp (currentValue, newValue) != 0)
			gconf_client_set_string (client, optionName,
				 		 newValue, NULL);
    		    g_free (currentValue);
		}
		free (newValue);
	    }
	}
	break;
    case OptionSpecial:
	{
	    const char *settingName = specialOptions[index].settingName;
	    const char *pluginName  = specialOptions[index].pluginName;

	    if (strcmp (settingName, "current_viewport") == 0)
	    {
		Bool currentViewport;

		if (!ccsGetBool (setting, &currentViewport))
		    break;

		gconf_client_set_bool (client, optionName,
				       !currentViewport, NULL);
	    }
	    else if (strcmp (settingName, "fullscreen_visual_bell") == 0)
	    {
		Bool  fullscreen;
		gchar *currentValue, *newValue;
		if (!ccsGetBool (setting, &fullscreen))
		    break;

		newValue = fullscreen ? "fullscreen" : "frame_flash";
		currentValue = gconf_client_get_string (client,
							optionName, &err);
		if (!err && currentValue)
		{
		    if (strcmp (currentValue, newValue) != 0)
			gconf_client_set_string (client, optionName,
						 newValue, NULL);
		    g_free (currentValue);
		}
	    }
	    else if (strcmp (settingName, "click_to_focus") == 0)
	    {
		Bool  clickToFocus;
		gchar *newValue, *currentValue;
		if (!ccsGetBool (setting, &clickToFocus))
		    break;

		newValue = clickToFocus ? "click" : "mouse";
		currentValue = gconf_client_get_string (client,
							optionName, &err);

		if (!err && currentValue)
		{
		    if (strcmp (currentValue, newValue) != 0)
			gconf_client_set_string (client, optionName,
						 newValue, NULL);
		    g_free (currentValue);
		}
	    }
	    else if (((strcmp (settingName, "initiate") == 0) &&
	    	      ((strcmp (pluginName, "move") == 0) ||
		       (strcmp (pluginName, "resize") == 0))) ||
		     (strcmp (settingName, "window_menu") == 0))
	    {
		char         *newValue;
		gchar        *currentValue;
		unsigned int modMask;

		modMask = setting->value->value.asAction.buttonModMask;
		newValue =
		    ccsKeyBindingToString (&setting->value->value.asAction);
		if (newValue && !strlen (newValue))
		{
		    free (newValue);
		    newValue = NULL;
		}
		if (!newValue)
		    newValue = strdup ("disabled");
		if (newValue)
		{
		    currentValue = gconf_client_get_string(client,
							   optionName, &err);

		    if (!err && currentValue)
		    {
			if (strcmp (currentValue, newValue) != 0)
    			    gconf_client_set_string(client, optionName,
						    newValue, NULL);
			g_free (currentValue);
    		    }
		    free (newValue);
		}

		setGnomeMouseButtonModifier (modMask);
		setButtonBindingForSetting (context, "move",
					    "initiate", 1, modMask);
		setButtonBindingForSetting (context, "resize",
					    "initiate", 2, modMask);
		setButtonBindingForSetting (context, "core",
					    "window_menu", 3, modMask);
	    }
	}
     	break;
    }

    if (err)
	g_error_free (err);
}

static void
resetOptionToDefault (CCSSetting * setting)
{
    KEYNAME;
    PATHNAME;

    if (setting->type != TypeAction)
	gconf_client_recursive_unset (client, pathName, 0, NULL);
    else
    {
	char itemPath[BUFSIZE];

	snprintf (itemPath, BUFSIZE, "%s_edge", pathName);
	gconf_client_recursive_unset (client, itemPath, 0, NULL);

	snprintf (itemPath, BUFSIZE, "%s_edgebutton", pathName);
	gconf_client_recursive_unset (client, itemPath, 0, NULL);

	snprintf (itemPath, BUFSIZE, "%s_button", pathName);
	gconf_client_recursive_unset (client, itemPath, 0, NULL);

	snprintf (itemPath, BUFSIZE, "%s_key", pathName);
	gconf_client_recursive_unset (client, itemPath, 0, NULL);

	snprintf (itemPath, BUFSIZE, "%s_bell", pathName);
	gconf_client_recursive_unset (client, itemPath, 0, NULL);
    }

    gconf_client_suggest_sync (client, NULL);
}

static void
writeOption (CCSSetting * setting)
{
    KEYNAME;
    PATHNAME;

    switch (setting->type)
    {
    case TypeString:
	{
	    char *value;
	    if (ccsGetString (setting, &value))
		gconf_client_set_string (client, pathName, value, NULL);
	}
	break;
    case TypeMatch:
	{
	    char *value;
	    if (ccsGetMatch (setting, &value))
		gconf_client_set_string (client, pathName, value, NULL);
	}
    case TypeFloat:
	{
	    float value;
	    if (ccsGetFloat (setting, &value))
		gconf_client_set_float (client, pathName, value, NULL);
	}
	break;
    case TypeInt:
	{
	    int value;
	    if (ccsGetInt (setting, &value))
		gconf_client_set_int (client, pathName, value, NULL);
	}
	break;
    case TypeBool:
	{
	    Bool value;
	    if (ccsGetBool (setting, &value))
		gconf_client_set_bool (client, pathName, value, NULL);
	}
	break;
    case TypeColor:
	{
	    CCSSettingColorValue value;
	    char                 *colString;

	    if (!ccsGetColor (setting, &value))
		break;

	    colString = ccsColorToString (&value);
	    if (!colString)
		break;

	    gconf_client_set_string (client, pathName, colString, NULL);
	    free (colString);
	}
	break;
    case TypeAction:
	{
	    CCSSettingActionValue value;
	    if (!ccsGetAction (setting, &value))
		break;

	    writeActionValue (&value, pathName);
	}
	break;
    case TypeList:
	writeListValue (setting, pathName);
	break;
    default:
	printf("GConf backend: attempt to write unsupported setting type %d\n",
	       setting->type);
	break;
    }
}

static void
updateCurrentProfileName (char *profile)
{
    GConfSchema *schema;
    GConfValue  *value;
    
    schema = gconf_schema_new ();
    if (!schema)
	return;

    value = gconf_value_new (GCONF_VALUE_STRING);
    if (!value)
    {
	gconf_schema_free (schema);
	return;
    }

    gconf_schema_set_type (schema, GCONF_VALUE_STRING);
    gconf_schema_set_locale (schema, "C");
    gconf_schema_set_short_desc (schema, "Current profile");
    gconf_schema_set_long_desc (schema, "Current profile of gconf backend");
    gconf_schema_set_owner (schema, "compizconfig");
    gconf_value_set_string (value, profile);
    gconf_schema_set_default_value (schema, value);

    gconf_client_set_schema (client, COMPIZCONFIG "/current_profile",
			     schema, NULL);

    gconf_schema_free (schema);
    gconf_value_free (value);
}

static char*
getCurrentProfileName (void)
{
    GConfSchema *schema = NULL;

    schema = gconf_client_get_schema (client,
    				      COMPIZCONFIG "/current_profile", NULL);

    if (schema)
    {
	GConfValue *value;
	char       *ret = NULL;

	value = gconf_schema_get_default_value (schema);
	if (value)
	    ret = strdup (gconf_value_get_string (value));
	gconf_schema_free (schema);

	return ret;
    }

    return strdup (DEFAULTPROF);
}

static Bool
checkProfile (CCSContext *context)
{
    char *profile, *lastProfile;

    lastProfile = currentProfile;

    profile = ccsGetProfile (context);
    if (!profile || !strlen (profile))
	currentProfile = strdup (DEFAULTPROF);
    else
	currentProfile = strdup (profile);

    if (strcmp (lastProfile, currentProfile) != 0)
    {
	char *pathName;

	/* copy /apps/compiz tree to profile path */
	asprintf (&pathName, "%s/%s", PROFILEPATH, lastProfile);
	if (pathName)
	{
	    copyGconfTree (context, "/apps/compiz", pathName,
	    		   TRUE, "/schemas/apps/compiz");
	    free (pathName);
	}

	/* reset /apps/compiz tree */
	gconf_client_recursive_unset (client, COMPIZ, 0, NULL);

	/* copy new profile tree to /apps/compiz */
	asprintf (&pathName, "%s/%s", PROFILEPATH, currentProfile);
	if (pathName)
	{
    	    copyGconfTree (context, pathName, COMPIZ, FALSE, NULL);

    	    /* delete the new profile tree in /apps/compizconfig
    	       to avoid user modification in the wrong tree */
    	    copyGconfTree (context, pathName, NULL, TRUE, NULL);
    	    free (pathName);
	}

	/* update current profile name */
	updateCurrentProfileName (currentProfile);
    }

    free (lastProfile);

    return TRUE;
}

static void
processEvents (unsigned int flags)
{
    if (!(flags & ProcessEventsNoGlibMainLoopMask))
    {
	while (g_main_context_pending(NULL))
	    g_main_context_iteration(NULL, FALSE);
    }
}

static Bool
initBackend (CCSContext * context)
{
    g_type_init ();

    conf = gconf_engine_get_default ();
    initClient (context);

    currentProfile = getCurrentProfileName ();

    return TRUE;
}

static Bool
finiBackend (CCSContext * context)
{
    processEvents (0);

    gconf_client_clear_cache (client);
    finiClient ();

    if (currentProfile)
    {
	free (currentProfile);
	currentProfile = NULL;
    }

    gconf_engine_unref (conf);
    conf = NULL;

    processEvents (0);
    return TRUE;
}

static Bool
readInit (CCSContext * context)
{
    return checkProfile (context);
}

static void
readSetting (CCSContext *context,
	     CCSSetting *setting)
{
    Bool status;
    int  index;

    if (ccsGetIntegrationEnabled (context) &&
	isIntegratedOption (setting, &index))
    {
	status = readIntegratedOption (context, setting, index);
    }
    else
	status = readOption (setting);

    if (!status)
	ccsResetToDefault (setting);
}

static Bool
writeInit (CCSContext * context)
{
    return checkProfile (context);
}

static void
writeSetting (CCSContext *context,
	      CCSSetting *setting)
{
    int index;

    if (ccsGetIntegrationEnabled (context) &&
	isIntegratedOption (setting, &index))
    {
	writeIntegratedOption (context, setting, index);
    }
    else if (setting->isDefault)
    {
	resetOptionToDefault (setting);
    }
    else
	writeOption (setting);

}

static Bool
getSettingIsIntegrated (CCSSetting * setting)
{
    if (!ccsGetIntegrationEnabled (setting->parent->context))
	return FALSE;

    if (!isIntegratedOption (setting, NULL))
	return FALSE;

    return TRUE;
}

static Bool
getSettingIsReadOnly (CCSSetting * setting)
{
    /* FIXME */
    return FALSE;
}

static CCSStringList
getExistingProfiles (CCSContext *context)
{
    GSList        *data, *tmp;
    CCSStringList ret = NULL;
    char          *name;

    gconf_client_suggest_sync (client, NULL);
    data = gconf_client_all_dirs (client, PROFILEPATH, NULL);

    for (tmp = data; tmp; tmp = g_slist_next (tmp))
    {
	name = strrchr (tmp->data, '/');
	if (name && (strcmp (name + 1, DEFAULTPROF) != 0))
	    ret = ccsStringListAppend (ret, strdup (name + 1));

	g_free (tmp->data);
    }

    g_slist_free (data);

    name = getCurrentProfileName ();
    if (strcmp (name, DEFAULTPROF) != 0)
	ret = ccsStringListAppend (ret, name);
    else
	free (name);

    return ret;
}

static Bool
deleteProfile (CCSContext *context,
	       char       *profile)
{
    char     path[BUFSIZE];
    gboolean status = FALSE;

    checkProfile (context);

    snprintf (path, BUFSIZE, "%s/%s", PROFILEPATH, profile);

    if (gconf_client_dir_exists (client, path, NULL))
    {
	status =
	    gconf_client_recursive_unset (client, path,
	   				  GCONF_UNSET_INCLUDING_SCHEMA_NAMES,
					  NULL);
	gconf_client_suggest_sync (client, NULL);
    }

    return status;
}

static CCSBackendVTable gconfVTable = {
    "gconf",
    "GConf Configuration Backend",
    "GConf Configuration Backend for libccs",
    TRUE,
    TRUE,
    processEvents,
    initBackend,
    finiBackend,
    readInit,
    readSetting,
    0,
    writeInit,
    writeSetting,
    0,
    getSettingIsIntegrated,
    getSettingIsReadOnly,
    getExistingProfiles,
    deleteProfile
};

CCSBackendVTable *
getBackendInfo (void)
{
    return &gconfVTable;
}



syntax highlighted by Code2HTML, v. 0.9.1