[gpointing-device-settings] Implement dry-run mode.



commit 4e1ca72400eed4bc70f720b3b673f5a6dc206278
Author: Hiroyuki Ikezoe <poincare ikezoe net>
Date:   Sat Mar 13 21:09:59 2010 +0900

    Implement dry-run mode.
    
    Now changing values does not affect until apply  button is clicked.
    
    In dry-run mode, changing values affect immediately, but the changes
    discard after "Finish dry run" button is clicked.

 modules/gpds-mouse-ui.c         |  210 ++++++++++++++++--
 modules/gpds-pointingstick-ui.c |  147 +++++++++++--
 modules/gpds-touchpad-ui.c      |  471 ++++++++++++++++++++++++++++----------
 src/Makefile.am                 |    2 +
 src/gpds-grayed-desktop.c       |  257 +++++++++++++++++++++
 src/gpds-grayed-desktop.h       |   55 +++++
 src/gpds-main-window.c          |  365 ++++++++++++++++++++++++++++--
 src/gpds-xinput-ui.c            |   25 ++
 src/gpds-xinput-ui.h            |    6 +-
 9 files changed, 1351 insertions(+), 187 deletions(-)
---
diff --git a/modules/gpds-mouse-ui.c b/modules/gpds-mouse-ui.c
index 8f07953..de0df1e 100644
--- a/modules/gpds-mouse-ui.c
+++ b/modules/gpds-mouse-ui.c
@@ -57,6 +57,9 @@ GType gpds_mouse_ui_get_type (void) G_GNUC_CONST;
 static void       dispose            (GObject *object);
 static gboolean   is_available       (GpdsUI  *ui, GError **error);
 static gboolean   build              (GpdsUI  *ui, GError **error);
+static gboolean   dry_run            (GpdsUI  *ui, GError **error);
+static void       finish_dry_run     (GpdsUI  *ui, GError **error);
+static gboolean   apply              (GpdsUI  *ui, GError **error);
 static GtkWidget *get_content_widget (GpdsUI  *ui, GError **error);
 static GdkPixbuf *get_icon_pixbuf    (GpdsUI  *ui, GError **error);
 
@@ -72,6 +75,9 @@ gpds_mouse_ui_class_init (GpdsMouseUIClass *klass)
 
     ui_class->is_available       = is_available;
     ui_class->build              = build;
+    ui_class->dry_run            = dry_run;
+    ui_class->finish_dry_run     = finish_dry_run;
+    ui_class->apply              = apply;
     ui_class->get_content_widget = get_content_widget;
     ui_class->get_icon_pixbuf    = get_icon_pixbuf;
 }
@@ -140,34 +146,52 @@ GPDS_XINPUT_UI_DEFINE_SCALE_VALUE_CHANGED_CALLBACK(wheel_emulation_timeout_scale
 GPDS_XINPUT_UI_DEFINE_SCALE_VALUE_CHANGED_CALLBACK(middle_button_timeout_scale,
                                                    GPDS_MOUSE_MIDDLE_BUTTON_TIMEOUT)
 
-static void
-cb_wheel_emulation_button_changed (GtkComboBox *combo, gpointer user_data)
+static gint
+get_wheel_emulation_button (GpdsMouseUI *ui)
 {
-    gint properties[1];
     GtkTreeIter iter;
-    GObject *list_store;
+    GtkTreeModel *model;
+    GObject *combo;
     GValue value = {0};
-    GError *error = NULL;
-    GpdsMouseUI *ui = GPDS_MOUSE_UI(user_data);
     GtkBuilder *builder;
-    GpdsXInput *xinput;
+    gint button;
 
-    if (!gtk_combo_box_get_active_iter(combo, &iter))
-        return;
+    builder = gpds_ui_get_builder(GPDS_UI(ui));
+    combo = gtk_builder_get_object(builder, "wheel_emulation_button");
 
-    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui));
-    if (!xinput)
-        return;
+    if (!GTK_IS_COMBO_BOX(combo))
+        return -1;
 
-    builder = gpds_ui_get_builder(GPDS_UI(ui));
-    list_store = gtk_builder_get_object(builder, "wheel_emulation_button_list_store");
-    gtk_tree_model_get_value(GTK_TREE_MODEL(list_store),
+    if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter))
+        return -1;
+
+    model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
+
+    gtk_tree_model_get_value(model,
                              &iter,
                              0,
                              &value);
-    properties[0] = g_value_get_int(&value);
+    button = g_value_get_int(&value);
     g_value_unset(&value);
 
+    return button;
+}
+
+static void
+set_wheel_emulation_button_to_xinput (GpdsMouseUI *ui)
+{
+    gint button;
+    gint properties[1];
+    GError *error = NULL;
+    GpdsXInput *xinput;
+
+    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui));
+    if (!xinput)
+        return;
+
+    button = get_wheel_emulation_button(ui);
+
+    properties[0] = button;
     if (!gpds_xinput_set_int_properties(xinput,
                                         GPDS_MOUSE_WHEEL_EMULATION_BUTTON,
                                         &error,
@@ -178,8 +202,26 @@ cb_wheel_emulation_button_changed (GtkComboBox *combo, gpointer user_data)
             g_error_free(error);
         }
     }
+}
+
+static void
+set_wheel_emulation_button_to_gconf (GpdsMouseUI *ui)
+{
+    gint button;
+
+    button = get_wheel_emulation_button(ui);
+
+    if (button < 0) {
+        gpds_ui_set_gconf_int(GPDS_UI(ui),
+                              GPDS_MOUSE_WHEEL_EMULATION_BUTTON_KEY,
+                              button);
+    }
+}
 
-    gpds_ui_set_gconf_int(GPDS_UI(ui), GPDS_MOUSE_WHEEL_EMULATION_BUTTON_KEY, properties[0]);
+static void
+cb_wheel_emulation_button_changed (GtkComboBox *combo, gpointer user_data)
+{
+    set_wheel_emulation_button_to_xinput(GPDS_MOUSE_UI(user_data));
 }
 
 static void
@@ -238,16 +280,18 @@ cb_ ## name ## _toggled (GtkToggleButton *button,
     GpdsMouseUI *ui = GPDS_MOUSE_UI(user_data);                                     \
     set_scroll_axes_property(ui);                                                   \
     enable = gtk_toggle_button_get_active(button);                                  \
-    gpds_ui_set_gconf_bool(GPDS_UI(ui), GPDS_MOUSE_ ## KEY_NAME ## _KEY, enable);   \
 }
 
 DEFINE_WHEEL_EMULATION_SCROLL_BUTTON_TOGGLED_CALLBACK(wheel_emulation_vertical, WHEEL_EMULATION_Y_AXIS)
 DEFINE_WHEEL_EMULATION_SCROLL_BUTTON_TOGGLED_CALLBACK(wheel_emulation_horizontal, WHEEL_EMULATION_X_AXIS)
 
 static void
