1335 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1335 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 
 | 
						|
 * Copyright (C) 2010 Daiki Ueno <ueno@unixuser.org>
 | 
						|
 * Copyright (C) 2010 Red Hat, Inc.
 | 
						|
 * 
 | 
						|
 * 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, either version 3 of the License, or
 | 
						|
 * (at your option) any later 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, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef HAVE_CONFIG_H
 | 
						|
#include "config.h"
 | 
						|
#endif  /* HAVE_CONFIG_H */
 | 
						|
 | 
						|
#if HAVE_CLUTTER_GTK
 | 
						|
#include <clutter-gtk/clutter-gtk.h>
 | 
						|
#if NEED_SWAP_EVENT_WORKAROUND
 | 
						|
#include <clutter/x11/clutter-x11.h>
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
#include <glib/gi18n.h>
 | 
						|
#include <gdk/gdkx.h>
 | 
						|
#include <gtk/gtk.h>
 | 
						|
#include <gconf/gconf-client.h>
 | 
						|
#include <libxklavier/xklavier.h>
 | 
						|
#include <fakekey/fakekey.h>
 | 
						|
#include <cspi/spi.h>
 | 
						|
#include <libnotify/notify.h>
 | 
						|
 | 
						|
#include <string.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#if HAVE_CLUTTER_GTK
 | 
						|
#include "eek/eek-clutter.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#include "eek/eek-gtk.h"
 | 
						|
#include "eek/eek-xkl.h"
 | 
						|
 | 
						|
#define CSW 640
 | 
						|
#define CSH 480
 | 
						|
 | 
						|
#if HAVE_CLUTTER_GTK
 | 
						|
#define USE_CLUTTER TRUE
 | 
						|
#else
 | 
						|
#define USE_CLUTTER FALSE
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef EEKBOARD_ENABLE_DEBUG
 | 
						|
#define EEKBOARD_NOTE(x,a...) g_message (G_STRLOC ": " x, ##a);
 | 
						|
#else
 | 
						|
#define EEKBOARD_NOTE(x,a...)
 | 
						|
#endif
 | 
						|
 | 
						|
#define LICENSE \
 | 
						|
    "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, either version 3 of the License, or " \
 | 
						|
    "(at your option) any later version." \
 | 
						|
    "\n\n" \
 | 
						|
    "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." \
 | 
						|
    "\n\n" \
 | 
						|
    "You should have received a copy of the GNU General Public License " \
 | 
						|
    "along with this program.  If not, see <http://www.gnu.org/licenses/>. " \
 | 
						|
 | 
						|
struct _Eekboard {
 | 
						|
    gboolean use_clutter;
 | 
						|
    gboolean need_swap_event_workaround;
 | 
						|
    gboolean accessibility_enabled;
 | 
						|
    Display *display;
 | 
						|
    FakeKey *fakekey;
 | 
						|
    GConfClient *gconfc;
 | 
						|
    GtkWidget *widget, *window;
 | 
						|
    gint width, height;
 | 
						|
    XklEngine *engine;
 | 
						|
    XklConfigRegistry *registry;
 | 
						|
    GtkUIManager *ui_manager;
 | 
						|
 | 
						|
    guint countries_merge_id;
 | 
						|
    GtkActionGroup *countries_action_group;
 | 
						|
 | 
						|
    guint languages_merge_id;
 | 
						|
    GtkActionGroup *languages_action_group;
 | 
						|
 | 
						|
    guint models_merge_id;
 | 
						|
    GtkActionGroup *models_action_group;
 | 
						|
 | 
						|
    guint layouts_merge_id;
 | 
						|
    GtkActionGroup *layouts_action_group;
 | 
						|
 | 
						|
    guint options_merge_id;
 | 
						|
    GtkActionGroup *options_action_group;
 | 
						|
 | 
						|
    EekKeyboard *keyboard;
 | 
						|
    EekLayout *layout;          /* FIXME: eek_keyboard_get_layout() */
 | 
						|
    guint modifiers;
 | 
						|
};
 | 
						|
typedef struct _Eekboard Eekboard;
 | 
						|
 | 
						|
struct _SetConfigCallbackData {
 | 
						|
    Eekboard *eekboard;
 | 
						|
    XklConfigRec *config;
 | 
						|
};
 | 
						|
typedef struct _SetConfigCallbackData SetConfigCallbackData;
 | 
						|
 | 
						|
struct _CreateMenuCallbackData {
 | 
						|
    Eekboard *eekboard;
 | 
						|
    GtkActionGroup *action_group;
 | 
						|
    guint merge_id;
 | 
						|
};
 | 
						|
typedef struct _CreateMenuCallbackData CreateMenuCallbackData;
 | 
						|
 | 
						|
struct _LayoutVariant {
 | 
						|
    XklConfigItem *layout;
 | 
						|
    XklConfigItem *variant;
 | 
						|
};
 | 
						|
typedef struct _LayoutVariant LayoutVariant;
 | 
						|
 | 
						|
static void       on_countries_menu (GtkAction       *action,
 | 
						|
                                     GtkWidget       *widget);
 | 
						|
static void       on_languages_menu (GtkAction       *action,
 | 
						|
                                     GtkWidget       *widget);
 | 
						|
static void       on_models_menu    (GtkAction       *action,
 | 
						|
                                     GtkWidget       *window);
 | 
						|
static void       on_layouts_menu   (GtkAction       *action,
 | 
						|
                                     GtkWidget       *window);
 | 
						|
static void       on_options_menu   (GtkAction       *action,
 | 
						|
                                     GtkWidget       *window);
 | 
						|
static void       on_about          (GtkAction       *action,
 | 
						|
                                     GtkWidget       *window);
 | 
						|
static void       on_quit           (GtkAction *      action,
 | 
						|
                                     GtkWidget       *window);
 | 
						|
static void       on_monitor_key_event_toggled
 | 
						|
                                    (GtkToggleAction *action,
 | 
						|
                                     GtkWidget       *window);
 | 
						|
static void       eekboard_free     (Eekboard        *eekboard);
 | 
						|
static GtkWidget *create_widget     (Eekboard        *eekboard,
 | 
						|
                                     gint             initial_width,
 | 
						|
                                     gint             initial_height);
 | 
						|
static GtkWidget *create_widget_gtk (Eekboard        *eekboard,
 | 
						|
                                     gint             initial_width,
 | 
						|
                                     gint             initial_height);
 | 
						|
 | 
						|
#if !HAVE_CLUTTER_GTK
 | 
						|
#define create_widget create_widget_gtk
 | 
						|
#endif
 | 
						|
 | 
						|
static const char ui_description[] =
 | 
						|
    "<ui>"
 | 
						|
    "  <menubar name='MainMenu'>"
 | 
						|
    "    <menu action='FileMenu'>"
 | 
						|
    "      <menuitem action='Quit'/>"
 | 
						|
    "    </menu>"
 | 
						|
    "    <menu action='KeyboardMenu'>"
 | 
						|
    "      <menuitem action='MonitorKeyEvent'/>"
 | 
						|
    "      <menu action='Country'>"
 | 
						|
    "        <placeholder name='CountriesPH'/>"
 | 
						|
    "      </menu>"
 | 
						|
    "      <menu action='Language'>"
 | 
						|
    "        <placeholder name='LanguagesPH'/>"
 | 
						|
    "      </menu>"
 | 
						|
    "      <separator/>"
 | 
						|
    "      <menu action='Model'>"
 | 
						|
    "        <placeholder name='ModelsPH'/>"
 | 
						|
    "      </menu>"
 | 
						|
    "      <menu action='Layout'>"
 | 
						|
    "        <placeholder name='LayoutsPH'/>"
 | 
						|
    "      </menu>"
 | 
						|
    "      <menu action='Option'>"
 | 
						|
    "        <placeholder name='OptionsPH'/>"
 | 
						|
    "      </menu>"
 | 
						|
    "    </menu>"
 | 
						|
    "    <menu action='HelpMenu'>"
 | 
						|
    "      <menuitem action='About'/>"
 | 
						|
    "    </menu>"
 | 
						|
    "  </menubar>"
 | 
						|
    "</ui>";
 | 
						|
 | 
						|
