Search
lxdream.org :: lxdream/src/gtkui/gtk_cfg.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gtkui/gtk_cfg.c
changeset 1072:d82e04e6d497
author nkeynes
date Tue Jul 21 20:33:21 2009 +1000 (13 years ago)
permissions -rw-r--r--
last change Heavy configuration management refactor
- Configuration groups now take both an on_change event handler and a
default keybinding handler, making most keybinding tasks quite simple
- GUI configuration all merged into a unified model, drastically reducing
the amount of GUI config code.

Bonuses
- OSX now has a hotkey preference pane
- GTK keybinding editor is much more usable
file annotate diff log raw
nkeynes@1072
     1
/**
nkeynes@1072
     2
 * $Id$
nkeynes@1072
     3
 *
nkeynes@1072
     4
 * Configuration pane to display a configuration group
nkeynes@1072
     5
 * TODO:
nkeynes@1072
     6
 *
nkeynes@1072
     7
 * Copyright (c) 2009 Nathan Keynes.
nkeynes@1072
     8
 *
nkeynes@1072
     9
 * This program is free software; you can redistribute it and/or modify
nkeynes@1072
    10
 * it under the terms of the GNU General Public License as published by
nkeynes@1072
    11
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@1072
    12
 * (at your option) any later version.
nkeynes@1072
    13
 *
nkeynes@1072
    14
 * This program is distributed in the hope that it will be useful,
nkeynes@1072
    15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@1072
    16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@1072
    17
 * GNU General Public License for more details.
nkeynes@1072
    18
 */
nkeynes@1072
    19
nkeynes@1072
    20
#include <assert.h>
nkeynes@1072
    21
#include <string.h>
nkeynes@1072
    22
#include <errno.h>
nkeynes@1072
    23
#include <gtk/gtk.h>
nkeynes@1072
    24
#include <gdk/gdkkeysyms.h>
nkeynes@1072
    25
nkeynes@1072
    26
#include "lxdream.h"
nkeynes@1072
    27
#include "config.h"
nkeynes@1072
    28
#include "lxpaths.h"
nkeynes@1072
    29
#include "display.h"
nkeynes@1072
    30
#include "gtkui/gtkui.h"
nkeynes@1072
    31
nkeynes@1072
    32
struct config_data {
nkeynes@1072
    33
    lxdream_config_group_t config;
nkeynes@1072
    34
    GtkWidget *fields[CONFIG_MAX_KEYS][2];
nkeynes@1072
    35
};
nkeynes@1072
    36
nkeynes@1072
    37
/**
nkeynes@1072
    38
 * Update the configuration data for the current value of the given field.
nkeynes@1072
    39
 */
nkeynes@1072
    40
static gboolean config_text_changed( GtkWidget *field, gpointer p )
nkeynes@1072
    41
{
nkeynes@1072
    42
    GtkWidget *panel = field->parent;
nkeynes@1072
    43
    struct config_data *data= (struct config_data *)gtk_object_get_data( GTK_OBJECT(panel), "config_data" );
nkeynes@1072
    44
    int tag = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(field), "tag" ) );
nkeynes@1072
    45
nkeynes@1072
    46
    char buf[64];
nkeynes@1072
    47
    GtkWidget *entry1, *entry2;
nkeynes@1072
    48
    const gchar *key1 = NULL, *key2 = NULL;
nkeynes@1072
    49
nkeynes@1072
    50
    if( data->fields[tag][0] != NULL ) {
nkeynes@1072
    51
        key1 = gtk_entry_get_text(GTK_ENTRY(data->fields[tag][0]));
nkeynes@1072
    52
    }
nkeynes@1072
    53
    if( data->fields[tag][1] != NULL ) {
nkeynes@1072
    54
        key2 = gtk_entry_get_text(GTK_ENTRY(data->fields[tag][1]));
nkeynes@1072
    55
    }
nkeynes@1072
    56
