/*
        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; You may only use version 2 of the License,
        you have no option to use any other 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.

        You should have received a copy of the GNU General Public License
        along with this program; if not, write to the Free Software
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

        GNOME Control Center - (c) 1998 Jonathan Blandford <jrb@redhat.com>
        xfce4 mcs plugin   - (c) 2002 Olivier Fourdan

 */

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

#include <X11/Xlib.h>

#ifdef USE_XKB
#include <X11/XKBlib.h>
#endif

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>

#include <libxfce4mcs/mcs-common.h>
#include <libxfce4mcs/mcs-manager.h>
#include <libxfce4util/libxfce4util.h>
#include <xfce-mcs-manager/manager-plugin.h>
#include <libxfcegui4/libxfcegui4.h>
#include <libxfcegui4/netk-trayicon.h>

#include "mouse-plugin-internal.h"

#define DEFAULT_ACCELERATION    2
#define DEFAULT_THRESHOLD       4
#define DEFAULT_DENOMINATOR     1
#define DEFAULT_DBL_CLICKTIME   300
#define DEFAULT_DND_THRESHOLD   8

#define ACCEL_MIN       1
#define ACCEL_MAX       30
#define THRESH_MIN      1
#define THRESH_MAX      20
#define DND_THRESH_MIN  1
#define DND_THRESH_MAX  50
#define DBL_CLICK_MIN   100
#define DBL_CLICK_MAX   2000
#define DEFAULT_ICON_SIZE 48
#define DEFAULT_PTR_MAP_SIZE 128

#ifdef USE_XKB
static const char * mouse_tray_image_xpm[] = {
"16 16 3 1",
"       c None",
".      c #000000",
"+      c #FFFFFF",
"                ",
"     ......     ",
"    .+.++.+.    ",
"    .+.++.+.    ",
"   .++.++.++.   ",
"   .++.++.++.   ",
"   ..........   ",
"   .++++++++.   ",
"   .++++++++.   ",
"   .++++++++.   ",
"   .++++++++.   ",
"   .++++++++.   ",
"   .++++++++.   ",
"    .++++++.    ",
"     ......     ",
"                "};
#endif

static void create_channel(McsPlugin * mcs_plugin);
static void run_dialog(McsPlugin * mcs_plugin);

static gboolean is_running = FALSE;
static gboolean right_handed = TRUE;
static int dbl_clicktime = DEFAULT_DBL_CLICKTIME;
static int dnd_threshold = DEFAULT_DND_THRESHOLD;
static int acceleration = DEFAULT_ACCELERATION;
static int threshold = DEFAULT_THRESHOLD;
static int denominator = DEFAULT_DENOMINATOR;

#ifdef USE_XKB
static gboolean mouse_key = FALSE;
static int mouse_keys_delay = 200;
static int mouse_keys_interval = 200;
static int mouse_keys_ttm = 200;
static int mouse_keys_max_speed = 200;
static gboolean xkbpresent = FALSE;
GtkWidget *mouse_tray_icon = NULL;
GtkWidget *mouse_tray_image = NULL;
#endif

static void get_mouse_values(int *accel_return, int *denom_return, int *thresh_return)
{
    gdk_flush();
    gdk_error_trap_push();

    /* Reset */
    XChangePointerControl(GDK_DISPLAY(), True, True, -1, -1, -1);
    gdk_flush();
    XGetPointerControl(GDK_DISPLAY(), accel_return, denom_return, thresh_return);

    /* Paranoid check */
    if (*denom_return < 1)
    {
        *denom_return = 1;
    }

    gdk_flush();
    gdk_error_trap_pop();
}

static void set_mouse_values(gboolean right_handed, int accel, int threshold)
{
    unsigned char *buttons;
    gint n_buttons, i;
    gint idx_1 = 0, idx_3 = 1;

    gdk_flush();
    gdk_error_trap_push();

    XChangePointerControl(GDK_DISPLAY(), True, True, accel, denominator, threshold);

    buttons = g_alloca (DEFAULT_PTR_MAP_SIZE);
    n_buttons = XGetPointerMapping (GDK_DISPLAY (), buttons, DEFAULT_PTR_MAP_SIZE);

    if (n_buttons > DEFAULT_PTR_MAP_SIZE)
    {
        buttons = g_alloca (n_buttons);
        n_buttons = XGetPointerMapping (GDK_DISPLAY (), buttons, n_buttons);
    }

    for (i = 0; i < n_buttons; i++)
    {
        if (buttons[i] == 1)
        {
            idx_1 = i;
        }
        else if (buttons[i] == ((n_buttons < 3) ? 2 : 3))
        {
            idx_3 = i;
        }
    }

    if ((!right_handed && (idx_1 < idx_3)) || (right_handed && (idx_1 > idx_3)))
    {
        buttons[idx_1] = ((n_buttons < 3) ? 2 : 3);
        buttons[idx_3] = 1;
    }

    XSetPointerMapping (GDK_DISPLAY (), buttons, n_buttons);
}

static void cb_dialog_response(GtkWidget * dialog, gint response_id)
{
    if(response_id == GTK_RESPONSE_HELP)
    {
        g_message("HELP: TBD");
    }
    else
    {
        is_running = FALSE;
        gtk_widget_destroy(dialog);
    }
}