-setup_signals (GpdsUI *ui, GtkBuilder *builder)
+connect_signals (GpdsUI *ui)
 {
     GObject *object;
+    GtkBuilder *builder;
+
+    builder = gpds_ui_get_builder(ui);
 
 #define CONNECT(object_name, signal_name)                               \
     object = gtk_builder_get_object(builder, #object_name);             \
@@ -268,6 +312,33 @@ setup_signals (GpdsUI *ui, GtkBuilder *builder)
 }
 
 static void
+disconnect_signals (GpdsUI *ui)
+{
+    GObject *object;
+    GtkBuilder *builder;
+
+    builder = gpds_ui_get_builder(ui);
+
+#define DISCONNECT(object_name, signal_name)                            \
+    object = gtk_builder_get_object(builder, #object_name);             \
+    g_signal_handlers_disconnect_by_func(                               \
+        object,                                                         \
+        G_CALLBACK(cb_ ## object_name ## _ ## signal_name),             \
+        ui)
+
+    DISCONNECT(middle_button_emulation, toggled);
+    DISCONNECT(middle_button_timeout_scale, value_changed);
+    DISCONNECT(wheel_emulation, toggled);
+    DISCONNECT(wheel_emulation_timeout_scale, value_changed);
+    DISCONNECT(wheel_emulation_button, changed);
+    DISCONNECT(wheel_emulation_inertia_scale, value_changed);
+    DISCONNECT(wheel_emulation_vertical, toggled);
+    DISCONNECT(wheel_emulation_horizontal, toggled);
+
+#undef DISCONNECT
+}
+
+static void
 set_scroll_axes_property_from_preference (GpdsUI *ui)
 {
     GObject *object;
@@ -390,7 +461,7 @@ set_wheel_emulation_button_property_from_preference (GpdsUI *ui)
 }
 
 static void
-setup_current_values (GpdsUI *ui)
+set_gconf_values_to_widget (GpdsUI *ui)
 {
     GpdsXInputUI *xinput_ui = GPDS_XINPUT_UI(ui);
 
@@ -467,8 +538,103 @@ build (GpdsUI  *ui, GError **error)
     g_object_unref(xinput);
 
     gpds_ui_set_gconf_string(ui, GPDS_GCONF_DEVICE_TYPE_KEY, "mouse");
-    setup_current_values(ui);
-    setup_signals(ui, builder);
+    set_gconf_values_to_widget(ui);
+
+    return TRUE;
+}
+
+static void
+set_widget_values_to_xinput (GpdsUI *ui)
+{
+    GObject *object;
+    GtkBuilder *builder;
+
+    builder = gpds_ui_get_builder(ui);
+
+#define SET_TOGGLE_VALUE(property_name, widget_name)                                       \
+    object = gtk_builder_get_object(builder, widget_name);                                 \
+    gpds_xinput_ui_set_xinput_property_from_toggle_button_state(GPDS_XINPUT_UI(ui),        \
+                                                                property_name,             \
+                                                                GTK_TOGGLE_BUTTON(object));
+#define SET_RANGE_VALUE(property_name, widget_name)                                \
+    object = gtk_builder_get_object(builder, widget_name);                         \
+    gpds_xinput_ui_set_xinput_property_from_range_value(GPDS_XINPUT_UI(ui),        \
+                                                        property_name,             \
+                                                        GTK_RANGE(object));
+
+    SET_TOGGLE_VALUE(GPDS_MOUSE_MIDDLE_BUTTON_EMULATION,
+                     "middle_button_emulation");
+    SET_TOGGLE_VALUE(GPDS_MOUSE_WHEEL_EMULATION,
+                     "wheel_emulation");
+
+    SET_RANGE_VALUE(GPDS_MOUSE_MIDDLE_BUTTON_TIMEOUT,
+                    "middle_button_timeout_scale");
+    SET_RANGE_VALUE(GPDS_MOUSE_WHEEL_EMULATION_TIMEOUT,
+                    "wheel_emulation_timeout_scale");
+    SET_RANGE_VALUE(GPDS_MOUSE_WHEEL_EMULATION_INERTIA,
+                    "wheel_emulation_inertia_scale");
+
+    set_wheel_emulation_button_to_xinput(GPDS_MOUSE_UI(ui));
+    set_scroll_axes_property(GPDS_MOUSE_UI(ui));
+}
+
+static void
+set_widget_values_to_gconf (GpdsUI *ui)
+{
+#define SET_GCONF_VALUE(gconf_key_name, widget_name)                \
+    gpds_xinput_ui_set_gconf_value_from_widget(GPDS_XINPUT_UI(ui),  \
+                                               gconf_key_name,      \
+                                               widget_name);
+
+    SET_GCONF_VALUE(GPDS_MOUSE_MIDDLE_BUTTON_EMULATION_KEY,
+                     "middle_button_emulation");
+    SET_GCONF_VALUE(GPDS_MOUSE_WHEEL_EMULATION_KEY,
+                     "wheel_emulation");
+    SET_GCONF_VALUE(GPDS_MOUSE_WHEEL_EMULATION_X_AXIS_KEY,
+                     "wheel_emulation_horizontal");
+    SET_GCONF_VALUE(GPDS_MOUSE_WHEEL_EMULATION_Y_AXIS_KEY,
+                     "wheel_emulation_vertical");
+
+    SET_GCONF_VALUE(GPDS_MOUSE_MIDDLE_BUTTON_TIMEOUT_KEY,
+                    "middle_button_timeout_scale");
+    SET_GCONF_VALUE(GPDS_MOUSE_WHEEL_EMULATION_TIMEOUT_KEY,
+                    "wheel_emulation_timeout_scale");
+    SET_GCONF_VALUE(GPDS_MOUSE_WHEEL_EMULATION_INERTIA_KEY,
+                    "wheel_emulation_inertia_scale");
+
+    set_wheel_emulation_button_to_gconf(GPDS_MOUSE_UI(ui));
+}
+
+static gboolean
+dry_run (GpdsUI *ui, GError **error)
+{
+    gboolean ret;
+
+    if (GPDS_UI_CLASS(gpds_mouse_ui_parent_class)->dry_run)
+        ret = GPDS_UI_CLASS(gpds_mouse_ui_parent_class)->dry_run(ui, error);
+
+    connect_signals(ui);
+
+    set_widget_values_to_xinput(ui);
+
+    return TRUE;
+}
+
+static void
+finish_dry_run(GpdsUI *ui, GError **error)
+{
+    disconnect_signals(ui);
+    set_gconf_values_to_widget(ui);
+
+    if (GPDS_UI_CLASS(gpds_mouse_ui_parent_class)->finish_dry_run)
+        GPDS_UI_CLASS(gpds_mouse_ui_parent_class)->finish_dry_run(ui, error);
+}
+
+static gboolean
+apply (GpdsUI *ui, GError **error)
+{
+    set_widget_values_to_xinput(ui);
+    set_widget_values_to_gconf(ui);
 
     return TRUE;
 }
diff --git a/modules/gpds-pointingstick-ui.c b/modules/gpds-pointingstick-ui.c
index 6028596..cbd9a18 100644
--- a/modules/gpds-pointingstick-ui.c
+++ b/modules/gpds-pointingstick-ui.c
@@ -57,6 +57,9 @@ GType gpds_pointingstick_ui_get_type (void) G_GNUC_CONST;
 static void       dispose            (GObject *object);
 static gboolean   is_available       (GpdsUI  *ui, GError **error);
 static gboolean   build              (GpdsUI  *ui, GError **error);
+static gboolean   dry_run            (GpdsUI  *ui, GError **error);
+static void       finish_dry_run     (GpdsUI  *ui, GError **error);
+static gboolean   apply              (GpdsUI  *ui, GError **error);
 static GtkWidget *get_content_widget (GpdsUI  *ui, GError **error);
 static GdkPixbuf *get_icon_pixbuf    (GpdsUI  *ui, GError **error);
 
@@ -72,6 +75,9 @@ gpds_pointingstick_ui_class_init (GpdsPointingStickUIClass *klass)
 
     ui_class->is_available       = is_available;
     ui_class->build              = build;
+    ui_class->dry_run            = dry_run;
+    ui_class->finish_dry_run     = finish_dry_run;
+    ui_class->apply              = apply;
     ui_class->get_content_widget = get_content_widget;
     ui_class->get_icon_pixbuf    = get_icon_pixbuf;
 }
@@ -117,15 +123,6 @@ dispose (GObject *object)
         G_OBJECT_CLASS(gpds_pointingstick_ui_parent_class)->dispose(object);
 }
 
-static void
-show_error (GError *error)
-{
-    if (!error)
-        return;
-
-    g_print("%s\n", error->message);
-}
-
 GPDS_XINPUT_UI_DEFINE_TOGGLE_BUTTON_CALLBACK(scrolling,
                                              GPDS_POINTINGSTICK_SCROLLING,
                                              "scrolling_box")
@@ -143,16 +140,18 @@ GPDS_XINPUT_UI_DEFINE_SCALE_VALUE_CHANGED_CALLBACK(press_to_select_threshold_sca
                                                    GPDS_POINTINGSTICK_PRESS_TO_SELECT_THRESHOLD)
 
 static void
-setup_signals (GpdsUI *ui, GtkBuilder *builder)
+connect_signals (GpdsUI *ui)
 {
     GObject *object;
+    GtkBuilder *builder;
+
+    builder = gpds_ui_get_builder(ui);
 
 #define CONNECT(object_name, signal_name)                               \
     object = gtk_builder_get_object(builder, #object_name);             \
     g_signal_connect(object, #signal_name,                              \
                      G_CALLBACK(cb_ ## object_name ## _ ## signal_name),\
                      ui)
-
     CONNECT(scrolling, toggled);
     CONNECT(press_to_select, toggled);
     CONNECT(middle_button_timeout_scale, value_changed);
@@ -164,7 +163,32 @@ setup_signals (GpdsUI *ui, GtkBuilder *builder)
 }
 
 static void
-setup_current_values (GpdsUI *ui)
+disconnect_signals (GpdsUI *ui)
+{
+    GObject *object;
+    GtkBuilder *builder;
+
+    builder = gpds_ui_get_builder(ui);
+
+#define DISCONNECT(object_name, signal_name)                            \
+    object = gtk_builder_get_object(builder, #object_name);             \
+    g_signal_handlers_disconnect_by_func(                               \
+        object,                                                         \
+        G_CALLBACK(cb_ ## object_name ## _ ## signal_name),             \
+        ui)
+
+    DISCONNECT(scrolling, toggled);
+    DISCONNECT(press_to_select, toggled);
+    DISCONNECT(middle_button_timeout_scale, value_changed);
+    DISCONNECT(sensitivity_scale, value_changed);
+    DISCONNECT(speed_scale, value_changed);
+    DISCONNECT(press_to_select_threshold_scale, value_changed);
+
+#undef DISCONNECT
+}
+
+static void
+set_gconf_values_to_widget (GpdsUI *ui)
 {
     GpdsXInputUI *xinput_ui = GPDS_XINPUT_UI(ui);
 
@@ -239,8 +263,103 @@ build (GpdsUI  *ui, GError **error)
     g_object_unref(xinput);
 
     gpds_ui_set_gconf_string(ui, GPDS_GCONF_DEVICE_TYPE_KEY, "pointingstick");
-    setup_current_values(ui);
-    setup_signals(ui, builder);
+    set_gconf_values_to_widget(ui);
+
+    return TRUE;
+}
+
+static void
+set_widget_values_to_xinput (GpdsUI *ui)
+{
+    GObject *object;
+    GtkBuilder *builder;
+
+    builder = gpds_ui_get_builder(ui);
+
+#define SET_TOGGLE_VALUE(property_name, widget_name)                                       \
+    object = gtk_builder_get_object(builder, widget_name);                                 \
+    gpds_xinput_ui_set_xinput_property_from_toggle_button_state(GPDS_XINPUT_UI(ui),        \
+                                                                property_name,             \
+                                                                GTK_TOGGLE_BUTTON(object));
+#define SET_RANGE_VALUE(property_name, widget_name)                                \
+    object = gtk_builder_get_object(builder, widget_name);                         \
+    gpds_xinput_ui_set_xinput_property_from_range_value(GPDS_XINPUT_UI(ui),        \
+                                                        property_name,             \
+                                                        GTK_RANGE(object));
+
+    SET_TOGGLE_VALUE(GPDS_POINTINGSTICK_SCROLLING,
+                     "scrolling");
+    SET_TOGGLE_VALUE(GPDS_POINTINGSTICK_PRESS_TO_SELECT,
+                     "press_to_select");
+
+    SET_RANGE_VALUE(GPDS_POINTINGSTICK_SENSITIVITY,
+                    "sensitivity_scale");
+    SET_RANGE_VALUE(GPDS_POINTINGSTICK_SPEED,
+                    "speed_scale");
+    SET_RANGE_VALUE(GPDS_POINTINGSTICK_PRESS_TO_SELECT_THRESHOLD,
+                    "press_to_select_threshold_scale");
+    SET_RANGE_VALUE(GPDS_POINTINGSTICK_MIDDLE_BUTTON_TIMEOUT,
+                    "middle_button_timeout_scale");
+
+#undef SET_TOGGLE_VALUE
+#undef SET_RANGE_VALUE
+}
+
+static void
+set_widget_values_to_gconf (GpdsUI *ui)
+{
+#define SET_GCONF_VALUE(gconf_key_name, widget_name)                \
+    gpds_xinput_ui_set_gconf_value_from_widget(GPDS_XINPUT_UI(ui),  \
+                                               gconf_key_name,      \
+                                               widget_name);
+
+    SET_GCONF_VALUE(GPDS_POINTINGSTICK_SCROLLING_KEY,
+                    "scrolling");
+    SET_GCONF_VALUE(GPDS_POINTINGSTICK_PRESS_TO_SELECT_KEY,
+                     "press_to_select");
+    SET_GCONF_VALUE(GPDS_POINTINGSTICK_MIDDLE_BUTTON_TIMEOUT_KEY,
+                    "middle_button_timeout_scale");
+    SET_GCONF_VALUE(GPDS_POINTINGSTICK_SENSITIVITY_KEY,
+                    "sensitivity_scale");
+    SET_GCONF_VALUE(GPDS_POINTINGSTICK_SPEED_KEY,
+                    "speed_scale");
+    SET_GCONF_VALUE(GPDS_POINTINGSTICK_PRESS_TO_SELECT_THRESHOLD_KEY,
+                    "press_to_select_threshold_scale");
+
+#undef SET_TOGGLE_VALUE
+#undef SET_RANGE_VALUE
+}
+
+static gboolean
+dry_run (GpdsUI *ui, GError **error)
+{
+    gboolean ret;
+
+    if (GPDS_UI_CLASS(gpds_pointingstick_ui_parent_class)->dry_run)
+        ret = GPDS_UI_CLASS(gpds_pointingstick_ui_parent_class)->dry_run(ui, error);
+
+    connect_signals(ui);
+
+    set_widget_values_to_xinput(ui);
+
+    return TRUE;
+}
+
+static void
+finish_dry_run(GpdsUI *ui, GError **error)
+{
+    disconnect_signals(ui);
+    set_gconf_values_to_widget(ui);
+
+    if (GPDS_UI_CLASS(gpds_pointingstick_ui_parent_class)->finish_dry_run)
+        GPDS_UI_CLASS(gpds_pointingstick_ui_parent_class)->finish_dry_run(ui, error);
+}
+
+static gboolean
+apply (GpdsUI *ui, GError **error)
+{
+    set_widget_values_to_xinput(ui);
+    set_widget_values_to_gconf(ui);
 
     return TRUE;
 }
diff --git a/modules/gpds-touchpad-ui.c b/modules/gpds-touchpad-ui.c
index 581b27c..6c12aed 100644
--- a/modules/gpds-touchpad-ui.c
+++ b/modules/gpds-touchpad-ui.c
@@ -56,6 +56,9 @@ GType gpds_touchpad_ui_get_type (void) G_GNUC_CONST;
 static void       dispose            (GObject *object);
 static gboolean   is_available       (GpdsUI  *ui, GError **error);
 static gboolean   build              (GpdsUI  *ui, GError **error);
+static gboolean   dry_run            (GpdsUI  *ui, GError **error);
+static void       finish_dry_run     (GpdsUI  *ui, GError **error);
+static gboolean   apply              (GpdsUI  *ui, GError **error);
 static GtkWidget *get_content_widget (GpdsUI  *ui, GError **error);
 static GdkPixbuf *get_icon_pixbuf    (GpdsUI  *ui, GError **error);
 
@@ -71,6 +74,9 @@ gpds_touchpad_ui_class_init (GpdsTouchpadUIClass *klass)
 
     ui_class->is_available       = is_available;
     ui_class->build              = build;
+    ui_class->dry_run            = dry_run;
+    ui_class->finish_dry_run     = finish_dry_run;
+    ui_class->apply              = apply;
     ui_class->get_content_widget = get_content_widget;
     ui_class->get_icon_pixbuf    = get_icon_pixbuf;
 }
@@ -273,86 +279,104 @@ set_circular_scrolling_trigger_property (GpdsUI *ui, GpdsTouchpadCircularScrolli
     }
 }
 
-#define DEFINE_PALM_DIMENSIONS_SCALE_VALUE_CHANGED_CALLBACK(type, TYPE)                                             \
-static void                                                                                                         \
-cb_palm_detection_ ## type ## _scale_value_changed (GtkRange *range, gpointer user_data)                            \
-{                                                                                                                   \
-    GtkBuilder *builder;                                                                                            \
-    gdouble distance;                                                                                               \
-    GpdsXInput *xinput;                                                                                             \
-    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(user_data));                                                  \
-    if (!xinput)                                                                                                    \
-        return;                                                                                                     \
-    builder = gpds_ui_get_builder(GPDS_UI(user_data));                                                              \
-    set_palm_dimensions_property(xinput, builder);                                                                  \
-    distance = gtk_range_get_value(range);                                                                          \
-    gpds_ui_set_gconf_bool(GPDS_UI(user_data), GPDS_TOUCHPAD_PALM_DETECTION_ ## TYPE ## _KEY, (gint)distance);      \
+#define DEFINE_PALM_DIMENSIONS_SCALE_VALUE_CHANGED_CALLBACK(type)                        \
+static void                                                                              \
+cb_palm_detection_ ## type ## _scale_value_changed (GtkRange *range, gpointer user_data) \
+{                                                                                        \
+    GtkBuilder *builder;                                                                 \
+    GpdsXInput *xinput;                                                                  \
+    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(user_data));                       \
+    if (!xinput)                                                                         \
+        return;                                                                          \
+    builder = gpds_ui_get_builder(GPDS_UI(user_data));                                   \
+    set_palm_dimensions_property(xinput, builder);                                       \
 }
 
-DEFINE_PALM_DIMENSIONS_SCALE_VALUE_CHANGED_CALLBACK(width, WIDTH)
-DEFINE_PALM_DIMENSIONS_SCALE_VALUE_CHANGED_CALLBACK(depth, DEPTH)
-
-#define DEFINE_SCROLLING_SCALE_VALUE_CHANGED_CALLBACK(type, TYPE)                                                   \
-static void                                                                                                         \
-cb_ ## type ## _scrolling_scale_value_changed (GtkRange *range, gpointer user_data)                                 \
-{                                                                                                                   \
-    GtkBuilder *builder;                                                                                            \
-    gdouble distance;                                                                                               \
-    GpdsXInput *xinput;                                                                                             \
-    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(user_data));                                                  \
-    if (!xinput)                                                                                                    \
-        return;                                                                                                     \
-    builder = gpds_ui_get_builder(GPDS_UI(user_data));                                                              \
-    set_scrolling_distance_property(xinput, builder);                                                               \
-    distance = gtk_range_get_value(range);                                                                          \
-    gpds_ui_set_gconf_bool(GPDS_UI(user_data), GPDS_TOUCHPAD_ ## TYPE ## _SCROLLING_DISTANCE_KEY, (gint)distance);  \
+DEFINE_PALM_DIMENSIONS_SCALE_VALUE_CHANGED_CALLBACK(width)
+DEFINE_PALM_DIMENSIONS_SCALE_VALUE_CHANGED_CALLBACK(depth)
+
+#define DEFINE_SCROLLING_SCALE_VALUE_CHANGED_CALLBACK(type)                         \
+static void                                                                         \
+cb_ ## type ## _scrolling_scale_value_changed (GtkRange *range, gpointer user_data) \
+{                                                                                   \
+    GtkBuilder *builder;                                                            \
+    GpdsXInput *xinput;                                                             \
+    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(user_data));                  \
+    if (!xinput)                                                                    \
+        return;                                                                     \
+    builder = gpds_ui_get_builder(GPDS_UI(user_data));                              \
+    set_scrolling_distance_property(xinput, builder);                               \
 }
 
-DEFINE_SCROLLING_SCALE_VALUE_CHANGED_CALLBACK(vertical, VERTICAL)
-DEFINE_SCROLLING_SCALE_VALUE_CHANGED_CALLBACK(horizontal, HORIZONTAL)
-
-#define DEFINE_EDGE_SCROLLING_CALLBACK(type, TYPE)                                          \
-static void                                                                                 \
-cb_ ## type ## _scrolling_toggled (GtkToggleButton *button,                                 \
-                                   gpointer user_data)                                      \
-{                                                                                           \
-    GpdsTouchpadUI *ui = GPDS_TOUCHPAD_UI(user_data);                                       \
-    GtkBuilder *builder;                                                                    \
-    gboolean check;                                                                         \
-    GpdsXInput *xinput;                                                                     \
-    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui));                                 \
-    if (!xinput)                                                                            \
-        return;                                                                             \
-    builder = gpds_ui_get_builder(GPDS_UI(user_data));                                      \
-    set_edge_scrolling_toggle_property(xinput, builder);                                    \
-    check = gtk_toggle_button_get_active(button);                                           \
-    gpds_ui_set_gconf_bool(GPDS_UI(ui), GPDS_TOUCHPAD_ ## TYPE ## _SCROLLING_KEY, check);   \
+DEFINE_SCROLLING_SCALE_VALUE_CHANGED_CALLBACK(vertical)
+DEFINE_SCROLLING_SCALE_VALUE_CHANGED_CALLBACK(horizontal)
+
+#define DEFINE_EDGE_SCROLLING_CALLBACK(type)                \
+static void                                                 \
+cb_ ## type ## _scrolling_toggled (GtkToggleButton *button, \
+                                   gpointer user_data)      \
+{                                                           \
+    GpdsTouchpadUI *ui = GPDS_TOUCHPAD_UI(user_data);       \
+    GtkBuilder *builder;                                    \
+    GpdsXInput *xinput;                                     \
+    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui)); \
+    if (!xinput)                                            \
+        return;                                             \
+    builder = gpds_ui_get_builder(GPDS_UI(user_data));      \
+    set_edge_scrolling_toggle_property(xinput, builder);    \
 }
 