nkeynes@1072
    57
    if( key1 == NULL || key1[0] == '\0') {
nkeynes@1072
    58
        lxdream_set_config_value( data->config, tag, key2 );
nkeynes@1072
    59
    } else if( key2 == NULL || key2[0] == '\0') {
nkeynes@1072
    60
        lxdream_set_config_value( data->config, tag, key1 );
nkeynes@1072
    61
    } else {
nkeynes@1072
    62
        char buf[strlen(key1) + strlen(key2) + 3];
nkeynes@1072
    63
        snprintf( buf, sizeof(buf), "%s, %s", key1, key2 );
nkeynes@1072
    64
        lxdream_set_config_value( data->config, tag, buf );
nkeynes@1072
    65
    }
nkeynes@1072
    66
    return TRUE;
nkeynes@1072
    67
}
nkeynes@1072
    68
nkeynes@1072
    69
/**
nkeynes@1072
    70
 * Reset the fields (identified by one of the widgets in the field) back to it's
nkeynes@1072
    71
 * value in the config group.
nkeynes@1072
    72
 */
nkeynes@1072
    73
static void config_text_reset( GtkWidget *field )
nkeynes@1072
    74
{
nkeynes@1072
    75
    GtkWidget *panel = field->parent;
nkeynes@1072
    76
    struct config_data *data= (struct config_data *)gtk_object_get_data( GTK_OBJECT(panel), "config_data" );
nkeynes@1072
    77
    int tag = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(field), "tag" ) );
nkeynes@1072
    78
nkeynes@1072
    79
    const gchar *value = data->config->params[tag].value;
nkeynes@1072
    80
    if( value == NULL ) {
nkeynes@1072
    81
        value = "";
nkeynes@1072
    82
    }
nkeynes@1072
    83
nkeynes@1072
    84
    if( data->fields[tag][0] == NULL ) {
nkeynes@1072
    85
        if( data->fields[tag][1] != NULL ) {
nkeynes@1072
    86
            gtk_entry_set_text( GTK_ENTRY(data->fields[tag][1]), value );
nkeynes@1072
    87
        }
nkeynes@1072
    88
    } else if( data->fields[tag][1] == NULL ) {
nkeynes@1072
    89
        gtk_entry_set_text( GTK_ENTRY(data->fields[tag][0]), value );
nkeynes@1072
    90
    } else { /* Split between two fields */
nkeynes@1072
    91
        gchar *v1 = "", *v2 = "";
nkeynes@1072
    92
        gchar **parts = g_strsplit(value,",",3);
nkeynes@1072
    93
        if( parts[0] != NULL ) {
nkeynes@1072
    94
            v1 = parts[0];
nkeynes@1072
    95
            if( parts[1] != NULL ) {
nkeynes@1072
    96
                v2 = parts[1];
nkeynes@1072
    97
            }
nkeynes@1072
    98
nkeynes@1072
    99
        }
nkeynes@1072
   100
        gtk_entry_set_text( GTK_ENTRY(data->fields[tag][0]), v1 );
nkeynes@1072
   101
        gtk_entry_set_text( GTK_ENTRY(data->fields[tag][1]), v2 );
nkeynes@1072
   102
        g_strfreev(parts);
nkeynes@1072
   103
    }
nkeynes@1072
   104
}
nkeynes@1072
   105
nkeynes@1072
   106
static void config_set_field( void *p, const gchar *keysym )
nkeynes@1072
   107
{
nkeynes@1072
   108
    GtkWidget *field = GTK_WIDGET(p);
nkeynes@1072
   109
    GtkWidget *panel = field->parent;
nkeynes@1072
   110
nkeynes@1072
   111
    gtk_entry_set_text( GTK_ENTRY(field), keysym );
nkeynes@1072
   112
    g_object_set_data( G_OBJECT(field), "keypress_mode", GINT_TO_POINTER(FALSE) );
nkeynes@1072
   113
    input_set_keysym_hook(NULL, NULL);
nkeynes@1072
   114
    config_text_changed( field, NULL );
nkeynes@1072
   115
}
nkeynes@1072
   116
