[geany/geany] 152103: Merge pull request #376 from kugel-/keybindings-rework3

Colomban Wendling git-noreply at xxxxx
Sat Oct 10 11:57:47 UTC 2015


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Sat, 10 Oct 2015 11:57:47 UTC
Commit:      152103392c04827a38e9486e3b55888effbb5372
             https://github.com/geany/geany/commit/152103392c04827a38e9486e3b55888effbb5372

Log Message:
-----------
Merge pull request #376 from kugel-/keybindings-rework3

Keybindings rework


Modified Paths:
--------------
    src/keybindings.c
    src/keybindings.h
    src/keybindingsprivate.h
    src/pluginutils.c
    src/pluginutils.h

Modified: src/keybindings.c
148 lines changed, 107 insertions(+), 41 deletions(-)
===================================================================
@@ -194,12 +194,68 @@ GeanyKeyBinding *keybindings_set_item(GeanyKeyGroup *group, gsize key_id,
 	kb->default_key = key;
 	kb->default_mods = mod;
 	kb->callback = callback;
+	kb->cb_func = NULL;
+	kb->cb_data = NULL;
 	kb->menu_item = menu_item;
 	kb->id = key_id;
 	return kb;
 }
 
 
+/** Creates a new keybinding using a GeanyKeyBindingFunc and attaches it to a keybinding group
+ *
+ * If given the callback should return @c TRUE if the keybinding was handled, otherwise @c FALSE
+ * to allow other callbacks to be run. This allows for multiplexing keybindings on the same keys,
+ * depending on the focused widget (or context). If the callback is NULL the group's callback will
+ * be invoked, but the same rule applies.
+ *
+ * @param group Group.
+ * @param key_id Keybinding index for the group.
+ * @param key (Lower case) default key, e.g. @c GDK_j, but usually 0 for unset.
+ * @param mod Default modifier, e.g. @c GDK_CONTROL_MASK, but usually 0 for unset.
+ * @param kf_name Key name for the configuration file, such as @c "menu_new".
+ * @param label Label used in the preferences dialog keybindings tab. May contain
+ * underscores - these won't be displayed.
+ * @param menu_item Optional widget to set an accelerator for, or @c NULL.
+ * @param cb New-style callback to be called when activated, or @c NULL to use the group callback.
+ * @param pdata Plugin-specific data passed back to the callback.
+ * @param destroy_notify Function that is invoked to free the plugin data when not needed anymore.
+ * @return The keybinding - normally this is ignored.
+ *
+ * @since 1.26 (API 226)
+ * @see See plugin_set_key_group_full
+ **/
+GEANY_API_SYMBOL
+GeanyKeyBinding *keybindings_set_item_full(GeanyKeyGroup *group, gsize key_id,
+		guint key, GdkModifierType mod, const gchar *kf_name, const gchar *label,
+		GtkWidget *menu_item, GeanyKeyBindingFunc cb, gpointer pdata,
+		GDestroyNotify destroy_notify)
+{
+	GeanyKeyBinding *kb;
+
+	/* For now, this is intended for plugins only */
+	g_assert(group->plugin);
+
+	kb = keybindings_set_item(group, key_id, NULL, key, mod, kf_name, label, menu_item);
+	kb->cb_func = cb;
+	kb->cb_data = pdata;
+	kb->cb_data_destroy = destroy_notify;
+	return kb;
+}
+
+
+static void free_key_binding(gpointer item)
+{
+	GeanyKeyBinding *kb = item;
+
+	g_free(kb->name);
+	g_free(kb->label);
+
+	if (kb->cb_data_destroy)
+		kb->cb_data_destroy(kb->cb_data);
+}
+
+
 static void add_kb_group(GeanyKeyGroup *group,
 		const gchar *name, const gchar *label, GeanyKeyGroupCallback callback, gboolean plugin)
 {
@@ -208,8 +264,11 @@ static void add_kb_group(GeanyKeyGroup *group,
 	group->name = name;
 	group->label = label;
 	group->callback = callback;
+	group->cb_func = NULL;
+	group->cb_data = NULL;
 	group->plugin = plugin;
-	group->key_items = g_ptr_array_new();
+	/* Only plugins use the destroy notify thus far */
+	group->key_items = g_ptr_array_new_with_free_func(plugin ? free_key_binding : NULL);
 }
 
 
@@ -637,10 +696,27 @@ static void init_default_kb(void)
 }
 
 
