diff --git a/data/rc.xml b/data/rc.xml
index a9a7a35..1f66840 100644
--- a/data/rc.xml
+++ b/data/rc.xml
@@ -637,6 +637,14 @@
entry in parent menu
if this is a negative value, then the delay is infinite and the
submenu will not be hidden until a different submenu is opened -->
+ no
+
+ no
+
yes
yes
diff --git a/data/rc.xsd b/data/rc.xsd
index c8f5638..9578b84 100644
--- a/data/rc.xsd
+++ b/data/rc.xsd
@@ -221,6 +221,8 @@
+
+
diff --git a/openbox/config.c b/openbox/config.c
index 436c555..709c777 100644
--- a/openbox/config.c
+++ b/openbox/config.c
@@ -98,6 +98,8 @@ guint config_submenu_show_delay;
guint config_submenu_hide_delay;
gboolean config_menu_manage_desktops;
gboolean config_menu_show_icons;
+gboolean config_menu_utf8_enabled;
+gboolean config_menu_utf8_allow_graph;
GSList *config_menu_files;
@@ -964,8 +966,13 @@ static void parse_menu(xmlNodePtr node, gpointer d)
config_submenu_hide_delay = obt_xml_node_int(n);
if ((n = obt_xml_find_node(node, "manageDesktops")))
config_menu_manage_desktops = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "utf8Enabled")))
+ config_menu_utf8_enabled = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "utf8AllowGraph")))
+ config_menu_utf8_allow_graph = obt_xml_node_bool(n);
if ((n = obt_xml_find_node(node, "showIcons"))) {
config_menu_show_icons = obt_xml_node_bool(n);
+
#if !defined(USE_IMLIB2) && !defined(USE_LIBRSVG)
if (config_menu_show_icons)
g_message(_("Openbox was compiled without image loading support. Icons in menus will not be loaded."));
@@ -1180,6 +1187,8 @@ void config_startup(ObtXmlInst *i)
config_submenu_show_delay = 100;
config_submenu_hide_delay = 400;
config_menu_manage_desktops = TRUE;
+ config_menu_utf8_enabled = FALSE;
+ config_menu_utf8_allow_graph = FALSE;
config_menu_files = NULL;
config_menu_show_icons = TRUE;
diff --git a/openbox/config.h b/openbox/config.h
index 5a694c0..d4f5130 100644
--- a/openbox/config.h
+++ b/openbox/config.h
@@ -210,6 +210,10 @@ extern guint config_submenu_show_delay;
extern guint config_submenu_hide_delay;
/*! Show manage desktops in client_list_menu */
extern gboolean config_menu_manage_desktops;
+/*! Enable utf8 support in menus */
+extern gboolean config_menu_utf8_enabled;
+/*! Allow using all graphs as menu shortcuts */
+extern gboolean config_menu_utf8_allow_graph;
/*! Load & show icons in user-defined menus */
extern gboolean config_menu_show_icons;
/*! User-specified menu files */
diff --git a/openbox/menu.c b/openbox/menu.c
index 8804e12..a708b38 100644
--- a/openbox/menu.c
+++ b/openbox/menu.c
@@ -55,6 +55,7 @@ static void menu_destroy_hash_value(ObMenu *self);
static void parse_menu_item(xmlNodePtr node, gpointer data);
static void parse_menu_separator(xmlNodePtr node, gpointer data);
static void parse_menu(xmlNodePtr node, gpointer data);
+static gboolean is_valid_shortcut(gchar *c);
static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
gchar **strippedlabel, guint *position,
gboolean *always_show);
@@ -201,6 +202,19 @@ static ObMenu* menu_from_name(gchar *name)
((c) >= 'A' && (c) <= 'Z') || \
((c) >= 'a' && (c) <= 'z'))
+static gboolean is_valid_shortcut(gchar *c)
+{
+ gunichar uc;
+
+ if(!config_menu_utf8_enabled) // unless utf8 support is explicitly enabled in config
+ return VALID_SHORTCUT(*c); // preserve old behaviour
+
+ uc = g_utf8_get_char_validated(c, MAX(OB_MAX_UTF8_CHAR_SZ, strlen(c)));
+ g_assert(uc > ((gunichar) - 1));
+
+ return config_menu_utf8_allow_graph ? g_unichar_isgraph(uc) : g_unichar_isalnum(uc);
+}
+
static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
gchar **strippedlabel, guint *position,
gboolean *always_show)
@@ -228,7 +242,7 @@ static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
i = *strippedlabel;
do {
escape = FALSE;
- i = strchr(i, '_');
+ i = config_menu_utf8_enabled ? g_utf8_strchr(i, -1, '_') : strchr(i, '_');
if (i && *(i+1) == '_') {
gchar *j;
@@ -244,10 +258,16 @@ static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
if (allow_shortcut && i != NULL) {
/* there is an underscore in the string */
- /* you have to use a printable ascii character for shortcuts
+ /* without utf8Enabled:
+ you have to use an alphanumeric ascii character for shortcuts
+
+ with utf8Enabled:
+ iff not utf8AllowGraph, you have to use an alphanumeric utf8 character
+ else any graph will do (printable character excluding whitespace/formatting
+
don't allow space either, so you can have like "a _ b"
*/
- if (VALID_SHORTCUT(*(i+1))) {
+ if (is_valid_shortcut(i+1)) {
shortcut = g_unichar_tolower(g_utf8_get_char(i+1));
*position = i - *strippedlabel;
*always_show = TRUE;
@@ -267,7 +287,7 @@ static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
instead */
for (i = *strippedlabel; *i != '\0'; ++i)
- if (VALID_SHORTCUT(*i)) {
+ if (is_valid_shortcut(i)) {
*position = i - *strippedlabel;
shortcut = g_unichar_tolower(g_utf8_get_char(i));
break;
diff --git a/openbox/misc.h b/openbox/misc.h
index 750dddd..43b686a 100644
--- a/openbox/misc.h
+++ b/openbox/misc.h
@@ -19,6 +19,9 @@
#ifndef __ob__misc_h
#define __ob__misc_h
+
+#define OB_MAX_UTF8_CHAR_SZ 4
+
/*! The alpha value to use for icons of iconified windows in various places
like the focus cycle popup and client list menus.
Give iconic windows 7/16 alpha. A little under 50%.