X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkaccelmap.c;h=179351a3bf0a031b2c9872017630f67d07ca7d19;hb=d45d970144fe0fd6422fab873a6c20335ca096b9;hp=5dd21c04eda0c6c8aafff50bb1c1f17db883549d;hpb=e8468d5e6d5d76d0068d4800f951a52cf6390a27;p=~andy%2Fgtk diff --git a/gtk/gtkaccelmap.c b/gtk/gtkaccelmap.c index 5dd21c04e..179351a3b 100644 --- a/gtk/gtkaccelmap.c +++ b/gtk/gtkaccelmap.c @@ -21,7 +21,11 @@ #include "gtkaccelmap.h" +#include "gtkmarshalers.h" #include "gtkwindow.h" /* in lack of GtkAcceleratable */ +#include "gtkintl.h" + +#include #include #include @@ -32,25 +36,45 @@ #ifdef G_OS_WIN32 #include #endif -#include /* --- structures --- */ +struct _GtkAccelMap +{ + GObject parent_instance; +}; + +struct _GtkAccelMapClass +{ + GObjectClass parent_class; +}; + typedef struct { const gchar *accel_path; guint accel_key; guint accel_mods; guint std_accel_key; guint std_accel_mods; - guint changed : 1; + guint changed : 1; + guint lock_count : 15; GSList *groups; } AccelEntry; +/* --- signals --- */ +enum { + CHANGED, + LAST_SIGNAL +}; /* --- variables --- */ -static GHashTable *accel_entry_ht = NULL; /* accel_path -> AccelEntry */ -static GSList *accel_filters = NULL; +static GHashTable *accel_entry_ht = NULL; /* accel_path -> AccelEntry */ +static GSList *accel_filters = NULL; +static gulong accel_map_signals[LAST_SIGNAL] = { 0, }; +static GtkAccelMap *accel_map; + +/* --- prototypes --- */ +static void do_accel_map_changed (AccelEntry *entry); /* --- functions --- */ static guint @@ -99,7 +123,7 @@ _gtk_accel_path_is_valid (const gchar *accel_path) accel_path[1] == '<' || accel_path[1] == '>' || !accel_path[1]) return FALSE; p = strchr (accel_path, '>'); - if (!p || p[1] != '/') + if (!p || (p[1] != 0 && p[1] != '/')) return FALSE; return TRUE; } @@ -124,6 +148,10 @@ _gtk_accel_path_is_valid (const gchar *accel_path) * e.g. "File/Save As", "Image/View/Zoom" or "Edit/Select All". * So a full valid accelerator path may look like: * "<Gimp-Toolbox>/File/Dialogs/Tool Options...". + * + * Note that @accel_path string will be stored in a #GQuark. Therefore, if you + * pass a static string, you can save some memory by interning it first with + * g_intern_static_string(). */ void gtk_accel_map_add_entry (const gchar *accel_path, @@ -153,22 +181,25 @@ gtk_accel_map_add_entry (const gchar *accel_path, } else { - entry = g_new0 (AccelEntry, 1); - entry->accel_path = g_quark_to_string (g_quark_from_string (accel_path)); + entry = g_slice_new0 (AccelEntry); + entry->accel_path = g_intern_string (accel_path); entry->std_accel_key = accel_key; entry->std_accel_mods = accel_mods; entry->accel_key = accel_key; entry->accel_mods = accel_mods; entry->changed = FALSE; + entry->lock_count = 0; g_hash_table_insert (accel_entry_ht, entry, entry); + + do_accel_map_changed (entry); } } /** * gtk_accel_map_lookup_entry: - * @accel_path: a valid accelerator path - * @key: the accelerator key to be filled in (optional) - * @returns: %TRUE if @accel_path is known, %FALSE otherwise + * @accel_path: a valid accelerator path + * @key: (allow-none) (out): the accelerator key to be filled in (optional) + * @returns: %TRUE if @accel_path is known, %FALSE otherwise * * Looks up the accelerator entry for @accel_path and fills in @key. */ @@ -240,6 +271,8 @@ internal_change_entry (const gchar *accel_path, entry->accel_key = accel_key; entry->accel_mods = accel_mods; entry->changed = TRUE; + + do_accel_map_changed (entry); } return TRUE; } @@ -252,6 +285,12 @@ internal_change_entry (const gchar *accel_path, return simulate ? TRUE : FALSE; } + /* The no-change case has already been handled, so + * simulate doesn't make a difference here. + */ + if (entry->lock_count > 0) + return FALSE; + /* nobody's interested, easy going */ if (!entry->groups) { @@ -260,6 +299,8 @@ internal_change_entry (const gchar *accel_path, entry->accel_key = accel_key; entry->accel_mods = accel_mods; entry->changed = TRUE; + + do_accel_map_changed (entry); } return TRUE; } @@ -277,7 +318,7 @@ internal_change_entry (const gchar *accel_path, { GtkAccelGroup *group = slist->data; - for (node = group->acceleratables; node; node = node->next) + for (node = _gtk_accel_group_get_accelerables (group); node; node = node->next) g_hash_table_insert (window_hm, node->data, node->data); } g_slist_free (group_list); @@ -325,11 +366,11 @@ internal_change_entry (const gchar *accel_path, for (i = 0; i < n; i++) { seen_accel = TRUE; - removable = !group->lock_count && !(ag_entry[i].key.accel_flags & GTK_ACCEL_LOCKED); + removable = !gtk_accel_group_get_is_locked (group) && !(ag_entry[i].key.accel_flags & GTK_ACCEL_LOCKED); if (!removable) goto break_loop_step5; if (ag_entry[i].accel_path_quark) - replace_list = g_slist_prepend (replace_list, GUINT_TO_POINTER (ag_entry->accel_path_quark)); + replace_list = g_slist_prepend (replace_list, GUINT_TO_POINTER (ag_entry[i].accel_path_quark)); } } break_loop_step5: @@ -348,8 +389,6 @@ internal_change_entry (const gchar *accel_path, if (change_accel && !simulate) { - guint old_accel_key, old_accel_mods; - /* ref accel groups */ for (slist = group_list; slist; slist = slist->next) g_object_ref (slist->data); @@ -359,17 +398,18 @@ internal_change_entry (const gchar *accel_path, internal_change_entry (g_quark_to_string (GPOINTER_TO_UINT (slist->data)), 0, 0, FALSE, FALSE); /* 9) install new accelerator */ - old_accel_key = entry->accel_key; - old_accel_mods = entry->accel_mods; entry->accel_key = accel_key; entry->accel_mods = accel_mods; entry->changed = TRUE; + for (slist = group_list; slist; slist = slist->next) _gtk_accel_group_reconnect (slist->data, g_quark_from_string (entry->accel_path)); /* unref accel groups */ for (slist = group_list; slist; slist = slist->next) g_object_unref (slist->data); + + do_accel_map_changed (entry); } g_slist_free (replace_list); g_slist_free (group_list); @@ -392,6 +432,10 @@ internal_change_entry (const gchar *accel_path, * conflicts. A change will only occur if all conflicts could be resolved (which * might not be the case if conflicting accelerators are locked). Successful * changes are indicated by a %TRUE return value. + * + * Note that @accel_path string will be stored in a #GQuark. Therefore, if you + * pass a static string, you can save some memory by interning it first with + * g_intern_static_string(). */ gboolean gtk_accel_map_change_entry (const gchar *accel_path, @@ -504,7 +548,7 @@ gtk_accel_map_load_scanner (GScanner *scanner) gchar *cpair_comment_single; gpointer saved_symbol; - g_return_if_fail (scanner != 0); + g_return_if_fail (scanner != NULL); /* configure scanner */ skip_comment_single = scanner->config->skip_comment_single; @@ -515,7 +559,7 @@ gtk_accel_map_load_scanner (GScanner *scanner) scanner->config->symbol_2_token = FALSE; saved_symbol = g_scanner_lookup_symbol (scanner, "gtk_accel_path"); g_scanner_scope_add_symbol (scanner, 0, "gtk_accel_path", - (gpointer) accel_map_parse_accel_path); + accel_map_parse_accel_path); /* outer parsing loop */ @@ -564,7 +608,8 @@ gtk_accel_map_load_fd (gint fd) /** * gtk_accel_map_load: - * @file_name: a file containing accelerator specifications + * @file_name: a file containing accelerator specifications, + * in the GLib file name encoding * * Parses a file previously saved with gtk_accel_map_save() for * accelerator specifications, and propagates them accordingly. @@ -579,7 +624,7 @@ gtk_accel_map_load (const gchar *file_name) if (!g_file_test (file_name, G_FILE_TEST_IS_REGULAR)) return; - fd = open (file_name, O_RDONLY); + fd = g_open (file_name, O_RDONLY, 0); if (fd < 0) return; @@ -674,7 +719,8 @@ gtk_accel_map_save_fd (gint fd) /** * gtk_accel_map_save: - * @file_name: the file to contain accelerator specifications + * @file_name: the name of the file to contain accelerator specifications, + * in the GLib file name encoding * * Saves current accelerator specifications (accelerator path, key * and modifiers) to @file_name. @@ -688,7 +734,7 @@ gtk_accel_map_save (const gchar *file_name) g_return_if_fail (file_name != NULL); - fd = open (file_name, O_CREAT | O_TRUNC | O_WRONLY, 0644); + fd = g_open (file_name, O_CREAT | O_TRUNC | O_WRONLY, 0644); if (fd < 0) return; @@ -825,3 +871,163 @@ _gtk_accel_map_remove_group (const gchar *accel_path, entry->groups = g_slist_remove (entry->groups, accel_group); } + + +/** + * gtk_accel_map_lock_path: + * @accel_path: a valid accelerator path + * + * Locks the given accelerator path. If the accelerator map doesn't yet contain + * an entry for @accel_path, a new one is created. + * + * Locking an accelerator path prevents its accelerator from being changed + * during runtime. A locked accelerator path can be unlocked by + * gtk_accel_map_unlock_path(). Refer to gtk_accel_map_change_entry() + * for information about runtime accelerator changes. + * + * If called more than once, @accel_path remains locked until + * gtk_accel_map_unlock_path() has been called an equivalent number + * of times. + * + * Note that locking of individual accelerator paths is independent from + * locking the #GtkAccelGroup containing them. For runtime accelerator + * changes to be possible both the accelerator path and its #GtkAccelGroup + * have to be unlocked. + * + * Since: 2.4 + **/ +void +gtk_accel_map_lock_path (const gchar *accel_path) +{ + AccelEntry *entry; + + g_return_if_fail (_gtk_accel_path_is_valid (accel_path)); + + entry = accel_path_lookup (accel_path); + + if (!entry) + { + gtk_accel_map_add_entry (accel_path, 0, 0); + entry = accel_path_lookup (accel_path); + } + + entry->lock_count += 1; +} + +/** + * gtk_accel_map_unlock_path: + * @accel_path: a valid accelerator path + * + * Undoes the last call to gtk_accel_map_lock_path() on this @accel_path. + * Refer to gtk_accel_map_lock_path() for information about accelerator path locking. + * + * Since: 2.4 + **/ +void +gtk_accel_map_unlock_path (const gchar *accel_path) +{ + AccelEntry *entry; + + g_return_if_fail (_gtk_accel_path_is_valid (accel_path)); + + entry = accel_path_lookup (accel_path); + + g_return_if_fail (entry != NULL && entry->lock_count > 0); + + entry->lock_count -= 1; +} + +G_DEFINE_TYPE (GtkAccelMap, gtk_accel_map, G_TYPE_OBJECT) + +static void +gtk_accel_map_class_init (GtkAccelMapClass *accel_map_class) +{ + /** + * GtkAccelMap::changed: + * @object: the global accel map object + * @accel_path: the path of the accelerator that changed + * @accel_key: the key value for the new accelerator + * @accel_mods: the modifier mask for the new accelerator + * + * Notifies of a change in the global accelerator map. + * The path is also used as the detail for the signal, + * so it is possible to connect to + * changed::accel_path. + * + * Since: 2.4 + */ + accel_map_signals[CHANGED] = g_signal_new (I_("changed"), + G_TYPE_FROM_CLASS (accel_map_class), + G_SIGNAL_DETAILED|G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _gtk_marshal_VOID__STRING_UINT_FLAGS, + G_TYPE_NONE, 3, + G_TYPE_STRING, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE); +} + +static void +gtk_accel_map_init (GtkAccelMap *accel_map) +{ +} + +/** + * gtk_accel_map_get: + * + * Gets the singleton global #GtkAccelMap object. This object + * is useful only for notification of changes to the accelerator + * map via the ::changed signal; it isn't a parameter to the + * other accelerator map functions. + * + * Return value: (transfer none): the global #GtkAccelMap object + * + * Since: 2.4 + **/ +GtkAccelMap * +gtk_accel_map_get (void) +{ + if (!accel_map) + accel_map = g_object_new (GTK_TYPE_ACCEL_MAP, NULL); + + return accel_map; +} + +static void +do_accel_map_changed (AccelEntry *entry) +{ + if (accel_map) + g_signal_emit (accel_map, + accel_map_signals[CHANGED], + g_quark_from_string (entry->accel_path), + entry->accel_path, + entry->accel_key, + entry->accel_mods); +} + +#if defined (G_OS_WIN32) && !defined (_WIN64) + +#undef gtk_accel_map_load + +void +gtk_accel_map_load (const gchar *file_name) +{ + gchar *utf8_file_name = g_locale_to_utf8 (file_name, -1, NULL, NULL, NULL); + + gtk_accel_map_load_utf8 (utf8_file_name); + + g_free (utf8_file_name); +} + +#undef gtk_accel_map_save + +void +gtk_accel_map_save (const gchar *file_name) +{ + gchar *utf8_file_name = g_locale_to_utf8 (file_name, -1, NULL, NULL, NULL); + + gtk_accel_map_save_utf8 (utf8_file_name); + + g_free (utf8_file_name); +} + +#endif