static void cb_righthandled_changed(GtkWidget * dialog, gpointer user_data)
{
    Itf *itf = (Itf *) user_data;
    McsPlugin *mcs_plugin = itf->mcs_plugin;

    right_handed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(itf->radiobutton2));
    set_mouse_values(right_handed, acceleration, threshold);

    mcs_manager_set_int(mcs_plugin->manager, "Mouse/RightHanded", CHANNEL2, right_handed ? 1 : 0);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL2);
    mouse_plugin_write_options(mcs_plugin);
}

static void cb_acceleration_changed(GtkWidget * dialog, gpointer user_data)
{
    Itf *itf = (Itf *) user_data;
    McsPlugin *mcs_plugin = itf->mcs_plugin;

    acceleration = (int)gtk_range_get_value(GTK_RANGE(itf->hscale1));
    set_mouse_values(right_handed, acceleration, threshold);

    mcs_manager_set_int(mcs_plugin->manager, "Mouse/Acceleration", CHANNEL2, acceleration);

    mcs_manager_notify(mcs_plugin->manager, CHANNEL2);
    mouse_plugin_write_options(mcs_plugin);
}

static void cb_threshold_changed(GtkWidget * dialog, gpointer user_data)
{
    Itf *itf = (Itf *) user_data;
    McsPlugin *mcs_plugin = itf->mcs_plugin;

    threshold = (int)gtk_range_get_value(GTK_RANGE(itf->hscale2));
    set_mouse_values(right_handed, acceleration, threshold);

    mcs_manager_set_int(mcs_plugin->manager, "Mouse/Threshold", CHANNEL2, threshold);

    mcs_manager_notify(mcs_plugin->manager, CHANNEL2);
    mouse_plugin_write_options(mcs_plugin);
}

static void cb_dnd_threshold_changed(GtkWidget * dialog, gpointer user_data)
{
    Itf *itf = (Itf *) user_data;
    McsPlugin *mcs_plugin = itf->mcs_plugin;

    dnd_threshold = (int)gtk_range_get_value(GTK_RANGE(itf->hscale3));

    mcs_manager_set_int(mcs_plugin->manager, "Net/DndDragThreshold", CHANNEL1, dnd_threshold);

    mcs_manager_notify(mcs_plugin->manager, CHANNEL1);
    mouse_plugin_write_options(mcs_plugin);
}

static void cb_dbl_clicktime_changed(GtkWidget * dialog, gpointer user_data)
{
    Itf *itf = (Itf *) user_data;
    McsPlugin *mcs_plugin = itf->mcs_plugin;

    dbl_clicktime = (int)gtk_range_get_value(GTK_RANGE(itf->hscale4));

    mcs_manager_set_int(mcs_plugin->manager, "Net/DoubleClickTime", CHANNEL1, dbl_clicktime);

    mcs_manager_notify(mcs_plugin->manager, CHANNEL1);
    mouse_plugin_write_options(mcs_plugin);
}

#ifdef USE_XKB
void
create_accessx_page(Itf *dialog)
{
    GtkWidget *label;
    GtkWidget *vbox;
    GtkWidget *hbox;
    GtkWidget *main_vbox = gtk_vbox_new(FALSE, 5);

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_widget_show (vbox);
    gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);

    dialog->checkbutton_mouse = gtk_check_button_new_with_mnemonic(_("Enable mouse emulation"));
    gtk_widget_show(dialog->checkbutton_mouse);
    gtk_box_pack_start(GTK_BOX(vbox), dialog->checkbutton_mouse, FALSE, FALSE, 0);
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->checkbutton_mouse), mouse_key);

    hbox = gtk_hbox_new(TRUE, 5);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    gtk_widget_show(hbox);

    vbox = gtk_vbox_new(FALSE, 5);
    gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
    gtk_widget_show(vbox);

    label = gtk_label_new (_("Delay :"));
    gtk_widget_show (label);
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
    gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);

    dialog->scale_mouse_keys_delay = gtk_hscale_new (GTK_ADJUSTMENT (gtk_adjustment_new (mouse_keys_delay, 10, 500, 10, 10, 0)));
    gtk_widget_show(dialog->scale_mouse_keys_delay);
    gtk_scale_set_draw_value (GTK_SCALE (dialog->scale_mouse_keys_delay), FALSE);
    gtk_range_set_update_policy (GTK_RANGE (dialog->scale_mouse_keys_delay), GTK_UPDATE_DISCONTINUOUS);
    gtk_box_pack_start(GTK_BOX(vbox), dialog->scale_mouse_keys_delay, FALSE, TRUE, 0);
    gtk_widget_set_sensitive(dialog->scale_mouse_keys_delay, mouse_key);

    label = gtk_label_new (_("Interval :"));
    gtk_widget_show (label);
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
    gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);

    dialog->scale_mouse_keys_interval = gtk_hscale_new (GTK_ADJUSTMENT (gtk_adjustment_new (mouse_keys_interval, 10, 500, 10, 10, 0)));
    gtk_widget_show(dialog->scale_mouse_keys_interval);
    gtk_scale_set_draw_value (GTK_SCALE (dialog->scale_mouse_keys_interval), FALSE);
    gtk_range_set_update_policy (GTK_RANGE (dialog->scale_mouse_keys_interval), GTK_UPDATE_DISCONTINUOUS);
    gtk_box_pack_start(GTK_BOX(vbox), dialog->scale_mouse_keys_interval, FALSE, TRUE, 0);
    gtk_widget_set_sensitive(dialog->scale_mouse_keys_interval, mouse_key);

    vbox = gtk_vbox_new(FALSE, 5);
    gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
    gtk_widget_show(vbox);

    label = gtk_label_new (_("Time to max :"));
    gtk_widget_show (label);
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
    gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);

    dialog->scale_mouse_keys_ttm= gtk_hscale_new (GTK_ADJUSTMENT (gtk_adjustment_new (mouse_keys_ttm, 10, 500, 10, 10, 0)));
    gtk_widget_show(dialog->scale_mouse_keys_ttm);
    gtk_scale_set_draw_value (GTK_SCALE (dialog->scale_mouse_keys_ttm), FALSE);
    gtk_range_set_update_policy (GTK_RANGE (dialog->scale_mouse_keys_ttm), GTK_UPDATE_DISCONTINUOUS);
    gtk_box_pack_start(GTK_BOX(vbox), dialog->scale_mouse_keys_ttm, FALSE, TRUE, 0);
    gtk_widget_set_sensitive(dialog->scale_mouse_keys_ttm, mouse_key);

    label = gtk_label_new (_("Max speed:"));
    gtk_widget_show (label);
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
    gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);

    dialog->scale_mouse_keys_max_speed = gtk_hscale_new (GTK_ADJUSTMENT (gtk_adjustment_new (mouse_keys_max_speed, 10, 500, 10, 10, 0)));
    gtk_widget_show(dialog->scale_mouse_keys_max_speed);
    gtk_scale_set_draw_value (GTK_SCALE (dialog->scale_mouse_keys_max_speed), FALSE);
    gtk_range_set_update_policy (GTK_RANGE (dialog->scale_mouse_keys_max_speed), GTK_UPDATE_DISCONTINUOUS);
    gtk_box_pack_start(GTK_BOX(vbox), dialog->scale_mouse_keys_max_speed, FALSE, TRUE, 0);
    gtk_widget_set_sensitive(dialog->scale_mouse_keys_max_speed, mouse_key);

    dialog->accessx_page = main_vbox;
}
#endif

