eekboard: config file support.
This commit is contained in:
@ -565,7 +565,7 @@ eek_xkl_layout_get_model (EekXklLayout *layout)
|
|||||||
EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (layout);
|
EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (layout);
|
||||||
|
|
||||||
g_return_val_if_fail (priv, NULL);
|
g_return_val_if_fail (priv, NULL);
|
||||||
return priv->config->model;
|
return g_strdup (priv->config->model);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -581,7 +581,7 @@ eek_xkl_layout_get_layouts (EekXklLayout *layout)
|
|||||||
EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (layout);
|
EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (layout);
|
||||||
|
|
||||||
g_return_val_if_fail (priv, NULL);
|
g_return_val_if_fail (priv, NULL);
|
||||||
return priv->config->layouts;
|
return g_strdupv (priv->config->layouts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -597,7 +597,7 @@ eek_xkl_layout_get_variants (EekXklLayout *layout)
|
|||||||
EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (layout);
|
EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (layout);
|
||||||
|
|
||||||
g_return_val_if_fail (priv, NULL);
|
g_return_val_if_fail (priv, NULL);
|
||||||
return priv->config->variants;
|
return g_strdupv (priv->config->variants);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -613,7 +613,7 @@ eek_xkl_layout_get_options (EekXklLayout *layout)
|
|||||||
EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (layout);
|
EekXklLayoutPrivate *priv = EEK_XKL_LAYOUT_GET_PRIVATE (layout);
|
||||||
|
|
||||||
g_return_val_if_fail (priv, NULL);
|
g_return_val_if_fail (priv, NULL);
|
||||||
return priv->config->options;
|
return g_strdupv (priv->config->options);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
|||||||
267
src/eekboard.c
267
src/eekboard.c
@ -76,15 +76,23 @@
|
|||||||
"You should have received a copy of the GNU General Public License " \
|
"You should have received a copy of the GNU General Public License " \
|
||||||
"along with this program. If not, see <http://www.gnu.org/licenses/>. " \
|
"along with this program. If not, see <http://www.gnu.org/licenses/>. " \
|
||||||
|
|
||||||
|
struct _Config {
|
||||||
|
gchar *name;
|
||||||
|
XklConfigRec *rec;
|
||||||
|
};
|
||||||
|
typedef struct _Config Config;
|
||||||
|
|
||||||
struct _Eekboard {
|
struct _Eekboard {
|
||||||
gboolean use_clutter;
|
gboolean use_clutter;
|
||||||
gboolean need_swap_event_workaround;
|
gboolean need_swap_event_workaround;
|
||||||
gboolean accessibility_enabled;
|
gboolean accessibility_enabled;
|
||||||
|
Config **config;
|
||||||
|
gint active_config;
|
||||||
Display *display;
|
Display *display;
|
||||||
FakeKey *fakekey;
|
FakeKey *fakekey;
|
||||||
GConfClient *gconfc;
|
GConfClient *gconfc;
|
||||||
Accessible *acc;
|
Accessible *acc;
|
||||||
GtkWidget *widget, *window;
|
GtkWidget *widget, *window, *combo;
|
||||||
gint width, height;
|
gint width, height;
|
||||||
XklEngine *engine;
|
XklEngine *engine;
|
||||||
XklConfigRegistry *registry;
|
XklConfigRegistry *registry;
|
||||||
@ -230,6 +238,7 @@ static gboolean opt_version = FALSE;
|
|||||||
static gchar *opt_toolkit = NULL;
|
static gchar *opt_toolkit = NULL;
|
||||||
#endif
|
#endif
|
||||||
static gboolean opt_standalone = FALSE;
|
static gboolean opt_standalone = FALSE;
|
||||||
|
static gchar *opt_config = NULL;
|
||||||
|
|
||||||
static const GOptionEntry options[] = {
|
static const GOptionEntry options[] = {
|
||||||
{"model", 'M', 0, G_OPTION_ARG_STRING, &opt_model,
|
{"model", 'M', 0, G_OPTION_ARG_STRING, &opt_model,
|
||||||
@ -250,6 +259,8 @@ static const GOptionEntry options[] = {
|
|||||||
#endif
|
#endif
|
||||||
{"standalone", 's', 0, G_OPTION_ARG_NONE, &opt_standalone,
|
{"standalone", 's', 0, G_OPTION_ARG_NONE, &opt_standalone,
|
||||||
N_("Start as a standalone application")},
|
N_("Start as a standalone application")},
|
||||||
|
{"config", 'c', 0, G_OPTION_ARG_STRING, &opt_config,
|
||||||
|
N_("Specify configuration file")},
|
||||||
{"version", 'v', 0, G_OPTION_ARG_NONE, &opt_version,
|
{"version", 'v', 0, G_OPTION_ARG_NONE, &opt_version,
|
||||||
N_("Display version")},
|
N_("Display version")},
|
||||||
{NULL}
|
{NULL}
|
||||||
@ -1080,6 +1091,31 @@ create_widget (Eekboard *eekboard,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_layouts (XklConfigRec *rec, const gchar *_layouts)
|
||||||
|
{
|
||||||
|
gchar **layouts, **variants;
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
layouts = g_strsplit (_layouts, ",", -1);
|
||||||
|
variants = g_strdupv (layouts);
|
||||||
|
for (i = 0; layouts[i]; i++) {
|
||||||
|
gchar *layout = layouts[i], *variant = variants[i],
|
||||||
|
*variant_start, *variant_end;
|
||||||
|
|
||||||
|
variant_start = strchr (layout, '(');
|
||||||
|
variant_end = strrchr (layout, ')');
|
||||||
|
if (variant_start && variant_end) {
|
||||||
|
*variant_start++ = '\0';
|
||||||
|
g_strlcpy (variant, variant_start,
|
||||||
|
variant_end - variant_start + 1);
|
||||||
|
} else
|
||||||
|
*variant = '\0';
|
||||||
|
}
|
||||||
|
rec->layouts = layouts;
|
||||||
|
rec->variants = variants;
|
||||||
|
}
|
||||||
|
|
||||||
Eekboard *
|
Eekboard *
|
||||||
eekboard_new (gboolean use_clutter,
|
eekboard_new (gboolean use_clutter,
|
||||||
gboolean need_swap_event_workaround,
|
gboolean need_swap_event_workaround,
|
||||||
@ -1091,6 +1127,7 @@ eekboard_new (gboolean use_clutter,
|
|||||||
eekboard->use_clutter = use_clutter;
|
eekboard->use_clutter = use_clutter;
|
||||||
eekboard->need_swap_event_workaround = need_swap_event_workaround;
|
eekboard->need_swap_event_workaround = need_swap_event_workaround;
|
||||||
eekboard->accessibility_enabled = accessibility_enabled;
|
eekboard->accessibility_enabled = accessibility_enabled;
|
||||||
|
eekboard->active_config = -1;
|
||||||
eekboard->display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
eekboard->display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||||
if (!eekboard->display) {
|
if (!eekboard->display) {
|
||||||
g_slice_free (Eekboard, eekboard);
|
g_slice_free (Eekboard, eekboard);
|
||||||
@ -1114,26 +1151,14 @@ eekboard_new (gboolean use_clutter,
|
|||||||
if (opt_model)
|
if (opt_model)
|
||||||
eek_xkl_layout_set_model (EEK_XKL_LAYOUT(eekboard->layout), opt_model);
|
eek_xkl_layout_set_model (EEK_XKL_LAYOUT(eekboard->layout), opt_model);
|
||||||
if (opt_layouts) {
|
if (opt_layouts) {
|
||||||
gchar **layouts, **variants;
|
XklConfigRec *rec = xkl_config_rec_new ();
|
||||||
gint i;
|
|
||||||
layouts = g_strsplit (opt_layouts, ",", -1);
|
|
||||||
variants = g_strdupv (layouts);
|
|
||||||
for (i = 0; layouts[i]; i++) {
|
|
||||||
gchar *layout = layouts[i], *variant = variants[i],
|
|
||||||
*variant_start, *variant_end;
|
|
||||||
|
|
||||||
variant_start = strchr (layout, '(');
|
parse_layouts (rec, opt_layouts);
|
||||||
variant_end = strrchr (layout, ')');
|
eek_xkl_layout_set_layouts (EEK_XKL_LAYOUT(eekboard->layout),
|
||||||
if (variant_start && variant_end) {
|
rec->layouts);
|
||||||
*variant_start++ = '\0';
|
eek_xkl_layout_set_variants (EEK_XKL_LAYOUT(eekboard->layout),
|
||||||
g_strlcpy (variant, variant_start,
|
rec->variants);
|
||||||
variant_end - variant_start + 1);
|
g_object_unref (rec);
|
||||||
} else
|
|
||||||
*variant = '\0';
|
|
||||||
}
|
|
||||||
eek_xkl_layout_set_layouts (EEK_XKL_LAYOUT(eekboard->layout), layouts);
|
|
||||||
g_strfreev (layouts);
|
|
||||||
g_strfreev (variants);
|
|
||||||
}
|
}
|
||||||
if (opt_options) {
|
if (opt_options) {
|
||||||
gchar **options;
|
gchar **options;
|
||||||
@ -1229,6 +1254,126 @@ on_notify_never_show (NotifyNotification *notification,
|
|||||||
&error);
|
&error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_layout_changed (GtkComboBox *combo,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
Eekboard *eekboard = user_data;
|
||||||
|
GtkTreeModel *model;
|
||||||
|
GtkTreeIter iter;
|
||||||
|
gint active;
|
||||||
|
|
||||||
|
model = gtk_combo_box_get_model (combo);
|
||||||
|
gtk_combo_box_get_active_iter (combo, &iter);
|
||||||
|
gtk_tree_model_get (model, &iter, 1, &active, -1);
|
||||||
|
|
||||||
|
if (eekboard->active_config != active) {
|
||||||
|
XklConfigRec *config, *config_base = eekboard->config[active]->rec;
|
||||||
|
|
||||||
|
config = xkl_config_rec_new ();
|
||||||
|
if (config_base->model)
|
||||||
|
config->model = g_strdup (config_base->model);
|
||||||
|
else
|
||||||
|
config->model = eek_xkl_layout_get_model (eekboard->layout);
|
||||||
|
|
||||||
|
if (config_base->layouts)
|
||||||
|
config->layouts = g_strdupv (config_base->layouts);
|
||||||
|
else
|
||||||
|
config->layouts = eek_xkl_layout_get_layouts (eekboard->layout);
|
||||||
|
|
||||||
|
if (config_base->variants)
|
||||||
|
config->variants = g_strdupv (config_base->variants);
|
||||||
|
else
|
||||||
|
config->variants = eek_xkl_layout_get_variants (eekboard->layout);
|
||||||
|
|
||||||
|
if (config_base->options)
|
||||||
|
config->options = g_strdupv (config_base->options);
|
||||||
|
else
|
||||||
|
config->options = eek_xkl_layout_get_options (eekboard->layout);
|
||||||
|
|
||||||
|
eek_xkl_layout_set_config (eekboard->layout, config);
|
||||||
|
g_object_unref (config);
|
||||||
|
|
||||||
|
eekboard->active_config = active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _ConfigContext {
|
||||||
|
GSList *list;
|
||||||
|
GString *text;
|
||||||
|
};
|
||||||
|
typedef struct _ConfigContext ConfigContext;
|
||||||
|
|
||||||
|
static void
|
||||||
|
config_parser_start_element (GMarkupParseContext *pcontext,
|
||||||
|
const gchar *element_name,
|
||||||
|
const gchar **attribute_names,
|
||||||
|
const gchar **attribute_values,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
ConfigContext *context = user_data;
|
||||||
|
if (g_strcmp0 (element_name, "config") == 0) {
|
||||||
|
Config *config = g_slice_new0 (Config);
|
||||||
|
config->rec = xkl_config_rec_new ();
|
||||||
|
context->list = g_slist_prepend (context->list, config);
|
||||||
|
} else
|
||||||
|
context->text = g_string_sized_new (100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
config_parser_end_element (GMarkupParseContext *pcontext,
|
||||||
|
const gchar *element_name,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
ConfigContext *context = user_data;
|
||||||
|
Config *config = context->list->data;
|
||||||
|
gchar *text;
|
||||||
|
|
||||||
|
if (g_strcmp0 (element_name, "config") == 0 &&
|
||||||
|
!config->name) {
|
||||||
|
if (error)
|
||||||
|
*error = g_error_new (G_MARKUP_ERROR,
|
||||||
|
G_MARKUP_ERROR_INVALID_CONTENT,
|
||||||
|
"\"name\" is missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context->text)
|
||||||
|
return;
|
||||||
|
|
||||||
|
text = g_string_free (context->text, FALSE);
|
||||||
|
context->text = NULL;
|
||||||
|
|
||||||
|
if (g_strcmp0 (element_name, "name") == 0)
|
||||||
|
config->name = text;
|
||||||
|
else if (g_strcmp0 (element_name, "model") == 0)
|
||||||
|
config->rec->model = text;
|
||||||
|
else if (g_strcmp0 (element_name, "layouts") == 0)
|
||||||
|
parse_layouts (config->rec, text);
|
||||||
|
else if (g_strcmp0 (element_name, "options") == 0)
|
||||||
|
config->rec->options = g_strsplit (text, ",", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
config_parser_text (GMarkupParseContext *pcontext,
|
||||||
|
const gchar *text,
|
||||||
|
gsize text_len,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
ConfigContext *context = user_data;
|
||||||
|
if (context->text)
|
||||||
|
context->text = g_string_append_len (context->text, text, text_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
GMarkupParser config_parser = {
|
||||||
|
.start_element = config_parser_start_element,
|
||||||
|
.end_element = config_parser_end_element,
|
||||||
|
.text = config_parser_text
|
||||||
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, char *argv[])
|
main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@ -1237,7 +1382,7 @@ main (int argc, char *argv[])
|
|||||||
gboolean need_swap_event_workaround = FALSE;
|
gboolean need_swap_event_workaround = FALSE;
|
||||||
gboolean accessibility_enabled = FALSE;
|
gboolean accessibility_enabled = FALSE;
|
||||||
Eekboard *eekboard;
|
Eekboard *eekboard;
|
||||||
GtkWidget *widget, *vbox, *menubar, *window;
|
GtkWidget *widget, *vbox, *menubar, *window, *combo = NULL;
|
||||||
GOptionContext *context;
|
GOptionContext *context;
|
||||||
GConfClient *gconfc;
|
GConfClient *gconfc;
|
||||||
GError *error;
|
GError *error;
|
||||||
@ -1351,6 +1496,81 @@ main (int argc, char *argv[])
|
|||||||
gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
|
gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opt_config) {
|
||||||
|
ConfigContext context;
|
||||||
|
GMarkupParseContext *pcontext;
|
||||||
|
GFile *file;
|
||||||
|
GError *error;
|
||||||
|
GFileInputStream *stream;
|
||||||
|
gchar buf[BUFSIZ];
|
||||||
|
GSList *head;
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
memset (&context, 0, sizeof context);
|
||||||
|
file = g_file_new_for_path (opt_config);
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
stream = g_file_read (file, NULL, &error);
|
||||||
|
if (stream) {
|
||||||
|
pcontext = g_markup_parse_context_new (&config_parser,
|
||||||
|
0,
|
||||||
|
&context,
|
||||||
|
NULL);
|
||||||
|
while (1) {
|
||||||
|
gssize len;
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
len = g_input_stream_read (stream, buf, sizeof buf, NULL,
|
||||||
|
&error);
|
||||||
|
if (len <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
if (!g_markup_parse_context_parse (pcontext, buf, len, &error))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_object_unref (stream);
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
g_markup_parse_context_end_parse (pcontext, &error);
|
||||||
|
g_markup_parse_context_free (pcontext);
|
||||||
|
}
|
||||||
|
g_object_unref (file);
|
||||||
|
|
||||||
|
if (context.list) {
|
||||||
|
eekboard->config =
|
||||||
|
g_slice_alloc0 ((g_slist_length (context.list) + 1) *
|
||||||
|
sizeof (*eekboard->config));
|
||||||
|
for (i = 0, head = context.list; head; head = head->next)
|
||||||
|
eekboard->config[i++] = head->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eekboard->config) {
|
||||||
|
GtkListStore *store;
|
||||||
|
GtkTreeIter iter;
|
||||||
|
GtkCellRenderer *renderer;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
|
||||||
|
for (i = 0; eekboard->config[i]; i++) {
|
||||||
|
gtk_list_store_append (store, &iter);
|
||||||
|
gtk_list_store_set (store, &iter,
|
||||||
|
0, eekboard->config[i]->name,
|
||||||
|
1, i,
|
||||||
|
-1);
|
||||||
|
}
|
||||||
|
combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
|
||||||
|
renderer = gtk_cell_renderer_text_new ();
|
||||||
|
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(combo),
|
||||||
|
renderer, TRUE);
|
||||||
|
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT(combo),
|
||||||
|
renderer, "text", 0, NULL);
|
||||||
|
gtk_box_pack_end (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
|
||||||
|
g_signal_connect (combo, "changed", G_CALLBACK(on_layout_changed),
|
||||||
|
eekboard);
|
||||||
|
}
|
||||||
|
|
||||||
gtk_container_add (GTK_CONTAINER(vbox), widget);
|
gtk_container_add (GTK_CONTAINER(vbox), widget);
|
||||||
gtk_container_add (GTK_CONTAINER(window), vbox);
|
gtk_container_add (GTK_CONTAINER(window), vbox);
|
||||||
|
|
||||||
@ -1397,6 +1617,11 @@ main (int argc, char *argv[])
|
|||||||
"focus:");
|
"focus:");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL);
|
||||||
|
|
||||||
|
if (combo)
|
||||||
|
gtk_combo_box_set_active (combo, 0);
|
||||||
|
|
||||||
gtk_main ();
|
gtk_main ();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user