Skip to content
Snippets Groups Projects
xfwm4_plugin.c 64 KiB
Newer Older
/*
	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.

        xfce4 mcs plugin   - (c) 2002 Olivier Fourdan

 */

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

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

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

#include <libxfce4mcs/mcs-common.h>
#include <libxfce4mcs/mcs-manager.h>
#include <xfce-mcs-manager/manager-plugin.h>
#include <libxfcegui4/libxfcegui4.h>
#include "inline-icon.h"
Olivier Fourdan's avatar
Olivier Fourdan committed
#include "my_intl.h"
#define RCDIR   "settings"
#define CHANNEL "xfwm4"
#define PLUGIN_NAME "xfwm4"

#define DEFAULT_THEME "microdeck2"
#define DEFAULT_KEY_THEME "default.keys"
#define DEFAULT_LAYOUT "OTS|HMC"
#define DEFAULT_ACTION "maximize"
#define DEFAULT_ALIGN "center"
#define DEFAULT_FONT "Sans Bold 10"

#define DEFAULT_ICON_SIZE 48
#define MAX_ELEMENTS_BEFORE_SCROLLING 6
#ifndef DATADIR
#define DATADIR "/usr/local/share"
#endif

#define STATES 8
#define STATE_HIDDEN (STATES - 1)

typedef enum
{
    DECORATION_THEMES = 0,
    KEYBINDING_THEMES = 1
}
ThemeType;

enum
{
    TITLE = 0,
    MENU,
    STICK,
    SHADE,
    HIDE,
    MAXIMIZE,
    CLOSE,
    END
};

typedef struct _TitleRadioButton TitleRadioButton;
struct _TitleRadioButton
{
    GtkWidget *radio_buttons[STATES];
    guint active;
    GSList *radio_group;
};

typedef struct _TitleButton TitleButton;
struct _TitleButton
{
    gchar *label;
    gchar code;
};

typedef struct _TitleButtonData TitleButtonData;
struct _TitleButtonData
{
    guint row;
    guint col;
    gpointer data;
};

typedef struct _RadioTmpl RadioTmpl;
struct _RadioTmpl
{
    gchar *label;
    gchar *action;
    GtkWidget *radio_button;
};

TitleButton title_buttons[] = {
Olivier Fourdan's avatar
Olivier Fourdan committed
    {N_("Title"), '|'},
    {N_("Menu"), 'O'},
    {N_("Stick"), 'T'},
    {N_("Shade"), 'S'},
    {N_("Hide"), 'H'},
    {N_("Maximize"), 'M'},
    {N_("Close"), 'C'}
Olivier Fourdan's avatar
Olivier Fourdan committed
    {N_("Shade window"), "shade", NULL},
    {N_("Hide window"), "hide", NULL},
    {N_("Maximize window"), "maximize", NULL},
    {N_("Nothing"), "none", NULL},
Olivier Fourdan's avatar
Olivier Fourdan committed
    {N_("Left"), "left", NULL},
    {N_("Center"), "center", NULL},
    {N_("Right"), "right", NULL},
    {NULL, NULL, NULL}
};

enum
{
    THEME_NAME_COLUMN,
    N_COLUMNS
};

typedef struct _ThemeInfo ThemeInfo;
struct _ThemeInfo
{
    gchar *path;
    gchar *name;
    gboolean has_decoration;
    gboolean has_keybinding;
    gboolean set_layout;
    gboolean set_align;
    gboolean set_font;
    gboolean user_writable;
};

typedef struct _Itf Itf;
struct _Itf
{
    McsPlugin *mcs_plugin;

    GSList *click_focus_radio_group;

