/* GTK - The GIMP Toolkit * Copyright (C) 1998, 2001 Tim Janik * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include "config.h" #include #include #include "gtkaccelgroup.h" #include "gtkaccelgroupprivate.h" #include "gtkintl.h" #include "gtkmarshalers.h" #include "gtkprivate.h" /* --- functions --- */ /** * gtk_accelerator_valid: * @keyval: a GDK keyval * @modifiers: modifier mask * * Determines whether a given keyval and modifier mask constitute * a valid keyboard accelerator. * * For example, the %GDK_KEY_a keyval plus %GDK_CONTROL_MASK mark is valid, * and matches the “Ctrl+a” accelerator. But, you can't, for instance, use * the %GDK_KEY_Control_L keyval as an accelerator. * * Returns: %TRUE if the accelerator is valid */ gboolean gtk_accelerator_valid (guint keyval, GdkModifierType modifiers) { static const guint invalid_accelerator_vals[] = { GDK_KEY_Shift_L, GDK_KEY_Shift_R, GDK_KEY_Shift_Lock, GDK_KEY_Caps_Lock, GDK_KEY_ISO_Lock, GDK_KEY_Control_L, GDK_KEY_Control_R, GDK_KEY_Meta_L, GDK_KEY_Meta_R, GDK_KEY_Alt_L, GDK_KEY_Alt_R, GDK_KEY_Super_L, GDK_KEY_Super_R, GDK_KEY_Hyper_L, GDK_KEY_Hyper_R, GDK_KEY_ISO_Level3_Shift, GDK_KEY_ISO_Next_Group, GDK_KEY_ISO_Prev_Group, GDK_KEY_ISO_First_Group, GDK_KEY_ISO_Last_Group, GDK_KEY_Mode_switch, GDK_KEY_Num_Lock, GDK_KEY_Multi_key, GDK_KEY_Scroll_Lock, GDK_KEY_Sys_Req, GDK_KEY_Tab, GDK_KEY_ISO_Left_Tab, GDK_KEY_KP_Tab, GDK_KEY_First_Virtual_Screen, GDK_KEY_Prev_Virtual_Screen, GDK_KEY_Next_Virtual_Screen, GDK_KEY_Last_Virtual_Screen, GDK_KEY_Terminate_Server, GDK_KEY_AudibleBell_Enable, 0 }; static const guint invalid_unmodified_vals[] = { GDK_KEY_Up, GDK_KEY_Down, GDK_KEY_Left, GDK_KEY_Right, GDK_KEY_KP_Up, GDK_KEY_KP_Down, GDK_KEY_KP_Left, GDK_KEY_KP_Right, 0 }; const guint *ac_val; modifiers &= GDK_MODIFIER_MASK; if (keyval <= 0xFF) return keyval >= 0x20; ac_val = invalid_accelerator_vals; while (*ac_val) { if (keyval == *ac_val++) return FALSE; } if (!modifiers) { ac_val = invalid_unmodified_vals; while (*ac_val) { if (keyval == *ac_val++) return FALSE; } } return TRUE; } static inline gboolean is_alt (const char *string) { return ((string[0] == '<') && (string[1] == 'a' || string[1] == 'A') && (string[2] == 'l' || string[2] == 'L') && (string[3] == 't' || string[3] == 'T') && (string[4] == '>')); } static inline gboolean is_ctl (const char *string) { return ((string[0] == '<') && (string[1] == 'c' || string[1] == 'C') && (string[2] == 't' || string[2] == 'T') && (string[3] == 'l' || string[3] == 'L') && (string[4] == '>')); } static inline gboolean is_ctrl (const char *string) { return ((string[0] == '<') && (string[1] == 'c' || string[1] == 'C') && (string[2] == 't' || string[2] == 'T') && (string[3] == 'r' || string[3] == 'R') && (string[4] == 'l' || string[4] == 'L') && (string[5] == '>')); } static inline gboolean is_shft (const char *string) { return ((string[0] == '<') && (string[1] == 's' || string[1] == 'S') && (string[2] == 'h' || string[2] == 'H') && (string[3] == 'f' || string[3] == 'F') && (string[4] == 't' || string[4] == 'T') && (string[5] == '>')); } static inline gboolean is_shift (const char *string) { return ((string[0] == '<') && (string[1] == 's' || string[1] == 'S') && (string[2] == 'h' || string[2] == 'H') && (string[3] == 'i' || string[3] == 'I') && (string[4] == 'f' || string[4] == 'F') && (string[5] == 't' || string[5] == 'T') && (string[6] == '>')); } static inline gboolean is_control (const char *string) { return ((string[0] == '<') && (string[1] == 'c' || string[1] == 'C') && (string[2] == 'o' || string[2] == 'O') && (string[3] == 'n' || string[3] == 'N') && (string[4] == 't' || string[4] == 'T') && (string[5] == 'r' || string[5] == 'R') && (string[6] == 'o' || string[6] == 'O') && (string[7] == 'l' || string[7] == 'L') && (string[8] == '>')); } static inline gboolean is_meta (const char *string) { return ((string[0] == '<') && (string[1] == 'm' || string[1] == 'M') && (string[2] == 'e' || string[2] == 'E') && (string[3] == 't' || string[3] == 'T') && (string[4] == 'a' || string[4] == 'A') && (string[5] == '>')); } static inline gboolean is_super (const char *string) { return ((string[0] == '<') && (string[1] == 's' || string[1] == 'S') && (string[2] == 'u' || string[2] == 'U') && (string[3] == 'p' || string[3] == 'P') && (string[4] == 'e' || string[4] == 'E') && (string[5] == 'r' || string[5] == 'R') && (string[6] == '>')); } static inline gboolean is_hyper (const char *string) { return ((string[0] == '<') && (string[1] == 'h' || string[1] == 'H') && (string[2] == 'y' || string[2] == 'Y') && (string[3] == 'p' || string[3] == 'P') && (string[4] == 'e' || string[4] == 'E') && (string[5] == 'r' || string[5] == 'R') && (string[6] == '>')); } static inline gboolean is_primary (const char *string) { return ((string[0] == '<') && (string[1] == 'p' || string[1] == 'P') && (string[2] == 'r' || string[2] == 'R') && (string[3] == 'i' || string[3] == 'I') && (string[4] == 'm' || string[4] == 'M') && (string[5] == 'a' || string[5] == 'A') && (string[6] == 'r' || string[6] == 'R') && (string[7] == 'y' || string[7] == 'Y') && (string[8] == '>')); } static inline gboolean is_keycode (const char *string) { return (string[0] == '0' && string[1] == 'x' && g_ascii_isxdigit (string[2]) && g_ascii_isxdigit (string[3])); } /** * gtk_accelerator_parse_with_keycode: * @accelerator: string representing an accelerator * @display: (nullable): the `GdkDisplay` to look up @accelerator_codes in * @accelerator_key: (out) (optional): return location for accelerator keyval * @accelerator_codes: (out) (array zero-terminated=1) (transfer full) (optional): * return location for accelerator keycodes * @accelerator_mods: (out) (optional): return location for accelerator * modifier mask * * Parses a string representing an accelerator. * * This is similar to [func@Gtk.accelerator_parse] but handles keycodes as * well. This is only useful for system-level components, applications should * use [func@Gtk.accelerator_parse] instead. * * If @accelerator_codes is given and the result stored in it is non-%NULL, * the result must be freed with g_free(). * * If a keycode is present in the accelerator and no @accelerator_codes * is given, the parse will fail. * * If the parse fails, @accelerator_key, @accelerator_mods and * @accelerator_codes will be set to 0 (zero). * * Returns: %TRUE if parsing succeeded */ gboolean gtk_accelerator_parse_with_keycode (const char *accelerator, GdkDisplay *display, guint *accelerator_key, guint **accelerator_codes, GdkModifierType *accelerator_mods) { guint keyval; GdkModifierType mods; int len; gboolean error; if (accelerator_key) *accelerator_key = 0; if (accelerator_mods) *accelerator_mods = 0; if (accelerator_codes) *accelerator_codes = NULL; g_return_val_if_fail (accelerator != NULL, FALSE); if (!display) display = gdk_display_get_default (); error = FALSE; keyval = 0; mods = 0; len = strlen (accelerator); while (len) { if (*accelerator == '<') { if (len >= 9 && is_primary (accelerator)) { accelerator += 9; len -= 9; mods |= GDK_CONTROL_MASK; } else if (len >= 9 && is_control (accelerator)) { accelerator += 9; len -= 9; mods |= GDK_CONTROL_MASK; } else if (len >= 7 && is_shift (accelerator)) { accelerator += 7; len -= 7; mods |= GDK_SHIFT_MASK; } else if (len >= 6 && is_shft (accelerator)) { accelerator += 6; len -= 6; mods |= GDK_SHIFT_MASK; } else if (len >= 6 && is_ctrl (accelerator)) { accelerator += 6; len -= 6; mods |= GDK_CONTROL_MASK; } else if (len >= 5 && is_ctl (accelerator)) { accelerator += 5; len -= 5; mods |= GDK_CONTROL_MASK; } else if (len >= 5 && is_alt (accelerator)) { accelerator += 5; len -= 5; mods |= GDK_ALT_MASK; } else if (len >= 6 && is_meta (accelerator)) { accelerator += 6; len -= 6; mods |= GDK_META_MASK; } else if (len >= 7 && is_hyper (accelerator)) { accelerator += 7; len -= 7; mods |= GDK_HYPER_MASK; } else if (len >= 7 && is_super (accelerator)) { accelerator += 7; len -= 7; mods |= GDK_SUPER_MASK; } else { char last_ch; last_ch = *accelerator; while (last_ch && last_ch != '>') { accelerator += 1; len -= 1; last_ch = *accelerator; } } } else { if (len >= 4 && is_keycode (accelerator)) { char keystring[5]; char *endptr; int tmp_keycode; memcpy (keystring, accelerator, 4); keystring [4] = '\000'; tmp_keycode = strtol (keystring, &endptr, 16); if (endptr == NULL || *endptr != '\000') { error = TRUE; goto out; } else if (accelerator_codes != NULL) { /* 0x00 is an invalid keycode too. */ if (tmp_keycode == 0) { error = TRUE; goto out; } else { *accelerator_codes = g_new0 (guint, 2); (*accelerator_codes)[0] = tmp_keycode; } } else { /* There was a keycode in the string, but * we cannot store it, so we have an error */ error = TRUE; goto out; } } else { keyval = gdk_keyval_from_name (accelerator); if (keyval == GDK_KEY_VoidSymbol) { error = TRUE; goto out; } } if (keyval && accelerator_codes != NULL) { GdkKeymapKey *keys; int n_keys, i, j; if (!gdk_display_map_keyval (display, keyval, &keys, &n_keys)) { /* Not in keymap */ error = TRUE; goto out; } else { *accelerator_codes = g_new0 (guint, n_keys + 1); /* Prefer level-0 group-0 keys to modified keys */ for (i = 0, j = 0; i < n_keys; ++i) { if (keys[i].level == 0 && keys[i].group == 0) (*accelerator_codes)[j++] = keys[i].keycode; } /* No level-0 group-0 keys? Find in the whole group-0 */ if (j == 0) { for (i = 0, j = 0; i < n_keys; ++i) { if (keys[i].group == 0) (*accelerator_codes)[j++] = keys[i].keycode; } } /* Still nothing? Try in other groups */ if (j == 0) { for (i = 0, j = 0; i < n_keys; ++i) (*accelerator_codes)[j++] = keys[i].keycode; } if (j == 0) { g_free (*accelerator_codes); *accelerator_codes = NULL; /* Not in keymap */ error = TRUE; goto out; } g_free (keys); } } accelerator += len; len -= len; } } out: if (error) keyval = mods = 0; if (accelerator_key) *accelerator_key = gdk_keyval_to_lower (keyval); if (accelerator_mods) *accelerator_mods = mods; return !error; } /** * gtk_accelerator_parse: * @accelerator: string representing an accelerator * @accelerator_key: (out) (optional): return location for accelerator keyval * @accelerator_mods: (out) (optional): return location for accelerator * modifier mask * * Parses a string representing an accelerator. * * The format looks like “`a`” or “`F1`”. * * The parser is fairly liberal and allows lower or upper case, and also * abbreviations such as “``” and “``”. * * Key names are parsed using [func@Gdk.keyval_from_name]. For character keys * the name is not the symbol, but the lowercase name, e.g. one would use * “`minus`” instead of “`-`”. * * Modifiers are enclosed in angular brackets `<>`, and match the * [enum@Gdk.ModifierType] mask: * * - `` for `GDK_SHIFT_MASK` * - `` for `GDK_CONTROL_MASK` * - `` for `GDK_ALT_MASK` * - `` for `GDK_META_MASK` * - `` for `GDK_SUPER_MASK` * - `` for `GDK_HYPER_MASK` * * If the parse operation fails, @accelerator_key and @accelerator_mods will * be set to 0 (zero). */ gboolean gtk_accelerator_parse (const char *accelerator, guint *accelerator_key, GdkModifierType *accelerator_mods) { return gtk_accelerator_parse_with_keycode (accelerator, NULL, accelerator_key, NULL, accelerator_mods); } /** * gtk_accelerator_name_with_keycode: * @display: (nullable): a `GdkDisplay` or %NULL to use the default display * @accelerator_key: accelerator keyval * @keycode: accelerator keycode * @accelerator_mods: accelerator modifier mask * * Converts an accelerator keyval and modifier mask * into a string parseable by gtk_accelerator_parse_with_keycode(). * * This is similar to [func@Gtk.accelerator_name] but handling keycodes. * This is only useful for system-level components, applications * should use [func@Gtk.accelerator_name] instead. * * Returns: a newly allocated accelerator name. */ char * gtk_accelerator_name_with_keycode (GdkDisplay *display, guint accelerator_key, guint keycode, GdkModifierType accelerator_mods) { char *gtk_name; gtk_name = gtk_accelerator_name (accelerator_key, accelerator_mods); if (!accelerator_key) { char *name; name = g_strdup_printf ("%s0x%02x", gtk_name, keycode); g_free (gtk_name); return name; } return gtk_name; } /** * gtk_accelerator_name: * @accelerator_key: accelerator keyval * @accelerator_mods: accelerator modifier mask * * Converts an accelerator keyval and modifier mask into a string * parseable by gtk_accelerator_parse(). * * For example, if you pass in %GDK_KEY_q and %GDK_CONTROL_MASK, * this function returns `q`. * * If you need to display accelerators in the user interface, * see [func@Gtk.accelerator_get_label]. * * Returns: (transfer full): a newly-allocated accelerator name */ char * gtk_accelerator_name (guint accelerator_key, GdkModifierType accelerator_mods) { #define TXTLEN(s) sizeof (s) - 1 static const struct { guint mask; const char *text; gsize text_len; } mask_text[] = { { GDK_SHIFT_MASK, "", TXTLEN ("") }, { GDK_CONTROL_MASK, "", TXTLEN ("") }, { GDK_ALT_MASK, "", TXTLEN ("") }, { GDK_META_MASK, "", TXTLEN ("") }, { GDK_SUPER_MASK, "", TXTLEN ("") }, { GDK_HYPER_MASK, "", TXTLEN ("") } }; #undef TXTLEN GdkModifierType saved_mods; guint l; guint name_len; const char *keyval_name; char *accelerator; int i; accelerator_mods &= GDK_MODIFIER_MASK; keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key)); if (!keyval_name) keyval_name = ""; name_len = strlen (keyval_name); saved_mods = accelerator_mods; for (i = 0; i < G_N_ELEMENTS (mask_text); i++) { if (accelerator_mods & mask_text[i].mask) name_len += mask_text[i].text_len; } if (name_len == 0) return g_strdup (keyval_name); name_len += 1; /* NUL byte */ accelerator = g_new (char, name_len); accelerator_mods = saved_mods; l = 0; for (i = 0; i < G_N_ELEMENTS (mask_text); i++) { if (accelerator_mods & mask_text[i].mask) { strcpy (accelerator + l, mask_text[i].text); l += mask_text[i].text_len; } } strcpy (accelerator + l, keyval_name); accelerator[name_len - 1] = '\0'; return accelerator; } /** * gtk_accelerator_get_label_with_keycode: * @display: (nullable): a `GdkDisplay` or %NULL to use the default display * @accelerator_key: accelerator keyval * @keycode: accelerator keycode * @accelerator_mods: accelerator modifier mask * * Converts an accelerator keyval and modifier mask * into a string that can be displayed to the user. * * The string may be translated. * * This function is similar to [func@Gtk.accelerator_get_label], * but handling keycodes. This is only useful for system-level * components, applications should use [func@Gtk.accelerator_get_label] * instead. * * Returns: (transfer full): a newly-allocated string representing the accelerator */ char * gtk_accelerator_get_label_with_keycode (GdkDisplay *display, guint accelerator_key, guint keycode, GdkModifierType accelerator_mods) { char *gtk_label; gtk_label = gtk_accelerator_get_label (accelerator_key, accelerator_mods); if (!accelerator_key) { char *label; label = g_strdup_printf ("%s0x%02x", gtk_label, keycode); g_free (gtk_label); return label; } return gtk_label; } /* Underscores in key names are better displayed as spaces * E.g., Page_Up should be “Page Up”. * * Some keynames also have prefixes that are not suitable * for display, e.g XF86AudioMute, so strip those out, too. * * This function is only called on untranslated keynames, * so no need to be UTF-8 safe. */ static void append_without_underscores (GString *s, const char *str) { const char *p; if (g_str_has_prefix (str, "XF86")) p = str + 4; else if (g_str_has_prefix (str, "ISO_")) p = str + 4; else p = str; for ( ; *p; p++) { if (*p == '_') g_string_append_c (s, ' '); else g_string_append_c (s, *p); } } /* On Mac, if the key has symbolic representation (e.g. arrow keys), * append it to gstring and return TRUE; otherwise return FALSE. * See http://docs.info.apple.com/article.html?path=Mac/10.5/en/cdb_symbs.html * for the list of special keys. */ static gboolean append_keyval_symbol (guint accelerator_key, GString *gstring) { #ifdef GDK_WINDOWING_MACOS switch (accelerator_key) { case GDK_KEY_Return: /* U+21A9 LEFTWARDS ARROW WITH HOOK */ g_string_append (gstring, "\xe2\x86\xa9"); return TRUE; case GDK_KEY_ISO_Enter: /* U+2324 UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS */ g_string_append (gstring, "\xe2\x8c\xa4"); return TRUE; case GDK_KEY_Left: /* U+2190 LEFTWARDS ARROW */ g_string_append (gstring, "\xe2\x86\x90"); return TRUE; case GDK_KEY_Up: /* U+2191 UPWARDS ARROW */ g_string_append (gstring, "\xe2\x86\x91"); return TRUE; case GDK_KEY_Right: /* U+2192 RIGHTWARDS ARROW */ g_string_append (gstring, "\xe2\x86\x92"); return TRUE; case GDK_KEY_Down: /* U+2193 DOWNWARDS ARROW */ g_string_append (gstring, "\xe2\x86\x93"); return TRUE; case GDK_KEY_Page_Up: /* U+21DE UPWARDS ARROW WITH DOUBLE STROKE */ g_string_append (gstring, "\xe2\x87\x9e"); return TRUE; case GDK_KEY_Page_Down: /* U+21DF DOWNWARDS ARROW WITH DOUBLE STROKE */ g_string_append (gstring, "\xe2\x87\x9f"); return TRUE; case GDK_KEY_Home: /* U+2196 NORTH WEST ARROW */ g_string_append (gstring, "\xe2\x86\x96"); return TRUE; case GDK_KEY_End: /* U+2198 SOUTH EAST ARROW */ g_string_append (gstring, "\xe2\x86\x98"); return TRUE; case GDK_KEY_Escape: /* U+238B BROKEN CIRCLE WITH NORTHWEST ARROW */ g_string_append (gstring, "\xe2\x8e\x8b"); return TRUE; case GDK_KEY_BackSpace: /* U+232B ERASE TO THE LEFT */ g_string_append (gstring, "\xe2\x8c\xab"); return TRUE; case GDK_KEY_Delete: /* U+2326 ERASE TO THE RIGHT */ g_string_append (gstring, "\xe2\x8c\xa6"); return TRUE; default: return FALSE; } #else /* !GDK_WINDOWING_MACOS */ return FALSE; #endif } static void append_separator (GString *string) { #ifndef GDK_WINDOWING_MACOS g_string_append (string, "+"); #else /* no separator on quartz */ #endif } /** * gtk_accelerator_get_label: * @accelerator_key: accelerator keyval * @accelerator_mods: accelerator modifier mask * * Converts an accelerator keyval and modifier mask into a string * which can be used to represent the accelerator to the user. * * Returns: (transfer full): a newly-allocated string representing the accelerator */ char * gtk_accelerator_get_label (guint accelerator_key, GdkModifierType accelerator_mods) { GString *gstring; gstring = g_string_new (NULL); gtk_accelerator_print_label (gstring, accelerator_key, accelerator_mods); return g_string_free (gstring, FALSE); } void gtk_accelerator_print_label (GString *gstring, guint accelerator_key, GdkModifierType accelerator_mods) { gboolean seen_mod = FALSE; gunichar ch; if (accelerator_mods & GDK_SHIFT_MASK) { #ifndef GDK_WINDOWING_MACOS /* This is the text that should appear next to menu accelerators * that use the shift key. If the text on this key isn't typically * translated on keyboards used for your language, don't translate * this. */ g_string_append (gstring, C_("keyboard label", "Shift")); #else /* U+21E7 UPWARDS WHITE ARROW */ g_string_append (gstring, "⇧"); #endif seen_mod = TRUE; } if (accelerator_mods & GDK_CONTROL_MASK) { if (seen_mod) append_separator (gstring); #ifndef GDK_WINDOWING_MACOS /* This is the text that should appear next to menu accelerators * that use the control key. If the text on this key isn't typically * translated on keyboards used for your language, don't translate * this. */ g_string_append (gstring, C_("keyboard label", "Ctrl")); #else /* U+2303 UP ARROWHEAD */ g_string_append (gstring, "⌃"); #endif seen_mod = TRUE; } if (accelerator_mods & GDK_ALT_MASK) { if (seen_mod) append_separator (gstring); #ifndef GDK_WINDOWING_MACOS /* This is the text that should appear next to menu accelerators * that use the alt key. If the text on this key isn't typically * translated on keyboards used for your language, don't translate * this. */ g_string_append (gstring, C_("keyboard label", "Alt")); #else /* U+2325 OPTION KEY */ g_string_append (gstring, "⌥"); #endif seen_mod = TRUE; } if (accelerator_mods & GDK_SUPER_MASK) { if (seen_mod) append_separator (gstring); /* This is the text that should appear next to menu accelerators * that use the super key. If the text on this key isn't typically * translated on keyboards used for your language, don't translate * this. */ g_string_append (gstring, C_("keyboard label", "Super")); seen_mod = TRUE; } if (accelerator_mods & GDK_HYPER_MASK) { if (seen_mod) append_separator (gstring); /* This is the text that should appear next to menu accelerators * that use the hyper key. If the text on this key isn't typically * translated on keyboards used for your language, don't translate * this. */ g_string_append (gstring, C_("keyboard label", "Hyper")); seen_mod = TRUE; } if (accelerator_mods & GDK_META_MASK) { if (seen_mod) append_separator (gstring); #ifndef GDK_WINDOWING_MACOS /* This is the text that should appear next to menu accelerators * that use the meta key. If the text on this key isn't typically * translated on keyboards used for your language, don't translate * this. */ g_string_append (gstring, C_("keyboard label", "Meta")); #else g_string_append (gstring, "⌘"); #endif seen_mod = TRUE; } ch = gdk_keyval_to_unicode (accelerator_key); if (ch && (ch == ' ' || g_unichar_isgraph (ch))) { if (seen_mod) append_separator (gstring); if (accelerator_key >= GDK_KEY_KP_Space && accelerator_key <= GDK_KEY_KP_Equal) { /* Translators: "KP" means "numeric key pad". This string will * be used in accelerators such as "Ctrl+Shift+KP 1" in menus, * and therefore the translation needs to be very short. */ g_string_append (gstring, C_("keyboard label", "KP")); g_string_append (gstring, " "); } switch (ch) { case ' ': g_string_append (gstring, C_("keyboard label", "Space")); break; case '\\': g_string_append (gstring, C_("keyboard label", "Backslash")); break; default: g_string_append_unichar (gstring, g_unichar_toupper (ch)); break; } } else if (!append_keyval_symbol (accelerator_key, gstring)) { const char *tmp; tmp = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key)); if (tmp != NULL) { if (seen_mod) append_separator (gstring); if (tmp[0] != 0 && tmp[1] == 0) g_string_append_c (gstring, g_ascii_toupper (tmp[0])); else { const char *str; str = g_dpgettext2 (GETTEXT_PACKAGE, "keyboard label", tmp); if (str == tmp) append_without_underscores (gstring, tmp); else g_string_append (gstring, str); } } } } /** * gtk_accelerator_get_default_mod_mask: * * Gets the modifier mask. * * The modifier mask determines which modifiers are considered significant * for keyboard accelerators. This includes all keyboard modifiers except * for %GDK_LOCK_MASK. * * Returns: the modifier mask for accelerators */ GdkModifierType gtk_accelerator_get_default_mod_mask (void) { return GDK_CONTROL_MASK|GDK_SHIFT_MASK|GDK_ALT_MASK| GDK_SUPER_MASK|GDK_HYPER_MASK|GDK_META_MASK; }