nkeynes@1072
   117
static gboolean config_key_buttonpress( GtkWidget *widget, GdkEventButton *event, gpointer user_data )
nkeynes@1072
   118
{
nkeynes@1072
   119
    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
nkeynes@1072
   120
    if( keypress_mode ) {
nkeynes@1072
   121
        gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, event->button);
nkeynes@1072
   122
        if( keysym != NULL ) {
nkeynes@1072
   123
            config_set_field( widget, keysym );
nkeynes@1072
   124
            g_free(keysym);
nkeynes@1072
   125
        }
nkeynes@1072
   126
        return TRUE;
nkeynes@1072
   127
    } else {
nkeynes@1072
   128
        gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
nkeynes@1072
   129
        g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
nkeynes@1072
   130
        input_set_keysym_hook( (display_keysym_callback_t)config_set_field, widget);
nkeynes@1072
   131
        gtk_widget_grab_focus( widget );
nkeynes@1072
   132
    }
nkeynes@1072
   133
    return FALSE;
nkeynes@1072
   134
}
nkeynes@1072
   135
nkeynes@1072
   136
static gboolean config_key_keypress( GtkWidget *widget, GdkEventKey *event, gpointer user_data )
nkeynes@1072
   137
{
nkeynes@1072
   138
    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
nkeynes@1072
   139
    if( keypress_mode ) {
nkeynes@1072
   140
        if( event->keyval == GDK_Escape ) {
nkeynes@1072
   141
            config_text_reset( widget );
nkeynes@1072
   142
            return TRUE;
nkeynes@1072
   143
        }
nkeynes@1072
   144
        GdkKeymap *keymap = gdk_keymap_get_default();
nkeynes@1072
   145
        guint keyval;
nkeynes@1072
   146
nkeynes@1072
   147
        gdk_keymap_translate_keyboard_state( keymap, event->hardware_keycode, 0, 0, &keyval,
nkeynes@1072
   148
                                             NULL, NULL, NULL );
nkeynes@1072
   149
        config_set_field( widget, gdk_keyval_name(keyval) );
nkeynes@1072
   150
        return TRUE;
nkeynes@1072
   151
    } else {
nkeynes@1072
   152
        switch( event->keyval ) {
nkeynes@1072
   153
        case GDK_Return:
nkeynes@1072
   154
        case GDK_KP_Enter:
nkeynes@1072
   155
            gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
nkeynes@1072
   156
            g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
nkeynes@1072
   157
            input_set_keysym_hook((display_keysym_callback_t)config_set_field, widget);
nkeynes@1072
   158
            return TRUE;
nkeynes@1072
   159
        case GDK_BackSpace:
nkeynes@1072
   160
        case GDK_Delete:
nkeynes@1072
   161
            config_set_field( widget, "" );
nkeynes@1072
   162
            return TRUE;
nkeynes@1072
   163
        }
nkeynes@1072
   164
        return FALSE;
nkeynes@1072
   165
    }
nkeynes@1072
   166
}
nkeynes@1072
   167
nkeynes@1072
   168
static gboolean config_key_unfocus( GtkWidget *widget, gpointer user_data )
nkeynes@1072
   169
{
nkeynes@1072
   170
    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
nkeynes@1072
   171
    if( keypress_mode ) {
nkeynes@1072
   172
        /* We've lost focus while waiting for a key binding - restore the old value */
nkeynes@1072
   173
        config_text_reset(widget);
nkeynes@1072
   174
        g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
nkeynes@1072
   175
        input_set_keysym_hook(NULL,NULL);
nkeynes@1072
   176
    }
nkeynes@1072
   177
    return TRUE;
nkeynes@1072
   178
}
nkeynes@1072
   179
nkeynes@1072
   180