    GtkWidget *box_move_check;
    GtkWidget *box_resize_check;
    GtkWidget *click_focus_radio;
    GtkWidget *click_raise_check;
    GtkWidget *closebutton1;
    GtkWidget *dialog_action_area1;
    GtkWidget *dialog_vbox1;
    GtkWidget *focus_follow_mouse_radio;
    GtkWidget *focus_new_check;
    GtkWidget *font_button;
    GtkWidget *font_selection;
    GtkWidget *frame1;
    GtkWidget *frame2;
    GtkWidget *frame3;
    GtkWidget *frame4;
    GtkWidget *frame5;
    GtkWidget *frame6;
    GtkWidget *frame8;
    GtkWidget *frame9;
    GtkWidget *frame10;
    GtkWidget *frame11;
    GtkWidget *frame12;
    GtkWidget *frame13;
    GtkWidget *frame14;
    GtkWidget *frame15;
    GtkWidget *hbox1;
    GtkWidget *hbox2;
    GtkWidget *hbox3;
    GtkWidget *hbox4;
    GtkWidget *hbox6;
    GtkWidget *label1;
    GtkWidget *label2;
    GtkWidget *label3;
    GtkWidget *label4;
    GtkWidget *label5;
    GtkWidget *label6;
    GtkWidget *label7;
    GtkWidget *label32;
    GtkWidget *label35;
    GtkWidget *label37;
    GtkWidget *label38;
    GtkWidget *label39;
    GtkWidget *label40;
    GtkWidget *label41;
    GtkWidget *label42;
    GtkWidget *label43;
    GtkWidget *label44;
    GtkWidget *label45;
    GtkWidget *label46;
    GtkWidget *label47;
    GtkWidget *label48;
    GtkWidget *label49;
    GtkWidget *notebook1;
    GtkWidget *raise_delay_scale;
    GtkWidget *raise_on_focus_check;
    GtkWidget *scrolledwindow1;
    GtkWidget *scrolledwindow2;
    GtkWidget *snap_to_border_check;
    GtkWidget *snap_width_scale;
    GtkWidget *table2;
    GtkWidget *table3;
    GtkWidget *treeview1;
    GtkWidget *treeview2;
    GtkWidget *vbox1;
    GtkWidget *vbox2;
    GtkWidget *vbox3;
    GtkWidget *vbox4;
    GtkWidget *vbox5;
    GtkWidget *vbox6;
    GtkWidget *vbox7;
    GtkWidget *wrap_workspaces_check;
    GtkWidget *xfwm4_dialog;
};

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

static gboolean setting_model = FALSE;
static gboolean is_running = FALSE;
static gchar *current_theme = NULL;
static gchar *current_key_theme = NULL;
static gchar *current_layout = NULL;
static gchar *current_font = NULL;
static gchar *dbl_click_action = NULL;
static gchar *title_align = NULL;
static gboolean click_to_focus = TRUE;
static gboolean focus_new = TRUE;
static gboolean focus_raise = FALSE;
static gboolean raise_on_click = TRUE;
static gboolean snap_to_border = TRUE;
static gboolean wrap_workspaces = TRUE;
static gboolean box_move = FALSE;
static gboolean box_resize = FALSE;
static int raise_delay;
static int snap_width;
static TitleRadioButton title_radio_buttons[END];

static GList *decoration_theme_list = NULL;
static GList *keybinding_theme_list = NULL;
static gboolean glib22_str_has_suffix (const gchar  *str, const gchar  *suffix)
{
    int str_len;
    int suffix_len;

    g_return_val_if_fail (str != NULL, FALSE);
    g_return_val_if_fail (suffix != NULL, FALSE);

    str_len = strlen (str);
    suffix_len = strlen (suffix);

    if (str_len < suffix_len)
	return FALSE;

    return strcmp (str + str_len - suffix_len, suffix) == 0;
}

static void sensitive_cb(GtkWidget * widget, gpointer user_data)
{
    gtk_widget_set_sensitive(widget, (gboolean) GPOINTER_TO_INT(user_data));
}

static void cb_layout_destroy_button(GtkWidget * widget, gpointer user_data)
{
    g_free(user_data);
}