Itf *create_mouse_dialog(McsPlugin * mcs_plugin)
{
    Itf *dialog;
    GtkSizeGroup *sg;
    GtkWidget *frame_bin;
    GtkWidget *label;

    dialog = g_new(Itf, 1);

    dialog->mcs_plugin = mcs_plugin;
    dialog->radiobutton1_group = NULL;

    dialog->mouse_dialog = xfce_titled_dialog_new();

    gtk_window_set_icon_name(GTK_WINDOW(dialog->mouse_dialog), "xfce4-mouse");

    gtk_window_set_title(GTK_WINDOW(dialog->mouse_dialog), _("Mouse Preferences"));
    gtk_dialog_set_has_separator(GTK_DIALOG(dialog->mouse_dialog), FALSE);

    dialog->dialog_vbox1 = GTK_DIALOG(dialog->mouse_dialog)->vbox;
    gtk_widget_show(dialog->dialog_vbox1);

    dialog->notebook_vbox = gtk_vbox_new(FALSE, 6);
    gtk_container_set_border_width(GTK_CONTAINER(dialog->notebook_vbox), 6);
    gtk_widget_show(dialog->notebook_vbox);
    gtk_box_pack_start(GTK_BOX(dialog->dialog_vbox1), dialog->notebook_vbox, TRUE, TRUE, 0);

    dialog->notebook = gtk_notebook_new();
    gtk_widget_show(dialog->notebook);
    gtk_box_pack_start(GTK_BOX(dialog->notebook_vbox), dialog->notebook, TRUE, TRUE, 0);

    label = gtk_label_new(_("Behavior"));
    gtk_widget_show(label);
    dialog->vbox1 = gtk_vbox_new(FALSE, 6);
    gtk_container_set_border_width(GTK_CONTAINER(dialog->vbox1), 6);
    gtk_widget_show(dialog->vbox1);
    gtk_notebook_append_page(GTK_NOTEBOOK(dialog->notebook), dialog->vbox1, label);

    sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);

    /* button order */
    dialog->frame1 = xfce_create_framebox (_("Button settings"), &frame_bin);
    gtk_widget_show(dialog->frame1);
    gtk_box_pack_start(GTK_BOX(dialog->vbox1), dialog->frame1, FALSE, TRUE, 0);

    dialog->hbox2 = gtk_hbox_new(FALSE, 0);
    gtk_widget_show(dialog->hbox2);
    gtk_container_add (GTK_CONTAINER (frame_bin), dialog->hbox2);

    dialog->radiobutton1 = gtk_radio_button_new_with_mnemonic(NULL, _("Left Handed"));
    gtk_widget_show(dialog->radiobutton1);
    gtk_box_pack_start(GTK_BOX(dialog->hbox2), dialog->radiobutton1, FALSE, FALSE, 0);
    gtk_radio_button_set_group(GTK_RADIO_BUTTON(dialog->radiobutton1), dialog->radiobutton1_group);
    dialog->radiobutton1_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->radiobutton1));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->radiobutton1), !right_handed);

    dialog->radiobutton2 = gtk_radio_button_new_with_mnemonic(NULL, _("Right handed"));
    gtk_widget_show(dialog->radiobutton2);
    gtk_box_pack_start(GTK_BOX(dialog->hbox2), dialog->radiobutton2, FALSE, FALSE, 0);
    gtk_radio_button_set_group(GTK_RADIO_BUTTON(dialog->radiobutton2), dialog->radiobutton1_group);
    dialog->radiobutton1_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->radiobutton2));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->radiobutton2), right_handed);

    /* motion settings */
    dialog->frame2 = xfce_create_framebox (_("Motion settings"), &frame_bin);
    gtk_widget_show(dialog->frame2);
    gtk_box_pack_start(GTK_BOX(dialog->vbox1), dialog->frame2, FALSE, TRUE, 0);

    dialog->table1 = gtk_table_new(2, 4, FALSE);
    gtk_widget_show(dialog->table1);
    gtk_container_add (GTK_CONTAINER (frame_bin), dialog->table1);

    dialog->label3 = gtk_label_new(_("Acceleration :"));
    gtk_widget_show(dialog->label3);
    gtk_table_attach(GTK_TABLE(dialog->table1), dialog->label3, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label3), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label3), 0, 0.5);
    gtk_size_group_add_widget (sg, dialog->label3);

    dialog->label4 = xfce_create_small_label(_("Slow"));
    gtk_widget_show(dialog->label4);
    gtk_table_attach(GTK_TABLE(dialog->table1), dialog->label4, 1, 2, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 6, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label4), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label4), 1, 0.5);

    dialog->hscale1 = gtk_hscale_new(GTK_ADJUSTMENT(gtk_adjustment_new(acceleration, ACCEL_MIN, ACCEL_MAX, 1, 5, 0)));
    gtk_widget_show(dialog->hscale1);
    gtk_table_attach(GTK_TABLE(dialog->table1), dialog->hscale1, 2, 3, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0);
    gtk_scale_set_draw_value(GTK_SCALE(dialog->hscale1), FALSE);
    gtk_scale_set_digits(GTK_SCALE(dialog->hscale1), 0);
    gtk_range_set_update_policy(GTK_RANGE(dialog->hscale1), GTK_UPDATE_DISCONTINUOUS);

    dialog->label5 = xfce_create_small_label(_("Fast"));
    gtk_widget_show(dialog->label5);
    gtk_table_attach(GTK_TABLE(dialog->table1), dialog->label5, 3, 4, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label5), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label5), 0, 0.5);

    dialog->label6 = gtk_label_new(_("Threshold :"));
    gtk_widget_show(dialog->label6);
    gtk_table_attach(GTK_TABLE(dialog->table1), dialog->label6, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label6), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label6), 0, 0.5);
    gtk_size_group_add_widget (sg, dialog->label6);

    dialog->label7 = xfce_create_small_label(_("Low"));
    gtk_widget_show(dialog->label7);
    gtk_table_attach(GTK_TABLE(dialog->table1), dialog->label7, 1, 2, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 6, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label7), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label7), 1, 0.5);

    dialog->hscale2 = gtk_hscale_new(GTK_ADJUSTMENT(gtk_adjustment_new(threshold, THRESH_MIN, THRESH_MAX, 1, 5, 0)));
    gtk_widget_show(dialog->hscale2);
    gtk_table_attach(GTK_TABLE(dialog->table1), dialog->hscale2, 2, 3, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0);
    gtk_scale_set_draw_value(GTK_SCALE(dialog->hscale2), FALSE);
    gtk_scale_set_digits(GTK_SCALE(dialog->hscale2), 0);
    gtk_range_set_update_policy(GTK_RANGE(dialog->hscale2), GTK_UPDATE_DISCONTINUOUS);

    dialog->label8 = xfce_create_small_label(_("High"));
    gtk_widget_show(dialog->label8);
    gtk_table_attach(GTK_TABLE(dialog->table1), dialog->label8, 3, 4, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label8), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label8), 0, 0.5);

    /* drag and drop */
    dialog->frame3 = xfce_create_framebox (_("Drag and drop"), &frame_bin);
    gtk_widget_show(dialog->frame3);
    gtk_box_pack_start(GTK_BOX(dialog->vbox1), dialog->frame3, FALSE, TRUE, 0);

    dialog->table2 = gtk_table_new(1, 4, FALSE);
    gtk_widget_show(dialog->table2);
    gtk_container_add (GTK_CONTAINER (frame_bin), dialog->table2);

    dialog->label10 = gtk_label_new(_("Threshold :"));
    gtk_widget_show(dialog->label10);
    gtk_table_attach(GTK_TABLE(dialog->table2), dialog->label10, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label10), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label10), 0, 0.5);
    gtk_size_group_add_widget (sg, dialog->label10);

    dialog->label11 = xfce_create_small_label(_("Low"));
    gtk_widget_show(dialog->label11);
    gtk_table_attach(GTK_TABLE(dialog->table2), dialog->label11, 1, 2, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 6, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label11), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label11), 1, 0.5);

    dialog->hscale3 = gtk_hscale_new(GTK_ADJUSTMENT(gtk_adjustment_new(dnd_threshold, DND_THRESH_MIN, DND_THRESH_MAX, 1, 5, 0)));
    gtk_widget_show(dialog->hscale3);
    gtk_table_attach(GTK_TABLE(dialog->table2), dialog->hscale3, 2, 3, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0);
    gtk_scale_set_draw_value(GTK_SCALE(dialog->hscale3), FALSE);
    gtk_scale_set_digits(GTK_SCALE(dialog->hscale3), 0);
    gtk_range_set_update_policy(GTK_RANGE(dialog->hscale3), GTK_UPDATE_DISCONTINUOUS);

    dialog->label12 = xfce_create_small_label(_("High"));
    gtk_widget_show(dialog->label12);
    gtk_table_attach(GTK_TABLE(dialog->table2), dialog->label12, 3, 4, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label12), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label12), 0, 0.5);

    /* double click */
    dialog->frame4 = xfce_create_framebox (_("Double click"), &frame_bin);
    gtk_widget_show(dialog->frame4);
    gtk_box_pack_start(GTK_BOX(dialog->vbox1), dialog->frame4, FALSE, TRUE, 0);

    dialog->table3 = gtk_table_new(1, 4, FALSE);
    gtk_widget_show(dialog->table3);
    gtk_container_add (GTK_CONTAINER (frame_bin), dialog->table3);

    dialog->label14 = gtk_label_new(_("Speed :"));
    gtk_widget_show(dialog->label14);
    gtk_table_attach(GTK_TABLE(dialog->table3), dialog->label14, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label14), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label14), 0, 0.5);
    gtk_size_group_add_widget (sg, dialog->label14);

    dialog->label15 = xfce_create_small_label(_("Slow"));
    gtk_widget_show(dialog->label15);
    gtk_table_attach(GTK_TABLE(dialog->table3), dialog->label15, 1, 2, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 6, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label15), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label15), 1, 0.5);

    dialog->hscale4 = gtk_hscale_new(GTK_ADJUSTMENT(gtk_adjustment_new(dbl_clicktime, DBL_CLICK_MIN, DBL_CLICK_MAX, 10, 100, 0)));
    gtk_widget_show(dialog->hscale4);
    gtk_table_attach(GTK_TABLE(dialog->table3), dialog->hscale4, 2, 3, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0);
    gtk_scale_set_draw_value(GTK_SCALE(dialog->hscale4), FALSE);
    gtk_scale_set_digits(GTK_SCALE(dialog->hscale4), 0);
    gtk_range_set_update_policy(GTK_RANGE(dialog->hscale4), GTK_UPDATE_DISCONTINUOUS);
    gtk_range_set_inverted(GTK_RANGE(dialog->hscale4), TRUE);

    dialog->label16 = xfce_create_small_label(_("Fast"));
    gtk_widget_show(dialog->label16);
    gtk_table_attach(GTK_TABLE(dialog->table3), dialog->label16, 3, 4, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(dialog->label16), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(dialog->label16), 0, 0.5);

    /* dialog action area */
    dialog->dialog_action_area1 = GTK_DIALOG(dialog->mouse_dialog)->action_area;
    gtk_widget_show(dialog->dialog_action_area1);
    gtk_button_box_set_layout(GTK_BUTTON_BOX(dialog->dialog_action_area1), GTK_BUTTONBOX_END);

    dialog->closebutton1 = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
    gtk_widget_show(dialog->closebutton1);
    gtk_dialog_add_action_widget(GTK_DIALOG(dialog->mouse_dialog), dialog->closebutton1, GTK_RESPONSE_CLOSE);
    GTK_WIDGET_SET_FLAGS(dialog->closebutton1, GTK_CAN_DEFAULT);