-DEFINE_EDGE_SCROLLING_CALLBACK(vertical, VERTICAL)
-DEFINE_EDGE_SCROLLING_CALLBACK(horizontal, HORIZONTAL)
-DEFINE_EDGE_SCROLLING_CALLBACK(continuous_edge, CONTINUOUS_EDGE)
-
-#define DEFINE_TWO_FINGER_SCROLLING_CALLBACK(direction, DIRECTION)                                          \
-static void                                                                                                 \
-cb_two_finger_ ## direction ## _scrolling_toggled (GtkToggleButton *button,                                 \
-                                                   gpointer user_data)                                      \
-{                                                                                                           \
-    GpdsTouchpadUI *ui = GPDS_TOUCHPAD_UI(user_data);                                                       \
-    GtkBuilder *builder;                                                                                    \
-    gboolean check;                                                                                         \
-    GpdsXInput *xinput;                                                                                     \
-    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui));                                                 \
-    if (!xinput)                                                                                            \
-        return;                                                                                             \
-    builder = gpds_ui_get_builder(GPDS_UI(user_data));                                                      \
-    set_two_finger_scrolling_toggle_property(xinput, builder);                                              \
-    check = gtk_toggle_button_get_active(button);                                                           \
-    gpds_ui_set_gconf_bool(GPDS_UI(ui), GPDS_TOUCHPAD_TWO_FINGER_ ## DIRECTION ## _SCROLLING_KEY, check);   \
+DEFINE_EDGE_SCROLLING_CALLBACK(vertical)
+DEFINE_EDGE_SCROLLING_CALLBACK(horizontal)
+DEFINE_EDGE_SCROLLING_CALLBACK(continuous_edge)
+
+#define DEFINE_TWO_FINGER_SCROLLING_CALLBACK(direction)                     \
+static void                                                                 \
+cb_two_finger_ ## direction ## _scrolling_toggled (GtkToggleButton *button, \
+                                                   gpointer user_data)      \
+{                                                                           \
+    GpdsTouchpadUI *ui = GPDS_TOUCHPAD_UI(user_data);                       \
+    GtkBuilder *builder;                                                    \
+    GpdsXInput *xinput;                                                     \
+    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui));                 \
+    if (!xinput)                                                            \
+        return;                                                             \
+    builder = gpds_ui_get_builder(GPDS_UI(user_data));                      \
+    set_two_finger_scrolling_toggle_property(xinput, builder);              \
 }
 