static void cb_layout_value_changed(GtkWidget * widget, gpointer user_data)
{
    static guint recursive = 0;
    guint i, j, k, l, m = 0;
    guint col, row;
    TitleButtonData *callback_data = (TitleButtonData *) user_data;
    McsPlugin *mcs_plugin = (McsPlugin *) callback_data->data;
    gchar result[STATES];

    if(recursive != 0)
    {
        return;
    }
    if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
    {
        return;
    }
    ++recursive;
    col = callback_data->col;
    row = callback_data->row;

    for(i = TITLE; i < END; i++)
    {
        if((i != row) && (title_radio_buttons[i].active == col))
        {
            for(j = 0; j < STATES; j++)
            {
                if((i == TITLE) && (title_radio_buttons[row].active == STATE_HIDDEN))
                {
                    gboolean in_use;

                    in_use = TRUE;
                    for(l = 0; (l < STATE_HIDDEN) && in_use; l++)
                    {
                        in_use = FALSE;
                        for(k = TITLE; k < END; k++)
                        {
                            if(title_radio_buttons[k].active == l)
                            {
                                in_use = TRUE;
                            }
                        }
                        if(!in_use)
                        {
                            m = l;
                        }
                    }
                    if(!in_use)
                    {
                        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(title_radio_buttons[TITLE].radio_buttons[m]), TRUE);
                        title_radio_buttons[TITLE].active = m;
                        break;
                    }
                }
                else if((col < STATE_HIDDEN) && title_radio_buttons[row].active == j)
                {
                    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(title_radio_buttons[i].radio_buttons[j]), TRUE);
                    title_radio_buttons[i].active = j;
                    break;
                }
            }
        }
    }
    title_radio_buttons[row].active = col;

    j = 0;

    for(l = 0; l < STATE_HIDDEN; l++)
    {
        for(k = TITLE; k < END; k++)
        {
            if(title_radio_buttons[k].active == l)
            {
                result[j++] = title_buttons[k].code;
            }
        }
    }
    result[j++] = '\0';

    if(current_layout)
    {
        g_free(current_layout);
    }

    current_layout = g_strdup(result);
    mcs_manager_set_string(mcs_plugin->manager, "Xfwm/ButtonLayout", CHANNEL, current_layout);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);

    --recursive;
}

static GtkWidget *create_layout_buttons(gchar * layout, gpointer user_data)
{
    GtkWidget *table;
    GtkWidget *label;
    GtkWidget *label_row;
    GtkWidget *radio_button;
    GSList *radio_button_group;
    TitleButtonData *callback_data;
    gchar *temp;
    guint i, j, len;
    gboolean visible;

    g_return_val_if_fail(layout != NULL, NULL);
    len = strlen(layout);
    g_return_val_if_fail(len > 0, NULL);

    table = gtk_table_new(8, 9, FALSE);
    gtk_widget_show(table);
    gtk_container_set_border_width(GTK_CONTAINER(table), 5);

    label = gtk_label_new(_("Layout :"));
    gtk_widget_show(label);
    gtk_table_attach(GTK_TABLE(table), label, 0, 9, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);

    for(i = TITLE; i < END; i++)
    {
Olivier Fourdan's avatar
Olivier Fourdan committed
        temp = g_strdup_printf("<small><i>%s :</i></small> ", _(title_buttons[i].label));
        label_row = gtk_label_new(temp);
        gtk_widget_show(label_row);
        gtk_table_attach(GTK_TABLE(table), label_row, 0, 1, i + 1, i + 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
        gtk_label_set_use_markup(GTK_LABEL(label_row), TRUE);
        gtk_label_set_justify(GTK_LABEL(label_row), GTK_JUSTIFY_LEFT);
        gtk_misc_set_alignment(GTK_MISC(label_row), 0, 0.5);

        radio_button_group = NULL;
        visible = FALSE;

        for(j = 0; j < STATES - 1; j++)
        {
            radio_button = gtk_radio_button_new(NULL);
            gtk_widget_show(radio_button);
            gtk_table_attach(GTK_TABLE(table), radio_button, j + 1, j + 2, i + 1, i + 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
            gtk_radio_button_set_group(GTK_RADIO_BUTTON(radio_button), radio_button_group);
            radio_button_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_button));
            title_radio_buttons[i].radio_buttons[j] = radio_button;

            if((j < len) && (layout[j] == title_buttons[i].code))
            {
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button), TRUE);
                visible = TRUE;
                title_radio_buttons[i].active = j;
            }
            callback_data = g_new(TitleButtonData, 1);
            callback_data->row = i;
            callback_data->col = j;
            callback_data->data = user_data;
            g_signal_connect(G_OBJECT(radio_button), "toggled", G_CALLBACK(cb_layout_value_changed), callback_data);
            g_signal_connect_after(G_OBJECT(radio_button), "destroy", G_CALLBACK(cb_layout_destroy_button), callback_data);
        }

        if(i != TITLE)
        {
            radio_button = gtk_radio_button_new_with_mnemonic(NULL, _("Hidden"));
            gtk_widget_show(radio_button);
            gtk_table_attach(GTK_TABLE(table), radio_button, STATES, STATES + 1, i + 1, i + 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
            gtk_radio_button_set_group(GTK_RADIO_BUTTON(radio_button), radio_button_group);
            radio_button_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_button));
            title_radio_buttons[i].radio_buttons[STATES - 1] = radio_button;

            if(!visible)
            {
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button), TRUE);
                title_radio_buttons[i].active = (STATES - 1);
            }
            callback_data = g_new(TitleButtonData, 1);
            callback_data->row = i;
            callback_data->col = STATES - 1;
            callback_data->data = user_data;
            g_signal_connect(G_OBJECT(radio_button), "toggled", G_CALLBACK(cb_layout_value_changed), callback_data);
            g_signal_connect_after(G_OBJECT(radio_button), "destroy", G_CALLBACK(cb_layout_destroy_button), callback_data);
        }
    }
    return (table);
}