#ifdef HAVE_XCURSOR_EXTENSION
    label = gtk_label_new(_("Cursor"));
    gtk_widget_show(label);
    mouse_plugin_create_cursor_page(dialog);
    gtk_notebook_append_page(GTK_NOTEBOOK(dialog->notebook), dialog->cursor_page, label);
#endif

#ifdef USE_XKB
    label = gtk_label_new(_("Accessibility"));
    gtk_widget_show(label);
    create_accessx_page(dialog);
    gtk_widget_show_all(dialog->accessx_page);
    gtk_notebook_append_page(GTK_NOTEBOOK(dialog->notebook), dialog->accessx_page, label);
#endif

    return dialog;
}

#ifdef USE_XKB
static void
cb_mouse_tray_icon_embedded(GtkWidget *tray_icon, gpointer user_data)
{
    gtk_container_add(GTK_CONTAINER(tray_icon), mouse_tray_image);
    gtk_widget_show_all(tray_icon);
}

static void
mouse_tray_icon_reconnect()
{
    mouse_tray_icon = netk_tray_icon_new(DefaultScreenOfDisplay(GDK_DISPLAY()));
    g_signal_connect(G_OBJECT(mouse_tray_icon), "embedded", G_CALLBACK(cb_mouse_tray_icon_embedded), NULL);
}