-DEFINE_TWO_FINGER_SCROLLING_CALLBACK(vertical, VERTICAL)
-DEFINE_TWO_FINGER_SCROLLING_CALLBACK(horizontal, HORIZONTAL)
+DEFINE_TWO_FINGER_SCROLLING_CALLBACK(vertical)
+DEFINE_TWO_FINGER_SCROLLING_CALLBACK(horizontal)
+
+static GpdsTouchpadCircularScrollingTrigger
+get_circular_scrolling_trigger_button_state (GpdsUI *ui)
+{
+    GtkBuilder *builder;
+
+    builder = gpds_ui_get_builder(GPDS_UI(ui));
+
+#define GET_TOGGLE_BUTTON_STATE(name)                                  \
+    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, name)))
+
+    if (GET_TOGGLE_BUTTON_STATE("trigger_top_toggle"))
+        return GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_TOP;
+    else if (GET_TOGGLE_BUTTON_STATE("trigger_top_right_toggle"))
+        return GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_TOP_RIGHT;
+    else if (GET_TOGGLE_BUTTON_STATE("trigger_right_toggle"))
+        return GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_RIGHT;
+    else if (GET_TOGGLE_BUTTON_STATE("trigger_right_bottom_toggle"))
+        return GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_RIGHT_BOTTOM;
+    else if (GET_TOGGLE_BUTTON_STATE("trigger_bottom_toggle"))
+        return GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_BOTTOM;
+    else if (GET_TOGGLE_BUTTON_STATE("trigger_bottom_left_toggle"))
+        return GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_BOTTOM_LEFT;
+    else if (GET_TOGGLE_BUTTON_STATE("trigger_left_toggle"))
+        return GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_LEFT;
+    else if (GET_TOGGLE_BUTTON_STATE("trigger_left_top_toggle"))
+        return GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_LEFT_TOP;
+    return GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_ANY;
+#undef GET_TOGGLE_BUTTON_STATE
+}
 
 static void
 set_circular_scrolling_trigger_button_state (GpdsUI *ui, 
@@ -468,6 +492,30 @@ set_sensitivity_depends_on_use_type (GpdsUI *ui,
 }
 
 static void
+set_touchpad_use_type_property (GpdsUI *ui, GpdsTouchpadUseType use_type)
+{
+    gint properties[1];
+    GError *error = NULL;
+    GpdsXInput *xinput;
+
+    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui));
+    if (!xinput)
+        return;
+
+    properties[0] = use_type;
+    if (!gpds_xinput_set_int_properties(xinput,
+                                        GPDS_TOUCHPAD_OFF,
+                                        &error,
+                                        properties,
+                                        1)) {
+        if (error) {
+            show_error(error);
+            g_error_free(error);
+        }
+    }
+}
+
+static void
 set_touchpad_use_type (GpdsUI *ui, GpdsTouchpadUseType use_type)
 {
     GtkToggleButton *button;
@@ -487,22 +535,14 @@ set_touchpad_use_type (GpdsUI *ui, GpdsTouchpadUseType use_type)
     set_sensitivity_depends_on_use_type(ui, use_type);
 }
 
-static void
-cb_touchpad_use_type_toggled (GtkToggleButton *button, gpointer user_data)
+static GpdsTouchpadUseType
+get_touchpad_use_type (GpdsUI *ui)
 {
-    GpdsUI *ui = GPDS_UI(user_data);
     GpdsTouchpadUseType use_type;
-    gint properties[1];
-    GError *error = NULL;
     GtkToggleButton *disable_touchpad_button;
     GtkToggleButton *disable_tapping_and_scrolling_button;
     GtkBuilder *builder;
     gboolean disable_touchpad, disable_tapping_and_scrolling;
-    GpdsXInput *xinput;
-
-    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui));
-    if (!xinput)
-        return;
 
     builder = gpds_ui_get_builder(ui);
     disable_touchpad_button =
@@ -522,20 +562,19 @@ cb_touchpad_use_type_toggled (GtkToggleButton *button, gpointer user_data)
             GPDS_TOUCHPAD_USE_TYPE_NORMAL;
     }
 
-    properties[0] = use_type;
-    if (!gpds_xinput_set_int_properties(xinput,
-                                        GPDS_TOUCHPAD_OFF,
-                                        &error,
-                                        properties,
-                                        1)) {
-        if (error) {
-            show_error(error);
-            g_error_free(error);
-        }
-    }
+    return use_type;
+}
 
-    gpds_ui_set_gconf_int(ui, GPDS_TOUCHPAD_OFF_KEY, (gint)use_type);
+static void
+cb_touchpad_use_type_toggled (GtkToggleButton *button, gpointer user_data)
+{
+    GpdsUI *ui = GPDS_UI(user_data);
+    GpdsTouchpadUseType use_type;
+
+    use_type = get_touchpad_use_type(ui);
     set_sensitivity_depends_on_use_type(ui, use_type);
+
+    set_touchpad_use_type_property(ui, use_type);
 }
 
 static void
@@ -561,31 +600,28 @@ cb_disable_while_other_device_exists_toggled (GtkToggleButton *button, gpointer
 }
 
 static void
-cb_disable_tapping_toggled (GtkToggleButton *button, gpointer user_data)
+set_tap_time_property (GpdsUI *ui)
 {
-    GtkBuilder *builder;
     GObject *object;
-    GpdsUI *ui = GPDS_UI(user_data);
-    GpdsXInput *xinput;
-    gint properties[1];
-    gboolean disable_tapping;
     gint tap_time;
+    gboolean disable_tapping;
+    gint properties[1];
+    GpdsXInput *xinput;
     GError *error = NULL;
 
-    disable_tapping = gtk_toggle_button_get_active(button);
+    object = gpds_ui_get_ui_object_by_name(ui, "tapping_time_scale");
+    tap_time = gtk_range_get_value(GTK_RANGE(object));
 
-    gpds_ui_set_gconf_bool(ui,
-                           GPDS_TOUCHPAD_DISABLE_TAPPING_KEY,
-                           disable_tapping);
+    object = gpds_ui_get_ui_object_by_name(ui, "disable_tapping");
+    disable_tapping = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object));
+
+    tap_time = disable_tapping ? 0 : tap_time;
 
     xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui));
     if (!xinput)
         return;
 
-    builder = gpds_ui_get_builder(ui);
-    object = gtk_builder_get_object(builder, "tapping_time_scale");
-    tap_time = gtk_range_get_value(GTK_RANGE(object));
-    properties[0] = disable_tapping ? 0 : tap_time;
+    properties[0] = tap_time;
     if (!gpds_xinput_set_int_properties(xinput,
                                         GPDS_TOUCHPAD_TAP_TIME,
                                         &error,
@@ -596,7 +632,20 @@ cb_disable_tapping_toggled (GtkToggleButton *button, gpointer user_data)
             g_error_free(error);
         }
     }
+}
+
+static void
+cb_disable_tapping_toggled (GtkToggleButton *button, gpointer user_data)
+{
+    GpdsUI *ui = GPDS_UI(user_data);
+    gboolean disable_tapping;
+    GtkBuilder *builder;
 
+    disable_tapping = gtk_toggle_button_get_active(button);
+
+    set_tap_time_property(ui);
+
+    builder = gpds_ui_get_builder(ui);
     set_widget_sensitivity(builder, "tapping_frame", !disable_tapping);
 }
 
@@ -645,37 +694,28 @@ cb_move_speed_scale_value_changed (GtkRange *range, gpointer user_data)
 static void
 cb_minimum_speed_scale_value_changed (GtkRange *range, gpointer user_data)
 {
-    gdouble value;
     cb_move_speed_scale_value_changed(range, user_data);
-
-    value = gtk_range_get_value(range);
-    gpds_ui_set_gconf_float(GPDS_UI(user_data), GPDS_TOUCHPAD_MINIMUM_SPEED_KEY, value);
 }
 
 static void
 cb_maximum_speed_scale_value_changed (GtkRange *range, gpointer user_data)
 {
-    gdouble value;
     cb_move_speed_scale_value_changed(range, user_data);
-
-    value = gtk_range_get_value(range);
-    gpds_ui_set_gconf_float(GPDS_UI(user_data), GPDS_TOUCHPAD_MAXIMUM_SPEED_KEY, value);
 }
 
 static void
 cb_acceleration_factor_scale_value_changed (GtkRange *range, gpointer user_data)
 {
-    gdouble value;
     cb_move_speed_scale_value_changed(range, user_data);
-
-    value = gtk_range_get_value(range);
-    gpds_ui_set_gconf_float(GPDS_UI(user_data), GPDS_TOUCHPAD_ACCELERATION_FACTOR_KEY, value);
 }
 
 static void