static GtkWidget *create_radio_button_table(RadioTmpl template[], guint size, gchar * display_label, gchar * value, GCallback handler, gpointer user_data)
{
    GtkWidget *table;
    GtkWidget *label;
    GSList *radio_group = NULL;
    guint i;

    table = gtk_table_new(size, 2, FALSE);
    gtk_widget_show(table);
    gtk_container_set_border_width(GTK_CONTAINER(table), 5);

    label = gtk_label_new(display_label);

    gtk_widget_show(label);
    gtk_table_attach(GTK_TABLE(table), label, 0, size, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);

    for(i = 0; i < size; i++)
    {
        template[i].radio_button = gtk_radio_button_new_with_mnemonic(NULL, _(template[i].label));
        gtk_widget_show(template[i].radio_button);
        gtk_table_attach(GTK_TABLE(table), template[i].radio_button, i, i + 1, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0);
        gtk_radio_button_set_group(GTK_RADIO_BUTTON(template[i].radio_button), radio_group);
        radio_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(template[i].radio_button));
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(template[i].radio_button), strcmp(value, template[i].action) ? FALSE : TRUE);
        g_signal_connect(G_OBJECT(template[i].radio_button), "toggled", G_CALLBACK(handler), user_data);
    }

    return table;
}

static void theme_info_free(ThemeInfo * info)
{
    g_free(info->path);
    g_free(info->name);
    g_free(info);
}