static void
cb_mouse_tray_icon_destroy(GtkWidget *tray_icon, GtkWidget *image)
{
    gtk_widget_ref(image);
    gtk_container_remove(GTK_CONTAINER(tray_icon), image);
    mouse_tray_icon_reconnect();
}

static void
toggle_accessx()
{
    if (xkbpresent)
    {
        XkbDescPtr xkb = XkbAllocKeyboard ();
        if (xkb)
        {
            gdk_error_trap_push ();
            XkbGetControls (GDK_DISPLAY (), XkbAllControlsMask, xkb);

            if(mouse_key)
            {
                xkb->ctrls->enabled_ctrls |= XkbMouseKeysMask;
                xkb->ctrls->mk_delay = mouse_keys_delay;
                xkb->ctrls->mk_interval = 1000 / mouse_keys_interval;
                xkb->ctrls->mk_time_to_max = mouse_keys_ttm;
                xkb->ctrls->mk_max_speed = mouse_keys_max_speed;

                if(!mouse_tray_icon)
                {
                    GdkPixbuf *pixbuf = gdk_pixbuf_new_from_xpm_data(mouse_tray_image_xpm);
                    mouse_tray_image = gtk_image_new_from_pixbuf(pixbuf);
                    mouse_tray_icon = netk_tray_icon_new(DefaultScreenOfDisplay(GDK_DISPLAY()));
                    gtk_container_add(GTK_CONTAINER(mouse_tray_icon), mouse_tray_image);
                    g_signal_connect(G_OBJECT(mouse_tray_icon), "destroy", G_CALLBACK(cb_mouse_tray_icon_destroy), mouse_tray_image);
                }
                if(mouse_tray_icon)
                    gtk_widget_show_all(mouse_tray_icon);
            }
            else
            {
                xkb->ctrls->enabled_ctrls &= ~XkbMouseKeysMask;
                if(mouse_tray_icon)
                    gtk_widget_hide(mouse_tray_icon);
            }

            XkbSetControls (GDK_DISPLAY (), XkbControlsEnabledMask | XkbMouseKeysMask | XkbMouseKeysAccelMask, xkb);
            XFree (xkb);
            gdk_flush ();
            gdk_error_trap_pop ();
        }
        else
        {
            g_warning ("XkbAllocKeyboard() returned null pointer");
        }
    }
}