+static void free_key_group(gpointer item)
+{
+	GeanyKeyGroup *group = item;
+
+	g_ptr_array_free(group->key_items, TRUE);
+
+	if (group->plugin)
+	{
+		if (group->cb_data_destroy)
+			group->cb_data_destroy(group->cb_data);
+		g_free(group->plugin_keys);
+		g_free(group);
+	}
+}
+
+
 void keybindings_init(void)
 {
 	memset(binding_ids, 0, sizeof binding_ids);
 	keybinding_groups = g_ptr_array_sized_new(GEANY_KEY_GROUP_COUNT);
+	g_ptr_array_set_free_func(keybinding_groups, free_key_group);
 	kb_accel_group = gtk_accel_group_new();
 
 	init_default_kb();
@@ -1207,6 +1283,30 @@ gboolean keybindings_check_event(GdkEventKey *ev, GeanyKeyBinding *kb)
 }
 
 
+static gboolean run_kb(GeanyKeyBinding *kb, GeanyKeyGroup *group)
+{
+	gboolean handled = TRUE;
+	/* call the corresponding handler/callback functions for this shortcut.
+	 * Check the individual keybindings first (handler first, callback second) and
+	 * group second (again handler first, callback second) */
+	if (kb->cb_func)
+		handled = kb->cb_func(kb, kb->id, kb->cb_data);
+	else if (kb->callback)
+		kb->callback(kb->id);
+	else if (group->cb_func)
+		handled = group->cb_func(group, kb->id, group->cb_data);
+	else if (group->callback)
+		handled = group->callback(kb->id);
+	else
+	{
+		g_warning("No callback or handler for keybinding %s: %s!", group->name, kb->name);
+		return FALSE;
+	}
+
+	return handled;
+}
+
+
 /* central keypress event handler, almost all keypress events go to this function */
 static gboolean on_key_press_event(GtkWidget *widget, GdkEventKey *ev, gpointer user_data)
 {
@@ -1249,20 +1349,8 @@ static gboolean on_key_press_event(GtkWidget *widget, GdkEventKey *ev, gpointer
 		{
 			if (keyval == kb->key && state == kb->mods)
 			{
-				/* call the corresponding callback function for this shortcut */
-				if (kb->callback)
-				{
-					kb->callback(kb->id);
+				if (run_kb(kb, group))
 					return TRUE;
-				}
-				else if (group->callback)
-				{
-					if (group->callback(kb->id))
-						return TRUE;
-					else
-						continue;	/* not handled */
-				}
-				g_warning("No callback for keybinding %s: %s!", group->name, kb->name);
 			}
 		}
 	}
@@ -1296,20 +1384,12 @@ GEANY_API_SYMBOL
 void keybindings_send_command(guint group_id, guint key_id)
 {
 	GeanyKeyBinding *kb;
+	GeanyKeyGroup *group;
 
 	kb = keybindings_lookup_item(group_id, key_id);
-	if (kb)
-	{
-		if (kb->callback)
-			kb->callback(key_id);
-		else
-		{
-			GeanyKeyGroup *group = keybindings_get_core_group(group_id);
-
-			if (group->callback)
-				group->callback(key_id);
-		}
-	}
+	group = keybindings_get_core_group(group_id);
+	if (kb && group)
+		run_kb(kb, group);
 }
 
 
@@ -2544,19 +2624,5 @@ GeanyKeyGroup *keybindings_set_group(GeanyKeyGroup *group, const gchar *section_
 
 void keybindings_free_group(GeanyKeyGroup *group)
 {
-	GeanyKeyBinding *kb;
-
-	g_ptr_array_free(group->key_items, TRUE);
-
-	if (group->plugin)
-	{
-		foreach_c_array(kb, group->plugin_keys, group->plugin_key_count)
-		{
-			g_free(kb->name);
-			g_free(kb->label);
-		}
-		g_free(group->plugin_keys);
-		g_ptr_array_remove_fast(keybinding_groups, group);
-		g_free(group);
-	}
+	g_ptr_array_remove_fast(keybinding_groups, group);
 }


Modified: src/keybindings.h
54 lines changed, 40 insertions(+), 14 deletions(-)
===================================================================
@@ -37,12 +37,42 @@ G_BEGIN_DECLS
 #define GEANY_PRIMARY_MOD_MASK GDK_CONTROL_MASK
 #endif
 
+/** A collection of keybindings grouped together. */
+typedef struct GeanyKeyGroup GeanyKeyGroup;
+typedef struct GeanyKeyBinding GeanyKeyBinding;
+
+/** Function pointer type used for keybinding group callbacks.
+ *
+ * You should return @c TRUE to indicate handling the callback. (Occasionally, if the keybinding
+ * cannot apply in the current situation, it is useful to return @c FALSE to allow a later keybinding
+ * with the same key combination to handle it). */
+typedef gboolean (*GeanyKeyGroupCallback) (guint key_id);
+
+/** Function pointer type used for keybinding group callbacks, with userdata for passing context.
+ *
+ * You should return @c TRUE to indicate handling the callback. (Occasionally, if the keybinding
+ * cannot apply in the current situation, it is useful to return @c FALSE to allow a later keybinding
+ * with the same key combination to handle it).
+ *
+ * @since 1.26 (API 226) */
+typedef gboolean (*GeanyKeyGroupFunc)(GeanyKeyGroup *group, guint key_id, gpointer pdata);
+
 /** Function pointer type used for keybinding callbacks. */
 typedef void (*GeanyKeyCallback) (guint key_id);
 
+/** Function pointer type used for keybinding callbacks, with userdata for passing context
+ *
+ * You should return @c TRUE to indicate handling the callback. (Occasionally, if the keybinding
+ * cannot apply in the current situation, it is useful to return @c FALSE to allow a later keybinding
+ * with the same key combination to handle it).
+ *
+ * @since 1.26 (API 226) */
+typedef gboolean (*GeanyKeyBindingFunc)(GeanyKeyBinding *key, guint key_id, gpointer pdata);
+
 /** Represents a single keybinding action.
+ *
  * Use keybindings_set_item() to set. */
-typedef struct GeanyKeyBinding
+struct GeanyKeyBinding
 {
 	guint key;				/**< Key value in lower-case, such as @c GDK_a or 0 */
 	GdkModifierType mods;	/**< Modifier keys, such as @c GDK_CONTROL_MASK or 0 */
@@ -57,19 +87,10 @@ typedef struct GeanyKeyBinding
 	guint id;
 	guint default_key;
 	GdkModifierType default_mods;
-}
-GeanyKeyBinding;
-
-
-/** Function pointer type used for keybinding group callbacks.
- * You should return @c TRUE to indicate handling the callback. (Occasionally, if the keybinding
- * cannot apply in the current situation, it is useful to return @c FALSE to allow a later keybinding
- * with the same key combination to handle it). */
-typedef gboolean (*GeanyKeyGroupCallback) (guint key_id);
-
-/** A collection of keybindings grouped together. */
-typedef struct GeanyKeyGroup GeanyKeyGroup;
-
+	GeanyKeyBindingFunc cb_func;
+	gpointer cb_data;
+	GDestroyNotify cb_data_destroy;
+};
 
 /* Note: we don't need to break the plugin ABI when appending keybinding or keygroup IDs,
  * just make sure to insert immediately before the _COUNT item, so
@@ -255,6 +276,11 @@ GeanyKeyBinding *keybindings_set_item(GeanyKeyGroup *group, gsize key_id,
 		GeanyKeyCallback callback, guint key, GdkModifierType mod,
 		const gchar *name, const gchar *label, GtkWidget *menu_item);
 
+GeanyKeyBinding *keybindings_set_item_full(GeanyKeyGroup *group, gsize key_id,
+		guint key, GdkModifierType mod, const gchar *kf_name, const gchar *label,
+		GtkWidget *menu_item, GeanyKeyBindingFunc func, gpointer pdata,
+		GDestroyNotify destroy_notify);
+
 GeanyKeyBinding *keybindings_get_item(GeanyKeyGroup *group, gsize key_id);
 
 GdkModifierType keybindings_get_modifiers(GdkModifierType mods);


Modified: src/keybindingsprivate.h
3 lines changed, 3 insertions(+), 0 deletions(-)
===================================================================
@@ -38,6 +38,9 @@ struct GeanyKeyGroup
 	GPtrArray *key_items;	/* pointers to GeanyKeyBinding structs */
 	gsize plugin_key_count;			/* number of keybindings the group holds */
 	GeanyKeyBinding *plugin_keys;	/* array of GeanyKeyBinding structs */
+	GeanyKeyGroupFunc cb_func;	/* use this or individual keybinding callbacks (new style) */
+	gpointer cb_data;
+	GDestroyNotify cb_data_destroy; /* used to destroy handler_data */
 };
 
 G_END_DECLS