-setup_signals (GpdsUI *ui, GtkBuilder *builder)
+connect_signals (GpdsUI *ui)
 {
     GObject *object;
+    GtkBuilder *builder;
+
+    builder = gpds_ui_get_builder(ui);
 
 #define CONNECT(object_name, signal_name)                               \
     object = gtk_builder_get_object(builder, #object_name);             \
@@ -724,6 +764,61 @@ setup_signals (GpdsUI *ui, GtkBuilder *builder)
 }
 
 static void
+disconnect_signals (GpdsUI *ui)
+{
+    GObject *object;
+    GtkBuilder *builder;
+
+    builder = gpds_ui_get_builder(ui);
+
+#define DISCONNECT(object_name, signal_name)                            \
+    object = gtk_builder_get_object(builder, #object_name);             \
+    g_signal_handlers_disconnect_by_func(                               \
+        object,                                                         \
+        G_CALLBACK(cb_ ## object_name ## _ ## signal_name),             \
+        ui)
+
+    DISCONNECT(disable_touchpad, toggled);
+    DISCONNECT(disable_tapping_and_scrolling, toggled);
+    DISCONNECT(disable_while_other_device_exists, toggled);
+    DISCONNECT(guest_mouse_off, toggled);
+    DISCONNECT(palm_detection, toggled);
+    DISCONNECT(palm_detection_width_scale, value_changed);
+    DISCONNECT(palm_detection_depth_scale, value_changed);
+    DISCONNECT(locked_drags, toggled);
+    DISCONNECT(locked_drags_timeout_scale, value_changed);
+    DISCONNECT(disable_tapping, toggled);
+    DISCONNECT(tapping_time_scale, value_changed);
+    DISCONNECT(tapping_move_scale, value_changed);
+    DISCONNECT(faster_tapping_check, toggled);
+    DISCONNECT(circular_scrolling, toggled);
+    DISCONNECT(vertical_scrolling, toggled);
+    DISCONNECT(continuous_edge_scrolling, toggled);
+    DISCONNECT(vertical_scrolling_scale, value_changed);
+    DISCONNECT(horizontal_scrolling, toggled);
+    DISCONNECT(horizontal_scrolling_scale, value_changed);
+    DISCONNECT(two_finger_vertical_scrolling, toggled);
+    DISCONNECT(two_finger_horizontal_scrolling, toggled);
+
+    DISCONNECT(minimum_speed_scale, value_changed);
+    DISCONNECT(maximum_speed_scale, value_changed);
+    DISCONNECT(acceleration_factor_scale, value_changed);
+
+    /* cirlular scrolling trigger */
+    DISCONNECT(trigger_top_toggle, button_press_event);
+    DISCONNECT(trigger_top_right_toggle, button_press_event);
+    DISCONNECT(trigger_right_toggle, button_press_event);
+    DISCONNECT(trigger_right_bottom_toggle, button_press_event);
+    DISCONNECT(trigger_bottom_toggle, button_press_event);
+    DISCONNECT(trigger_bottom_left_toggle, button_press_event);
+    DISCONNECT(trigger_left_toggle, button_press_event);
+    DISCONNECT(trigger_left_top_toggle, button_press_event);
+    DISCONNECT(trigger_any_toggle, button_press_event);
+
+#undef DISCONNECT
+}
+
+static void
 set_edge_scrolling_property_from_preference (GpdsUI *ui,
                                              GtkBuilder *builder)
 {
@@ -977,9 +1072,10 @@ set_tapping_time_from_preference (GpdsUI *ui, GtkBuilder *builder)
 }
 
 static void
-setup_current_values (GpdsUI *ui, GtkBuilder *builder)
+set_gconf_values_to_widget (GpdsUI *ui)
 {
     GpdsXInputUI *xinput_ui = GPDS_XINPUT_UI(ui);
+    GtkBuilder *builder;
 
 #define SET_INT_VALUE(PROP_NAME, widget_name)                           \
     gpds_xinput_ui_set_widget_value_from_preference(                    \
@@ -1002,6 +1098,8 @@ setup_current_values (GpdsUI *ui, GtkBuilder *builder)
     SET_INT_VALUE(GPDS_TOUCHPAD_LOCKED_DRAGS_TIMEOUT, "locked_drags_timeout_scale");
     SET_BOOLEAN_VALUE(GPDS_TOUCHPAD_CIRCULAR_SCROLLING, "circular_scrolling");
 
+    builder = gpds_ui_get_builder(ui);
+
     set_tapping_time_from_preference(ui, builder);
     set_edge_scrolling_property_from_preference(ui, builder);
     set_palm_dimensions_property_from_preference(ui, builder);
@@ -1056,8 +1154,133 @@ build (GpdsUI  *ui, GError **error)
     g_object_unref(xinput);
 
     gpds_ui_set_gconf_string(ui, GPDS_GCONF_DEVICE_TYPE_KEY, "touchpad");
-    setup_current_values(ui, builder);
-    setup_signals(ui, builder);
+    set_gconf_values_to_widget(ui);
+
+    return TRUE;
+}
+
+static void
+set_widget_values_to_xinput (GpdsUI *ui)
+{
+    GObject *object;
+    GtkBuilder *builder;
+    GpdsXInput *xinput;
+    GpdsTouchpadUseType use_type;
+    GpdsTouchpadCircularScrollingTrigger trigger;
+
+    builder = gpds_ui_get_builder(ui);
+
+#define SET_TOGGLE_VALUE(property_name, widget_name)                                       \
+    object = gtk_builder_get_object(builder, widget_name);                                 \
+    gpds_xinput_ui_set_xinput_property_from_toggle_button_state(GPDS_XINPUT_UI(ui),        \
+                                                                property_name,             \
+                                                                GTK_TOGGLE_BUTTON(object));
+#define SET_RANGE_VALUE(property_name, widget_name)                                \
+    object = gtk_builder_get_object(builder, widget_name);                         \
+    gpds_xinput_ui_set_xinput_property_from_range_value(GPDS_XINPUT_UI(ui),        \
+                                                        property_name,             \
+                                                        GTK_RANGE(object));
+
+
+    SET_RANGE_VALUE(GPDS_TOUCHPAD_TAP_MOVE, "tapping_move_scale");
+    SET_TOGGLE_VALUE(GPDS_TOUCHPAD_TAP_FAST_TAP, "faster_tapping_check");
+    SET_TOGGLE_VALUE(GPDS_TOUCHPAD_GUEST_MOUSE_OFF, "guest_mouse_off");
+    SET_TOGGLE_VALUE(GPDS_TOUCHPAD_PALM_DETECTION, "palm_detection");
+    SET_TOGGLE_VALUE(GPDS_TOUCHPAD_LOCKED_DRAGS, "locked_drags");
+    SET_RANGE_VALUE(GPDS_TOUCHPAD_LOCKED_DRAGS_TIMEOUT, "locked_drags_timeout_scale");
+    SET_TOGGLE_VALUE(GPDS_TOUCHPAD_CIRCULAR_SCROLLING, "circular_scrolling");
+
+    xinput = gpds_xinput_ui_get_xinput(GPDS_XINPUT_UI(ui));
+    set_two_finger_scrolling_toggle_property(xinput, builder);
+    set_palm_dimensions_property(xinput, builder);
+    set_scrolling_distance_property(xinput, builder);
+    set_edge_scrolling_toggle_property(xinput, builder);
+    set_move_speed_property(xinput, builder);
+
+    use_type = get_touchpad_use_type(ui);
+    set_touchpad_use_type_property(ui, use_type);
+
+    trigger = get_circular_scrolling_trigger_button_state(ui);
+    set_circular_scrolling_trigger_property(ui, trigger);
+
+    set_tap_time_property(ui);
+}
+
+static void
+set_widget_values_to_gconf (GpdsUI *ui)
+{
+    GpdsTouchpadUseType use_type;
+    GpdsTouchpadCircularScrollingTrigger trigger;
+
+#define SET_GCONF_VALUE(gconf_key_name, widget_name)                \
+    gpds_xinput_ui_set_gconf_value_from_widget(GPDS_XINPUT_UI(ui),  \
+                                               gconf_key_name,      \
+                                               widget_name);
+
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_TAP_MOVE_KEY, "tapping_move_scale");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_TAP_TIME_KEY, "tapping_time_scale");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_TAP_FAST_TAP_KEY, "faster_tapping_check");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_GUEST_MOUSE_OFF_KEY, "guest_mouse_off");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_PALM_DETECTION_KEY, "palm_detection");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_LOCKED_DRAGS_KEY, "locked_drags");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_LOCKED_DRAGS_TIMEOUT_KEY, "locked_drags_timeout_scale");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_CIRCULAR_SCROLLING_KEY, "circular_scrolling");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_TWO_FINGER_VERTICAL_SCROLLING_KEY, "two_finger_vertical_scrolling");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_TWO_FINGER_HORIZONTAL_SCROLLING_KEY, "two_finger_horizontal_scrolling");
+
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_PALM_DETECTION_WIDTH_KEY, "palm_detection_width_scale");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_PALM_DETECTION_DEPTH_KEY, "palm_detection_depth_scale");
+
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_VERTICAL_SCROLLING_DISTANCE_KEY, "vertical_scrolling_scale");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_HORIZONTAL_SCROLLING_DISTANCE_KEY, "horizontal_scrolling_scale");
+
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_VERTICAL_SCROLLING_KEY, "vertical_scrolling");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_HORIZONTAL_SCROLLING_KEY, "horizontal_scrolling");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_CONTINUOUS_EDGE_SCROLLING_KEY, "conitnuous_edge_scrolling");
+
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_MINIMUM_SPEED_KEY, "minimum_speed_scale");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_MAXIMUM_SPEED_KEY, "maximum_speed_scale");
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_ACCELERATION_FACTOR_KEY, "acceleration_factor_scale");
+
+    SET_GCONF_VALUE(GPDS_TOUCHPAD_DISABLE_TAPPING_KEY, "disable_tapping");
+
+    use_type = get_touchpad_use_type(ui);
+    gpds_ui_set_gconf_int(ui, GPDS_TOUCHPAD_OFF_KEY, (gint)use_type);
+
+    trigger = get_circular_scrolling_trigger_button_state(ui);
+    gpds_ui_set_gconf_int(ui, GPDS_TOUCHPAD_CIRCULAR_SCROLLING_TRIGGER_KEY, (gint)trigger);
+}
+
+static gboolean
+dry_run (GpdsUI *ui, GError **error)
+{
+    gboolean ret;
+
+    if (GPDS_UI_CLASS(gpds_touchpad_ui_parent_class)->dry_run)
+        ret = GPDS_UI_CLASS(gpds_touchpad_ui_parent_class)->dry_run(ui, error);
+
+    connect_signals(ui);
+
+    set_widget_values_to_xinput(ui);
+
+    return TRUE;
+}
+
+static void
+finish_dry_run(GpdsUI *ui, GError **error)
+{
+    disconnect_signals(ui);
+    set_gconf_values_to_widget(ui);
+
+    if (GPDS_UI_CLASS(gpds_touchpad_ui_parent_class)->finish_dry_run)
+        GPDS_UI_CLASS(gpds_touchpad_ui_parent_class)->finish_dry_run(ui, error);
+}
+
+static gboolean
+apply (GpdsUI *ui, GError **error)
+{
+    set_widget_values_to_xinput(ui);
+    set_widget_values_to_gconf(ui);
 
     return TRUE;
 }