#define COUNTRIES_UI_PATH "/MainMenu/KeyboardMenu/Country/CountriesPH"
 | 
						|
#define LANGUAGES_UI_PATH "/MainMenu/KeyboardMenu/Language/LanguagesPH"
 | 
						|
#define MODELS_UI_PATH "/MainMenu/KeyboardMenu/Model/ModelsPH"
 | 
						|
#define LAYOUTS_UI_PATH "/MainMenu/KeyboardMenu/Layout/LayoutsPH"
 | 
						|
#define OPTIONS_UI_PATH "/MainMenu/KeyboardMenu/Option/OptionsPH"
 | 
						|
 | 
						|
static const GtkActionEntry action_entry[] = {
 | 
						|
    {"FileMenu", NULL, N_("_File")},
 | 
						|
    {"KeyboardMenu", NULL, N_("_Keyboard")},
 | 
						|
    {"HelpMenu", NULL, N_("_Help")},
 | 
						|
    {"Quit", GTK_STOCK_QUIT, NULL, NULL, NULL, G_CALLBACK (on_quit)},
 | 
						|
    {"Country", NULL, N_("Country"), NULL, NULL,
 | 
						|
     G_CALLBACK(on_countries_menu)},
 | 
						|
    {"Language", NULL, N_("Language"), NULL, NULL,
 | 
						|
     G_CALLBACK(on_languages_menu)},
 | 
						|
    {"Model", NULL, N_("Model"), NULL, NULL,
 | 
						|
     G_CALLBACK(on_models_menu)},
 | 
						|
    {"Layout", NULL, N_("Layout"), NULL, NULL,
 | 
						|
     G_CALLBACK(on_layouts_menu)},
 | 
						|
    {"Option", NULL, N_("Option"), NULL, NULL,
 | 
						|
     G_CALLBACK(on_options_menu)},
 | 
						|
    {"About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK (on_about)}
 | 
						|
};
 | 
						|
 | 
						|
static const GtkToggleActionEntry toggle_action_entry[] = {
 | 
						|
    {"MonitorKeyEvent", NULL, N_("Monitor Key Typing"), NULL, NULL,
 | 
						|
     G_CALLBACK(on_monitor_key_event_toggled), FALSE}
 | 
						|
};
 | 
						|
 | 
						|
static gchar *opt_model = NULL;
 | 
						|
static gchar *opt_layouts = NULL;
 | 
						|
static gchar *opt_options = NULL;
 | 
						|
static gboolean opt_list_models = FALSE;
 | 
						|
static gboolean opt_list_layouts = FALSE;
 | 
						|
static gboolean opt_list_options = FALSE;
 | 
						|
static gboolean opt_version = FALSE;
 | 
						|
 | 
						|
static const GOptionEntry options[] = {
 | 
						|
    {"model", 'M', 0, G_OPTION_ARG_STRING, &opt_model,
 | 
						|
     N_("Keyboard model to display")},
 | 
						|
    {"layouts", 'L', 0, G_OPTION_ARG_STRING, &opt_layouts,
 | 
						|
     N_("Keyboard layouts to display, separated with commas")},
 | 
						|
    {"options", 'O', 0, G_OPTION_ARG_STRING, &opt_options,
 | 
						|
     N_("Keyboard layout options to display, separated with commas")},
 | 
						|
    {"list-models", '\0', 0, G_OPTION_ARG_NONE, &opt_list_models,
 | 
						|
     N_("List keyboard models")},
 | 
						|
    {"list-layouts", '\0', 0, G_OPTION_ARG_NONE, &opt_list_layouts,
 | 
						|
     N_("List all available keyboard layouts and variants")},
 | 
						|
    {"list-options", 'O', 0, G_OPTION_ARG_NONE, &opt_list_options,
 | 
						|
     N_("List all available keyboard layout options")},
 | 
						|
    {"version", 'v', 0, G_OPTION_ARG_NONE, &opt_version,
 | 
						|
     N_("Display version")},
 | 
						|
    {NULL}
 | 
						|
};
 | 
						|
 | 
						|
static void
 | 
						|
on_about (GtkAction * action, GtkWidget *window)
 | 
						|
{
 | 
						|
  const gchar *authors[] = { "Daiki Ueno", NULL };
 | 
						|
 | 
						|
  gtk_show_about_dialog (GTK_WINDOW (window),
 | 
						|
                         "program-name", PACKAGE,
 | 
						|
                         "version", VERSION,
 | 
						|
                         "copyright",
 | 
						|
                         "Copyright \xc2\xa9 2010 Daiki Ueno\n"
 | 
						|
                         "Copyright \xc2\xa9 2010 Red Hat, Inc.",
 | 
						|
                         "license", LICENSE,
 | 
						|
                         "comments",
 | 
						|
                         _("A virtual keyboard for GNOME"),
 | 
						|
                         "authors", authors,
 | 
						|
                         "website",
 | 
						|
                         "http://github.com/ueno/eek/",
 | 
						|
                         "website-label", _("Eekboard web site"),
 | 
						|
                         "wrap-license", TRUE, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_quit (GtkAction * action, GtkWidget *window)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = g_object_get_data (G_OBJECT(window), "eekboard");
 | 
						|
 | 
						|
    fakekey_release (eekboard->fakekey);
 | 
						|
    eekboard_free (eekboard);
 | 
						|
    gtk_main_quit ();
 | 
						|
}
 | 
						|
 | 
						|
static SPIBoolean
 | 
						|
a11y_focus_listener (const AccessibleEvent *event,
 | 
						|
                     void                  *user_data)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = user_data;
 | 
						|
    Accessible *acc = event->source;
 | 
						|
    AccessibleStateSet *state_set = Accessible_getStateSet (acc);
 | 
						|
 | 
						|
    if (AccessibleStateSet_contains (state_set, SPI_STATE_EDITABLE) ||
 | 
						|
        Accessible_getRole (acc) == SPI_ROLE_TERMINAL) {
 | 
						|
        gtk_widget_show (eekboard->window);
 | 
						|
    } else {
 | 
						|
        gtk_widget_hide (eekboard->window);
 | 
						|
    }
 | 
						|
 | 
						|
    return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static SPIBoolean
 | 
						|
a11y_keystroke_listener (const AccessibleKeystroke *stroke,
 | 
						|
                         void                      *user_data)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = user_data;
 | 
						|
    EekKey *key;
 | 
						|
 | 
						|
    //g_return_val_if_fail (stroke->modifiers == SPI_KEYMASK_UNMODIFIED, FALSE);
 | 
						|
    key = eek_keyboard_find_key_by_keycode (eekboard->keyboard,
 | 
						|
                                            stroke->keycode);
 | 
						|
    g_return_val_if_fail (key, FALSE);
 | 
						|
    if (stroke->type == SPI_KEY_PRESSED)
 | 
						|
        g_signal_emit_by_name (key, "pressed");
 | 
						|
    else
 | 
						|
        g_signal_emit_by_name (key, "released");
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static AccessibleEventListener* focusListener;
 | 
						|
static AccessibleEventListener* keystrokeListener;
 | 
						|
 | 
						|
static void
 | 
						|
on_monitor_key_event_toggled (GtkToggleAction *action,
 | 
						|
                              GtkWidget       *window)
 | 
						|
{
 | 
						|
 | 
						|
    Eekboard *eekboard = g_object_get_data (G_OBJECT(window), "eekboard");
 | 
						|
 | 
						|
    if (!keystrokeListener) {
 | 
						|
        keystrokeListener =
 | 
						|
            SPI_createAccessibleKeystrokeListener (a11y_keystroke_listener,
 | 
						|
                                                   eekboard);
 | 
						|
    }
 | 
						|
    if (gtk_toggle_action_get_active (action)) {
 | 
						|
        if (!SPI_registerAccessibleKeystrokeListener (keystrokeListener,
 | 
						|
                                                      SPI_KEYSET_ALL_KEYS,
 | 
						|
                                                      0,
 | 
						|
                                                      SPI_KEY_PRESSED |
 | 
						|
                                                      SPI_KEY_RELEASED,
 | 
						|
                                                      SPI_KEYLISTENER_NOSYNC))
 | 
						|
            g_warning ("failed to register keystroke listener");
 | 
						|
    } else
 | 
						|
        if (!SPI_deregisterAccessibleKeystrokeListener (keystrokeListener, 0))
 | 
						|
            g_warning ("failed to deregister keystroke listener");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_key_pressed (EekKeyboard *keyboard,
 | 
						|
                EekKey      *key,
 | 
						|
                gpointer user_data)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = user_data;
 | 
						|
    guint keysym;
 | 
						|
 | 
						|
    keysym = eek_key_get_keysym (key);
 | 
						|
    EEKBOARD_NOTE("%s %X", eek_keysym_to_string (keysym), eekboard->modifiers);
 | 
						|
    
 | 
						|
    if (keysym == XK_Shift_L || keysym == XK_Shift_R) {
 | 
						|
        gint group, level;
 | 
						|
 | 
						|
        eekboard->modifiers ^= ShiftMask;
 | 
						|
        eek_keyboard_get_keysym_index (keyboard, &group, &level);
 | 
						|
        eek_keyboard_set_keysym_index (keyboard, group,
 | 
						|
                                       eekboard->modifiers & ShiftMask ? 1 : 0);
 | 
						|
    } else if (keysym == XK_Control_L || keysym == XK_Control_R) {
 | 
						|
        eekboard->modifiers ^= ControlMask;
 | 
						|
    } else if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
 | 
						|
        eekboard->modifiers ^= Mod1Mask;
 | 
						|
    } else
 | 
						|
        fakekey_press_keysym (eekboard->fakekey, keysym, eekboard->modifiers);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_key_released (EekKeyboard *keyboard,
 | 
						|
                 EekKey      *key,
 | 
						|
                 gpointer user_data)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = user_data;
 | 
						|
    fakekey_release (eekboard->fakekey);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_config_activate (GtkAction *action,
 | 
						|
             gpointer   user_data)
 | 
						|
{
 | 
						|
    SetConfigCallbackData *data = user_data;
 | 
						|
    eek_xkl_layout_set_config (EEK_XKL_LAYOUT(data->eekboard->layout),
 | 
						|
                               data->config);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_option_toggled (GtkToggleAction *action,
 | 
						|
                   gpointer         user_data)
 | 
						|
{
 | 
						|
    SetConfigCallbackData *data = user_data;
 | 
						|
    if (gtk_toggle_action_get_active (action))
 | 
						|
        eek_xkl_layout_enable_option (EEK_XKL_LAYOUT(data->eekboard->layout),
 | 
						|
                                      data->config->options[0]);
 | 
						|
    else
 | 
						|
        eek_xkl_layout_disable_option (EEK_XKL_LAYOUT(data->eekboard->layout),
 | 
						|
                                       data->config->options[0]);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_changed (EekLayout *layout, gpointer user_data)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = user_data;
 | 
						|
    GtkWidget *vbox, *widget;
 | 
						|
    GtkAllocation allocation;
 | 
						|
 | 
						|
    gtk_widget_get_allocation (GTK_WIDGET (eekboard->widget), &allocation);
 | 
						|
    vbox = gtk_widget_get_parent (eekboard->widget);
 | 
						|
    /* gtk_widget_destroy() seems not usable for GtkClutterEmbed */
 | 
						|
    gtk_container_remove (GTK_CONTAINER(vbox), eekboard->widget);
 | 
						|
 | 
						|
    g_object_unref (eekboard->keyboard);
 | 
						|
    widget = create_widget (eekboard, allocation.width, allocation.height);
 | 
						|
    gtk_container_add (GTK_CONTAINER(vbox), widget);
 | 
						|
    gtk_widget_show_all (vbox);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
collect_layout_variant (XklConfigRegistry *registry,
 | 
						|
                        const XklConfigItem *layout,
 | 
						|
                        const XklConfigItem *variant,
 | 
						|
                        gpointer user_data)
 | 
						|
{
 | 
						|
    GSList **r_lv_list = user_data;
 | 
						|
    LayoutVariant *lv;
 | 
						|
 | 
						|
    lv = g_slice_new0 (LayoutVariant);
 | 
						|
    lv->layout = g_slice_dup (XklConfigItem, layout);
 | 
						|
    if (variant)
 | 
						|
        lv->variant = g_slice_dup (XklConfigItem, variant);
 | 
						|
    *r_lv_list = g_slist_prepend (*r_lv_list, lv);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
add_layout_variant_actions (CreateMenuCallbackData *data,
 | 
						|
                            const gchar            *name,
 | 
						|
                            const gchar            *path,
 | 
						|
                            GSList                 *lv_list)
 | 
						|
{
 | 
						|
    gchar variant_action_name[128];
 | 
						|
    SetConfigCallbackData *config;
 | 
						|
    GSList *head;
 | 
						|
 | 
						|
    for (head = lv_list; head; head = head->next) {
 | 
						|
        LayoutVariant *lv = head->data;
 | 
						|
        GtkAction *action;
 | 
						|
        gchar description[XKL_MAX_CI_DESC_LENGTH * 2 + 4];
 | 
						|
 | 
						|
        if (lv->variant) {
 | 
						|
            g_snprintf (variant_action_name, sizeof (variant_action_name),
 | 
						|
                        "SetLayoutVariant %s %s %s",
 | 
						|
                        name,
 | 
						|
                        lv->layout->name,
 | 
						|
                        lv->variant->name);
 | 
						|
            g_snprintf (description, sizeof (description),
 | 
						|
                        "%s (%s)",
 | 
						|
                        lv->layout->description,
 | 
						|
                        lv->variant->description);
 | 
						|
        } else {
 | 
						|
            g_snprintf (variant_action_name, sizeof (variant_action_name),
 | 
						|
                        "SetLayout %s %s",
 | 
						|
                        name,
 | 
						|
                        lv->layout->name);
 | 
						|
            g_strlcpy (description,
 | 
						|
                       lv->layout->description,
 | 
						|
                       sizeof (description));
 | 
						|
        }
 | 
						|
 | 
						|
        action = gtk_action_new (variant_action_name,
 | 
						|
                                 description,
 | 
						|
                                 NULL,
 | 
						|
                                 NULL);
 | 
						|
 | 
						|
        config = g_slice_new (SetConfigCallbackData);
 | 
						|
        config->eekboard = data->eekboard;
 | 
						|
        config->config = xkl_config_rec_new ();
 | 
						|
        config->config->layouts = g_new0 (char *, 2);
 | 
						|
        config->config->layouts[0] = g_strdup (lv->layout->name);
 | 
						|
        config->config->layouts[1] = NULL;
 | 
						|
        if (lv->variant) {
 | 
						|
            config->config->variants = g_new0 (char *, 2);
 | 
						|
            config->config->variants[0] = g_strdup (lv->variant->name);
 | 
						|
            config->config->variants[1] = NULL;
 | 
						|
        }
 | 
						|
        g_signal_connect (action, "activate", G_CALLBACK (on_config_activate), config);
 | 
						|
 | 
						|
        gtk_action_group_add_action (data->action_group, action);
 | 
						|
        g_object_unref (action);
 | 
						|
 | 
						|
        gtk_ui_manager_add_ui (data->eekboard->ui_manager, data->merge_id,
 | 
						|
                               path,
 | 
						|
                               variant_action_name, variant_action_name,
 | 
						|
                               GTK_UI_MANAGER_MENUITEM, FALSE);
 | 
						|
 | 
						|
        g_slice_free (XklConfigItem, lv->layout);
 | 
						|
        if (lv->variant)
 | 
						|
            g_slice_free (XklConfigItem, lv->variant);
 | 
						|
        g_slice_free (LayoutVariant, lv);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
country_callback (XklConfigRegistry   *registry,
 | 
						|
                  const XklConfigItem *item,
 | 
						|
                  gpointer             user_data)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData *data = user_data;
 | 
						|
    GtkAction *action;
 | 
						|
    GSList *lv_list = NULL;
 | 
						|
    char country_action_name[128];
 | 
						|
    char country_action_path[128];
 | 
						|
 | 
						|
    g_snprintf (country_action_name, sizeof (country_action_name),
 | 
						|
                "Country %s", item->name);
 | 
						|
    action = gtk_action_new (country_action_name, item->description,
 | 
						|
                             NULL, NULL);
 | 
						|
    gtk_action_group_add_action (data->action_group, action);
 | 
						|
    g_object_unref (action);
 | 
						|
 | 
						|
    gtk_ui_manager_add_ui (data->eekboard->ui_manager, data->merge_id,
 | 
						|
                           COUNTRIES_UI_PATH,
 | 
						|
                           country_action_name, country_action_name,
 | 
						|
                           GTK_UI_MANAGER_MENU, FALSE);
 | 
						|
    g_snprintf (country_action_path, sizeof (country_action_path),
 | 
						|
                COUNTRIES_UI_PATH "/%s", country_action_name);
 | 
						|
 | 
						|
    xkl_config_registry_foreach_country_variant (data->eekboard->registry,
 | 
						|
                                                 item->name,
 | 
						|
                                                 collect_layout_variant,
 | 
						|
                                                 &lv_list);
 | 
						|
    add_layout_variant_actions (data, item->name, country_action_path,
 | 
						|
                                lv_list);
 | 
						|
    g_slist_free (lv_list);
 | 
						|
}
 | 
						|
 | 
						|
static guint
 | 
						|
create_countries_menu (Eekboard *eekboard)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData data;
 | 
						|
 | 
						|
    data.eekboard = eekboard;
 | 
						|
    data.merge_id = gtk_ui_manager_new_merge_id (eekboard->ui_manager);
 | 
						|
    data.action_group = eekboard->countries_action_group;
 | 
						|
 | 
						|
    xkl_config_registry_foreach_country (eekboard->registry,
 | 
						|
                                         country_callback,
 | 
						|
                                         &data);
 | 
						|
    return data.merge_id;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_countries_menu (GtkAction *action, GtkWidget *window)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = g_object_get_data (G_OBJECT(window), "eekboard");
 | 
						|
    if (eekboard->countries_merge_id == 0)
 | 
						|
        eekboard->countries_merge_id = create_countries_menu (eekboard);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
language_callback (XklConfigRegistry *registry,
 | 
						|
                   const XklConfigItem *item,
 | 
						|
                   gpointer user_data)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData *data = user_data;
 | 
						|
    GtkAction *action;
 | 
						|
    GSList *lv_list = NULL;
 | 
						|
    char language_action_name[128];
 | 
						|
    char language_action_path[128];
 | 
						|
 | 
						|
    g_snprintf (language_action_name, sizeof (language_action_name),
 | 
						|
                "Language %s", item->name);
 | 
						|
    action = gtk_action_new (language_action_name, item->description,
 | 
						|
                             NULL, NULL);
 | 
						|
    gtk_action_group_add_action (data->action_group, action);
 | 
						|
    g_object_unref (action);
 | 
						|
 | 
						|
    gtk_ui_manager_add_ui (data->eekboard->ui_manager, data->merge_id,
 | 
						|
                           LANGUAGES_UI_PATH,
 | 
						|
                           language_action_name, language_action_name,
 | 
						|
                           GTK_UI_MANAGER_MENU, FALSE);
 | 
						|
    g_snprintf (language_action_path, sizeof (language_action_path),
 | 
						|
                LANGUAGES_UI_PATH "/%s", language_action_name);
 | 
						|
 | 
						|
    xkl_config_registry_foreach_language_variant (data->eekboard->registry,
 | 
						|
                                                  item->name,
 | 
						|
                                                  collect_layout_variant,
 | 
						|
                                                  &lv_list);
 | 
						|
    add_layout_variant_actions (data, item->name, language_action_path,
 | 
						|
                                lv_list);
 | 
						|
    g_slist_free (lv_list);
 | 
						|
}
 | 
						|
 | 
						|
static guint
 | 
						|
create_languages_menu (Eekboard *eekboard)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData data;
 | 
						|
 | 
						|
    data.eekboard = eekboard;
 | 
						|
    data.merge_id = gtk_ui_manager_new_merge_id (eekboard->ui_manager);
 | 
						|
    data.action_group = eekboard->languages_action_group;
 | 
						|
 | 
						|
    xkl_config_registry_foreach_language (eekboard->registry,
 | 
						|
                                          language_callback,
 | 
						|
                                          &data);
 | 
						|
    return data.merge_id;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_languages_menu (GtkAction *action, GtkWidget *window)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = g_object_get_data (G_OBJECT(window), "eekboard");
 | 
						|
    if (eekboard->languages_merge_id == 0)
 | 
						|
        eekboard->languages_merge_id = create_languages_menu (eekboard);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
model_callback (XklConfigRegistry *registry,
 | 
						|
                const XklConfigItem *item,
 | 
						|
                gpointer user_data)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData *data = user_data;
 | 
						|
    GtkAction *action;
 | 
						|
    char model_action_name[128];
 | 
						|
    SetConfigCallbackData *config;
 | 
						|
 | 
						|
    g_snprintf (model_action_name, sizeof (model_action_name),
 | 
						|
                "Model %s", item->name);
 | 
						|
    action = gtk_action_new (model_action_name, item->description, NULL, NULL);
 | 
						|
    gtk_action_group_add_action (data->action_group, action);
 | 
						|
 | 
						|
    config = g_slice_new (SetConfigCallbackData);
 | 
						|
    config->eekboard = data->eekboard;
 | 
						|
    config->config = xkl_config_rec_new ();
 | 
						|
    config->config->model = g_strdup (item->name);
 | 
						|
 | 
						|
    g_signal_connect (action, "activate", G_CALLBACK (on_config_activate),
 | 
						|
                      config);
 | 
						|
    g_object_unref (action);
 | 
						|
    gtk_ui_manager_add_ui (data->eekboard->ui_manager, data->merge_id,
 | 
						|
                           MODELS_UI_PATH,
 | 
						|
                           model_action_name, model_action_name,
 | 
						|
                           GTK_UI_MANAGER_MENUITEM, FALSE);
 | 
						|
}
 | 
						|
 | 
						|
static guint
 | 
						|
create_models_menu (Eekboard *eekboard)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData data;
 | 
						|
 | 
						|
    data.eekboard = eekboard;
 | 
						|
    data.merge_id = gtk_ui_manager_new_merge_id (eekboard->ui_manager);
 | 
						|
    data.action_group = eekboard->models_action_group;
 | 
						|
 | 
						|
    xkl_config_registry_foreach_model (eekboard->registry,
 | 
						|
                                       model_callback,
 | 
						|
                                       &data);
 | 
						|
    return data.merge_id;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_models_menu (GtkAction *action, GtkWidget *window)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = g_object_get_data (G_OBJECT(window), "eekboard");
 | 
						|
    if (eekboard->models_merge_id == 0)
 | 
						|
        eekboard->models_merge_id = create_models_menu (eekboard);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
collect_variant (XklConfigRegistry *registry,
 | 
						|
                 const XklConfigItem *variant,
 | 
						|
                 gpointer user_data)
 | 
						|
{
 | 
						|
    GSList **r_variants = user_data;
 | 
						|
    XklConfigItem *item;
 | 
						|
 | 
						|
    item = g_slice_dup (XklConfigItem, variant);
 | 
						|
    *r_variants = g_slist_prepend (*r_variants, item);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
layout_callback (XklConfigRegistry *registry,
 | 
						|
                 const XklConfigItem *item,
 | 
						|
                 gpointer user_data)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData *data = user_data;
 | 
						|
    GtkAction *action;
 | 
						|
    GSList *variants = NULL;
 | 
						|
    char layout_action_name[128], variant_action_name[128];
 | 
						|
    SetConfigCallbackData *config;
 | 
						|
 | 
						|
    g_snprintf (layout_action_name, sizeof (layout_action_name),
 | 
						|
                "SetLayout %s", item->name);
 | 
						|
    action = gtk_action_new (layout_action_name, item->description, NULL, NULL);
 | 
						|
    gtk_action_group_add_action (data->action_group, action);
 | 
						|
 | 
						|
    xkl_config_registry_foreach_layout_variant (registry,
 | 
						|
                                                item->name,
 | 
						|
                                                collect_variant,
 | 
						|
                                                &variants);
 | 
						|
 | 
						|
    if (!variants) {
 | 
						|
        config = g_slice_new (SetConfigCallbackData);
 | 
						|
        config->eekboard = data->eekboard;
 | 
						|
        config->config = xkl_config_rec_new ();
 | 
						|
        config->config->layouts = g_new0 (char *, 2);
 | 
						|
        config->config->layouts[0] = g_strdup (item->name);
 | 
						|
        config->config->layouts[1] = NULL;
 | 
						|
        /* Reset the existing variant setting. */
 | 
						|
        config->config->variants = g_new0 (char *, 1);
 | 
						|
        config->config->variants[0] = NULL;
 | 
						|
        g_signal_connect (action, "activate", G_CALLBACK (on_config_activate),
 | 
						|
                          config);
 | 
						|
        g_object_unref (action);
 | 
						|
        gtk_ui_manager_add_ui (data->eekboard->ui_manager, data->merge_id,
 | 
						|
                               LAYOUTS_UI_PATH,
 | 
						|
                               layout_action_name, layout_action_name,
 | 
						|
                               GTK_UI_MANAGER_MENUITEM, FALSE);
 | 
						|
    } else {
 | 
						|
        char layout_path[128];
 | 
						|
        GSList *head;
 | 
						|
 | 
						|
        g_object_unref (action);
 | 
						|
        gtk_ui_manager_add_ui (data->eekboard->ui_manager, data->merge_id,
 | 
						|
                               LAYOUTS_UI_PATH,
 | 
						|
                               layout_action_name, layout_action_name,
 | 
						|
                               GTK_UI_MANAGER_MENU, FALSE);
 | 
						|
        g_snprintf (layout_path, sizeof (layout_path),
 | 
						|
                    LAYOUTS_UI_PATH "/%s", layout_action_name);
 | 
						|
 | 
						|
        for (head = variants; head; head = head->next) {
 | 
						|
            XklConfigItem *_item = head->data;
 | 
						|
 | 
						|
            g_snprintf (variant_action_name, sizeof (variant_action_name),
 | 
						|
                        "SetLayoutVariant %s %s", item->name, _item->name);
 | 
						|
            action = gtk_action_new (variant_action_name,
 | 
						|
                                     _item->description,
 | 
						|
                                     NULL,
 | 
						|
                                     NULL);
 | 
						|
 | 
						|
            config = g_slice_new (SetConfigCallbackData);
 | 
						|
            config->eekboard = data->eekboard;
 | 
						|
            config->config = xkl_config_rec_new ();
 | 
						|
            config->config->layouts = g_new0 (char *, 2);
 | 
						|
            config->config->layouts[0] = g_strdup (item->name);
 | 
						|
            config->config->layouts[1] = NULL;
 | 
						|
            config->config->variants = g_new0 (char *, 2);
 | 
						|
            config->config->variants[0] = g_strdup (_item->name);
 | 
						|
            config->config->variants[1] = NULL;
 | 
						|
            g_signal_connect (action, "activate", G_CALLBACK (on_config_activate),
 | 
						|
                              config);
 | 
						|
 | 
						|
            gtk_action_group_add_action (data->action_group, action);
 | 
						|
            g_object_unref (action);
 | 
						|
 | 
						|
            gtk_ui_manager_add_ui (data->eekboard->ui_manager, data->merge_id,
 | 
						|
                                   layout_path,
 | 
						|
                                   variant_action_name, variant_action_name,
 | 
						|
                                   GTK_UI_MANAGER_MENUITEM, FALSE);
 | 
						|
            g_slice_free (XklConfigItem, _item);
 | 
						|
        }
 | 
						|
        g_slist_free (variants);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static guint
 | 
						|
create_layouts_menu (Eekboard *eekboard)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData data;
 | 
						|
 | 
						|
    data.eekboard = eekboard;
 | 
						|
    data.merge_id = gtk_ui_manager_new_merge_id (eekboard->ui_manager);
 | 
						|
    data.action_group = eekboard->layouts_action_group;
 | 
						|
 | 
						|
    xkl_config_registry_foreach_layout (eekboard->registry,
 | 
						|
                                        layout_callback,
 | 
						|
                                        &data);
 | 
						|
    return data.merge_id;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_layouts_menu (GtkAction *action, GtkWidget *window)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = g_object_get_data (G_OBJECT(window), "eekboard");
 | 
						|
    if (eekboard->layouts_merge_id == 0)
 | 
						|
        eekboard->layouts_merge_id = create_layouts_menu (eekboard);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
collect_option (XklConfigRegistry *registry,
 | 
						|
                 const XklConfigItem *option,
 | 
						|
                 gpointer user_data)
 | 
						|
{
 | 
						|
    GSList **r_options = user_data;
 | 
						|
    XklConfigItem *item;
 | 
						|
 | 
						|
    item = g_slice_dup (XklConfigItem, option);
 | 
						|
    *r_options = g_slist_prepend (*r_options, item);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
option_group_callback (XklConfigRegistry   *registry,
 | 
						|
                       const XklConfigItem *item,
 | 
						|
                       gpointer             user_data)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData *data = user_data;
 | 
						|
    GtkAction *action;
 | 
						|
    GSList *options = NULL, *head;
 | 
						|
    gchar option_group_action_name[128], option_action_name[128];
 | 
						|
    gchar option_group_path[128];
 | 
						|
    SetConfigCallbackData *config;
 | 
						|
 | 
						|
    g_snprintf (option_group_action_name, sizeof (option_group_action_name),
 | 
						|
                "OptionGroup %s", item->name);
 | 
						|
    action = gtk_action_new (option_group_action_name, item->description,
 | 
						|
                             NULL, NULL);
 | 
						|
    gtk_action_group_add_action (data->action_group, action);
 | 
						|
    g_object_unref (action);
 | 
						|
 | 
						|
    xkl_config_registry_foreach_option (registry,
 | 
						|
                                        item->name,
 | 
						|
                                        collect_option,
 | 
						|
                                        &options);
 | 
						|
    g_return_if_fail (options);
 | 
						|
 | 
						|
    gtk_ui_manager_add_ui (data->eekboard->ui_manager, data->merge_id,
 | 
						|
                           OPTIONS_UI_PATH,
 | 
						|
                           option_group_action_name, option_group_action_name,
 | 
						|
                           GTK_UI_MANAGER_MENU, FALSE);
 | 
						|
    g_snprintf (option_group_path, sizeof (option_group_path),
 | 
						|
                OPTIONS_UI_PATH "/%s", option_group_action_name);
 | 
						|
 | 
						|
    for (head = options; head; head = head->next) {
 | 
						|
        XklConfigItem *_item = head->data;
 | 
						|
        GtkToggleAction *toggle;
 | 
						|
 | 
						|
        g_snprintf (option_action_name, sizeof (option_action_name),
 | 
						|
                    "SetOption %s", _item->name);
 | 
						|
        toggle = gtk_toggle_action_new (option_action_name,
 | 
						|
                                        _item->description,
 | 
						|
                                        NULL,
 | 
						|
                                        NULL);
 | 
						|
 | 
						|
        config = g_slice_new (SetConfigCallbackData);
 | 
						|
        config->eekboard = data->eekboard;
 | 
						|
        config->config = xkl_config_rec_new ();
 | 
						|
        config->config->options = g_new0 (char *, 2);
 | 
						|
        config->config->options[0] = g_strdup (_item->name);
 | 
						|
        config->config->options[1] = NULL;
 | 
						|
        g_signal_connect (toggle, "toggled", G_CALLBACK (on_option_toggled),
 | 
						|
                          config);
 | 
						|
 | 
						|
        gtk_action_group_add_action (data->action_group, GTK_ACTION(toggle));
 | 
						|
        g_object_unref (toggle);
 | 
						|
 | 
						|
        gtk_ui_manager_add_ui (data->eekboard->ui_manager, data->merge_id,
 | 
						|
                               option_group_path,
 | 
						|
                               option_action_name, option_action_name,
 | 
						|
                               GTK_UI_MANAGER_MENUITEM, FALSE);
 | 
						|
        g_slice_free (XklConfigItem, _item);
 | 
						|
    }
 | 
						|
    g_slist_free (options);
 | 
						|
}
 | 
						|
 | 
						|
static guint
 | 
						|
create_options_menu (Eekboard *eekboard)
 | 
						|
{
 | 
						|
    CreateMenuCallbackData data;
 | 
						|
 | 
						|
    data.eekboard = eekboard;
 | 
						|
    data.merge_id = gtk_ui_manager_new_merge_id (eekboard->ui_manager);
 | 
						|
    data.action_group = eekboard->options_action_group;
 | 
						|
 | 
						|
    xkl_config_registry_foreach_option_group (eekboard->registry,
 | 
						|
                                              option_group_callback,
 | 
						|
                                              &data);
 | 
						|
    return data.merge_id;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_options_menu (GtkAction *action, GtkWidget *window)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = g_object_get_data (G_OBJECT(window), "eekboard");
 | 
						|
    if (eekboard->options_merge_id == 0)
 | 
						|
        eekboard->options_merge_id = create_options_menu (eekboard);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
create_menus (Eekboard      *eekboard,
 | 
						|
              GtkWidget     *window)
 | 
						|
{
 | 
						|
    GtkActionGroup *action_group;
 | 
						|
 | 
						|
    action_group = gtk_action_group_new ("MenuActions");
 | 
						|
    gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
 | 
						|
 | 
						|
    gtk_action_group_add_actions (action_group, action_entry,
 | 
						|
                                  G_N_ELEMENTS (action_entry), window);
 | 
						|
    gtk_action_group_add_toggle_actions (action_group, toggle_action_entry,
 | 
						|
                                         G_N_ELEMENTS (toggle_action_entry),
 | 
						|
                                         window);
 | 
						|
 | 
						|
    gtk_ui_manager_insert_action_group (eekboard->ui_manager, action_group, 0);
 | 
						|
    gtk_ui_manager_add_ui_from_string (eekboard->ui_manager, ui_description, -1, NULL);
 | 
						|
 | 
						|
    eekboard->countries_action_group = gtk_action_group_new ("Countries");
 | 
						|
    gtk_ui_manager_insert_action_group (eekboard->ui_manager,
 | 
						|
                                        eekboard->countries_action_group,
 | 
						|
                                        -1);
 | 
						|
 | 
						|
    eekboard->languages_action_group = gtk_action_group_new ("Languages");
 | 
						|
    gtk_ui_manager_insert_action_group (eekboard->ui_manager,
 | 
						|
                                        eekboard->languages_action_group,
 | 
						|
                                        -1);
 | 
						|
    
 | 
						|
    eekboard->models_action_group = gtk_action_group_new ("Models");
 | 
						|
    gtk_ui_manager_insert_action_group (eekboard->ui_manager,
 | 
						|
                                        eekboard->models_action_group,
 | 
						|
                                        -1);
 | 
						|
 | 
						|
    eekboard->layouts_action_group = gtk_action_group_new ("Layouts");
 | 
						|
    gtk_ui_manager_insert_action_group (eekboard->ui_manager,
 | 
						|
                                        eekboard->layouts_action_group,
 | 
						|
                                        -1);
 | 
						|
 | 
						|
    eekboard->options_action_group = gtk_action_group_new ("Options");
 | 
						|
    gtk_ui_manager_insert_action_group (eekboard->ui_manager,
 | 
						|
                                        eekboard->options_action_group,
 | 
						|
                                        -1);
 | 
						|
}
 | 
						|
 | 
						|
static GtkWidget *
 | 
						|
create_widget_gtk (Eekboard *eekboard,
 | 
						|
                   gint      initial_width,
 | 
						|
                   gint      initial_height)
 | 
						|
{
 | 
						|
    EekBounds bounds;
 | 
						|
 | 
						|
    bounds.x = bounds.y = 0;
 | 
						|
    bounds.width = initial_width;
 | 
						|
    bounds.height = initial_height;
 | 
						|
 | 
						|
    eekboard->keyboard = eek_gtk_keyboard_new ();
 | 
						|
    eek_keyboard_set_layout (eekboard->keyboard, eekboard->layout);
 | 
						|
    eek_element_set_bounds (EEK_ELEMENT(eekboard->keyboard), &bounds);
 | 
						|
    g_signal_connect (eekboard->keyboard, "key-pressed",
 | 
						|
                      G_CALLBACK(on_key_pressed), eekboard);
 | 
						|
    g_signal_connect (eekboard->keyboard, "key-released",
 | 
						|
                      G_CALLBACK(on_key_released), eekboard);
 | 
						|
 | 
						|
    eekboard->widget =
 | 
						|
        eek_gtk_keyboard_get_widget (EEK_GTK_KEYBOARD (eekboard->keyboard));
 | 
						|
    eek_element_get_bounds (EEK_ELEMENT(eekboard->keyboard), &bounds);
 | 
						|
    eekboard->width = bounds.width;
 | 
						|
    eekboard->height = bounds.height;
 | 
						|
    return eekboard->widget;
 | 
						|
}
 | 
						|
 | 
						|
#if HAVE_CLUTTER_GTK
 | 
						|
#if NEED_SWAP_EVENT_WORKAROUND
 | 
						|
static GdkFilterReturn
 | 
						|
gtk_clutter_filter_func (GdkXEvent *native_event,
 | 
						|
                         GdkEvent  *event,
 | 
						|
                         gpointer   user_data)
 | 
						|
{
 | 
						|
  XEvent *xevent = native_event;
 | 
						|
 | 
						|
  clutter_x11_handle_event (xevent);
 | 
						|
 | 
						|
  return GDK_FILTER_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_gtk_clutter_embed_realize (GtkWidget *widget, gpointer user_data)
 | 
						|
{
 | 
						|
    gdk_window_add_filter (NULL, gtk_clutter_filter_func, widget);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static GtkWidget *
 | 
						|
create_widget_clutter (Eekboard *eekboard,
 | 
						|
                       gint      initial_width,
 | 
						|
                       gint      initial_height)
 | 
						|
{
 | 
						|
    ClutterActor *stage, *actor;
 | 
						|
    ClutterColor stage_color = { 0xff, 0xff, 0xff, 0xff };
 | 
						|
    EekBounds bounds;
 | 
						|
 | 
						|
    bounds.x = bounds.y = 0;
 | 
						|
    bounds.width = initial_width;
 | 
						|
    bounds.height = initial_height;
 | 
						|
 | 
						|
    eekboard->keyboard = eek_clutter_keyboard_new ();
 | 
						|
    eek_keyboard_set_layout (eekboard->keyboard, eekboard->layout);
 | 
						|
    eek_element_set_bounds (EEK_ELEMENT(eekboard->keyboard), &bounds);
 | 
						|
    g_signal_connect (eekboard->keyboard, "key-pressed",
 | 
						|
                      G_CALLBACK(on_key_pressed), eekboard);
 | 
						|
    g_signal_connect (eekboard->keyboard, "key-released",
 | 
						|
                      G_CALLBACK(on_key_released), eekboard);
 | 
						|
 | 
						|
    eekboard->widget = gtk_clutter_embed_new ();
 | 
						|
#if NEED_SWAP_EVENT_WORKAROUND
 | 
						|
    if (eekboard->need_swap_event_workaround)
 | 
						|
        g_signal_connect (eekboard->widget, "realize",
 | 
						|
                          G_CALLBACK(on_gtk_clutter_embed_realize), NULL);
 | 
						|
#endif
 | 
						|
    stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED(eekboard->widget));
 | 
						|
    clutter_stage_set_color (CLUTTER_STAGE(stage), &stage_color);
 | 
						|
    clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
 | 
						|
 | 
						|
    actor = eek_clutter_keyboard_get_actor
 | 
						|
        (EEK_CLUTTER_KEYBOARD(eekboard->keyboard));
 | 
						|
    clutter_container_add_actor (CLUTTER_CONTAINER(stage), actor);
 | 
						|
    eek_element_get_bounds (EEK_ELEMENT(eekboard->keyboard), &bounds);
 | 
						|
    clutter_actor_set_size (stage, bounds.width, bounds.height);
 | 
						|
    eekboard->width = bounds.width;
 | 
						|
    eekboard->height = bounds.height;
 | 
						|
    return eekboard->widget;
 | 
						|
}
 | 
						|
 | 
						|
static GtkWidget *
 | 
						|
create_widget (Eekboard *eekboard,
 | 
						|
               gint    initial_width,
 | 
						|
               gint    initial_height)
 | 
						|
{
 | 
						|
    if (eekboard->use_clutter)
 | 
						|
        return create_widget_clutter (eekboard, initial_width, initial_height);
 | 
						|
    else
 | 
						|
        return create_widget_gtk (eekboard, initial_width, initial_height);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
Eekboard *
 | 
						|
eekboard_new (gboolean use_clutter,
 | 
						|
              gboolean need_swap_event_workaround,
 | 
						|
              gboolean accessibility_enabled)
 | 
						|
{
 | 
						|
    Eekboard *eekboard;
 | 
						|
 | 
						|
    eekboard = g_slice_new0 (Eekboard);
 | 
						|
    eekboard->use_clutter = use_clutter;
 | 
						|
    eekboard->need_swap_event_workaround = need_swap_event_workaround;
 | 
						|
    eekboard->accessibility_enabled = accessibility_enabled;
 | 
						|
    eekboard->display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
 | 
						|
    if (!eekboard->display) {
 | 
						|
        g_slice_free (Eekboard, eekboard);
 | 
						|
        g_warning ("Can't open display");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    eekboard->fakekey = fakekey_init (eekboard->display);
 | 
						|
    if (!eekboard->fakekey) {
 | 
						|
        g_slice_free (Eekboard, eekboard);
 | 
						|
        g_warning ("Can't init fakekey");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    eekboard->layout = eek_xkl_layout_new ();
 | 
						|
    if (!eekboard->layout) {
 | 
						|
        g_slice_free (Eekboard, eekboard);
 | 
						|
        g_warning ("Can't create layout");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (opt_model)
 | 
						|
        eek_xkl_layout_set_model (EEK_XKL_LAYOUT(eekboard->layout), opt_model);
 | 
						|
    if (opt_layouts) {
 | 
						|
        gchar **layouts, **variants;
 | 
						|
        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, '(');
 | 
						|
            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';
 | 
						|
        }
 | 
						|
        eek_xkl_layout_set_layouts (EEK_XKL_LAYOUT(eekboard->layout), layouts);
 | 
						|
        g_strfreev (layouts);
 | 
						|
        g_strfreev (variants);
 | 
						|
    }
 | 
						|
    if (opt_options) {
 | 
						|
        gchar **options;
 | 
						|
        options = g_strsplit (opt_options, ",", -1);
 | 
						|
        eek_xkl_layout_set_options (EEK_XKL_LAYOUT(eekboard->layout), options);
 | 
						|
        g_strfreev (options);
 | 
						|
    }
 | 
						|
    g_signal_connect (eekboard->layout, "changed",
 | 
						|
                      G_CALLBACK(on_changed), eekboard);
 | 
						|
 | 
						|
    eekboard->ui_manager = gtk_ui_manager_new ();
 | 
						|
    eekboard->engine = xkl_engine_get_instance (eekboard->display);
 | 
						|
    eekboard->registry = xkl_config_registry_get_instance (eekboard->engine);
 | 
						|
    xkl_config_registry_load (eekboard->registry, FALSE);
 | 
						|
 | 
						|
    return eekboard;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eekboard_free (Eekboard *eekboard)
 | 
						|
{
 | 
						|
    if (eekboard->layout)
 | 
						|
        g_object_unref (eekboard->layout);
 | 
						|
#if 0
 | 
						|
    if (eekboard->keyboard)
 | 
						|
        g_object_unref (eekboard->keyboard);
 | 
						|
#endif
 | 
						|
    if (eekboard->registry)
 | 
						|
        g_object_unref (eekboard->registry);
 | 
						|
    if (eekboard->engine)
 | 
						|
        g_object_unref (eekboard->engine);
 | 
						|
    g_slice_free (Eekboard, eekboard);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
print_layout (XklConfigRegistry   *registry,
 | 
						|
              const XklConfigItem *item,
 | 
						|
              gpointer             user_data)
 | 
						|
{
 | 
						|
    GSList *variants = NULL;
 | 
						|
    xkl_config_registry_foreach_layout_variant (registry,
 | 
						|
                                                item->name,
 | 
						|
                                                collect_variant,
 | 
						|
                                                &variants);
 | 
						|
    if (!variants)
 | 
						|
        printf ("%s: %s\n", item->name, item->description);
 | 
						|
    else {
 | 
						|
        GSList *head;
 | 
						|
        for (head = variants; head; head = head->next) {
 | 
						|
            XklConfigItem *_item = head->data;
 | 
						|
            
 | 
						|
            printf ("%s(%s): %s %s\n",
 | 
						|
                    item->name,
 | 
						|
                    _item->name,
 | 
						|
                    item->description,
 | 
						|
                    _item->description);
 | 
						|
            g_slice_free (XklConfigItem, _item);
 | 
						|
        }
 | 
						|
        g_slist_free (variants);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
print_item (XklConfigRegistry *registry,
 | 
						|
            const XklConfigItem *item,
 | 
						|
            gpointer user_data)
 | 
						|
{
 | 
						|
    printf ("%s: %s\n", item->name, item->description);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
print_option_group (XklConfigRegistry *registry,
 | 
						|
                    const XklConfigItem *item,
 | 
						|
                    gpointer user_data)
 | 
						|
{
 | 
						|
    xkl_config_registry_foreach_option (registry,
 | 
						|
                                        item->name,
 | 
						|
                                        print_item,
 | 
						|
                                        NULL);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_notify_never_show (NotifyNotification *notification,
 | 
						|
                      char *action,
 | 
						|
                      gpointer user_data)
 | 
						|
{
 | 
						|
    Eekboard *eekboard = user_data;
 | 
						|
    GError *error;
 | 
						|
 | 
						|
    gconf_client_set_bool (eekboard->gconfc,
 | 
						|
                           "/apps/eekboard/inhibit-startup-notify",
 | 
						|
                           TRUE,
 | 
						|
                           &error);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
main (int argc, char *argv[])
 | 
						|
{
 | 
						|
    const gchar *env;
 | 
						|
    gboolean use_clutter = USE_CLUTTER;
 | 
						|
    gboolean need_swap_event_workaround = FALSE;
 | 
						|
    gboolean accessibility_enabled = FALSE;
 | 
						|
    Eekboard *eekboard;
 | 
						|
    GtkWidget *widget, *vbox, *menubar, *window;
 | 
						|
    GOptionContext *context;
 | 
						|
    GConfClient *gconfc;
 | 
						|
    GError *error;
 | 
						|
 | 
						|
    context = g_option_context_new ("eekboard");
 | 
						|
    g_option_context_add_main_entries (context, options, NULL);
 | 
						|
    g_option_context_parse (context, &argc, &argv, NULL);
 | 
						|
    g_option_context_free (context);
 | 
						|
 | 
						|
    if (opt_version) {
 | 
						|
        g_print ("eekboard %s\n", VERSION);
 | 
						|
        exit (0);
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef ENABLE_NLS
 | 
						|
    bindtextdomain (GETTEXT_PACKAGE, EEKBOARD_LOCALEDIR);
 | 
						|
    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 | 
						|
#endif
 | 
						|
 | 
						|
    error = NULL;
 | 
						|
    gconfc = gconf_client_get_default ();
 | 
						|
    if (gconf_client_get_bool (gconfc,
 | 
						|
                               "/desktop/gnome/interface/accessibility",
 | 
						|
                               &error) ||
 | 
						|
        gconf_client_get_bool (gconfc,
 | 
						|
                               "/desktop/gnome/interface/accessibility2",
 | 
						|
                               &error)) {
 | 
						|
        if (SPI_init () == 0)
 | 
						|
            accessibility_enabled = TRUE;
 | 
						|
        else
 | 
						|
            g_warning("AT-SPI initialization failed");
 | 
						|
    }
 | 
						|
 | 
						|
    env = g_getenv ("EEKBOARD_DISABLE_CLUTTER");
 | 
						|
    if (env && g_strcmp0 (env, "1") == 0)
 | 
						|
        use_clutter = FALSE;
 | 
						|
 | 
						|
#if HAVE_CLUTTER_GTK
 | 
						|
    if (use_clutter &&
 | 
						|
        gtk_clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) {
 | 
						|
        g_warning ("Can't init Clutter-Gtk...fallback to GTK");
 | 
						|
        use_clutter = FALSE;
 | 
						|
    }
 | 
						|
#ifdef NEED_SWAP_EVENT_WORKAROUND
 | 
						|
    if (use_clutter &&
 | 
						|
        clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)) {
 | 
						|
        g_warning ("Enabling GLX_INTEL_swap_event workaround for Clutter-Gtk");
 | 
						|
        need_swap_event_workaround = TRUE;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
    if (!use_clutter && !gtk_init_check (&argc, &argv)) {
 | 
						|
        g_warning ("Can't init GTK");
 | 
						|
        exit (1);
 | 
						|
    }
 | 
						|
 | 
						|
    eekboard = eekboard_new (use_clutter,
 | 
						|
                             need_swap_event_workaround,
 | 
						|
                             accessibility_enabled);
 | 
						|
    if (opt_list_models) {
 | 
						|
        xkl_config_registry_foreach_model (eekboard->registry,
 | 
						|
                                           print_item,
 | 
						|
                                           NULL);
 | 
						|
        eekboard_free (eekboard);
 | 
						|
        exit (0);
 | 
						|
    }
 | 
						|
    if (opt_list_layouts) {
 | 
						|
        xkl_config_registry_foreach_layout (eekboard->registry,
 | 
						|
                                            print_layout,
 | 
						|
                                            NULL);
 | 
						|
        eekboard_free (eekboard);
 | 
						|
        exit (0);
 | 
						|
    }
 | 
						|
    if (opt_list_options) {
 | 
						|
        xkl_config_registry_foreach_option_group (eekboard->registry,
 | 
						|
                                                  print_option_group,
 | 
						|
                                                  NULL);
 | 
						|
        eekboard_free (eekboard);
 | 
						|
        exit (0);
 | 
						|
    }
 | 
						|
 | 
						|
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 | 
						|
    gtk_widget_set_can_focus (window, FALSE);
 | 
						|
    g_object_set (G_OBJECT(window), "accept_focus", FALSE, NULL);
 | 
						|
    gtk_window_set_title (GTK_WINDOW(window), "Keyboard");
 | 
						|
    g_signal_connect (G_OBJECT (window), "destroy",
 | 
						|
                      G_CALLBACK (gtk_main_quit), NULL);
 | 
						|
 | 
						|
    vbox = gtk_vbox_new (FALSE, 0);
 | 
						|
 | 
						|
    g_object_set_data (G_OBJECT(window), "eekboard", eekboard);
 | 
						|
    widget = create_widget (eekboard, CSW, CSH);
 | 
						|
    
 | 
						|
    create_menus (eekboard, window);
 | 
						|
    menubar = gtk_ui_manager_get_widget (eekboard->ui_manager, "/MainMenu");
 | 
						|
    gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
 | 
						|
 | 
						|
    gtk_container_add (GTK_CONTAINER(vbox), widget);
 | 
						|
    gtk_container_add (GTK_CONTAINER(window), vbox);
 | 
						|
  
 | 
						|
    gtk_widget_set_size_request (widget, eekboard->width, eekboard->height);
 | 
						|
    gtk_widget_show_all (window);
 | 
						|
    gtk_widget_set_size_request (widget, -1, -1);
 | 
						|
 | 
						|
    notify_init ("eekboard");
 | 
						|
    eekboard->window = window;
 | 
						|
    eekboard->gconfc = gconfc;
 | 
						|
    if (eekboard->accessibility_enabled) {
 | 
						|
        NotifyNotification *notification;
 | 
						|
 | 
						|
        error = NULL;
 | 
						|
        if (!gconf_client_get_bool (eekboard->gconfc,
 | 
						|
                                    "/apps/eekboard/inhibit-startup-notify",
 | 
						|
                                    &error)) {
 | 
						|
            notification = notify_notification_new
 | 
						|
                ("eekboard started in background",
 | 
						|
                 "As GNOME accessibility support enabled, "
 | 
						|
                 "eekboard is starting without a window.\n"
 | 
						|
                 "To make eekboard show up, click on some window with "
 | 
						|
                 "an editable widget.",
 | 
						|
                 NULL,
 | 
						|
                 NULL);
 | 
						|
            notify_notification_add_action
 | 
						|
                (notification,
 | 
						|
                 "dont-ask",
 | 
						|
                 "Don't show up",
 | 
						|
                 NOTIFY_ACTION_CALLBACK(on_notify_never_show),
 | 
						|
                 eekboard,
 | 
						|
                 NULL);
 | 
						|
            error = NULL;
 | 
						|
            notify_notification_show (notification, &error);
 | 
						|
        }
 | 
						|
 
 | 
						|
        gtk_widget_hide (window);
 | 
						|
 | 
						|
        focusListener = SPI_createAccessibleEventListener (a11y_focus_listener,
 | 
						|
                                                           eekboard);
 | 
						|
        SPI_registerGlobalEventListener (focusListener,
 | 
						|
                                         "object:state-changed:focused");
 | 
						|
    }
 | 
						|
 | 
						|
    gtk_main ();
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 |