static void
cb_scale_accessx_changed (GtkWidget * widget, gpointer user_data)
{
    Itf *itf = (Itf *)user_data;
                McsPlugin *mcs_plugin = itf->mcs_plugin;

    mouse_keys_delay = (int) gtk_range_get_value (GTK_RANGE (itf->scale_mouse_keys_delay));
    mouse_keys_interval = (int) gtk_range_get_value (GTK_RANGE (itf->scale_mouse_keys_interval));
    mouse_keys_ttm= (int) gtk_range_get_value (GTK_RANGE (itf->scale_mouse_keys_ttm));
    mouse_keys_max_speed= (int) gtk_range_get_value (GTK_RANGE (itf->scale_mouse_keys_max_speed));

    toggle_accessx();
    mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeysDelay", CHANNEL2, mouse_keys_delay);
    mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeysInterval", CHANNEL2, mouse_keys_interval);
    mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeysTimeToMax", CHANNEL2, mouse_keys_ttm);
    mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeysMaxSpeed", CHANNEL2, mouse_keys_max_speed);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL2);
    mouse_plugin_write_options (mcs_plugin);
}

static void
cb_checkbutton_accessx_changed (GtkWidget * widget, gpointer user_data)
{
    Itf *itf = (Itf *)user_data;
    McsPlugin *mcs_plugin = itf->mcs_plugin;

    mouse_key = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (itf->checkbutton_mouse));
    gtk_widget_set_sensitive(itf->scale_mouse_keys_delay, mouse_key);
    gtk_widget_set_sensitive(itf->scale_mouse_keys_interval, mouse_key);
    gtk_widget_set_sensitive(itf->scale_mouse_keys_ttm, mouse_key);
    gtk_widget_set_sensitive(itf->scale_mouse_keys_max_speed, mouse_key);

    toggle_accessx();
    mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeys", CHANNEL2, mouse_key ? 1 : 0);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL2);
    mouse_plugin_write_options (mcs_plugin);
}
#endif

static void setup_dialog(Itf * itf)
{
    g_signal_connect(G_OBJECT(itf->mouse_dialog), "response", G_CALLBACK(cb_dialog_response), itf->mcs_plugin);

    g_signal_connect(G_OBJECT(itf->radiobutton2), "toggled", G_CALLBACK(cb_righthandled_changed), itf);

    g_signal_connect(G_OBJECT(itf->hscale1), "value_changed", (GCallback) cb_acceleration_changed, itf);
    g_signal_connect(G_OBJECT(itf->hscale2), "value_changed", (GCallback) cb_threshold_changed, itf);
    g_signal_connect(G_OBJECT(itf->hscale3), "value_changed", (GCallback) cb_dnd_threshold_changed, itf);
    g_signal_connect(G_OBJECT(itf->hscale4), "value_changed", (GCallback) cb_dbl_clicktime_changed, itf);

#ifdef USE_XKB
    g_signal_connect(G_OBJECT(itf->checkbutton_mouse), "toggled", (GCallback) cb_checkbutton_accessx_changed , itf);
    g_signal_connect(G_OBJECT(itf->scale_mouse_keys_delay), "value_changed", (GCallback) cb_scale_accessx_changed , itf);
    g_signal_connect(G_OBJECT(itf->scale_mouse_keys_interval), "value_changed", (GCallback) cb_scale_accessx_changed, itf);
    g_signal_connect(G_OBJECT(itf->scale_mouse_keys_ttm), "value_changed", (GCallback) cb_scale_accessx_changed, itf);
    g_signal_connect(G_OBJECT(itf->scale_mouse_keys_max_speed), "value_changed", (GCallback) cb_scale_accessx_changed , itf);
#endif


    xfce_gtk_window_center_on_monitor_with_pointer (GTK_WINDOW (itf->mouse_dialog));
    gdk_x11_window_set_user_time(GTK_WIDGET (itf->mouse_dialog)->window,
    gdk_x11_get_server_time (GTK_WIDGET (itf->mouse_dialog)->window));
    gtk_widget_show(itf->mouse_dialog);
}