diff --git a/src/Makefile.am b/src/Makefile.am
index 0c704ae..5cf4e5c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,6 +15,8 @@ libgpds_la_SOURCES =			\
 	$(libgpds_public_headers)	\
 	gpds-module.c			\
 	gpds-module.h			\
+	gpds-grayed-desktop.c		\
+	gpds-grayed-desktop.h		\
 	gpds-main-window.c		\
 	gpds-main-window.h		\
 	gpds-ui.c			\
diff --git a/src/gpds-grayed-desktop.c b/src/gpds-grayed-desktop.c
new file mode 100644
index 0000000..6df0799
--- /dev/null
+++ b/src/gpds-grayed-desktop.c
@@ -0,0 +1,257 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ *  Copyright (C) 2010 Hiroyuki Ikezoe  <poincare ikezoe net>
+ *
+ *  This library 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This library 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include "gpds-grayed-desktop.h"
+#include "gpds-utils.h"
+
+enum
+{
+    PROP_0,
+    PROP_MAIN_WINDOW
+};
+
+typedef struct _GpdsGrayedDesktopPriv GpdsGrayedDesktopPriv;
+struct _GpdsGrayedDesktopPriv
+{
+    GtkWidget *main_window;
+    GdkPixbuf *background;
+};
+
+#define GPDS_GRAYED_DESKTOP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), GPDS_TYPE_GRAYED_DESKTOP, GpdsGrayedDesktopPriv))
+
+G_DEFINE_TYPE(GpdsGrayedDesktop, gpds_grayed_desktop, GTK_TYPE_WINDOW)
+
+static GObject *constructor    (GType             type,
+                                guint             n_props,
+                                GObjectConstructParam
+                                                 *props);
+static void     dispose        (GObject          *object);
+static void     set_property   (GObject          *object,
+                                guint             prop_id,
+                                const GValue     *value,
+                                GParamSpec       *pspec);
+static void     get_property   (GObject          *object,
+                                guint             prop_id,
+                                GValue           *value,
+                                GParamSpec       *pspec);
+static gboolean expose         (GtkWidget        *widget,
+					            GdkEventExpose   *event);
+
+static void
+gpds_grayed_desktop_class_init (GpdsGrayedDesktopClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+    gobject_class->constructor  = constructor;
+    gobject_class->dispose      = dispose;
+    gobject_class->set_property = set_property;
+    gobject_class->get_property = get_property;
+
+    widget_class->expose_event = expose;
+
+    g_object_class_install_property
+        (gobject_class,
+         PROP_MAIN_WINDOW,
+         g_param_spec_object("main-window",
+             "Main Window",
+             "The main window",
+             GTK_TYPE_WIDGET,
+             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+    g_type_class_add_private(gobject_class, sizeof(GpdsGrayedDesktopPriv));
+}
+
+static GdkPixbuf *
+create_grayscaled_background (void)
+{
+    GdkWindow *root;
+    GdkPixbuf *pixbuf;
+    gint width, height;
+    guchar *pixels;
+    int rowstride, n_channels;
+    int x, y;
+
+    root = gdk_get_default_root_window();
+    gdk_drawable_get_size(root, &width, &height);
+    pixbuf = gdk_pixbuf_get_from_drawable(NULL,
+                                          root,
+                                          NULL,
+                                          0, 0,
+                                          0, 0,
+                                          width, height);
+
+    pixels = gdk_pixbuf_get_pixels(pixbuf);
+    rowstride = gdk_pixbuf_get_rowstride(pixbuf);
+    n_channels = gdk_pixbuf_get_n_channels(pixbuf);
+
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width * n_channels; x += n_channels) {
+            guchar grayscale;
+            guchar *p;
+
+            p = pixels + y * rowstride + x;
+            grayscale = (p[0] * 11 + p[1] * 16 + p[2] * 5) / 32;
+            p[0] = grayscale;
+            p[1] = grayscale;
+            p[2] = grayscale;
+            p[3] = 1;
+        }
+    }
+
+    return pixbuf;
+}
+
+static void
+cb_main_window_destroyed (GtkWindow *main_window, gpointer user_data)
+{
+    gtk_widget_destroy(GTK_WIDGET(user_data));
+}
+
+static GObject *
+constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+    GObject *object;
+    GObjectClass *klass;
+    GpdsGrayedDesktopPriv *priv;
+    gint x, y;
+
+    klass = G_OBJECT_CLASS(gpds_grayed_desktop_parent_class);
+    object = klass->constructor(type, n_props, props);
+
+    priv = GPDS_GRAYED_DESKTOP_GET_PRIVATE(object);
+    gtk_window_get_position(GTK_WINDOW(priv->main_window), &x, &y);
+    gtk_widget_hide(GTK_WIDGET(priv->main_window));
+    priv->background = create_grayscaled_background();
+    gtk_widget_show_now(GTK_WIDGET(priv->main_window));
+
+    gtk_window_set_transient_for(GTK_WINDOW(priv->main_window), GTK_WINDOW(object));
+    gtk_window_move(GTK_WINDOW(priv->main_window), x, y);
+    g_signal_connect(priv->main_window, "destroy",
+                     G_CALLBACK(cb_main_window_destroyed), object);
+
+    return object;
+}
+
+static gboolean
+expose (GtkWidget *widget, GdkEventExpose *event)
+{
+    GpdsGrayedDesktopPriv *priv = GPDS_GRAYED_DESKTOP_GET_PRIVATE(widget);
+    GdkWindow *window;
+
+    window = gtk_widget_get_window(widget);
+
+    gdk_draw_pixbuf(window,
+                    NULL,
+                    priv->background,
+                    event->area.x, event->area.y,
+                    event->area.x, event->area.y,
+                    event->area.width, event->area.height,
+                    GDK_RGB_DITHER_NONE,
+                    0, 0);
+
+    return FALSE;
+}
+
+static void
+gpds_grayed_desktop_init (GpdsGrayedDesktop *window)
+{
+    GpdsGrayedDesktopPriv *priv = GPDS_GRAYED_DESKTOP_GET_PRIVATE(window);
+
+    priv->main_window = NULL;
+    priv->background = NULL;
+
+    gtk_window_fullscreen(GTK_WINDOW(window));
+    gtk_window_set_keep_above(GTK_WINDOW(window), TRUE);
+}
+
+static void
+dispose (GObject *object)
+{
+    GpdsGrayedDesktopPriv *priv = GPDS_GRAYED_DESKTOP_GET_PRIVATE(object);
+
+    if (priv->main_window) {
+        g_object_unref(priv->main_window);
+        priv->main_window = NULL;
+    }
+
+    if (priv->background) {
+        g_object_unref(priv->background);
+        priv->background = NULL;
+    }
+
+    if (G_OBJECT_CLASS(gpds_grayed_desktop_parent_class)->dispose)
+        G_OBJECT_CLASS(gpds_grayed_desktop_parent_class)->dispose(object);
+}
+
+static void
+set_property (GObject      *object,
+              guint         prop_id,
+              const GValue *value,
+              GParamSpec   *pspec)
+{
+    GpdsGrayedDesktopPriv *priv = GPDS_GRAYED_DESKTOP_GET_PRIVATE(object);
+
+    switch (prop_id) {
+    case PROP_MAIN_WINDOW:
+        priv->main_window = g_value_dup_object(value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+get_property (GObject    *object,
+              guint       prop_id,
+              GValue     *value,
+              GParamSpec *pspec)
+{
+    GpdsGrayedDesktopPriv *priv = GPDS_GRAYED_DESKTOP_GET_PRIVATE(object);
+
+    switch (prop_id) {
+    case PROP_MAIN_WINDOW:
+        g_value_set_object(value, priv->main_window);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+GtkWidget *
+gpds_grayed_desktop_new (GtkWindow *main_window)
+{
+    return GTK_WIDGET(g_object_new(GPDS_TYPE_GRAYED_DESKTOP,
+                                   "type", GTK_WINDOW_TOPLEVEL,
+                                   "type-hint", GDK_WINDOW_TYPE_HINT_NORMAL,
+                                   "main-window", main_window,
+                                   NULL));
+}
+
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff --git a/src/gpds-grayed-desktop.h b/src/gpds-grayed-desktop.h
new file mode 100644
index 0000000..4d55adc
--- /dev/null
+++ b/src/gpds-grayed-desktop.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ *  Copyright (C) 2010 Hiroyuki Ikezoe  <poincare ikezoe net>
+ *
+ *  This library 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This library 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GPDS_GRAYED_DESKTOP_H__
+#define __GPDS_GRAYED_DESKTOP_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GPDS_TYPE_GRAYED_DESKTOP            (gpds_grayed_desktop_get_type ())
+#define GPDS_GRAYED_DESKTOP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GPDS_TYPE_GRAYED_DESKTOP, GpdsGrayedDesktop))
+#define GPDS_GRAYED_DESKTOP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GPDS_TYPE_GRAYED_DESKTOP, GpdsGrayedDesktopClass))
+#define GPDS_IS_GRAYED_DESKTOP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GPDS_TYPE_GRAYED_DESKTOP))
+#define GPDS_IS_GRAYED_DESKTOP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GPDS_TYPE_GRAYED_DESKTOP))
+#define GPDS_GRAYED_DESKTOP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GPDS_TYPE_GRAYED_DESKTOP, GpdsGrayedDesktopClass))
+
+typedef struct _GpdsGrayedDesktop GpdsGrayedDesktop;
+typedef struct _GpdsGrayedDesktopClass GpdsGrayedDesktopClass;
+
+struct _GpdsGrayedDesktop
+{
+    GtkWindow parent;
+};
+
+struct _GpdsGrayedDesktopClass
+{
+    GtkWindowClass parent_class;
+};
+
+GType       gpds_grayed_desktop_get_type (void) G_GNUC_CONST;
+GtkWidget  *gpds_grayed_desktop_new      (GtkWindow *main_window);
+
+G_END_DECLS
+
+#endif /* __GPDS_GRAYED_DESKTOP_H__ */
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff --git a/src/gpds-main-window.c b/src/gpds-main-window.c
index 0e41b96..76375b8 100644
--- a/src/gpds-main-window.c
+++ b/src/gpds-main-window.c
@@ -29,10 +29,12 @@
 #include "gpds-xinput-pointer-info.h"
 #include "gpds-ui.h"
 #include "gpds-utils.h"
+#include "gpds-grayed-desktop.h"
 
 enum {
     DEVICE_NAME_COLUMN,
     ICON_COLUMN,
+    ORIGINAL_ICON_COLUMN,
     N_COLUMNS
 };
 
@@ -40,6 +42,14 @@ typedef struct _GpdsMainWindowPriv GpdsMainWindowPriv;
 struct _GpdsMainWindowPriv
 {
     GList *uis;
+    GtkWidget *background;
+    GtkWidget *dry_run_button;
+    GtkIconView *icon_view;
+    GdkColor *original_text_color;
+    GdkColor *original_base_color;
+    guint timeout_id;
+    gboolean heartbeat;
+    gboolean dry_run;
 };
 
 #define GPDS_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), GPDS_TYPE_MAIN_WINDOW, GpdsMainWindowPriv))
@@ -49,16 +59,30 @@ G_DEFINE_TYPE(GpdsMainWindow, gpds_main_window, GTK_TYPE_DIALOG)
 static void     dispose        (GObject          *object);
 static void     response       (GtkDialog        *dialog,
                                 gint              response);
+static gboolean button_press   (GtkWidget        *widget,
+                                GdkEventButton   *event);
+static gboolean button_release (GtkWidget        *widget,
+                                GdkEventButton   *event);
+static gboolean motion_notify  (GtkWidget        *widget,
+                                GdkEventMotion   *event);
+static gboolean scroll         (GtkWidget        *widget,
+                                GdkEventScroll   *event);
 
 static void
 gpds_main_window_class_init (GpdsMainWindowClass *klass)
 {
     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
     GtkDialogClass *dialog_class = GTK_DIALOG_CLASS(klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
 
     gobject_class->dispose      = dispose;
     dialog_class->response      = response;
 
+    widget_class->motion_notify_event  = motion_notify;
+    widget_class->button_press_event   = button_press;
+    widget_class->button_release_event = button_release;
+    widget_class->scroll_event         = scroll;
+
     g_type_class_add_private(gobject_class, sizeof(GpdsMainWindowPriv));
 }
 
@@ -99,6 +123,7 @@ append_ui (GtkIconView *icon_view, GtkNotebook *notebook,
     gtk_list_store_set(list_store, &iter,
                        DEVICE_NAME_COLUMN, device_name,
                        ICON_COLUMN, pixbuf,
+                       ORIGINAL_ICON_COLUMN, pixbuf,
                        -1);
     gtk_notebook_append_page(notebook, widget, NULL);
     gtk_widget_show_all(widget);
@@ -166,9 +191,9 @@ append_uis (GpdsMainWindow *window, GtkIconView *icon_view, GtkNotebook *noteboo
 
         ui = create_ui_from_pointer_info(info);
         if (ui) {
-            priv->uis = g_list_prepend(priv->uis, ui);
-            loaded_ui_names = g_list_prepend(loaded_ui_names,
-                                             g_strdup(gpds_xinput_pointer_info_get_type_name(info)));
+            priv->uis = g_list_append(priv->uis, ui);
+            loaded_ui_names = g_list_append(loaded_ui_names,
+                                            g_strdup(gpds_xinput_pointer_info_get_type_name(info)));
             append_ui(icon_view, notebook, ui);
         }
     }
@@ -190,7 +215,7 @@ append_uis (GpdsMainWindow *window, GtkIconView *icon_view, GtkNotebook *noteboo
             g_object_unref(ui);
             continue;
         }
-        priv->uis = g_list_prepend(priv->uis, ui);
+        priv->uis = g_list_append(priv->uis, ui);
         append_ui(icon_view, notebook, ui);
     }
     g_list_free(ui_names);
@@ -204,16 +229,13 @@ static void
 cb_selection_changed (GtkIconView *icon_view, gpointer data)
 {
     GtkTreePath *path = NULL;
-    GtkCellRenderer *cell = NULL;
-    GtkTreeModel *model;
     GtkNotebook *notebook = GTK_NOTEBOOK(data);
     gint *indices;
 
-    gtk_icon_view_get_cursor(icon_view, &path, &cell);
+    gtk_icon_view_get_cursor(icon_view, &path, NULL);
 
     if (!path)
         return;
-    model = gtk_icon_view_get_model(icon_view);
     indices = gtk_tree_path_get_indices(path);
     gtk_notebook_set_current_page(notebook, indices[0]);
 
@@ -225,12 +247,20 @@ gpds_main_window_init (GpdsMainWindow *window)
 {
     GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(window);
     GtkWidget *hbox, *vbox;
-    GtkWidget *notebook, *icon_view;
+    GtkWidget *notebook;
     GtkListStore *device_store;
 
     priv->uis = NULL;
+    priv->timeout_id = 0;
+    priv->heartbeat = FALSE;
+    priv->dry_run = FALSE;
+    priv->background = NULL;
+    priv->original_text_color = NULL;
+    priv->original_base_color = NULL;
+
     device_store = gtk_list_store_new(N_COLUMNS,
                                       G_TYPE_STRING,
+                                      GDK_TYPE_PIXBUF,
                                       GDK_TYPE_PIXBUF);
 
     vbox = gtk_dialog_get_content_area(GTK_DIALOG(window));
@@ -239,32 +269,35 @@ gpds_main_window_init (GpdsMainWindow *window)
     gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
     gtk_widget_show(hbox);
 
-    icon_view = gtk_icon_view_new_with_model(GTK_TREE_MODEL(device_store));
+    priv->icon_view = GTK_ICON_VIEW(gtk_icon_view_new_with_model(GTK_TREE_MODEL(device_store)));
     g_object_unref(device_store);
-    gtk_icon_view_set_text_column(GTK_ICON_VIEW(icon_view), DEVICE_NAME_COLUMN);
-    gtk_icon_view_set_pixbuf_column(GTK_ICON_VIEW(icon_view), ICON_COLUMN);
-    gtk_icon_view_set_columns(GTK_ICON_VIEW(icon_view), 1);
-    gtk_icon_view_set_item_width(GTK_ICON_VIEW(icon_view), 128);
-    gtk_icon_view_set_margin(GTK_ICON_VIEW(icon_view), 0);
-    gtk_icon_view_set_column_spacing(GTK_ICON_VIEW(icon_view), 0);
-    gtk_icon_view_set_row_spacing(GTK_ICON_VIEW(icon_view), 0);
-    gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(icon_view), GTK_SELECTION_BROWSE);
-    gtk_box_pack_start(GTK_BOX(hbox), icon_view, TRUE, TRUE, 0);
-    gtk_widget_show(icon_view);
+    gtk_icon_view_set_text_column(priv->icon_view, DEVICE_NAME_COLUMN);
+    gtk_icon_view_set_pixbuf_column(priv->icon_view, ICON_COLUMN);
+    gtk_icon_view_set_columns(priv->icon_view, 1);
+    gtk_icon_view_set_item_width(priv->icon_view, 128);
+    gtk_icon_view_set_margin(priv->icon_view, 0);
+    gtk_icon_view_set_column_spacing(priv->icon_view, 0);
+    gtk_icon_view_set_row_spacing(priv->icon_view, 0);
+    gtk_icon_view_set_selection_mode(priv->icon_view, GTK_SELECTION_BROWSE);
+    gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(priv->icon_view), TRUE, TRUE, 0);
+    gtk_widget_show(GTK_WIDGET(priv->icon_view));
 
     notebook = gtk_notebook_new();
     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
     gtk_box_pack_start(GTK_BOX(hbox), notebook, TRUE, TRUE, 0);
     gtk_widget_show(notebook);
 
-    g_signal_connect(icon_view, "selection-changed",
+    g_signal_connect(priv->icon_view, "selection-changed",
                      G_CALLBACK(cb_selection_changed), notebook);
 
-    append_uis(window, GTK_ICON_VIEW(icon_view), GTK_NOTEBOOK(notebook));
+    append_uis(window, priv->icon_view, GTK_NOTEBOOK(notebook));
 
-    gtk_dialog_add_buttons(GTK_DIALOG(window),
-                           GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
-                           NULL);
+    gtk_dialog_add_button(GTK_DIALOG(window),
+                          GTK_STOCK_APPLY, GTK_RESPONSE_APPLY);
+    priv->dry_run_button = gtk_dialog_add_button(GTK_DIALOG(window),
+                                                 _("Dry _run"),   1);
+    gtk_dialog_add_button(GTK_DIALOG(window),
+                           GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
 
     gtk_window_set_default_icon_name("preferences-desktop-peripherals");
 }
@@ -280,14 +313,296 @@ dispose (GObject *object)
         priv->uis = NULL;
     }
 
+    if (priv->original_text_color) {
+        gdk_color_free(priv->original_text_color);
+        priv->original_text_color = NULL;
+    }
+
+    if (priv->original_base_color) {
+        gdk_color_free(priv->original_base_color);
+        priv->original_base_color = NULL;
+    }
+
     if (G_OBJECT_CLASS(gpds_main_window_parent_class)->dispose)
         G_OBJECT_CLASS(gpds_main_window_parent_class)->dispose(object);
 }
 
 static void
+heartbeat (GtkWidget *widget)
+{
+    GPDS_MAIN_WINDOW_GET_PRIVATE(widget)->heartbeat = TRUE;
+}
+
+static gboolean
+button_press (GtkWidget *widget, GdkEventButton *event)
+{
+    heartbeat(widget);
+
+    return FALSE;
+}
+
+static gboolean
+button_release (GtkWidget *widget, GdkEventButton *event)
+{
+    heartbeat(widget);
+
+    return FALSE;
+}
+
+static gboolean
+motion_notify (GtkWidget *widget, GdkEventMotion *event)
+{
+    heartbeat(widget);
+
+    return FALSE;
+}
+
+static gboolean
+scroll (GtkWidget *widget, GdkEventScroll *event)
+{
+    heartbeat(widget);
+
+    return FALSE;
+}
+
+static gboolean
+restore_original_pixbuf (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+    GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(data);
+    GdkPixbuf *pixbuf = NULL;
+
+    if (gtk_icon_view_path_is_selected(priv->icon_view, path))
+        return FALSE;
+
+    gtk_tree_model_get(model, iter,
+                       ORIGINAL_ICON_COLUMN, &pixbuf,
+                       -1);
+    gtk_list_store_set(GTK_LIST_STORE(model), iter,
+                       ICON_COLUMN, pixbuf,
+                       -1);
+    g_object_unref(pixbuf);
+
+    return FALSE;
+}
+
+static GpdsUI *
+get_current_ui (GpdsMainWindow *window)
+{
+    GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(window);
+    GtkTreePath *path = NULL;
+    GList *selected;
+    gint *indices, index;
+
+    selected = gtk_icon_view_get_selected_items(priv->icon_view);
+
+    path = (GtkTreePath*)g_list_first(selected)->data;
+    indices = gtk_tree_path_get_indices(path);
+    index = indices[0];
+    g_list_foreach(selected, (GFunc)gtk_tree_path_free, NULL);
+    g_list_free(selected);
+
+    return GPDS_UI(g_list_nth_data(priv->uis, index));
+}
+
+static void
+finish_dry_run (GpdsMainWindow *window)
+{
+    GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(window);
+    GtkTreeModel *model;
+    GtkStyle *style;
+    GpdsUI *ui;
+
+    ui = get_current_ui(window);
+    if (!ui)
+        return;
+
+    priv->dry_run = FALSE;
+    gtk_button_set_label(GTK_BUTTON(priv->dry_run_button),
+                         _("Dry _run"));
+
+    gpds_ui_finish_dry_run(ui, NULL);
+
+    gdk_pointer_ungrab(GDK_CURRENT_TIME);
+    if (priv->background) {
+        gtk_widget_destroy(priv->background);
+        priv->background = NULL;
+    }
+    gtk_icon_view_set_selection_mode(priv->icon_view, GTK_SELECTION_BROWSE);
+    model = gtk_icon_view_get_model(priv->icon_view);
+    gtk_tree_model_foreach(model, restore_original_pixbuf, window);
+
+    style = gtk_widget_get_style(GTK_WIDGET(priv->icon_view));
+    gtk_widget_modify_text(GTK_WIDGET(priv->icon_view),
+                           GTK_STATE_NORMAL, priv->original_text_color);
+    gtk_widget_modify_base(GTK_WIDGET(priv->icon_view),
+                           GTK_STATE_NORMAL, priv->original_base_color);
+    gtk_widget_queue_draw(GTK_WIDGET(priv->icon_view));
+
+}
+
+static gboolean
+cb_timeout (gpointer data)
+{
+    GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(data);
+
+    if (!priv->heartbeat) {
+        finish_dry_run(GPDS_MAIN_WINDOW(data));
+        return FALSE;
+    }
+
+    priv->heartbeat = FALSE;
+
+    return TRUE;
+}
+
+static gboolean
+grab_pointer (GpdsMainWindow *window)
+{
+    GdkGrabStatus status;
+    GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(window);
+
+    status = gdk_pointer_grab(gtk_widget_get_window(GTK_WIDGET(window)),
+                              TRUE,
+                              GDK_POINTER_MOTION_MASK |
+                              GDK_BUTTON_PRESS_MASK |
+                              GDK_BUTTON_RELEASE_MASK |
+                              GDK_SCROLL_MASK,
+                              NULL,
+                              NULL,
+                              GDK_CURRENT_TIME);
+    if (status != GDK_GRAB_SUCCESS)
+        return FALSE;
+
+    priv->timeout_id = g_timeout_add_seconds(3, cb_timeout, window);
+
+    return TRUE;
+}
+
+static GdkPixbuf *
+convert_to_grayscaled_pixbuf (GdkPixbuf *src)
+{
+    GdkPixbuf *dest;
+    gint width, height;
+    guchar *pixels;
+    int rowstride, n_channels;
+    int x, y;
+
+    dest = gdk_pixbuf_copy(src);
+    width = gdk_pixbuf_get_width(dest);
+    height = gdk_pixbuf_get_height(dest);
+    rowstride = gdk_pixbuf_get_rowstride(dest);
+    n_channels = gdk_pixbuf_get_n_channels(dest);
+    pixels = gdk_pixbuf_get_pixels(dest);
+
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width * n_channels; x += n_channels) {
+            guchar grayscale;
+            guchar *p;
+
+            p = pixels + y * rowstride + x;
+            grayscale = (p[0] * 11 + p[1] * 16 + p[2] * 5) / 32;
+            p[0] = grayscale;
+            p[1] = grayscale;
+            p[2] = grayscale;
+        }
+    }
+
+    return dest;
+}
+
+static gboolean
+set_grayscaled_pixbuf (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+    GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(data);
+    GdkPixbuf *pixbuf = NULL;
+    GdkPixbuf *gray;
+
+    if (gtk_icon_view_path_is_selected(priv->icon_view, path))
+        return FALSE;
+
+    gtk_tree_model_get(model, iter,
+                       ORIGINAL_ICON_COLUMN, &pixbuf,
+                       -1);
+    gray = convert_to_grayscaled_pixbuf(pixbuf);
+
+    gtk_list_store_set(GTK_LIST_STORE(model), iter,
+                       ICON_COLUMN, gray,
+                       -1);
+    g_object_unref(pixbuf);
+
+    return FALSE;
+}
+
+static void
+start_dry_run (GpdsMainWindow *window)
+{
+    GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(window);
+    GtkTreeModel *model;
+    GtkStyle *style;
+    GpdsUI *ui;
+
+    ui = get_current_ui(window);
+    if (!ui)
+        return;
+    priv->dry_run = TRUE;
+    gpds_ui_dry_run(ui, NULL);
+
+    gtk_button_set_label(GTK_BUTTON(priv->dry_run_button),
+                         _("_Finish dry run"));
+
+    priv->background = gpds_grayed_desktop_new(GTK_WINDOW(window));
+    gtk_widget_show(priv->background);
+
+    model = gtk_icon_view_get_model(priv->icon_view);
+    gtk_tree_model_foreach(model, set_grayscaled_pixbuf, window);
+    gtk_icon_view_set_selection_mode(priv->icon_view, GTK_SELECTION_NONE);
+
+    style = gtk_widget_get_style(GTK_WIDGET(priv->icon_view));
+
+    if (priv->original_text_color)
+        gdk_color_free(priv->original_text_color);
+    priv->original_text_color = gdk_color_copy(&style->text[GTK_STATE_NORMAL]);
+    if (priv->original_base_color)
+        gdk_color_free(priv->original_base_color);
+    priv->original_base_color = gdk_color_copy(&style->base[GTK_STATE_NORMAL]);
+
+    gtk_widget_modify_text(GTK_WIDGET(priv->icon_view),
+                           GTK_STATE_NORMAL, &style->text[GTK_STATE_INSENSITIVE]);
+    gtk_widget_modify_base(GTK_WIDGET(priv->icon_view),
+                           GTK_STATE_NORMAL, &style->base[GTK_STATE_INSENSITIVE]);
+    gtk_widget_queue_draw(GTK_WIDGET(priv->icon_view));
+
+    grab_pointer(window);
+}
+
+static void
+apply (GpdsUI *ui, gpointer user_data)
+{
+    gpds_ui_apply(ui, NULL);
+}
+
+static void
+apply_all (GpdsMainWindow *window)
+{
+    GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(window);
+
+    g_list_foreach(priv->uis, (GFunc)apply, NULL);
+}
+
+static void
 response (GtkDialog *dialog, gint response_id)
 {
+    GpdsMainWindowPriv *priv = GPDS_MAIN_WINDOW_GET_PRIVATE(dialog);
     switch (response_id) {
+    case 1:
+        if (!priv->dry_run)
+            start_dry_run(GPDS_MAIN_WINDOW(dialog));
+        else
+            finish_dry_run(GPDS_MAIN_WINDOW(dialog));
+        break;
+    case GTK_RESPONSE_APPLY:
+        apply_all(GPDS_MAIN_WINDOW(dialog));
+        break;
     case GTK_RESPONSE_DELETE_EVENT:
     case GTK_RESPONSE_CLOSE:
     default:
diff --git a/src/gpds-xinput-ui.c b/src/gpds-xinput-ui.c
index 00d19b3..c8a17c3 100644
--- a/src/gpds-xinput-ui.c
+++ b/src/gpds-xinput-ui.c
@@ -278,6 +278,31 @@ gpds_xinput_ui_set_xinput_property_from_range_value (GpdsXInputUI *ui,
 }
 
 void
+gpds_xinput_ui_set_gconf_value_from_widget (GpdsXInputUI *ui,
+                                            const gchar *gconf_key_name,
+                                            const gchar *widget_name)
+{
+    GObject *widget;
+
+    widget = gpds_ui_get_ui_object_by_name(GPDS_UI(ui), widget_name);
+
+    if (!widget)
+        return;
+
+    if (GTK_IS_RANGE(widget)) {
+        gdouble value;
+        value = gtk_range_get_value(GTK_RANGE(widget));
+
+        gpds_ui_set_gconf_int(GPDS_UI(ui), gconf_key_name, (gint)value);
+    } else if (GTK_IS_TOGGLE_BUTTON(widget)) {
+        gboolean value;
+        value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+
+        gpds_ui_set_gconf_bool(GPDS_UI(ui), gconf_key_name, value);
+    }
+}
+
+void
 gpds_xinput_ui_set_toggle_button_state_from_preference (GpdsXInputUI *ui,
                                                         gint property,
                                                         const gchar *gconf_key_name,
diff --git a/src/gpds-xinput-ui.h b/src/gpds-xinput-ui.h
index 0b8c9bf..0dcb6ad 100644
--- a/src/gpds-xinput-ui.h
+++ b/src/gpds-xinput-ui.h
@@ -74,6 +74,10 @@ void        gpds_xinput_ui_set_toggle_button_state_from_preference
                                              gint property,
                                              const gchar *gconf_key_name,
                                              const gchar *button_name);
+void        gpds_xinput_ui_set_gconf_value_from_widget
+                                            (GpdsXInputUI *ui,
+                                             const gchar *gconf_key_name,
+                                             const gchar *widget_name);
 void        gpds_xinput_ui_set_widget_value_from_preference
                                             (GpdsXInputUI *ui,
                                              gint property,
@@ -92,7 +96,6 @@ cb_ ## function_name ## _toggled (GtkToggleButton *button,
                                                                 PROPERTY_NAME,                          \
                                                                 button);                                \
     enable = gtk_toggle_button_get_active(button);                                                      \
-    gpds_ui_set_gconf_bool(GPDS_UI(user_data), PROPERTY_NAME ## _KEY, enable);                          \
     builder = gpds_ui_get_builder(GPDS_UI(user_data));                                                  \
     if (!depend_widget_name)                                                                            \
         return;                                                                                         \
@@ -111,7 +114,6 @@ cb_ ## function_name ## _value_changed (GtkRange *range, gpointer user_data)
                                                         PROPERTY_NAME,                      \
                                                         range);                             \
     value = gtk_range_get_value(range);                                                     \
-    gpds_ui_set_gconf_int(GPDS_UI(user_data), PROPERTY_NAME ## _KEY, (gint)value);          \
 }
 
 G_END_DECLS



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]