static gboolean path_file_button_clicked( GtkWidget *button, gpointer user_data )
nkeynes@1072
   181
{
nkeynes@1072
   182
    GtkWidget *entry = GTK_WIDGET(user_data);
nkeynes@1072
   183
    GtkWidget *file = gtk_file_chooser_dialog_new( _("Select file"), NULL,
nkeynes@1072
   184
            GTK_FILE_CHOOSER_ACTION_OPEN,
nkeynes@1072
   185
            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
nkeynes@1072
   186
            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
nkeynes@1072
   187
            NULL );
nkeynes@1072
   188
    gchar *filename = get_expanded_path(gtk_entry_get_text(GTK_ENTRY(entry)));
nkeynes@1072
   189
    gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(file), filename );
nkeynes@1072
   190
    gtk_window_set_modal( GTK_WINDOW(file), TRUE );
nkeynes@1072
   191
    gtk_widget_show_all( file );
nkeynes@1072
   192
    gint result = gtk_dialog_run(GTK_DIALOG(file));
nkeynes@1072
   193
    g_free(filename);
nkeynes@1072
   194
    if( result == GTK_RESPONSE_ACCEPT ) {
nkeynes@1072
   195
        filename = get_escaped_path(gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(file) ));
nkeynes@1072
   196
        config_set_field( entry, filename );
nkeynes@1072
   197
        g_free(filename);
nkeynes@1072
   198
    }
nkeynes@1072
   199
    gtk_widget_destroy(file);
nkeynes@1072
   200
    return TRUE;
nkeynes@1072
   201
}
nkeynes@1072
   202
nkeynes@1072
   203
static gboolean path_dir_button_clicked( GtkWidget *button, gpointer user_data )
nkeynes@1072
   204
{
nkeynes@1072
   205
    GtkWidget *entry = GTK_WIDGET(user_data);
nkeynes@1072
   206
    GtkWidget *file = gtk_file_chooser_dialog_new( _("Select file"), NULL,
nkeynes@1072
   207
            GTK_FILE_CHOOSER_ACTION_OPEN,
nkeynes@1072
   208
            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
nkeynes@1072
   209
            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
nkeynes@1072
   210
            NULL );
nkeynes@1072
   211
    gchar *filename = get_expanded_path(gtk_entry_get_text(GTK_ENTRY(entry)));
nkeynes@1072
   212
    gtk_file_chooser_set_action( GTK_FILE_CHOOSER(file), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
nkeynes@1072
   213
    gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(file), filename );
nkeynes@1072
   214
    gtk_window_set_modal( GTK_WINDOW(file), TRUE );
nkeynes@1072
   215
    gtk_widget_show_all( file );
nkeynes@1072
   216
    gint result = gtk_dialog_run(GTK_DIALOG(file));
nkeynes@1072
   217
    g_free(filename);
nkeynes@1072
   218
    if( result == GTK_RESPONSE_ACCEPT ) {
nkeynes@1072
   219
        filename = get_escaped_path(gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(file) ));
nkeynes@1072
   220
        config_set_field( entry, filename );
nkeynes@1072
   221
        g_free(filename);
nkeynes@1072
   222
    }
nkeynes@1072
   223
    gtk_widget_destroy(file);
nkeynes@1072
   224
    return TRUE;
nkeynes@1072
   225
}
nkeynes@1072
   226
nkeynes@1072
   227
nkeynes@1072
   228
static void lxdream_configuration_panel_destroy( GtkWidget *panel, gpointer data )
nkeynes@1072
   229
{
nkeynes@1072
   230
    input_set_keysym_hook(NULL, NULL);
nkeynes@1072
   231
}
nkeynes@1072
   232
nkeynes@1072
   233