static ThemeInfo *find_theme_info_by_name(const gchar * theme_name, GList * theme_list)
{
    GList *list;

    for(list = theme_list; list; list = list->next)
    {
        ThemeInfo *info = list->data;

        if(!strcmp(info->name, theme_name))
static gboolean parserc(const gchar * filename, gboolean *set_layout, gboolean *set_align, gboolean *set_font)
{
    gchar buf[80];
    gchar *lvalue, *rvalue;
    FILE *fp;

    *set_layout = FALSE;
    *set_align = FALSE;
    *set_font = FALSE;
    
    fp = fopen(filename, "r");
    if(!fp)
    {
        return FALSE;
    }
    while(fgets(buf, sizeof(buf), fp))
    {
        lvalue = strtok(buf, "=");
        rvalue = strtok(NULL, "\n");
        if((lvalue) && (rvalue))
        {
            if (!g_ascii_strcasecmp(lvalue, "button_layout"))
	    {
	        *set_layout = TRUE;
	    }
	    else if (!g_ascii_strcasecmp(lvalue, "title_alignment"))
	    {
	        *set_align = TRUE;
	    }
	    else if (!g_ascii_strcasecmp(lvalue, "title_font"))
	    {
	        *set_font = TRUE;
	    }
        }
	
    }
    fclose(fp);
    return TRUE;
}

static GList *update_theme_dir(const gchar * theme_dir, GList * theme_list)
{
    ThemeInfo *info = NULL;
    gchar *theme_name;
    GList *list = theme_list;
    gboolean has_decoration = FALSE;
    gboolean has_keybinding = FALSE;
    gboolean set_layout = FALSE;
    gboolean set_align = FALSE;
    gboolean set_font = FALSE;
    if(glib22_str_has_suffix(theme_dir, ".keys"))
    {
        tmp = g_build_filename(theme_dir, "keythemerc", NULL);
        if(g_file_test(tmp, G_FILE_TEST_IS_REGULAR) && parserc(tmp, &set_layout, &set_align, &set_font))
        {
            has_keybinding = TRUE;
        }
        g_free(tmp);
    }
    else
    {
        tmp = g_build_filename(theme_dir, "themerc", NULL);
        if(g_file_test(tmp, G_FILE_TEST_IS_REGULAR) && parserc(tmp, &set_layout, &set_align, &set_font))
    theme_name = g_strdup(strrchr(theme_dir, G_DIR_SEPARATOR) + 1);
    info = find_theme_info_by_name(theme_name, list);
    
    if(info)
    {
        if(!has_decoration && !has_keybinding)
        {
            list = g_list_remove(list, info);
            theme_info_free(info);
        }
        else if((info->has_keybinding != has_keybinding) || (info->has_decoration != has_decoration) || (info->set_layout != set_layout) || (info->set_align != set_align) || (info->set_font != set_font))
        {
            info->has_keybinding = has_keybinding;
            info->has_decoration = has_decoration;
            info->set_layout = set_layout;
            info->set_align = set_align;
            info->set_font = set_font;
        }
    }
    else
    {
        if(has_decoration || has_keybinding)
        {
            info = g_new0(ThemeInfo, 1);
            info->path = g_strdup(theme_dir);
            info->name = g_strdup(theme_name);
            info->has_decoration = has_decoration;
            info->has_keybinding = has_keybinding;
            info->set_layout = set_layout;
            info->set_align = set_align;
            info->set_font = set_font;
    return list;
}

static GList *themes_common_list_add_dir(const char *dirname, GList * theme_list)
{
    DIR *dir;
    struct dirent *de;
    GList *list = theme_list;

    g_return_val_if_fail(dirname != NULL, list);

    dir = opendir(dirname);

    if(!dir)
        return list;

    while((de = readdir(dir)))
    {
        char *tmp;

        if(de->d_name[0] == '.')
            continue;

        tmp = g_build_filename(dirname, de->d_name, NULL);
        list = update_theme_dir(tmp, list);
        g_free(tmp);
    }
    closedir(dir);

    return list;
}


static GList *theme_common_init(GList * theme_list)
{
    gchar *dir;
    GList *list = theme_list;

    dir = g_build_filename(g_get_home_dir(), ".themes", G_DIR_SEPARATOR_S, "xfwm4", NULL);
    list = themes_common_list_add_dir(dir, list);
    g_free(dir);

    dir = g_build_filename(DATADIR, "themes", NULL);
    list = themes_common_list_add_dir(dir, list);
    g_free(dir);

    return list;
}

static gboolean dialog_update_from_theme(Itf *itf, const gchar * theme_name, GList * theme_list)
{
    ThemeInfo *info = NULL;
    
    g_return_val_if_fail (theme_name != NULL, FALSE);
    g_return_val_if_fail (theme_list != NULL, FALSE);
    
    info = find_theme_info_by_name(theme_name, theme_list);
    if (info)
    {
	gtk_container_foreach (GTK_CONTAINER(itf->frame2), sensitive_cb, GINT_TO_POINTER((gint) !(info->set_layout)));
	gtk_container_foreach (GTK_CONTAINER(itf->frame14), sensitive_cb, GINT_TO_POINTER((gint) !(info->set_align)));
        gtk_widget_set_sensitive(itf->font_button, !(info->set_font));
	return TRUE;
    }
    return FALSE;
}

static void decoration_selection_changed(GtkTreeSelection * selection, gpointer data)
{
    GtkTreeModel *model;
    gchar *new_theme;
    GtkTreeIter iter;
    g_return_if_fail (data != NULL);
    
    }
    
    itf = (Itf *) data;
    mcs_plugin = itf->mcs_plugin;

    if(gtk_tree_selection_get_selected(selection, &model, &iter))
    {
        gtk_tree_model_get(model, &iter, THEME_NAME_COLUMN, &new_theme, -1);
    }
    else
    {
        new_theme = NULL;
    }

    if(new_theme != NULL)
    {
        if(current_theme && strcmp(current_theme, new_theme))
        {
            g_free(current_theme);
            current_theme = new_theme;
            dialog_update_from_theme(itf, current_theme, decoration_theme_list);
	    mcs_manager_set_string(mcs_plugin->manager, "Xfwm/ThemeName", CHANNEL, current_theme);
            mcs_manager_notify(mcs_plugin->manager, CHANNEL);
            write_options(mcs_plugin);
        }
    }
}

static void keybinding_selection_changed(GtkTreeSelection * selection, gpointer data)
{
    GtkTreeModel *model;
    gchar *new_key_theme;
    GtkTreeIter iter;
    g_return_if_fail (data != NULL);
    
    }
    
    itf = (Itf *) data;
    mcs_plugin = itf->mcs_plugin;

    if(gtk_tree_selection_get_selected(selection, &model, &iter))
    {
        gtk_tree_model_get(model, &iter, THEME_NAME_COLUMN, &new_key_theme, -1);
    }
    else
    {
        new_key_theme = NULL;
    }

    if(new_key_theme != NULL)
    {
        if(current_key_theme && strcmp(current_key_theme, new_key_theme))
        {
            g_free(current_key_theme);
            current_key_theme = new_key_theme;
            mcs_manager_set_string(mcs_plugin->manager, "Xfwm/KeyThemeName", CHANNEL, current_key_theme);
            mcs_manager_notify(mcs_plugin->manager, CHANNEL);
            write_options(mcs_plugin);
        }
    }
}

static GList *read_themes(GList * theme_list, GtkWidget * treeview, GtkWidget * swindow, ThemeType type, gchar * current_value)
{
    GList *list;
    GList *new_list = theme_list;
    GtkTreeModel *model;
    GtkTreeView *tree_view;
    gint i = 0;
    gboolean current_value_found = FALSE;
    GtkTreeRowReference *row_ref = NULL;

    new_list = theme_common_init(new_list);
    tree_view = GTK_TREE_VIEW(treeview);
    model = gtk_tree_view_get_model(tree_view);

    setting_model = TRUE;
    gtk_list_store_clear(GTK_LIST_STORE(model));

    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
    gtk_widget_set_usize(swindow, -1, -1);

    for(list = new_list; list; list = list->next)
    {
        ThemeInfo *info = list->data;
        GtkTreeIter iter;

        if(((type == DECORATION_THEMES) && !(info->has_decoration)) || ((type == KEYBINDING_THEMES) && !(info->has_keybinding)))
            continue;

        gtk_list_store_prepend(GTK_LIST_STORE(model), &iter);
        gtk_list_store_set(GTK_LIST_STORE(model), &iter, THEME_NAME_COLUMN, info->name, -1);

        if(strcmp(current_value, info->name) == 0)
        {
            GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
            row_ref = gtk_tree_row_reference_new(model, path);
            gtk_tree_path_free(path);
            current_value_found = TRUE;
        }

        if(i == MAX_ELEMENTS_BEFORE_SCROLLING)
        {
            GtkRequisition rectangle;
            gtk_widget_size_request(GTK_WIDGET(tree_view), &rectangle);
            gtk_widget_set_usize(swindow, -1, rectangle.height);
            gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
        }
        i++;
    }

    if(!current_value_found)
    {
        GtkTreeIter iter;
        GtkTreePath *path;

        gtk_list_store_prepend(GTK_LIST_STORE(model), &iter);
        gtk_list_store_set(GTK_LIST_STORE(model), &iter, THEME_NAME_COLUMN, current_value, -1);

        path = gtk_tree_model_get_path(model, &iter);
        row_ref = gtk_tree_row_reference_new(model, path);
        gtk_tree_path_free(path);
    }

    if(row_ref)
    {
        GtkTreePath *path;

        path = gtk_tree_row_reference_get_path(row_ref);
        gtk_tree_view_set_cursor(tree_view, path, NULL, FALSE);
        gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, 0.5, 0.0);
        gtk_tree_path_free(path);
        gtk_tree_row_reference_free(row_ref);
    }
    setting_model = FALSE;

    return new_list;
}

static gint sort_func(GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data)
{
    gchar *a_str = NULL;
    gchar *b_str = NULL;

    gtk_tree_model_get(model, a, 0, &a_str, -1);
    gtk_tree_model_get(model, b, 0, &b_str, -1);

    if(a_str == NULL)
        a_str = g_strdup("");
    if(b_str == NULL)
        b_str = g_strdup("");

    if(!strcmp(a_str, DEFAULT_THEME))
        return -1;
    if(!strcmp(b_str, DEFAULT_THEME))
        return 1;

    return g_utf8_collate(a_str, b_str);
}

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

    click_to_focus = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(itf->click_focus_radio));

    mcs_manager_set_int(mcs_plugin->manager, "Xfwm/ClickToFocus", CHANNEL, click_to_focus ? 1 : 0);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);
}

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

    focus_new = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(itf->focus_new_check));

    mcs_manager_set_int(mcs_plugin->manager, "Xfwm/FocusNewWindow", CHANNEL, focus_new ? 1 : 0);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);
}

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

    focus_raise = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(itf->raise_on_focus_check));
    gtk_widget_set_sensitive(itf->raise_delay_scale, focus_raise);

    mcs_manager_set_int(mcs_plugin->manager, "Xfwm/FocusRaise", CHANNEL, focus_raise ? 1 : 0);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);
}

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

    raise_delay = (int)gtk_range_get_value(GTK_RANGE(itf->raise_delay_scale));
    mcs_manager_set_int(mcs_plugin->manager, "Xfwm/RaiseDelay", CHANNEL, raise_delay);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);
}

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

    raise_on_click = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(itf->click_raise_check));

    mcs_manager_set_int(mcs_plugin->manager, "Xfwm/RaiseOnClick", CHANNEL, raise_on_click ? 1 : 0);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);
}

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

    snap_to_border = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(itf->snap_to_border_check));
    gtk_widget_set_sensitive(itf->snap_width_scale, snap_to_border);

    mcs_manager_set_int(mcs_plugin->manager, "Xfwm/SnapToBorder", CHANNEL, snap_to_border ? 1 : 0);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);
}

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

    snap_width = (int)gtk_range_get_value(GTK_RANGE(itf->snap_width_scale));
    mcs_manager_set_int(mcs_plugin->manager, "Xfwm/SnapWidth", CHANNEL, snap_width);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);
}

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

    wrap_workspaces = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(itf->wrap_workspaces_check));

    mcs_manager_set_int(mcs_plugin->manager, "Xfwm/WrapWorkspaces", CHANNEL, wrap_workspaces ? 1 : 0);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);
}

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

    box_move = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(itf->box_move_check));

    mcs_manager_set_int(mcs_plugin->manager, "Xfwm/BoxMove", CHANNEL, box_move ? 1 : 0);
    mcs_manager_notify(mcs_plugin->manager, CHANNEL);
    write_options(mcs_plugin);
}

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

    box_resize = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(itf->box_resize_check));