/* 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 "gtkaccellabelprivate.h" #include "gtkintl.h" #include "gtkmarshalers.h" #include "gtkprivate.h" /** * SECTION:gtkaccelgroup * @Short_description: Utilities for accelerators * @Title: Keyboard Accelerators * * We have various utility functions to parse and generate * textual representations of keyboard accelerators. */ /* --- variables --- */ static guint default_accel_mod_mask = 0; /* --- 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 is valid - this is a “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 gchar *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 gchar *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_modx (const gchar *string) { return ((string[0] == '<') && (string[1] == 'm' || string[1] == 'M') && (string[2] == 'o' || string[2] == 'O') && (string[3] == 'd' || string[3] == 'D') && (string[4] >= '1' && string[4] <= '5') && (string[5] == '>')); } static inline gboolean is_ctrl (const gchar *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 gchar *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 gchar *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 gchar *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 gchar *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 gchar *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 gchar *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 gchar *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 gchar *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: (allow-none): the #GdkDisplay to look up @accelerator_codes in * @accelerator_key: (out) (allow-none): return location for accelerator * keyval, or %NULL * @accelerator_codes: (out) (array zero-terminated=1) (transfer full) (allow-none): * return location for accelerator keycodes, or %NULL * @accelerator_mods: (out) (allow-none): return location for accelerator * modifier mask, %NULL * * Parses a string representing an accelerator, similarly to * gtk_accelerator_parse() but handles keycodes as well. This is only * useful for system-level components, applications should use * 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 gchar *accelerator, GdkDisplay *display, guint *accelerator_key, guint **accelerator_codes, GdkModifierType *accelerator_mods) { guint keyval; GdkModifierType mods; gint 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); error = FALSE; keyval = 0; mods = 0; len = strlen (accelerator); while (len) { if (*accelerator == '<') { if (len >= 9 && is_primary (accelerator)) { accelerator += 9; len -= 9; mods |= _gtk_get_primary_accel_mod (); } 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 >= 6 && is_modx (accelerator)) { static const guint mod_vals[] = { GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK, GDK_MOD4_MASK, GDK_MOD5_MASK }; len -= 6; accelerator += 4; mods |= mod_vals[*accelerator - '1']; accelerator += 2; } 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_MOD1_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 { gchar 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]; gchar *endptr; gint 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) { GdkKeymap *keymap = gdk_display_get_keymap (display ? display : gdk_display_get_default ()); GdkKeymapKey *keys; gint n_keys, i, j; if (!gdk_keymap_get_entries_for_keyval (keymap, 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) (allow-none): return location for accelerator * keyval, or %NULL * @accelerator_mods: (out) (allow-none): return location for accelerator * modifier mask, %NULL * * Parses a string representing an accelerator. The format looks like * “a” or “F1” or “z” (the last one is * for key release). * * The parser is fairly liberal and allows lower or upper case, and also * abbreviations such as “” and “”. Key names are parsed using * 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 * “-”. * * If the parse fails, @accelerator_key and @accelerator_mods will * be set to 0 (zero). */ gboolean gtk_accelerator_parse (const gchar *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: (allow-none): 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(), * similarly to gtk_accelerator_name() but handling keycodes. * This is only useful for system-level components, applications * should use gtk_accelerator_parse() instead. * * Returns: a newly allocated accelerator name. */ gchar * gtk_accelerator_name_with_keycode (GdkDisplay *display, guint accelerator_key, guint keycode, GdkModifierType accelerator_mods) { gchar *gtk_name; if (display == NULL) display = gdk_display_manager_get_default_display (gdk_display_manager_get ()); gdk_keymap_add_virtual_modifiers (gdk_display_get_keymap (display), &accelerator_mods); gtk_name = gtk_accelerator_name (accelerator_key, accelerator_mods); if (!accelerator_key) { gchar *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 gtk_accelerator_get_label(). * * Returns: a newly-allocated accelerator name */ gchar* gtk_accelerator_name (guint accelerator_key, GdkModifierType accelerator_mods) { static const gchar text_primary[] = ""; static const gchar text_shift[] = ""; static const gchar text_control[] = ""; static const gchar text_mod1[] = ""; static const gchar text_mod2[] = ""; static const gchar text_mod3[] = ""; static const gchar text_mod4[] = ""; static const gchar text_mod5[] = ""; static const gchar text_meta[] = ""; static const gchar text_super[] = ""; static const gchar text_hyper[] = ""; GdkModifierType saved_mods; guint l; const char *keyval_name; gchar *accelerator; accelerator_mods &= GDK_MODIFIER_MASK; keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key)); if (!keyval_name) keyval_name = ""; saved_mods = accelerator_mods; l = 0; if (accelerator_mods & _gtk_get_primary_accel_mod ()) { l += sizeof (text_primary) - 1; accelerator_mods &= ~_gtk_get_primary_accel_mod (); /* consume the default accel */ } if (accelerator_mods & GDK_SHIFT_MASK) l += sizeof (text_shift) - 1; if (accelerator_mods & GDK_CONTROL_MASK) l += sizeof (text_control) - 1; if (accelerator_mods & GDK_MOD1_MASK) l += sizeof (text_mod1) - 1; if (accelerator_mods & GDK_MOD2_MASK) l += sizeof (text_mod2) - 1; if (accelerator_mods & GDK_MOD3_MASK) l += sizeof (text_mod3) - 1; if (accelerator_mods & GDK_MOD4_MASK) l += sizeof (text_mod4) - 1; if (accelerator_mods & GDK_MOD5_MASK) l += sizeof (text_mod5) - 1; l += strlen (keyval_name); if (accelerator_mods & GDK_META_MASK) l += sizeof (text_meta) - 1; if (accelerator_mods & GDK_HYPER_MASK) l += sizeof (text_hyper) - 1; if (accelerator_mods & GDK_SUPER_MASK) l += sizeof (text_super) - 1; accelerator = g_new (gchar, l + 1); accelerator_mods = saved_mods; l = 0; accelerator[l] = 0; if (accelerator_mods & _gtk_get_primary_accel_mod ()) { strcpy (accelerator + l, text_primary); l += sizeof (text_primary) - 1; accelerator_mods &= ~_gtk_get_primary_accel_mod (); /* consume the default accel */ } if (accelerator_mods & GDK_SHIFT_MASK) { strcpy (accelerator + l, text_shift); l += sizeof (text_shift) - 1; } if (accelerator_mods & GDK_CONTROL_MASK) { strcpy (accelerator + l, text_control); l += sizeof (text_control) - 1; } if (accelerator_mods & GDK_MOD1_MASK) { strcpy (accelerator + l, text_mod1); l += sizeof (text_mod1) - 1; } if (accelerator_mods & GDK_MOD2_MASK) { strcpy (accelerator + l, text_mod2); l += sizeof (text_mod2) - 1; } if (accelerator_mods & GDK_MOD3_MASK) { strcpy (accelerator + l, text_mod3); l += sizeof (text_mod3) - 1; } if (accelerator_mods & GDK_MOD4_MASK) { strcpy (accelerator + l, text_mod4); l += sizeof (text_mod4) - 1; } if (accelerator_mods & GDK_MOD5_MASK) { strcpy (accelerator + l, text_mod5); l += sizeof (text_mod5) - 1; } if (accelerator_mods & GDK_META_MASK) { strcpy (accelerator + l, text_meta); l += sizeof (text_meta) - 1; } if (accelerator_mods & GDK_HYPER_MASK) { strcpy (accelerator + l, text_hyper); l += sizeof (text_hyper) - 1; } if (accelerator_mods & GDK_SUPER_MASK) { strcpy (accelerator + l, text_super); l += sizeof (text_super) - 1; } strcpy (accelerator + l, keyval_name); return accelerator; } /** * gtk_accelerator_get_label_with_keycode: * @display: (allow-none): 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 (possibly translated) string that can be displayed to * a user, similarly to gtk_accelerator_get_label(), but handling * keycodes. * * This is only useful for system-level components, applications * should use gtk_accelerator_parse() instead. * * Returns: a newly-allocated string representing the accelerator. */ gchar * gtk_accelerator_get_label_with_keycode (GdkDisplay *display, guint accelerator_key, guint keycode, GdkModifierType accelerator_mods) { gchar *gtk_label; if (display == NULL) display = gdk_display_manager_get_default_display (gdk_display_manager_get ()); gdk_keymap_add_virtual_modifiers (gdk_display_get_keymap (display), &accelerator_mods); gtk_label = gtk_accelerator_get_label (accelerator_key, accelerator_mods); if (!accelerator_key) { gchar *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_QUARTZ 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_QUARTZ */ return FALSE; #endif } static void append_separator (GString *string) { #ifndef GDK_WINDOWING_QUARTZ 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: a newly-allocated string representing the accelerator. */ gchar* 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_QUARTZ /* 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, "\xe2\x87\xa7"); #endif seen_mod = TRUE; } if (accelerator_mods & GDK_CONTROL_MASK) { if (seen_mod) append_separator (gstring); #ifndef GDK_WINDOWING_QUARTZ /* 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, "\xe2\x8c\x83"); #endif seen_mod = TRUE; } if (accelerator_mods & GDK_MOD1_MASK) { if (seen_mod) append_separator (gstring); #ifndef GDK_WINDOWING_QUARTZ /* 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, "\xe2\x8c\xa5"); #endif seen_mod = TRUE; } if (accelerator_mods & GDK_MOD2_MASK) { if (seen_mod) append_separator (gstring); g_string_append (gstring, "Mod2"); seen_mod = TRUE; } if (accelerator_mods & GDK_MOD3_MASK) { if (seen_mod) append_separator (gstring); g_string_append (gstring, "Mod3"); seen_mod = TRUE; } if (accelerator_mods & GDK_MOD4_MASK) { if (seen_mod) append_separator (gstring); g_string_append (gstring, "Mod4"); seen_mod = TRUE; } if (accelerator_mods & GDK_MOD5_MASK) { if (seen_mod) append_separator (gstring); g_string_append (gstring, "Mod5"); 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_QUARTZ /* 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 /* Command key symbol U+2318 PLACE OF INTEREST SIGN */ g_string_append (gstring, "\xe2\x8c\x98"); #endif seen_mod = TRUE; } ch = gdk_keyval_to_unicode (accelerator_key); if (ch && (ch == ' ' || g_unichar_isgraph (ch))) { if (seen_mod) append_separator (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_set_default_mod_mask: * @default_mod_mask: accelerator modifier mask * * Sets the modifiers that will be considered significant for keyboard * accelerators. The default mod mask depends on the GDK backend in use, * but will typically include #GDK_CONTROL_MASK | #GDK_SHIFT_MASK | * #GDK_MOD1_MASK | #GDK_SUPER_MASK | #GDK_HYPER_MASK | #GDK_META_MASK. * In other words, Control, Shift, Alt, Super, Hyper and Meta. Other * modifiers will by default be ignored by #GtkAccelGroup. * * You must include at least the three modifiers Control, Shift * and Alt in any value you pass to this function. * * The default mod mask should be changed on application startup, * before using any accelerator groups. */ void gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask) { default_accel_mod_mask = (default_mod_mask & GDK_MODIFIER_MASK) | (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK); } /** * gtk_accelerator_get_default_mod_mask: * * Gets the modifier mask. * * The modifier mask determines which modifiers are considered significant * for keyboard accelerators. See gtk_accelerator_set_default_mod_mask(). * * Returns: the default accelerator modifier mask */ GdkModifierType gtk_accelerator_get_default_mod_mask (void) { if (!default_accel_mod_mask) { GdkDisplay *display; display = gdk_display_get_default (); if (!display) return GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK; default_accel_mod_mask = gdk_keymap_get_modifier_mask (gdk_display_get_keymap (display), GDK_MODIFIER_INTENT_DEFAULT_MOD_MASK); } return default_accel_mod_mask; }