McsPluginInitResult
mcs_plugin_init(McsPlugin *mcs_plugin)
{
    /* This is required for UTF-8 at least - Please don't remove it */
    xfce_textdomain(GETTEXT_PACKAGE, LOCALEDIR, "UTF-8");

    create_channel(mcs_plugin);
    mcs_plugin->plugin_name = g_strdup(PLUGIN_NAME);
    /* the button label in the xfce-mcs-manager dialog */
    mcs_plugin->caption = g_strdup (Q_ ("Button Label|Mouse"));
    mcs_plugin->run_dialog = run_dialog;
    mcs_manager_notify(mcs_plugin->manager, CHANNEL1);
    mcs_plugin->icon = xfce_themed_icon_load ("xfce4-mouse", 48);
    if (G_LIKELY (mcs_plugin->icon != NULL))
        g_object_set_data_full (G_OBJECT (mcs_plugin->icon), "mcs-plugin-icon-name", g_strdup ("xfce4-mouse"), g_free);

    return(MCS_PLUGIN_INIT_OK);
}

static void
create_channel(McsPlugin *mcs_plugin)
{
    McsSetting *setting;
    gchar *rcfile, *path;
#ifdef USE_XKB
    int xkbmajor = XkbMajorVersion, xkbminor = XkbMinorVersion;
    int xkbopcode, xkbevent, xkberror;
#endif

    path = g_build_filename ("xfce4", RCDIR, RCFILE1, NULL);
    rcfile = xfce_resource_lookup (XFCE_RESOURCE_CONFIG, path);

    if (!rcfile)
    {
        rcfile = xfce_get_userfile(OLDRCDIR, RCFILE1, NULL);
    }

    if (g_file_test (rcfile, G_FILE_TEST_EXISTS))
    {
        mcs_manager_add_channel_from_file(mcs_plugin->manager, CHANNEL1,
                                          rcfile);
    }
    else
    {
        mcs_manager_add_channel(mcs_plugin->manager, CHANNEL1);
    }

    g_free(path);
    g_free(rcfile);

    path = g_build_filename ("xfce4", RCDIR, RCFILE2, NULL);
    rcfile = xfce_resource_lookup (XFCE_RESOURCE_CONFIG, path);

    if (!rcfile)
    {
        rcfile = xfce_get_userfile(OLDRCDIR, RCFILE2, NULL);
    }

    if (g_file_test (rcfile, G_FILE_TEST_EXISTS))
    {
        mcs_manager_add_channel_from_file(mcs_plugin->manager, CHANNEL2, rcfile);
    }
    else
    {
        mcs_manager_add_channel(mcs_plugin->manager, CHANNEL2);
    }

    g_free(path);
    g_free(rcfile);

    /* Retrieve server defaults */
    get_mouse_values(&acceleration, &denominator, &threshold);

    setting = mcs_manager_setting_lookup(mcs_plugin->manager, "Net/DoubleClickTime", CHANNEL1);
    if (setting)
    {
        dbl_clicktime = setting->data.v_int;
    }
    else
    {
        dbl_clicktime = DEFAULT_DBL_CLICKTIME;
        mcs_manager_set_int(mcs_plugin->manager, "Net/DoubleClickTime", CHANNEL1, dbl_clicktime);
    }

    setting = mcs_manager_setting_lookup(mcs_plugin->manager, "Net/DndDragThreshold", CHANNEL1);
    if(setting)
    {
        dnd_threshold = setting->data.v_int;
    }
    else
    {
        dnd_threshold = DEFAULT_DND_THRESHOLD;
        mcs_manager_set_int(mcs_plugin->manager, "Net/DndDragThreshold", CHANNEL1, dnd_threshold);
    }

    setting = mcs_manager_setting_lookup(mcs_plugin->manager, "Mouse/RightHanded", CHANNEL2);
    if(setting)
    {
        right_handed = (setting->data.v_int ? TRUE : FALSE);
    }
    else
    {
        right_handed = TRUE;
        mcs_manager_set_int(mcs_plugin->manager, "Mouse/RightHanded", CHANNEL2, right_handed ? 1 : 0);
    }

    setting = mcs_manager_setting_lookup(mcs_plugin->manager, "Mouse/Denominator", CHANNEL2);
    if((setting) && (setting->data.v_int))
    {
        denominator = setting->data.v_int;
        setting = mcs_manager_setting_lookup(mcs_plugin->manager, "Mouse/Acceleration", CHANNEL2);
        if(setting)
        {
            acceleration = setting->data.v_int;
        }
        else
        {
            mcs_manager_set_int(mcs_plugin->manager, "Mouse/Acceleration", CHANNEL2, acceleration);
        }

        setting = mcs_manager_setting_lookup(mcs_plugin->manager, "Mouse/Threshold", CHANNEL2);
        if(setting)
        {
            threshold = setting->data.v_int;
        }
        else
        {
            mcs_manager_set_int(mcs_plugin->manager, "Mouse/Threshold", CHANNEL2, threshold);
        }
    }
    else
    {
        /* Set default server values */
        mcs_manager_set_int(mcs_plugin->manager, "Mouse/Denominator", CHANNEL2, denominator);
        mcs_manager_set_int(mcs_plugin->manager, "Mouse/Acceleration", CHANNEL2, acceleration);
        mcs_manager_set_int(mcs_plugin->manager, "Mouse/Threshold", CHANNEL2, threshold);
    }

#ifdef USE_XKB
#ifdef DEBUG
    g_message ("Querying Xkb extension");
#endif
    if (XkbQueryExtension (GDK_DISPLAY (), &xkbopcode, &xkbevent, &xkberror, &xkbmajor, &xkbminor))
    {
#ifdef DEBUG
        g_message ("Xkb extension found");
#endif
        xkbpresent = TRUE;
    }
    else
    {
#ifdef DEBUG
        g_message ("Your X server does not support Xkb extension");
#endif
        xkbpresent = FALSE;
    }
    setting = mcs_manager_setting_lookup (mcs_plugin->manager, "Mouse/MouseKeys", CHANNEL2);
    if(setting)
    {
        mouse_key = (setting->data.v_int ? TRUE : FALSE);
    }
    else
    {
        mouse_key = FALSE;
        mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeys", CHANNEL2, mouse_key ? 1 : 0);
    }

    setting = mcs_manager_setting_lookup (mcs_plugin->manager, "Mouse/MouseKeysDelay", CHANNEL2);
    if(setting)
    {
        mouse_keys_delay = setting->data.v_int;
    }
    else
    {
        mouse_keys_delay = 200;
        mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeysDelay", CHANNEL2, mouse_keys_delay);
    }

    setting = mcs_manager_setting_lookup (mcs_plugin->manager, "Mouse/MouseKeysInterval", CHANNEL2);
    if(setting)
    {
        mouse_keys_interval = setting->data.v_int;
    }
    else
    {
        mouse_keys_interval = 200;
        mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeysInterval", CHANNEL2, mouse_keys_interval);
    }

    setting = mcs_manager_setting_lookup (mcs_plugin->manager, "Mouse/MouseKeysMaxSpeed", CHANNEL2);
    if(setting)
    {
        mouse_keys_max_speed = setting->data.v_int;
    }
    else
    {
        mouse_keys_max_speed = 200;
        mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeysMaxSpeed", CHANNEL2, mouse_keys_max_speed);
    }

    setting = mcs_manager_setting_lookup (mcs_plugin->manager, "Mouse/MouseKeysTimeToMax", CHANNEL2);
    if(setting)
    {
        mouse_keys_ttm = setting->data.v_int;
    }
    else
    {
        mouse_keys_ttm = 200;
        mcs_manager_set_int (mcs_plugin->manager, "Mouse/MouseKeysTimeToMax", CHANNEL2, mouse_keys_ttm);
    }

    toggle_accessx();
#endif
    set_mouse_values(right_handed, acceleration, threshold);

#ifdef HAVE_XCURSOR_EXTENSION
    mouse_plugin_set_initial_cursor_values(mcs_plugin);
#endif

#if 0
    /* I fail to see why we need to save the options here, during startup... */
    mouse_plugin_write_options (mcs_plugin);
#endif
}

