diff options
author | Perberos <[email protected]> | 2011-11-14 18:24:48 -0300 |
---|---|---|
committer | Perberos <[email protected]> | 2011-11-14 18:24:48 -0300 |
commit | 312ba610a1e98fc656fb58178227d7d45a64494e (patch) | |
tree | 54a3c2b6084c80e63fb0526c6e7b8e01627acbd7 /accessx-status/applet.c | |
download | mate-applets-312ba610a1e98fc656fb58178227d7d45a64494e.tar.bz2 mate-applets-312ba610a1e98fc656fb58178227d7d45a64494e.tar.xz |
initial
Diffstat (limited to 'accessx-status/applet.c')
-rw-r--r-- | accessx-status/applet.c | 1369 |
1 files changed, 1369 insertions, 0 deletions
diff --git a/accessx-status/applet.c b/accessx-status/applet.c new file mode 100644 index 00000000..b6ee8934 --- /dev/null +++ b/accessx-status/applet.c @@ -0,0 +1,1369 @@ +/* Keyboard Accessibility Status Applet + * Copyright 2003, 2004 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include <stdlib.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <glib-object.h> +#include <gdk/gdkkeysyms.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> +#include <mate-panel-applet-mateconf.h> +#include <X11/XKBlib.h> + +#define XK_MISCELLANY +#define XK_XKB_KEYS + +#include <X11/keysymdef.h> +#include "applet.h" + +static int xkb_base_event_type = 0; + +static GtkIconSize icon_size_spec; + +#define ALT_GRAPH_LED_MASK (0x10) + +typedef struct { + char* stock_id; + char* name; + GtkStateType state; + gboolean wildcarded; +} AppletStockIcon; + +static AppletStockIcon stock_icons[] = { + {ACCESSX_APPLET, "ax-applet.png", GTK_STATE_NORMAL, True}, + {ACCESSX_BASE_ICON, "ax-key-base.png", GTK_STATE_NORMAL, True}, + {ACCESSX_BASE_ICON, "ax-key-none.png", GTK_STATE_INSENSITIVE, False}, + {ACCESSX_BASE_ICON, "ax-key-inverse.png", GTK_STATE_SELECTED, False}, + {ACCESSX_ACCEPT_BASE, "ax-key-yes.png", GTK_STATE_NORMAL, True}, + {ACCESSX_REJECT_BASE, "ax-key-no.png", GTK_STATE_NORMAL, True}, + {MOUSEKEYS_BASE_ICON, "mousekeys-base.png", GTK_STATE_NORMAL, True}, + {MOUSEKEYS_BUTTON_LEFT, "mousekeys-pressed-left.png", GTK_STATE_NORMAL, True}, + {MOUSEKEYS_BUTTON_MIDDLE, "mousekeys-pressed-middle.png", GTK_STATE_NORMAL, True}, + {MOUSEKEYS_BUTTON_RIGHT, "mousekeys-pressed-right.png", GTK_STATE_NORMAL, True}, + {MOUSEKEYS_DOT_LEFT, "mousekeys-default-left.png", GTK_STATE_NORMAL, True}, + {MOUSEKEYS_DOT_MIDDLE, "mousekeys-default-middle.png", GTK_STATE_NORMAL, True}, + {MOUSEKEYS_DOT_RIGHT, "mousekeys-default-right.png", GTK_STATE_NORMAL, True}, + {SHIFT_KEY_ICON, "sticky-shift-latched.png", GTK_STATE_NORMAL, False}, + {SHIFT_KEY_ICON, "sticky-shift-locked.png", GTK_STATE_SELECTED, False}, + {SHIFT_KEY_ICON, "sticky-shift-none.png", GTK_STATE_INSENSITIVE, True}, + {CONTROL_KEY_ICON, "sticky-ctrl-latched.png", GTK_STATE_NORMAL, False}, + {CONTROL_KEY_ICON, "sticky-ctrl-locked.png", GTK_STATE_SELECTED, False}, + {CONTROL_KEY_ICON, "sticky-ctrl-none.png", GTK_STATE_INSENSITIVE, True}, + {ALT_KEY_ICON, "sticky-alt-latched.png", GTK_STATE_NORMAL, False}, + {ALT_KEY_ICON, "sticky-alt-locked.png", GTK_STATE_SELECTED, False}, + {ALT_KEY_ICON, "sticky-alt-none.png", GTK_STATE_INSENSITIVE, True}, + {META_KEY_ICON, "sticky-meta-latched.png", GTK_STATE_NORMAL, False}, + {META_KEY_ICON, "sticky-meta-locked.png", GTK_STATE_SELECTED, False}, + {META_KEY_ICON, "sticky-meta-none.png", GTK_STATE_INSENSITIVE, True}, + {SUPER_KEY_ICON, "sticky-super-latched.png", GTK_STATE_NORMAL, False}, + {SUPER_KEY_ICON, "sticky-super-locked.png", GTK_STATE_SELECTED, False}, + {SUPER_KEY_ICON, "sticky-super-none.png", GTK_STATE_INSENSITIVE, True}, + {HYPER_KEY_ICON, "sticky-hyper-latched.png", GTK_STATE_NORMAL, False}, + {HYPER_KEY_ICON, "sticky-hyper-locked.png", GTK_STATE_SELECTED, False}, + {HYPER_KEY_ICON, "sticky-hyper-none.png", GTK_STATE_INSENSITIVE, True}, + {SLOWKEYS_IDLE_ICON, "ax-slowkeys.png", GTK_STATE_NORMAL, True}, + {SLOWKEYS_PENDING_ICON, "ax-slowkeys-pending.png", GTK_STATE_NORMAL, True}, + {SLOWKEYS_ACCEPT_ICON, "ax-slowkeys-yes.png", GTK_STATE_NORMAL, True}, + {SLOWKEYS_REJECT_ICON, "ax-slowkeys-no.png", GTK_STATE_NORMAL, True}, + {BOUNCEKEYS_ICON, "ax-bouncekeys.png", GTK_STATE_NORMAL, True} +}; + +typedef struct { + unsigned int mask; + GtkWidget* indicator; +} ModifierStruct; + +static ModifierStruct modifiers[] = { + {ShiftMask, NULL}, + {ControlMask, NULL}, + {Mod1Mask, NULL}, + {Mod2Mask, NULL}, + {Mod3Mask, NULL}, + {Mod4Mask, NULL}, + {Mod5Mask, NULL} +}; + +typedef struct { + unsigned int mask; + gchar* stock_id; +} ButtonIconStruct; + +static ButtonIconStruct button_icons[] = { + {Button1Mask, MOUSEKEYS_BUTTON_LEFT}, + {Button2Mask, MOUSEKEYS_BUTTON_MIDDLE}, + {Button3Mask, MOUSEKEYS_BUTTON_RIGHT} +}; + +static void popup_error_dialog(AccessxStatusApplet* sapplet); + +/* cribbed from geyes */ +static void about_cb(GtkAction* action, AccessxStatusApplet* sapplet) +{ + static const gchar* authors[] = { + "Calum Benson <[email protected]>", + "Bill Haneman <[email protected]>", + NULL + }; + + const gchar* documenters[] = { + "Bill Haneman <[email protected]>", + "Sun MATE Documentation Team <[email protected]>", + NULL + }; + + gtk_show_about_dialog(NULL, + "version", VERSION, + "comments", _("Shows the state of AccessX features such as latched modifiers"), + "copyright", "\xC2\xA9 2003 Sun Microsystems", + "authors", authors, + "documenters", documenters, + "translator-credits", _("translator-credits"), + "logo-icon-name", "ax-applet", + NULL); +} + +static void help_cb(GtkAction* action, AccessxStatusApplet* sapplet) +{ + GError* error = NULL; + GdkScreen* screen = gtk_widget_get_screen(GTK_WIDGET(sapplet->applet)); + + gtk_show_uri(screen, "ghelp:accessx-status", gtk_get_current_event_time(), &error); + + if (error) + { + GtkWidget* parent = gtk_widget_get_parent(GTK_WIDGET(sapplet->applet)); + + GtkWidget* dialog = gtk_message_dialog_new(GTK_WINDOW(parent), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("There was an error launching the help viewer: %s"), error->message); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL); + + gtk_window_set_screen(GTK_WINDOW(dialog), screen); + gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); + + gtk_widget_show(dialog); + g_error_free(error); + } +} + +static void dialog_cb(GtkAction* action, AccessxStatusApplet* sapplet) +{ + GError* error = NULL; + + if (sapplet->error_type != ACCESSX_STATUS_ERROR_NONE) + { + popup_error_dialog(sapplet); + return; + } + + + GdkScreen* screen = gtk_widget_get_screen(GTK_WIDGET(sapplet->applet)); + /* This is the old way of calling things, we try this just in case + * we're in a mixed-version enviroment. It has to be tried first, + * because the new command doesn't fail in a way useful to + * gdk_spawn_command_line_on_screen and its error parameter. */ + gdk_spawn_command_line_on_screen(screen, "mate-accessibility-keyboard-properties", &error); + + if (error != NULL) + { + g_error_free(error); + error = NULL; + gdk_spawn_command_line_on_screen(screen, "mate-keyboard-properties --a11y", &error); + } + + if (error != NULL) + { + GtkWidget* dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("There was an error launching the keyboard preferences dialog: %s"), error->message); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL); + + gtk_window_set_screen(GTK_WINDOW(dialog), screen); + gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); + + gtk_widget_show(dialog); + g_error_free(error); + } +} + +static const GtkActionEntry accessx_status_applet_menu_actions[] = { + {"Dialog", GTK_STOCK_PROPERTIES, N_("_Keyboard Accessibility Preferences"), NULL, NULL, G_CALLBACK(dialog_cb)}, + {"Help", GTK_STOCK_HELP, N_("_Help"), NULL, NULL, G_CALLBACK(help_cb)}, + {"About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, G_CALLBACK(about_cb)} +}; + +static XkbDescPtr accessx_status_applet_get_xkb_desc(AccessxStatusApplet* sapplet) +{ + Display* display; + + if (sapplet->xkb == NULL) + { + int ir, reason_return; + char* display_name = getenv("DISPLAY"); + display = XkbOpenDisplay(display_name, &xkb_base_event_type, &ir, NULL, NULL, &reason_return); + g_assert(display); /* TODO: change error message below to something user-viewable */ + + if (display == NULL) + { + g_warning("Xkb extension could not be initialized! (error code %x)", reason_return); + } + else + { + sapplet->xkb = XkbGetMap(display, XkbAllComponentsMask, XkbUseCoreKbd); + } + + g_assert(sapplet->xkb); + + if (sapplet->xkb == NULL) + { + g_warning("Xkb keyboard description not available!"); + } + + sapplet->xkb_display = display; + } + return sapplet->xkb; +} + +static gboolean accessx_status_applet_xkb_select(AccessxStatusApplet* sapplet) +{ + int opcode_rtn, error_rtn; + gboolean retval = FALSE; + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(sapplet->applet)); + + g_assert(sapplet && sapplet->applet && window); + + Display* display = GDK_WINDOW_XDISPLAY(window); + + g_assert(display); + + retval = XkbQueryExtension(display, &opcode_rtn, &xkb_base_event_type, &error_rtn, NULL, NULL); + + if (retval) + { + retval = XkbSelectEvents(display, XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask); + sapplet->xkb = accessx_status_applet_get_xkb_desc(sapplet); + } + else + { + sapplet->error_type = ACCESSX_STATUS_ERROR_XKB_DISABLED; + } + + return retval; +} + +static void accessx_status_applet_init_modifiers(AccessxStatusApplet* sapplet) +{ + int i; + unsigned int hyper_mask, super_mask, alt_gr_mask; + + unsigned int alt_mask = XkbKeysymToModifiers(sapplet->xkb_display, XK_Alt_L); + unsigned int meta_mask = XkbKeysymToModifiers(sapplet->xkb_display, XK_Meta_L); + + g_assert(sapplet->meta_indicator); + + if (meta_mask && (meta_mask != alt_mask)) + { + gtk_widget_show(sapplet->meta_indicator); + } + else + { + gtk_widget_hide(sapplet->meta_indicator); + } + + hyper_mask = XkbKeysymToModifiers(sapplet->xkb_display, XK_Hyper_L); + + if (hyper_mask) + { + gtk_widget_show(sapplet->hyper_indicator); + } + else + { + gtk_widget_hide(sapplet->hyper_indicator); + } + + super_mask = XkbKeysymToModifiers(sapplet->xkb_display, XK_Super_L); + + if (super_mask) + { + gtk_widget_show(sapplet->super_indicator); + } + else + { + gtk_widget_hide(sapplet->super_indicator); + } + + alt_gr_mask = XkbKeysymToModifiers(sapplet->xkb_display, XK_Mode_switch) | + XkbKeysymToModifiers(sapplet->xkb_display, XK_ISO_Level3_Shift) | + XkbKeysymToModifiers(sapplet->xkb_display, XK_ISO_Level3_Latch) | + XkbKeysymToModifiers(sapplet->xkb_display, XK_ISO_Level3_Lock); + + if (alt_gr_mask) + { + gtk_widget_show(sapplet->alt_graph_indicator); + } + else + { + gtk_widget_hide(sapplet->alt_graph_indicator); + } + + for (i = 0; i < G_N_ELEMENTS(modifiers); ++i) + { + if (modifiers[i].mask == ShiftMask) + { + modifiers[i].indicator = sapplet->shift_indicator; + } + else if (modifiers[i].mask == ControlMask) + { + modifiers[i].indicator = sapplet->ctrl_indicator; + } + else if (modifiers[i].mask == alt_mask) + { + modifiers[i].indicator = sapplet->alt_indicator; + } + else if (modifiers[i].mask == meta_mask) + { + modifiers[i].indicator = sapplet->meta_indicator; + } + else if (modifiers[i].mask == hyper_mask) + { + modifiers[i].indicator = sapplet->hyper_indicator; + } + else if (modifiers[i].mask == super_mask) + { + modifiers[i].indicator = sapplet->super_indicator; + } + else if (modifiers[i].mask == alt_gr_mask) + { + modifiers[i].indicator = sapplet->alt_graph_indicator; + } + } +} + +static guint _sk_timeout = 0; +static guint _bk_timeout = 0; + +static gboolean timer_reset_slowkeys_image(gpointer user_data) +{ + GdkPixbuf* pixbuf = gtk_widget_render_icon(GTK_WIDGET(user_data), SLOWKEYS_IDLE_ICON, icon_size_spec, NULL); + + gtk_image_set_from_pixbuf(GTK_IMAGE(user_data), pixbuf); + g_object_unref(pixbuf); + + return FALSE; +} + +static gboolean timer_reset_bouncekeys_image(gpointer user_data) +{ + GdkPixbuf* pixbuf = gtk_widget_render_icon(GTK_WIDGET(user_data), BOUNCEKEYS_ICON, icon_size_spec, NULL); + + gtk_image_set_from_pixbuf(GTK_IMAGE(user_data), pixbuf); + g_object_unref(pixbuf); + + return FALSE; +} + +static GdkPixbuf* accessx_status_applet_get_glyph_pixbuf(AccessxStatusApplet* sapplet, GtkWidget* widget, GdkPixbuf* base, GdkColor* fg, GdkColor* bg, gchar* glyphstring) +{ + GdkPixbuf* glyph_pixbuf, *alpha_pixbuf; + GdkPixmap* pixmap; + PangoLayout* layout; + PangoRectangle ink, logic; + PangoContext* pango_context; + GdkColormap* cmap; + GdkGC* gc; + GdkVisual* visual = gdk_visual_get_best(); + gint w = gdk_pixbuf_get_width(base); + gint h = gdk_pixbuf_get_height(base); + pixmap = gdk_pixmap_new(NULL, gdk_pixbuf_get_width(base), gdk_pixbuf_get_height(base), visual->depth); + pango_context = gtk_widget_get_pango_context(widget); + layout = pango_layout_new(pango_context); + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + pango_layout_set_text(layout, glyphstring, -1); + gc = gdk_gc_new(GDK_DRAWABLE(pixmap)); + cmap = gdk_drawable_get_colormap(GDK_DRAWABLE(pixmap)); + + if (!cmap) + { + cmap = gdk_colormap_new(visual, FALSE); + gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap), cmap); + } + else + { + g_object_ref(cmap); + } + + gdk_colormap_alloc_color(cmap, fg, FALSE, TRUE); + gdk_colormap_alloc_color(cmap, bg, FALSE, TRUE); + gdk_gc_set_foreground(gc, bg); + gdk_draw_rectangle(pixmap, gc, True, 0, 0, w, h); + gdk_gc_set_foreground(gc, fg); + gdk_gc_set_background(gc, bg); + gdk_gc_set_function(gc, GDK_COPY); + pango_layout_get_pixel_extents(layout, &ink, &logic); + gdk_draw_layout(GDK_DRAWABLE(pixmap), gc, (w - ink.x - ink.width) / 2, (h - ink.y - ink.height) / 2, layout); + g_object_unref(layout); + glyph_pixbuf = gdk_pixbuf_get_from_drawable(NULL, pixmap, cmap, 0, 0, 0, 0, w, h); + g_object_unref(pixmap); + alpha_pixbuf = gdk_pixbuf_add_alpha(glyph_pixbuf, TRUE, bg->red >> 8, bg->green >> 8, bg->blue >> 8); + g_object_unref(G_OBJECT(glyph_pixbuf)); + g_object_unref(G_OBJECT(cmap)); + g_object_unref(G_OBJECT(gc)); + + return alpha_pixbuf; +} + +static GdkPixbuf* accessx_status_applet_slowkeys_image(AccessxStatusApplet* sapplet, XkbAccessXNotifyEvent* event) +{ + GdkPixbuf* ret_pixbuf; + GdkWindow* window; + gboolean is_idle = TRUE; + gchar* stock_id = SLOWKEYS_IDLE_ICON; + GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(sapplet->applet)); + GdkColor bg = style->bg[GTK_STATE_NORMAL]; + + if (event != NULL) + { + is_idle = FALSE; + + switch (event->detail) + { + case XkbAXN_SKPress: + stock_id = ACCESSX_BASE_ICON; + if (_sk_timeout) + { + g_source_remove(_sk_timeout); + _sk_timeout = 0; + } + break; + case XkbAXN_SKAccept: + stock_id = ACCESSX_ACCEPT_BASE; + gdk_color_parse("#009900", &bg); + break; + case XkbAXN_SKReject: + stock_id = ACCESSX_REJECT_BASE; + gdk_color_parse("#990000", &bg); + _sk_timeout = g_timeout_add_full(G_PRIORITY_HIGH_IDLE, MAX(event->sk_delay, 150), timer_reset_slowkeys_image, sapplet->slowfoo, NULL); + break; + case XkbAXN_SKRelease: + default: + stock_id = SLOWKEYS_IDLE_ICON; + is_idle = TRUE; + break; + } + } + + ret_pixbuf = gtk_widget_render_icon(GTK_WIDGET(sapplet->applet), stock_id, icon_size_spec, NULL); + + if (!is_idle) + { + GdkPixbuf* glyph_pixbuf; + GdkPixbuf* tmp_pixbuf; + GdkColor fg; + gchar* glyphstring = N_("a"); + tmp_pixbuf = ret_pixbuf; + ret_pixbuf = gdk_pixbuf_copy(tmp_pixbuf); + g_object_unref(tmp_pixbuf); + + window = gtk_widget_get_window(GTK_WIDGET(sapplet->applet)); + + if (event && window) + { + KeySym keysym = XKeycodeToKeysym(GDK_WINDOW_XDISPLAY(window), event->keycode, 0); + glyphstring = XKeysymToString(keysym); + + if ((!g_utf8_validate(glyphstring, -1, NULL)) || (g_utf8_strlen(glyphstring, -1) > 1)) + { + glyphstring = ""; + } + } + + fg = style->fg[gtk_widget_get_state(GTK_WIDGET(sapplet->applet))]; + glyph_pixbuf = accessx_status_applet_get_glyph_pixbuf(sapplet, GTK_WIDGET(sapplet->applet), ret_pixbuf, &fg, &bg, glyphstring); + gdk_pixbuf_composite(glyph_pixbuf, ret_pixbuf, 0, 0, gdk_pixbuf_get_width(glyph_pixbuf), gdk_pixbuf_get_height(glyph_pixbuf), 0., 0., 1.0, 1.0, GDK_INTERP_NEAREST, 255); + g_object_unref(glyph_pixbuf); + } + + return ret_pixbuf; +} + +static GdkPixbuf* accessx_status_applet_bouncekeys_image(AccessxStatusApplet* sapplet, XkbAccessXNotifyEvent* event) +{ + GtkStyle* style; + GdkColor fg, bg; + GdkPixbuf* icon_base; + GdkPixbuf* tmp_pixbuf; + /* Note to translators: the first letter of the alphabet, not the indefinite article */ + gchar* glyphstring = N_("a"); + gchar* stock_id = ACCESSX_BASE_ICON; + + g_assert(sapplet->applet); + style = gtk_widget_get_style(GTK_WIDGET(sapplet->applet)); + fg = style->text[gtk_widget_get_state(GTK_WIDGET(sapplet->applet))]; + bg = style->base[GTK_STATE_NORMAL]; + + if (event != NULL) + { + switch (event->detail) + { + case XkbAXN_BKReject: + stock_id = SLOWKEYS_REJECT_ICON; + _bk_timeout = g_timeout_add_full(G_PRIORITY_HIGH_IDLE, MAX(event->debounce_delay, 150), timer_reset_bouncekeys_image, sapplet->bouncefoo, NULL); + break; + default: + stock_id = ACCESSX_BASE_ICON; + break; + } + } + tmp_pixbuf = gtk_widget_render_icon(GTK_WIDGET(sapplet->applet), stock_id, icon_size_spec, NULL); + + if (tmp_pixbuf) + { + GdkPixbuf* glyph_pixbuf; + icon_base = gdk_pixbuf_copy(tmp_pixbuf); + g_object_unref(tmp_pixbuf); + glyph_pixbuf = accessx_status_applet_get_glyph_pixbuf(sapplet, GTK_WIDGET(sapplet->applet), icon_base, &fg, &bg, glyphstring); + gdk_pixbuf_composite(glyph_pixbuf, icon_base, 2, 2, gdk_pixbuf_get_width(glyph_pixbuf) - 2, gdk_pixbuf_get_height(glyph_pixbuf) - 2, -2., -2., 1.0, 1.0, GDK_INTERP_NEAREST, 96); + gdk_pixbuf_composite(glyph_pixbuf, icon_base, 1, 1, gdk_pixbuf_get_width(glyph_pixbuf) - 1, gdk_pixbuf_get_height(glyph_pixbuf) - 1, 1., 1., 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + g_object_unref(glyph_pixbuf); + } + return icon_base; +} + +static GdkPixbuf* accessx_status_applet_mousekeys_image(AccessxStatusApplet* sapplet, XkbStateNotifyEvent* event) +{ + GdkPixbuf* mouse_pixbuf = NULL, *button_pixbuf, *dot_pixbuf, *tmp_pixbuf; + gchar* which_dot = MOUSEKEYS_DOT_LEFT; + tmp_pixbuf = gtk_widget_render_icon(GTK_WIDGET(sapplet->applet), MOUSEKEYS_BASE_ICON, icon_size_spec, NULL); + mouse_pixbuf = gdk_pixbuf_copy(tmp_pixbuf); + g_object_unref(tmp_pixbuf); + /* composite in the buttons */ + if (mouse_pixbuf && event && event->ptr_buttons) + { + gint i; + + for (i = 0; i < G_N_ELEMENTS(button_icons); ++i) + { + if (event->ptr_buttons & button_icons[i].mask) + { + button_pixbuf = gtk_widget_render_icon(GTK_WIDGET(sapplet->applet), button_icons[i].stock_id, icon_size_spec, NULL); + gdk_pixbuf_composite(button_pixbuf, mouse_pixbuf, 0, 0, gdk_pixbuf_get_width(button_pixbuf), gdk_pixbuf_get_height(button_pixbuf), 0.0, 0.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255); + } + } + } + + if (event) + { + switch (sapplet->xkb->ctrls->mk_dflt_btn) + { + case Button2: + which_dot = MOUSEKEYS_DOT_MIDDLE; + break; + case Button3: + which_dot = MOUSEKEYS_DOT_RIGHT; + break; + case Button1: + default: + which_dot = MOUSEKEYS_DOT_LEFT; + break; + } + } + dot_pixbuf = gtk_widget_render_icon(GTK_WIDGET(sapplet->applet), which_dot, icon_size_spec, NULL); + + gdk_pixbuf_composite(dot_pixbuf, mouse_pixbuf, 0, 0, gdk_pixbuf_get_width(dot_pixbuf), gdk_pixbuf_get_height(dot_pixbuf), 0.0, 0.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + return mouse_pixbuf; +} + + +static void accessx_status_applet_update(AccessxStatusApplet* sapplet, AccessxStatusNotifyType notify_type, XkbEvent* event) +{ + GtkWindow* window; + gint i; + + window = gtk_widget_get_window(GTK_WIDGET(sapplet->applet)); + + if (notify_type & ACCESSX_STATUS_MODIFIERS) + { + unsigned int locked_mods = 0, latched_mods = 0; + + if (event != NULL) + { + locked_mods = event->state.locked_mods; + latched_mods = event->state.latched_mods; + } + else if (sapplet->applet && window) + { + XkbStateRec state; + XkbGetState(GDK_WINDOW_XDISPLAY(window), XkbUseCoreKbd, &state); + locked_mods = state.locked_mods; + latched_mods = state.latched_mods; + } + /* determine which modifiers are locked, and set state accordingly */ + for (i = 0; i < G_N_ELEMENTS(modifiers); ++i) + { + if (modifiers[i].indicator != NULL && modifiers[i].mask) + { + if (locked_mods & modifiers[i].mask) + { + gtk_widget_set_sensitive(modifiers[i].indicator, TRUE); + gtk_widget_set_state(modifiers[i].indicator, GTK_STATE_SELECTED); + } + else if (latched_mods & modifiers[i].mask) + { + gtk_widget_set_sensitive(modifiers[i].indicator, TRUE); + gtk_widget_set_state(modifiers[i].indicator, GTK_STATE_NORMAL); + } + else + { + gtk_widget_set_sensitive(modifiers[i].indicator, FALSE); + } + } + } + } + + if ((notify_type & ACCESSX_STATUS_SLOWKEYS) && (event != NULL)) + { + GdkPixbuf* pixbuf = accessx_status_applet_slowkeys_image(sapplet, &event->accessx); + gtk_image_set_from_pixbuf(GTK_IMAGE(sapplet->slowfoo), pixbuf); + g_object_unref(pixbuf); + } + + if ((notify_type & ACCESSX_STATUS_BOUNCEKEYS) && (event != NULL)) + { + GdkPixbuf* pixbuf = accessx_status_applet_bouncekeys_image(sapplet, &event->accessx); + gtk_image_set_from_pixbuf(GTK_IMAGE(sapplet->bouncefoo), pixbuf); + g_object_unref(pixbuf); + } + + if (notify_type & ACCESSX_STATUS_MOUSEKEYS) + { + GdkPixbuf* pixbuf = accessx_status_applet_mousekeys_image(sapplet, &event->state); + gtk_image_set_from_pixbuf(GTK_IMAGE(sapplet->mousefoo), pixbuf); + g_object_unref(pixbuf); + } + + if (notify_type & ACCESSX_STATUS_ENABLED) + { + /* Update the visibility of widgets in the box */ + /* XkbMouseKeysMask | XkbStickyKeysMask | XkbSlowKeysMask | XkbBounceKeysMask */ + XkbGetControls(GDK_WINDOW_XDISPLAY(window), XkbAllControlsMask, sapplet->xkb); + + if (!(sapplet->xkb->ctrls->enabled_ctrls & (XkbMouseKeysMask | XkbStickyKeysMask | XkbSlowKeysMask | XkbBounceKeysMask))) + { + gtk_widget_show(sapplet->idlefoo); + } + else + { + gtk_widget_hide(sapplet->idlefoo); + } + + if (sapplet->xkb->ctrls->enabled_ctrls & XkbMouseKeysMask) + { + gtk_widget_show(sapplet->mousefoo); + } + else + { + gtk_widget_hide(sapplet->mousefoo); + } + + if (sapplet->xkb->ctrls->enabled_ctrls & XkbStickyKeysMask) + { + gtk_widget_show(sapplet->stickyfoo); + } + else + { + gtk_widget_hide(sapplet->stickyfoo); + } + + if (sapplet->xkb->ctrls->enabled_ctrls & XkbSlowKeysMask) + { + gtk_widget_show(sapplet->slowfoo); + } + else + { + gtk_widget_hide(sapplet->slowfoo); + } + + if (sapplet->xkb->ctrls->enabled_ctrls & XkbBounceKeysMask) + { + gtk_widget_show(sapplet->bouncefoo); + } + else + { + gtk_widget_hide(sapplet->bouncefoo); + } + } + + return; +} + +static void accessx_status_applet_notify_xkb_ax(AccessxStatusApplet* sapplet, XkbAccessXNotifyEvent* event) +{ + AccessxStatusNotifyType notify_mask = 0; + + switch (event->detail) + { + case XkbAXN_SKPress: + case XkbAXN_SKAccept: + case XkbAXN_SKRelease: + case XkbAXN_SKReject: + notify_mask |= ACCESSX_STATUS_SLOWKEYS; + break; + case XkbAXN_BKAccept: + case XkbAXN_BKReject: + notify_mask |= ACCESSX_STATUS_BOUNCEKEYS; + break; + case XkbAXN_AXKWarning: + break; + default: + break; + } + + accessx_status_applet_update(sapplet, notify_mask, (XkbEvent*) event); +} + +static void accessx_status_applet_notify_xkb_state(AccessxStatusApplet* sapplet, XkbStateNotifyEvent* event) +{ + AccessxStatusNotifyType notify_mask = 0; + + if (event->changed & XkbPointerButtonMask) + { + notify_mask |= ACCESSX_STATUS_MOUSEKEYS; + } + + if (event->changed & (XkbModifierLatchMask | XkbModifierLockMask)) + { + notify_mask |= ACCESSX_STATUS_MODIFIERS; + } + + accessx_status_applet_update(sapplet, notify_mask, (XkbEvent*) event); +} + +static void accessx_status_applet_notify_xkb_device(AccessxStatusApplet* sapplet, XkbExtensionDeviceNotifyEvent* event) +{ + if (event->reason == XkbXI_IndicatorStateMask) + { + if (event->led_state &= ALT_GRAPH_LED_MASK) + { + gtk_widget_set_sensitive(sapplet->alt_graph_indicator, TRUE); + gtk_widget_set_state(sapplet->alt_graph_indicator, GTK_STATE_NORMAL); + } + else + { + gtk_widget_set_sensitive(sapplet->alt_graph_indicator, FALSE); + } + } +} + +static void accessx_status_applet_notify_xkb_controls(AccessxStatusApplet* sapplet, XkbControlsNotifyEvent* event) +{ + unsigned int mask = XkbStickyKeysMask | XkbSlowKeysMask | XkbBounceKeysMask | XkbMouseKeysMask; + unsigned int notify_mask = 0; + + XkbGetControls(sapplet->xkb_display, XkbMouseKeysMask, sapplet->xkb); + + if (event->enabled_ctrl_changes & mask) + { + notify_mask = ACCESSX_STATUS_ENABLED; + } + + if (event->changed_ctrls & XkbMouseKeysMask) + { + notify_mask |= ACCESSX_STATUS_MOUSEKEYS; + } + + if (notify_mask) + { + accessx_status_applet_update(sapplet, notify_mask, (XkbEvent*) event); + } +} + +static void accessx_status_applet_notify_xkb_event(AccessxStatusApplet* sapplet, XkbEvent* event) +{ + switch (event->any.xkb_type) + { + case XkbStateNotify: + accessx_status_applet_notify_xkb_state(sapplet, &event->state); + break; + case XkbAccessXNotify: + accessx_status_applet_notify_xkb_ax(sapplet, &event->accessx); + break; + case XkbControlsNotify: + accessx_status_applet_notify_xkb_controls(sapplet, &event->ctrls); + break; + case XkbExtensionDeviceNotify: + /* This is a hack around the fact that XFree86's XKB doesn't give AltGr notifications */ + accessx_status_applet_notify_xkb_device(sapplet, &event->device); + break; + default: + break; + } +} + +static GdkFilterReturn accessx_status_xkb_filter(GdkXEvent* gdk_xevent, GdkEvent* event, gpointer user_data) +{ + AccessxStatusApplet* sapplet = user_data; + XkbEvent* xevent = gdk_xevent; + + if (xevent->any.type == xkb_base_event_type) + { + accessx_status_applet_notify_xkb_event(sapplet, xevent); + } + + return GDK_FILTER_CONTINUE; +} + +static GtkIconSet* accessx_status_applet_altgraph_icon_set(AccessxStatusApplet* sapplet, GtkWidget* widget) +{ + GtkIconSet* icon_set = gtk_icon_set_new(); + gint i; + GtkStateType states[3] = {GTK_STATE_NORMAL, GTK_STATE_INSENSITIVE, GTK_STATE_SELECTED}; + GtkStyle* style = gtk_widget_get_style(widget); + GdkPixbuf* icon_base; + + gtk_widget_set_sensitive(widget, TRUE); + + for (i = 0; i < 3; ++i) + { + int alpha; + GdkColor* fg; + GdkColor* bg; + GtkIconSource* source = gtk_icon_source_new(); + GdkPixbuf* pixbuf; + GdkPixbuf* glyph_pixbuf; + gboolean wildcarded = FALSE; + fg = &style->text[states[i]]; + bg = &style->white; + switch (states[i]) + { + case GTK_STATE_NORMAL: + alpha = 255; + gtk_widget_set_sensitive(widget, TRUE); + gtk_widget_set_state(widget, GTK_STATE_NORMAL); + break; + case GTK_STATE_SELECTED: + /* FIXME: should use text/base here, for selected ? */ + fg = &style->white; + bg = &style->black; + alpha = 255; + gtk_widget_set_sensitive(widget, TRUE); + gtk_widget_set_state(widget, GTK_STATE_SELECTED); + break; + case GTK_STATE_INSENSITIVE: + default: + alpha = 63; + gtk_widget_set_sensitive(widget, FALSE); + wildcarded = TRUE; + break; + } + + icon_base = gtk_widget_render_icon(widget, ACCESSX_BASE_ICON, icon_size_spec, NULL); + pixbuf = gdk_pixbuf_copy(icon_base); + g_object_unref(icon_base); + /* + * should be N_("ae")); + * need en_ locale for this. + */ + /* + * Translators: substitute an easily-recognized single glyph + * from Level 2, i.e. an AltGraph character from a common keyboard + * in your locale. + */ + glyph_pixbuf = accessx_status_applet_get_glyph_pixbuf(sapplet, widget, pixbuf, fg, bg, ("æ")); + gdk_pixbuf_composite(glyph_pixbuf, pixbuf, 0, 0, gdk_pixbuf_get_width(glyph_pixbuf), gdk_pixbuf_get_height(glyph_pixbuf), 0., 0., 1.0, 1.0, GDK_INTERP_NEAREST, alpha); + g_object_unref(glyph_pixbuf); + gtk_icon_source_set_pixbuf(source, pixbuf); + gtk_icon_source_set_state(source, states[i]); + gtk_icon_source_set_state_wildcarded(source, wildcarded); + gtk_icon_set_add_source(icon_set, source); + gtk_icon_source_free(source); + } + /* we mucked about with the box's state to create the icons; restore it to normal */ + gtk_widget_set_state(widget, GTK_STATE_NORMAL); + gtk_widget_set_sensitive(widget, TRUE); + + return icon_set; +} + +static void accessx_applet_add_stock_icons(AccessxStatusApplet* sapplet, GtkWidget* widget) +{ + GtkIconFactory* factory = gtk_icon_factory_new(); + gint i = 0; + GtkIconSet* icon_set; + + gtk_icon_factory_add_default(factory); + + while (i < G_N_ELEMENTS(stock_icons)) + { + gchar* set_name = stock_icons[i].stock_id; + icon_set = gtk_icon_set_new(); + + do { + char* filename; + GtkIconSource* source = gtk_icon_source_new(); + filename = g_build_filename(ACCESSX_PIXMAPS_DIR, stock_icons[i].name, NULL); + + if (g_file_test(filename, G_FILE_TEST_EXISTS) && g_file_test(filename, G_FILE_TEST_IS_REGULAR)) + { + gtk_icon_source_set_filename(source, filename); + } + else + { + GtkIconSet* default_set = gtk_icon_factory_lookup_default(GTK_STOCK_MISSING_IMAGE); + gtk_icon_source_set_pixbuf(source, gtk_icon_set_render_icon(default_set, gtk_widget_get_style(widget), GTK_TEXT_DIR_NONE, GTK_STATE_NORMAL, icon_size_spec, widget, NULL)); + } + g_free(filename); + gtk_icon_source_set_state(source, stock_icons[i].state); + gtk_icon_source_set_state_wildcarded(source, stock_icons[i].wildcarded); + gtk_icon_set_add_source(icon_set, source); + gtk_icon_source_free(source); + ++i; + } while (set_name == stock_icons[i].stock_id); + gtk_icon_factory_add(factory, set_name, icon_set); + gtk_icon_set_unref(icon_set); + } + /* now create the stock icons for AltGr, which are internationalized */ + icon_set = accessx_status_applet_altgraph_icon_set (sapplet, widget); + gtk_icon_factory_add(factory, ALTGRAPH_KEY_ICON, icon_set); + gtk_icon_set_unref(icon_set); + sapplet->icon_factory = factory; +} + +static void accessx_status_applet_reparent_widget(GtkWidget* widget, GtkContainer* container) +{ + if (widget) + { + if (gtk_widget_get_parent(widget)) + { + g_object_ref(G_OBJECT(widget)); + gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(widget)), widget); + } + + gtk_container_add(container, widget); + } +} + +static void accessx_status_applet_layout_box(AccessxStatusApplet* sapplet, GtkWidget* box, GtkWidget* stickyfoo) +{ + AtkObject* atko; + + accessx_status_applet_reparent_widget(sapplet->shift_indicator, GTK_CONTAINER(stickyfoo)); + accessx_status_applet_reparent_widget(sapplet->ctrl_indicator, GTK_CONTAINER(stickyfoo)); + accessx_status_applet_reparent_widget(sapplet->alt_indicator, GTK_CONTAINER(stickyfoo)); + accessx_status_applet_reparent_widget(sapplet->meta_indicator, GTK_CONTAINER(stickyfoo)); + accessx_status_applet_reparent_widget(sapplet->hyper_indicator, GTK_CONTAINER(stickyfoo)); + accessx_status_applet_reparent_widget(sapplet->super_indicator, GTK_CONTAINER(stickyfoo)); + accessx_status_applet_reparent_widget(sapplet->alt_graph_indicator, GTK_CONTAINER(stickyfoo)); + accessx_status_applet_reparent_widget(sapplet->idlefoo, GTK_CONTAINER(box)); + accessx_status_applet_reparent_widget(sapplet->mousefoo, GTK_CONTAINER(box)); + accessx_status_applet_reparent_widget(stickyfoo, GTK_CONTAINER(box)); + accessx_status_applet_reparent_widget(sapplet->slowfoo, GTK_CONTAINER(box)); + accessx_status_applet_reparent_widget(sapplet->bouncefoo, GTK_CONTAINER(box)); + + if (sapplet->stickyfoo) + { + gtk_widget_destroy(sapplet->stickyfoo); + } + + if (sapplet->box) + { + gtk_container_remove(GTK_CONTAINER(sapplet->applet), sapplet->box); + } + + gtk_container_add(GTK_CONTAINER(sapplet->applet), box); + sapplet->stickyfoo = stickyfoo; + sapplet->box = box; + + atko = gtk_widget_get_accessible(sapplet->box); + atk_object_set_name(atko, _("AccessX Status")); + atk_object_set_description(atko, _("Shows keyboard status when accessibility features are used.")); + + gtk_widget_show(sapplet->box); + gtk_widget_show(GTK_WIDGET(sapplet->applet)); + + if (gtk_widget_get_realized(sapplet->box) && sapplet->initialized) + { + accessx_status_applet_update(sapplet, ACCESSX_STATUS_ALL, NULL); + } +} + +static void disable_applet(AccessxStatusApplet* sapplet) +{ + gtk_widget_hide(sapplet->meta_indicator); + gtk_widget_hide(sapplet->hyper_indicator); + gtk_widget_hide(sapplet->super_indicator); + gtk_widget_hide(sapplet->alt_graph_indicator); + gtk_widget_hide(sapplet->shift_indicator); + gtk_widget_hide(sapplet->ctrl_indicator); + gtk_widget_hide(sapplet->alt_indicator); + gtk_widget_hide(sapplet->meta_indicator); + gtk_widget_hide(sapplet->mousefoo); + gtk_widget_hide(sapplet->stickyfoo); + gtk_widget_hide(sapplet->slowfoo); + gtk_widget_hide(sapplet->bouncefoo); +} + +static void popup_error_dialog(AccessxStatusApplet* sapplet) +{ + GtkWidget* dialog; + gchar* error_txt; + + switch (sapplet->error_type) + { + case ACCESSX_STATUS_ERROR_XKB_DISABLED: + error_txt = g_strdup(_("XKB Extension is not enabled")); + break; + + case ACCESSX_STATUS_ERROR_UNKNOWN: + + default: error_txt = g_strdup(_("Unknown error")); + break; + } + + dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Error: %s"), error_txt); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL); + + gtk_window_set_screen(GTK_WINDOW(dialog), gtk_widget_get_screen(GTK_WIDGET(sapplet->applet))); + + gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); + + gtk_widget_show(dialog); + g_free(error_txt); +} + +static AccessxStatusApplet* create_applet(MatePanelApplet* applet) +{ + AccessxStatusApplet* sapplet = g_new0(AccessxStatusApplet, 1); + GtkWidget* box; + GtkWidget* stickyfoo; + AtkObject* atko; + GdkPixbuf* pixbuf; + gint large_toolbar_pixels; + + g_set_application_name(_("AccessX Status")); + + sapplet->xkb = NULL; + sapplet->xkb_display = NULL; + sapplet->box = NULL; + sapplet->initialized = False; /* there must be a better way */ + sapplet->error_type = ACCESSX_STATUS_ERROR_NONE; + sapplet->applet = applet; + mate_panel_applet_set_flags(applet, MATE_PANEL_APPLET_EXPAND_MINOR); + sapplet->orient = mate_panel_applet_get_orient(applet); + + if (sapplet->orient == MATE_PANEL_APPLET_ORIENT_LEFT || sapplet->orient == MATE_PANEL_APPLET_ORIENT_RIGHT) + { + box = gtk_vbox_new(FALSE, 0); + stickyfoo = gtk_vbox_new(TRUE, 0); + } + else + { + box = gtk_hbox_new(FALSE, 0); + stickyfoo = gtk_hbox_new(TRUE, 0); + } + + large_toolbar_pixels = 24; /* FIXME */ + + if (mate_panel_applet_get_size(sapplet->applet) >= large_toolbar_pixels) + { + icon_size_spec = GTK_ICON_SIZE_LARGE_TOOLBAR; + } + else + { + icon_size_spec = GTK_ICON_SIZE_SMALL_TOOLBAR; + } + + accessx_applet_add_stock_icons(sapplet, box); + pixbuf = accessx_status_applet_mousekeys_image(sapplet, NULL); + sapplet->mousefoo = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); + gtk_widget_hide(sapplet->mousefoo); + + sapplet->shift_indicator = gtk_image_new_from_stock(SHIFT_KEY_ICON, icon_size_spec); + gtk_widget_hide(sapplet->mousefoo); + sapplet->ctrl_indicator = gtk_image_new_from_stock(CONTROL_KEY_ICON, icon_size_spec); + sapplet->alt_indicator = gtk_image_new_from_stock(ALT_KEY_ICON, icon_size_spec); + sapplet->meta_indicator = gtk_image_new_from_stock(META_KEY_ICON, icon_size_spec); + gtk_widget_set_sensitive(sapplet->meta_indicator, FALSE); + gtk_widget_hide(sapplet->meta_indicator); + sapplet->hyper_indicator = gtk_image_new_from_stock(HYPER_KEY_ICON, icon_size_spec); + gtk_widget_set_sensitive(sapplet->hyper_indicator, FALSE); + gtk_widget_hide(sapplet->hyper_indicator); + sapplet->super_indicator = gtk_image_new_from_stock(SUPER_KEY_ICON, icon_size_spec); + gtk_widget_set_sensitive(sapplet->super_indicator, FALSE); + gtk_widget_hide(sapplet->super_indicator); + sapplet->alt_graph_indicator = gtk_image_new_from_stock(ALTGRAPH_KEY_ICON, icon_size_spec); + gtk_widget_set_sensitive(sapplet->alt_graph_indicator, FALSE); + + pixbuf = accessx_status_applet_slowkeys_image(sapplet, NULL); + sapplet->slowfoo = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); + gtk_widget_hide(sapplet->slowfoo); + + pixbuf = accessx_status_applet_bouncekeys_image(sapplet, NULL); + sapplet->bouncefoo = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); + gtk_widget_hide(sapplet->bouncefoo); + + sapplet->idlefoo = gtk_image_new_from_stock(ACCESSX_APPLET, icon_size_spec); + gtk_widget_show(sapplet->slowfoo); + + accessx_status_applet_layout_box(sapplet, box, stickyfoo); + atko = gtk_widget_get_accessible(GTK_WIDGET(sapplet->applet)); + atk_object_set_name(atko, _("AccessX Status")); + atk_object_set_description(atko, _("Shows keyboard status when accessibility features are used.")); + return sapplet; +} + +static void accessx_status_applet_destroy(GtkWidget* widget, gpointer user_data) +{ + AccessxStatusApplet* sapplet = user_data; + /* do we need to free the icon factory ? */ + + gdk_window_remove_filter(NULL, accessx_status_xkb_filter, sapplet); + + if (sapplet->xkb) + { + XkbFreeKeyboard(sapplet->xkb, 0, True); + } + + if (sapplet->xkb_display) + { + XCloseDisplay(sapplet->xkb_display); + } +} + +static void accessx_status_applet_reorient(GtkWidget* widget, MatePanelAppletOrient o, gpointer user_data) +{ + AccessxStatusApplet* sapplet = user_data; + GtkWidget* box; + GtkWidget* stickyfoo; + + sapplet->orient = o; + + if (o == MATE_PANEL_APPLET_ORIENT_LEFT || o == MATE_PANEL_APPLET_ORIENT_RIGHT) + { + box = gtk_vbox_new(FALSE, 0); + stickyfoo = gtk_vbox_new(TRUE, 0); + } + else + { + box = gtk_hbox_new(FALSE, 0); + stickyfoo = gtk_hbox_new(TRUE, 0); + } + + accessx_status_applet_layout_box(sapplet, box, stickyfoo); +} + +static void accessx_status_applet_resize(GtkWidget* widget, int size, gpointer user_data) +{ + /* TODO: either rescale icons to fit panel, or tile them when possible */ +} + +static void accessx_status_applet_background(MatePanelApplet* a, MatePanelAppletBackgroundType type, GdkColor* color, GdkPixmap* pixmap, gpointer user_data) +{ + AccessxStatusApplet* sapplet = user_data; + + GtkRcStyle* rc_style; + GtkStyle* style; + + /* reset style */ + gtk_widget_set_style(GTK_WIDGET(sapplet->applet), NULL); + rc_style = gtk_rc_style_new(); + gtk_widget_modify_style(GTK_WIDGET(sapplet->applet), rc_style); + g_object_unref(rc_style); + + switch (type) + { + case PANEL_COLOR_BACKGROUND: + gtk_widget_modify_bg(GTK_WIDGET(sapplet->applet), GTK_STATE_NORMAL, color); + break; + + case PANEL_PIXMAP_BACKGROUND: + style = gtk_style_copy(gtk_widget_get_style(GTK_WIDGET(sapplet->applet))); + + if (style->bg_pixmap[GTK_STATE_NORMAL]) + { + g_object_unref(style->bg_pixmap[GTK_STATE_NORMAL]); + } + + style->bg_pixmap[GTK_STATE_NORMAL] = g_object_ref(pixmap); + gtk_widget_set_style(GTK_WIDGET(sapplet->applet), style); + g_object_unref(style); + break; + + case PANEL_NO_BACKGROUND: + default: + break; + } +} + +static gboolean button_press_cb(GtkWidget* widget, GdkEventButton* event, AccessxStatusApplet* sapplet) +{ + if (event->button == 1 && event->type == GDK_BUTTON_PRESS) + { + dialog_cb(NULL, sapplet); + } + + return FALSE; +} + +static gboolean key_press_cb(GtkWidget* widget, GdkEventKey* event, AccessxStatusApplet* sapplet) +{ + switch (event->keyval) + { + case GDK_KP_Enter: + case GDK_ISO_Enter: + case GDK_3270_Enter: + case GDK_Return: + case GDK_space: + case GDK_KP_Space: + dialog_cb(NULL, sapplet); + return TRUE; + + default: + break; + } + + return FALSE; +} + +static gboolean accessx_status_applet_reset(gpointer user_data) +{ + AccessxStatusApplet* sapplet = user_data; + g_assert(sapplet->applet); + accessx_status_applet_reorient(GTK_WIDGET(sapplet->applet), mate_panel_applet_get_orient(sapplet->applet), sapplet); + + return FALSE; +} + +static gboolean accessx_status_applet_initialize(AccessxStatusApplet* sapplet) +{ + if (!sapplet->initialized) + { + sapplet->initialized = True; + + if (!accessx_status_applet_xkb_select(sapplet)) + { + disable_applet(sapplet); + popup_error_dialog(sapplet); + return FALSE ; + } + + gdk_window_add_filter(NULL, accessx_status_xkb_filter, sapplet); + } + + accessx_status_applet_init_modifiers(sapplet); + accessx_status_applet_update(sapplet, ACCESSX_STATUS_ALL, NULL); + + return TRUE; +} + +static void accessx_status_applet_realize(GtkWidget* widget, gpointer user_data) +{ + AccessxStatusApplet* sapplet = user_data; + + if (!accessx_status_applet_initialize(sapplet)) + { + return; + } + + g_idle_add(accessx_status_applet_reset, sapplet); + + return; +} + +static gboolean accessx_status_applet_fill(MatePanelApplet* applet) +{ + AccessxStatusApplet* sapplet; + AtkObject* atk_object; + GtkActionGroup* action_group; + gchar* ui_path; + gboolean was_realized = FALSE; + + sapplet = create_applet(applet); + + if (!gtk_widget_get_realized(sapplet->box)) + { + g_signal_connect_after(G_OBJECT(sapplet->box), "realize", G_CALLBACK(accessx_status_applet_realize), sapplet); + } + else + { + accessx_status_applet_initialize(sapplet); + was_realized = TRUE; + } + + g_object_connect(sapplet->applet, + "signal::destroy", accessx_status_applet_destroy, sapplet, + "signal::change_orient", accessx_status_applet_reorient, sapplet, + "signal::change_size", accessx_status_applet_resize, sapplet, + "signal::change_background", accessx_status_applet_background, sapplet, + NULL); + + g_signal_connect(sapplet->applet, "button_press_event", G_CALLBACK(button_press_cb), sapplet); + g_signal_connect(sapplet->applet, "key_press_event", G_CALLBACK(key_press_cb), sapplet); + + action_group = gtk_action_group_new("Accessx Applet Actions"); + gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions(action_group, accessx_status_applet_menu_actions, G_N_ELEMENTS(accessx_status_applet_menu_actions), sapplet); + ui_path = g_build_filename(ACCESSX_MENU_UI_DIR, "accessx-status-applet-menu.xml", NULL); + mate_panel_applet_setup_menu_from_file(sapplet->applet, ui_path, action_group); + g_free(ui_path); + + if (mate_panel_applet_get_locked_down(sapplet->applet)) + { + GtkAction* action = gtk_action_group_get_action(action_group, "Dialog"); + gtk_action_set_visible(action, FALSE); + } + + g_object_unref(action_group); + + gtk_widget_set_tooltip_text(GTK_WIDGET(sapplet->applet), _("Keyboard Accessibility Status")); + + atk_object = gtk_widget_get_accessible(GTK_WIDGET(sapplet->applet)); + atk_object_set_name(atk_object, _("AccessX Status")); + atk_object_set_description(atk_object, _("Displays current state of keyboard accessibility features")); + gtk_widget_show_all(GTK_WIDGET(sapplet->applet)); + + if (was_realized) + { + accessx_status_applet_reset(sapplet); + } + + return TRUE; +} + +static gboolean accessx_status_applet_factory(MatePanelApplet* applet, const gchar* iid, gpointer data) +{ + gboolean retval = FALSE; + + if (!strcmp(iid, "AccessxStatusApplet")) + { + retval = accessx_status_applet_fill(applet); + } + + return retval; +} + +MATE_PANEL_APPLET_OUT_PROCESS_FACTORY("AccessxStatusAppletFactory", PANEL_TYPE_APPLET, "accessx-status", accessx_status_applet_factory, NULL) + |