static GtkWidget *gtk_configuration_panel_new( lxdream_config_group_t conf )
nkeynes@1072
   234
{
nkeynes@1072
   235
    int count, i;
nkeynes@1072
   236
    for( count=0; conf->params[count].label != NULL; count++ );
nkeynes@1072
   237
nkeynes@1072
   238
    GtkWidget *table = gtk_table_new( count, 5, FALSE );
nkeynes@1072
   239
    struct config_data *data = g_malloc0( sizeof(struct config_data) );
nkeynes@1072
   240
    data->config = conf;
nkeynes@1072
   241
    GList *focus_chain = NULL;
nkeynes@1072
   242
    gtk_object_set_data_full( GTK_OBJECT(table), "config_data", data, g_free );
nkeynes@1072
   243
    g_signal_connect( table, "destroy_event", G_CALLBACK(lxdream_configuration_panel_destroy), NULL );
nkeynes@1072
   244
    for( i=0; conf->params[i].label != NULL; i++ ) {
nkeynes@1072
   245
        GtkWidget *text, *text2, *button;
nkeynes@1072
   246
        int x=0;
nkeynes@1072
   247
        int y=i;
nkeynes@1072
   248
        gtk_table_attach( GTK_TABLE(table), gtk_label_new(Q_(conf->params[i].label)), x, x+1, y, y+1,
nkeynes@1072
   249
                          GTK_SHRINK, GTK_SHRINK, 0, 0 );
nkeynes@1072
   250
        switch( conf->params[i].type ) {
nkeynes@1072
   251
        case CONFIG_TYPE_KEY:
nkeynes@1072
   252
            data->fields[i][0] = text = gtk_entry_new();
nkeynes@1072
   253
            gtk_entry_set_width_chars( GTK_ENTRY(text), 11 );
nkeynes@1072
   254
            gtk_entry_set_editable( GTK_ENTRY(text), FALSE );
nkeynes@1072
   255
            g_signal_connect( text, "key_press_event",
nkeynes@1072
   256
                              G_CALLBACK(config_key_keypress), NULL );
nkeynes@1072
   257
            g_signal_connect( text, "button_press_event",
nkeynes@1072
   258
                              G_CALLBACK(config_key_buttonpress), NULL );
nkeynes@1072
   259
            g_signal_connect( text, "focus_out_event",
nkeynes@1072
   260
                              G_CALLBACK(config_key_unfocus), NULL);
nkeynes@1072
   261
            g_object_set_data( G_OBJECT(text), "keypress_mode", GINT_TO_POINTER(FALSE) );
nkeynes@1072
   262
            g_object_set_data( G_OBJECT(text), "tag", GINT_TO_POINTER(i) );
nkeynes@1072
   263
            gtk_table_attach_defaults( GTK_TABLE(table), text, x+1, x+2, y, y+1);
nkeynes@1072
   264
            focus_chain = g_list_append( focus_chain, text );
nkeynes@1072
   265
nkeynes@1072
   266
            data->fields[i][1] = text2 = gtk_entry_new();
nkeynes@1072
   267
            gtk_entry_set_width_chars( GTK_ENTRY(text2), 11 );
nkeynes@1072
   268
            gtk_entry_set_editable( GTK_ENTRY(text2), FALSE );
nkeynes@1072
   269
            g_signal_connect( text2, "key_press_event",
nkeynes@1072
   270
                              G_CALLBACK(config_key_keypress), NULL );
nkeynes@1072
   271
            g_signal_connect( text2, "button_press_event",
nkeynes@1072
   272
                              G_CALLBACK(config_key_buttonpress), NULL );
nkeynes@1072
   273
            g_signal_connect( text2, "focus_out_event",
nkeynes@1072
   274
                              G_CALLBACK(config_key_unfocus), NULL);
nkeynes@1072
   275
            g_object_set_data( G_OBJECT(text2), "keypress_mode", GINT_TO_POINTER(FALSE) );
nkeynes@1072
   276
            g_object_set_data( G_OBJECT(text2), "tag", GINT_TO_POINTER(i) );
nkeynes@1072
   277
            gtk_table_attach_defaults( GTK_TABLE(table), text2, x+2, x+3, y, y+1);
nkeynes@1072
   278
            focus_chain = g_list_append( focus_chain, text2 );
nkeynes@1072
   279
nkeynes@1072
   280
            if( conf->params[i].value != NULL ) {
nkeynes@1072
   281
                gchar **parts = g_strsplit(conf->params[i].value,",",3);
nkeynes@1072
   282
                if( parts[0] != NULL ) {
nkeynes@1072
   283
                    gtk_entry_set_text( GTK_ENTRY(text), g_strstrip(parts[0]) );
nkeynes@1072
   284
                    if( parts[1] != NULL ) {
nkeynes@1072
   285
                        gtk_entry_set_text( GTK_ENTRY(text2), g_strstrip(parts[1]) );
nkeynes@1072
   286
                    }
nkeynes@1072
   287
                }
nkeynes@1072
   288
                g_strfreev(parts);
nkeynes@1072
   289
            }
nkeynes@1072
   290
            break;
nkeynes@1072
   291
        case CONFIG_TYPE_FILE:
nkeynes@1072
   292
        case CONFIG_TYPE_PATH:
nkeynes@1072
   293
            data->fields[i][0] = text = gtk_entry_new();
nkeynes@1072
   294
            data->fields[i][1] = NULL;
nkeynes@1072
   295
            button = gtk_button_new();
nkeynes@1072
   296
            gtk_entry_set_text( GTK_ENTRY(text), conf->params[i].value );
nkeynes@1072
   297
            gtk_entry_set_width_chars( GTK_ENTRY(text), 48 );
nkeynes@1072
   298
            g_object_set_data( G_OBJECT(text), "tag", GINT_TO_POINTER(i) );
nkeynes@1072
   299
            gtk_table_attach_defaults( GTK_TABLE(table), text, 1, 2, y, y+1 );
nkeynes@1072
   300
            gtk_table_attach( GTK_TABLE(table), button, 2, 3, y, y+1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
nkeynes@1072
   301
            g_signal_connect( text, "changed", G_CALLBACK(config_text_changed), NULL );
nkeynes@1072
   302
            if( conf->params[i].type == CONFIG_TYPE_FILE ) {
nkeynes@1072
   303
                GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
nkeynes@1072
   304
                gtk_button_set_image( GTK_BUTTON(button), image );
nkeynes@1072
   305
                g_signal_connect( button, "clicked", G_CALLBACK(path_file_button_clicked), text );
nkeynes@1072
   306
            } else {
nkeynes@1072
   307
                GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU);
nkeynes@1072
   308
                gtk_button_set_image( GTK_BUTTON(button), image );
nkeynes@1072
   309
                g_signal_connect( button, "clicked", G_CALLBACK(path_dir_button_clicked), text );
nkeynes@1072
   310
            }
nkeynes@1072
   311
            break;
nkeynes@1072
   312
        }
nkeynes@1072
   313
    }
nkeynes@1072
   314
    gtk_container_set_focus_chain( GTK_CONTAINER(table), focus_chain );
nkeynes@1072
   315
//    gtk_gui_run_property_dialog( _("Controller Configuration"), table, controller_config_done );
nkeynes@1072
   316
    return table;
nkeynes@1072
   317
}
nkeynes@1072
   318
nkeynes@1072
   319
int gtk_configuration_panel_run( const gchar *title, lxdream_config_group_t group )
nkeynes@1072
   320
{
nkeynes@1072
   321
    struct lxdream_config_group tmp;
nkeynes@1072
   322
    lxdream_clone_config_group( &tmp, group );
nkeynes@1072
   323
    GtkWidget *panel = gtk_configuration_panel_new( &tmp );
nkeynes@1072
   324
    int result = gtk_gui_run_property_dialog( title, panel, NULL );
nkeynes@1072
   325
    if( result == GTK_RESPONSE_ACCEPT ) {
nkeynes@1072
   326
        lxdream_copy_config_group( group, &tmp );
nkeynes@1072
   327
        lxdream_save_config();
nkeynes@1072
   328
    }
nkeynes@1072
   329
    return result;
nkeynes@1072
   330
}
.