Merge pull request #376 from kugel-/keybindings-rework3
Keybindings rework
This commit is contained in:
commit
152103392c
@ -194,12 +194,68 @@ GeanyKeyBinding *keybindings_set_item(GeanyKeyGroup *group, gsize key_id,
|
|||||||
kb->default_key = key;
|
kb->default_key = key;
|
||||||
kb->default_mods = mod;
|
kb->default_mods = mod;
|
||||||
kb->callback = callback;
|
kb->callback = callback;
|
||||||
|
kb->cb_func = NULL;
|
||||||
|
kb->cb_data = NULL;
|
||||||
kb->menu_item = menu_item;
|
kb->menu_item = menu_item;
|
||||||
kb->id = key_id;
|
kb->id = key_id;
|
||||||
return kb;
|
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,
|
static void add_kb_group(GeanyKeyGroup *group,
|
||||||
const gchar *name, const gchar *label, GeanyKeyGroupCallback callback, gboolean plugin)
|
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->name = name;
|
||||||
group->label = label;
|
group->label = label;
|
||||||
group->callback = callback;
|
group->callback = callback;
|
||||||
|
group->cb_func = NULL;
|
||||||
|
group->cb_data = NULL;
|
||||||
group->plugin = plugin;
|
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)
|
void keybindings_init(void)
|
||||||
{
|
{
|
||||||
memset(binding_ids, 0, sizeof binding_ids);
|
memset(binding_ids, 0, sizeof binding_ids);
|
||||||
keybinding_groups = g_ptr_array_sized_new(GEANY_KEY_GROUP_COUNT);
|
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();
|
kb_accel_group = gtk_accel_group_new();
|
||||||
|
|
||||||
init_default_kb();
|
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 */
|
/* 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)
|
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)
|
if (keyval == kb->key && state == kb->mods)
|
||||||
{
|
{
|
||||||
/* call the corresponding callback function for this shortcut */
|
if (run_kb(kb, group))
|
||||||
if (kb->callback)
|
|
||||||
{
|
|
||||||
kb->callback(kb->id);
|
|
||||||
return TRUE;
|
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)
|
void keybindings_send_command(guint group_id, guint key_id)
|
||||||
{
|
{
|
||||||
GeanyKeyBinding *kb;
|
GeanyKeyBinding *kb;
|
||||||
|
GeanyKeyGroup *group;
|
||||||
|
|
||||||
kb = keybindings_lookup_item(group_id, key_id);
|
kb = keybindings_lookup_item(group_id, key_id);
|
||||||
if (kb)
|
group = keybindings_get_core_group(group_id);
|
||||||
{
|
if (kb && group)
|
||||||
if (kb->callback)
|
run_kb(kb, group);
|
||||||
kb->callback(key_id);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GeanyKeyGroup *group = keybindings_get_core_group(group_id);
|
|
||||||
|
|
||||||
if (group->callback)
|
|
||||||
group->callback(key_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2544,19 +2624,5 @@ GeanyKeyGroup *keybindings_set_group(GeanyKeyGroup *group, const gchar *section_
|
|||||||
|
|
||||||
void keybindings_free_group(GeanyKeyGroup *group)
|
void keybindings_free_group(GeanyKeyGroup *group)
|
||||||
{
|
{
|
||||||
GeanyKeyBinding *kb;
|
g_ptr_array_remove_fast(keybinding_groups, group);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,42 @@ G_BEGIN_DECLS
|
|||||||
#define GEANY_PRIMARY_MOD_MASK GDK_CONTROL_MASK
|
#define GEANY_PRIMARY_MOD_MASK GDK_CONTROL_MASK
|
||||||
#endif
|
#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. */
|
/** Function pointer type used for keybinding callbacks. */
|
||||||
typedef void (*GeanyKeyCallback) (guint key_id);
|
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.
|
/** Represents a single keybinding action.
|
||||||
|
*
|
||||||
* Use keybindings_set_item() to set. */
|
* 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 */
|
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 */
|
GdkModifierType mods; /**< Modifier keys, such as @c GDK_CONTROL_MASK or 0 */
|
||||||
@ -57,19 +87,10 @@ typedef struct GeanyKeyBinding
|
|||||||
guint id;
|
guint id;
|
||||||
guint default_key;
|
guint default_key;
|
||||||
GdkModifierType default_mods;
|
GdkModifierType default_mods;
|
||||||
}
|
GeanyKeyBindingFunc cb_func;
|
||||||
GeanyKeyBinding;
|
gpointer cb_data;
|
||||||
|
GDestroyNotify cb_data_destroy;
|
||||||
|
};
|
||||||
/** 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;
|
|
||||||
|
|
||||||
|
|
||||||
/* Note: we don't need to break the plugin ABI when appending keybinding or keygroup IDs,
|
/* 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
|
* 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,
|
GeanyKeyCallback callback, guint key, GdkModifierType mod,
|
||||||
const gchar *name, const gchar *label, GtkWidget *menu_item);
|
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);
|
GeanyKeyBinding *keybindings_get_item(GeanyKeyGroup *group, gsize key_id);
|
||||||
|
|
||||||
GdkModifierType keybindings_get_modifiers(GdkModifierType mods);
|
GdkModifierType keybindings_get_modifiers(GdkModifierType mods);
|
||||||
|
@ -38,6 +38,9 @@ struct GeanyKeyGroup
|
|||||||
GPtrArray *key_items; /* pointers to GeanyKeyBinding structs */
|
GPtrArray *key_items; /* pointers to GeanyKeyBinding structs */
|
||||||
gsize plugin_key_count; /* number of keybindings the group holds */
|
gsize plugin_key_count; /* number of keybindings the group holds */
|
||||||
GeanyKeyBinding *plugin_keys; /* array of GeanyKeyBinding structs */
|
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
|
G_END_DECLS
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "geanyobject.h"
|
#include "geanyobject.h"
|
||||||
|
#include "keybindings.h"
|
||||||
|
#include "keybindingsprivate.h"
|
||||||
#include "plugindata.h"
|
#include "plugindata.h"
|
||||||
#include "pluginprivate.h"
|
#include "pluginprivate.h"
|
||||||
#include "plugins.h"
|
#include "plugins.h"
|
||||||
@ -306,6 +308,37 @@ GeanyKeyGroup *plugin_set_key_group(GeanyPlugin *plugin,
|
|||||||
return priv->key_group;
|
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)
|
static void on_pref_btn_clicked(gpointer btn, Plugin *p)
|
||||||
{
|
{
|
||||||
|
@ -54,6 +54,9 @@ guint plugin_idle_add(struct GeanyPlugin *plugin, GSourceFunc function, gpointer
|
|||||||
struct GeanyKeyGroup *plugin_set_key_group(struct GeanyPlugin *plugin,
|
struct GeanyKeyGroup *plugin_set_key_group(struct GeanyPlugin *plugin,
|
||||||
const gchar *section_name, gsize count, GeanyKeyGroupCallback callback);
|
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_show_configure(struct GeanyPlugin *plugin);
|
||||||
|
|
||||||
void plugin_builder_connect_signals(struct GeanyPlugin *plugin,
|
void plugin_builder_connect_signals(struct GeanyPlugin *plugin,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user