gboolean
mouse_plugin_write_options(McsPlugin *mcs_plugin)
{
    gboolean result = FALSE;
    gchar *rcfile, *path;

    path = g_build_filename ("xfce4", RCDIR, RCFILE1, NULL);
    rcfile = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, path, TRUE);
    if (G_LIKELY (rcfile != NULL))
    {
        result = mcs_manager_save_channel_to_file (mcs_plugin->manager, CHANNEL1, rcfile);
        g_free (rcfile);
    }
    g_free (path);

    path = g_build_filename ("xfce4", RCDIR, RCFILE2, NULL);
    rcfile = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, path, TRUE);
    if (G_LIKELY (rcfile != NULL))
    {
        result &= mcs_manager_save_channel_to_file (mcs_plugin->manager, CHANNEL2, rcfile);
        g_free (rcfile);
    }
    g_free (path);

    return(result);
}

static void
run_dialog(McsPlugin *mcs_plugin)
{
    static Itf *dialog = NULL;

    xfce_textdomain (GETTEXT_PACKAGE, LOCALEDIR, "UTF-8");

    if (is_running)
    {
        if((dialog) && (dialog->mouse_dialog))
        {
            gtk_window_present(GTK_WINDOW(dialog->mouse_dialog));
        }
        return;
    }

    is_running = TRUE;
    dialog = create_mouse_dialog(mcs_plugin);
    setup_dialog(dialog);
}

/* macro defined in manager-plugin.h */
MCS_PLUGIN_CHECK_INIT


syntax highlighted by Code2HTML, v. 0.9.1