Modified: src/pluginutils.c
33 lines changed, 33 insertions(+), 0 deletions(-)
===================================================================
@@ -33,6 +33,8 @@
 
 #include "app.h"
 #include "geanyobject.h"
+#include "keybindings.h"
+#include "keybindingsprivate.h"
 #include "plugindata.h"
 #include "pluginprivate.h"
 #include "plugins.h"
@@ -306,6 +308,37 @@ GeanyKeyGroup *plugin_set_key_group(GeanyPlugin *plugin,
 	return priv->key_group;
 }
 
+/** Sets up or resizes a keybinding group for the plugin
+ *
+ * You should then call keybindings_set_item() or keybindings_set_item_full() for each
+ * keybinding in the group.
+ * @param plugin Must be @ref geany_plugin.
+ * @param section_name Name used in the configuration file, such as @c "html_chars".
+ * @param count Number of keybindings for the group.
+ * @param cb New-style group callback, or @c NULL if you only want individual keybinding callbacks.
+ * @param pdata Plugin specific data, passed to the group callback.
+ * @param destroy_notify Function that is invoked to free the plugin data when not needed anymore.
+ * @return The plugin's keybinding group.
+ *
+ * @since 1.26 (API 226)
+ * @see See keybindings_set_item
+ * @see See keybindings_set_item_full
+ **/
+GEANY_API_SYMBOL
+GeanyKeyGroup *plugin_set_key_group_full(GeanyPlugin *plugin,
+		const gchar *section_name, gsize count,
+		GeanyKeyGroupFunc cb, gpointer pdata, GDestroyNotify destroy_notify)
+{
+	GeanyKeyGroup *group;
+
+	group = plugin_set_key_group(plugin, section_name, count, NULL);
+	group->cb_func = cb;
+	group->cb_data = pdata;
+	group->cb_data_destroy = destroy_notify;
+
+	return group;
+}
+
 
 static void on_pref_btn_clicked(gpointer btn, Plugin *p)
 {


Modified: src/pluginutils.h
3 lines changed, 3 insertions(+), 0 deletions(-)
===================================================================
@@ -54,6 +54,9 @@ guint plugin_idle_add(struct GeanyPlugin *plugin, GSourceFunc function, gpointer
 struct GeanyKeyGroup *plugin_set_key_group(struct GeanyPlugin *plugin,
 		const gchar *section_name, gsize count, GeanyKeyGroupCallback callback);
 
+GeanyKeyGroup *plugin_set_key_group_full(struct GeanyPlugin *plugin,
+		const gchar *section_name, gsize count, GeanyKeyGroupFunc cb, gpointer pdata, GDestroyNotify destroy_notify);
+
 void plugin_show_configure(struct GeanyPlugin *plugin);
 
 void plugin_builder_connect_signals(struct GeanyPlugin *plugin,



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list