Update PLUGIN_KEY_GROUP() macro so it doesn't allocate any
GeanyKeyBinding or GeanyKeyGroup structs, so we don't need to break the ABI when adding fields to them. Add plugin_set_key_group() for plugins to dynamically set a keybinding group (e.g. for the Lua script plugin). Used in Split Window plugin as an example. Improve keybinding docs a little. git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@4115 ea778897-0a13-0410-b9d1-a72fbfd435f5
This commit is contained in:
parent
47c09a28dc
commit
770d40ab98
15
ChangeLog
15
ChangeLog
@ -1,3 +1,18 @@
|
||||
2009-08-24 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
|
||||
|
||||
* src/keybindings.c, src/keybindings.h, src/plugindata.h,
|
||||
src/pluginutils.c, src/plugins.c, src/pluginutils.h,
|
||||
doc/pluginsymbols.c, plugins/geanyfunctions.h,
|
||||
plugins/splitwindow.c:
|
||||
Update PLUGIN_KEY_GROUP() macro so it doesn't allocate any
|
||||
GeanyKeyBinding or GeanyKeyGroup structs, so we don't need to break
|
||||
the ABI when adding fields to them.
|
||||
Add plugin_set_key_group() for plugins to dynamically set a
|
||||
keybinding group (e.g. for the Lua script plugin). Used in Split
|
||||
Window plugin as an example.
|
||||
Improve keybinding docs a little.
|
||||
|
||||
|
||||
2009-08-20 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
|
||||
|
||||
* doc/Doxyfile.in, plugins/geanyfunctions.h, plugins/genapi.py:
|
||||
|
||||
@ -70,16 +70,10 @@ PluginFields *plugin_fields;
|
||||
* @see plugin_signal_connect(). */
|
||||
PluginCallback plugin_callbacks[];
|
||||
|
||||
/** Most plugins should use the PLUGIN_KEY_GROUP() macro to define it. However,
|
||||
* its fields are not read until after plugin_init() is called for the plugin, so it
|
||||
* is possible to setup a variable number of keybindings, e.g. based on the
|
||||
* plugin's configuration file settings.
|
||||
* - The @c name field must not be empty or match Geany's default group name.
|
||||
* - The @c label field is set by Geany after plugin_init() is called to the name of the
|
||||
* plugin.
|
||||
* @note This is a single element array for implementation reasons,
|
||||
* but you can treat it like a pointer. */
|
||||
KeyBindingGroup plugin_key_group[1];
|
||||
/** Plugins must use the PLUGIN_KEY_GROUP() macro to define it.
|
||||
* To setup a variable number of keybindings, e.g. based on the
|
||||
* plugin's configuration file settings, use plugin_set_key_group() instead. */
|
||||
KeyBindingGroup *plugin_key_group;
|
||||
|
||||
|
||||
/** Called before showing the plugin preferences dialog to let the user set some basic
|
||||
|
||||
@ -22,6 +22,8 @@
|
||||
geany_functions->p_plugin->module_make_resident
|
||||
#define plugin_signal_connect \
|
||||
geany_functions->p_plugin->signal_connect
|
||||
#define plugin_set_key_group \
|
||||
geany_functions->p_plugin->set_key_group
|
||||
#define document_new_file \
|
||||
geany_functions->p_document->new_file
|
||||
#define document_get_current \
|
||||
|
||||
@ -39,6 +39,7 @@ PLUGIN_SET_INFO(_("Split Window"), _("Splits the editor view into two windows.")
|
||||
|
||||
GeanyData *geany_data;
|
||||
GeanyFunctions *geany_functions;
|
||||
GeanyPlugin *geany_plugin;
|
||||
|
||||
|
||||
/* Keybinding(s) */
|
||||
@ -50,9 +51,6 @@ enum
|
||||
KB_COUNT
|
||||
};
|
||||
|
||||
PLUGIN_KEY_GROUP(split_window, KB_COUNT);
|
||||
|
||||
|
||||
enum State
|
||||
{
|
||||
STATE_SPLIT_HORIZONTAL,
|
||||
@ -396,6 +394,7 @@ static void kb_activate(guint key_id)
|
||||
void plugin_init(GeanyData *data)
|
||||
{
|
||||
GtkWidget *item, *menu;
|
||||
GeanyKeyGroup *key_group;
|
||||
|
||||
menu_items.main = item = gtk_menu_item_new_with_mnemonic(_("_Split Window"));
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(geany_data->main_widgets->tools_menu), item);
|
||||
@ -424,11 +423,12 @@ void plugin_init(GeanyData *data)
|
||||
set_state(STATE_UNSPLIT);
|
||||
|
||||
/* setup keybindings */
|
||||
keybindings_set_item(plugin_key_group, KB_SPLIT_HORIZONTAL, kb_activate,
|
||||
key_group = plugin_set_key_group(geany_plugin, "split_window", KB_COUNT, NULL);
|
||||
keybindings_set_item(key_group, KB_SPLIT_HORIZONTAL, kb_activate,
|
||||
0, 0, "split_horizontal", _("Split Horizontally"), menu_items.horizontal);
|
||||
keybindings_set_item(plugin_key_group, KB_SPLIT_VERTICAL, kb_activate,
|
||||
keybindings_set_item(key_group, KB_SPLIT_VERTICAL, kb_activate,
|
||||
0, 0, "split_vertical", _("Split Vertically"), menu_items.vertical);
|
||||
keybindings_set_item(plugin_key_group, KB_SPLIT_UNSPLIT, kb_activate,
|
||||
keybindings_set_item(key_group, KB_SPLIT_UNSPLIT, kb_activate,
|
||||
0, 0, "split_unsplit", _("Unsplit"), menu_items.unsplit);
|
||||
}
|
||||
|
||||
|
||||
@ -114,11 +114,11 @@ static void add_popup_menu_accels(void);
|
||||
/** Simple convenience function to fill a GeanyKeyBinding struct item.
|
||||
* @param group Group.
|
||||
* @param key_id Keybinding index for the group.
|
||||
* @param callback Function to call when activated.
|
||||
* @param callback Function to call when activated, or @c NULL.
|
||||
* @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 name Not duplicated - use a static string.
|
||||
* @param label Currently not duplicated - use a static or heap-allocated (e.g. translated) string.
|
||||
* @param name Key name for the configuration file, such as @c "menu_new".
|
||||
* @param label Label used in the preferences dialog keybindings tab.
|
||||
* @param menu_item Optional widget to set an accelerator for, or @c NULL. */
|
||||
void keybindings_set_item(GeanyKeyGroup *group, gsize key_id,
|
||||
GeanyKeyCallback callback, guint key, GdkModifierType mod,
|
||||
|
||||
@ -31,30 +31,30 @@
|
||||
#define GEANY_KEYBINDINGS_H 1
|
||||
|
||||
|
||||
/** Function pointer type used for keybinding callbacks */
|
||||
/** Function pointer type used for keybinding callbacks. */
|
||||
typedef void (*GeanyKeyCallback) (guint key_id);
|
||||
|
||||
/** Represents a single keybinding action */
|
||||
/** Represents a single keybinding action. */
|
||||
/* Note: name and label are not const strings so plugins can set them to malloc'd strings
|
||||
* and free them in cleanup(). */
|
||||
typedef struct GeanyKeyBinding
|
||||
{
|
||||
guint key; /**< Key value in lower-case, such as @c GDK_a */
|
||||
GdkModifierType mods; /**< Modifier keys, such as @c GDK_CONTROL_MASK */
|
||||
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 */
|
||||
gchar *name; /**< Key name for the configuration file, such as @c "menu_new" */
|
||||
gchar *label; /**< Label used in the preferences dialog keybindings tab */
|
||||
GeanyKeyCallback callback; /**< Callback function called when the key combination is pressed */
|
||||
GtkWidget *menu_item; /**< Menu item widget for setting the menu accelerator */
|
||||
GeanyKeyCallback callback; /**< Function called when the key combination is pressed, or @c NULL */
|
||||
GtkWidget *menu_item; /**< Optional widget to set an accelerator for, or @c NULL */
|
||||
} GeanyKeyBinding;
|
||||
|
||||
|
||||
/** A collection of keybindings grouped together. */
|
||||
/** A collection of keybindings grouped together. Plugins should not set these fields. */
|
||||
typedef struct GeanyKeyGroup
|
||||
{
|
||||
const gchar *name; /**< Group name used in the configuration file, such as @c "html_chars" */
|
||||
const gchar *label; /**< Group label used in the preferences dialog keybindings tab */
|
||||
gsize count; /**< Count of GeanyKeyBinding structs in @c keys */
|
||||
GeanyKeyBinding *keys; /**< Fixed array of GeanyKeyBinding structs */
|
||||
const gchar *label; /* Group label used in the preferences dialog keybindings tab */
|
||||
gsize count; /**< The number of keybindings the group holds */
|
||||
GeanyKeyBinding *keys; /* array of GeanyKeyBinding structs */
|
||||
}
|
||||
GeanyKeyGroup;
|
||||
|
||||
|
||||
@ -50,13 +50,13 @@
|
||||
enum {
|
||||
/** The Application Programming Interface (API) version, incremented
|
||||
* whenever any plugin data types are modified or appended to. */
|
||||
GEANY_API_VERSION = 150,
|
||||
GEANY_API_VERSION = 151,
|
||||
|
||||
/** The Application Binary Interface (ABI) version, incremented whenever
|
||||
* existing fields in the plugin data types have to be changed or reordered. */
|
||||
/* This should usually stay the same if fields are only appended, assuming only pointers to
|
||||
* structs and not structs themselves are declared by plugins. */
|
||||
GEANY_ABI_VERSION = 63
|
||||
GEANY_ABI_VERSION = 64
|
||||
};
|
||||
|
||||
/** Check the plugin can be loaded by Geany.
|
||||
@ -119,23 +119,32 @@ GeanyPlugin;
|
||||
}
|
||||
|
||||
|
||||
/** @see PLUGIN_KEY_GROUP() macro. */
|
||||
typedef struct GeanyKeyGroupInfo
|
||||
{
|
||||
const gchar *name; /**< Group name used in the configuration file, such as @c "html_chars" */
|
||||
gsize count; /**< The number of keybindings the group will hold */
|
||||
}
|
||||
GeanyKeyGroupInfo;
|
||||
|
||||
/** Declare and initialise a keybinding group.
|
||||
* @code GeanyKeyGroup plugin_key_group[1]; @endcode
|
||||
* You must then set the @c plugin_key_group::keys[] entries for the group in plugin_init().
|
||||
* @code GeanyKeyGroup *plugin_key_group; @endcode
|
||||
* You must then set the @c plugin_key_group::keys[] entries for the group in plugin_init(),
|
||||
* normally using keybindings_set_item().
|
||||
* The @c plugin_key_group::label field is set by Geany after @c plugin_init()
|
||||
* is called, to the name of the plugin.
|
||||
* @param group_name A unique group name (without quotes) to be used in the
|
||||
* configuration file, such as @c html_chars.
|
||||
* @param key_count The number of keybindings the group will hold. */
|
||||
* @param key_count The number of keybindings the group will hold.
|
||||
* @see plugin_set_key_group() to set the group size dynamically. */
|
||||
#define PLUGIN_KEY_GROUP(group_name, key_count) \
|
||||
static GeanyKeyBinding plugin_keys[key_count]; \
|
||||
\
|
||||
/* We have to declare plugin_key_group as a single element array.
|
||||
/* We have to declare this as a single element array.
|
||||
* Declaring as a pointer to a struct doesn't work with g_module_symbol(). */ \
|
||||
GeanyKeyGroup plugin_key_group[1] = \
|
||||
GeanyKeyGroupInfo plugin_key_group_info[1] = \
|
||||
{ \
|
||||
{G_STRINGIFY(group_name), NULL, key_count, plugin_keys} \
|
||||
};
|
||||
{G_STRINGIFY(group_name), key_count} \
|
||||
};\
|
||||
GeanyKeyGroup *plugin_key_group = NULL;
|
||||
|
||||
|
||||
/** Callback array entry type used with the @ref plugin_callbacks symbol. */
|
||||
@ -225,7 +234,7 @@ typedef struct GeanyFunctions
|
||||
struct NavQueueFuncs *p_navqueue; /**< See navqueue.h */
|
||||
struct EditorFuncs *p_editor; /**< See editor.h */
|
||||
struct MainFuncs *p_main; /**< See main.h */
|
||||
struct PluginFuncs *p_plugin; /**< See plugins.c */
|
||||
struct PluginFuncs *p_plugin; /**< See pluginutils.c */
|
||||
struct ScintillaFuncs *p_scintilla; /**< See ScintillaFuncs */
|
||||
struct MsgWinFuncs *p_msgwin; /**< See msgwindow.h */
|
||||
}
|
||||
@ -541,7 +550,10 @@ typedef struct EditorFuncs
|
||||
EditorFuncs;
|
||||
|
||||
|
||||
/* See plugins.c */
|
||||
/* avoid including keybindings.h */
|
||||
typedef gboolean (*_GeanyKeyGroupCallback) (guint key_id);
|
||||
|
||||
/* See pluginutils.c */
|
||||
typedef struct PluginFuncs
|
||||
{
|
||||
void (*add_toolbar_item)(GeanyPlugin *plugin, GtkToolItem *item);
|
||||
@ -549,6 +561,8 @@ typedef struct PluginFuncs
|
||||
void (*signal_connect) (GeanyPlugin *plugin,
|
||||
GObject *object, gchar *signal_name, gboolean after,
|
||||
GCallback callback, gpointer user_data);
|
||||
struct GeanyKeyGroup* (*set_key_group)(GeanyPlugin *plugin,
|
||||
const gchar *section_name, gsize count, _GeanyKeyGroupCallback callback);
|
||||
}
|
||||
PluginFuncs;
|
||||
|
||||
@ -556,7 +570,8 @@ PluginFuncs;
|
||||
/* Deprecated aliases */
|
||||
#ifndef GEANY_DISABLE_DEPRECATED
|
||||
|
||||
/** @c NULL-safe way to get the index of @a doc_ptr in the documents array. */
|
||||
/** @deprecated - copy into your plugin code if needed.
|
||||
* @c NULL-safe way to get the index of @a doc_ptr in the documents array. */
|
||||
#define DOC_IDX(doc_ptr) \
|
||||
(doc_ptr ? doc_ptr->index : -1)
|
||||
#define DOC_IDX_VALID(doc_idx) \
|
||||
|
||||
@ -82,7 +82,8 @@ static void pm_show_dialog(GtkMenuItem *menuitem, gpointer user_data);
|
||||
static PluginFuncs plugin_funcs = {
|
||||
&plugin_add_toolbar_item,
|
||||
&plugin_module_make_resident,
|
||||
&plugin_signal_connect
|
||||
&plugin_signal_connect,
|
||||
&plugin_set_key_group
|
||||
};
|
||||
|
||||
static DocumentFuncs doc_funcs = {
|
||||
@ -447,42 +448,36 @@ static void add_callbacks(Plugin *plugin, PluginCallback *callbacks)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_kb_group(Plugin *plugin)
|
||||
static void read_key_group(Plugin *plugin)
|
||||
{
|
||||
guint i;
|
||||
GeanyKeyGroupInfo *p_key_info;
|
||||
GeanyKeyGroup **p_key_group;
|
||||
|
||||
if (!NZV(plugin->key_group->name))
|
||||
g_module_symbol(plugin->module, "plugin_key_group_info", (void *) &p_key_info);
|
||||
g_module_symbol(plugin->module, "plugin_key_group", (void *) &p_key_group);
|
||||
if (p_key_info && p_key_group)
|
||||
{
|
||||
geany_debug("Plugin \"%s\" has not set a name for its keybinding group"
|
||||
" - ignoring all keybindings!",
|
||||
plugin->info.name);
|
||||
return;
|
||||
}
|
||||
g_return_if_fail(! g_str_equal(plugin->key_group->name, keybindings_keyfile_group_name));
|
||||
GeanyKeyGroupInfo *key_info = p_key_info;
|
||||
|
||||
for (i = 0; i < plugin->key_group->count; i++)
|
||||
{
|
||||
GeanyKeyBinding *kb = &plugin->key_group->keys[i];
|
||||
|
||||
if (!NZV(kb->name))
|
||||
if (*p_key_group)
|
||||
geany_debug("Ignoring plugin_key_group symbol for plugin '%s' - "
|
||||
"use plugin_set_key_group() instead to allocate keybindings dynamically.",
|
||||
plugin->info.name);
|
||||
else
|
||||
{
|
||||
geany_debug("Plugin \"%s\" has not set a name for keybinding %d"
|
||||
" - ignoring all keybindings!",
|
||||
plugin->info.name, i);
|
||||
plugin->key_group->count = 0;
|
||||
break;
|
||||
if (key_info->count)
|
||||
{
|
||||
GeanyKeyGroup *key_group =
|
||||
plugin_set_key_group(&plugin->public, key_info->name, key_info->count, NULL);
|
||||
if (key_group)
|
||||
*p_key_group = key_group;
|
||||
}
|
||||
else
|
||||
geany_debug("Ignoring plugin_key_group_info symbol for plugin '%s' - "
|
||||
"count field is zero. Maybe use plugin_set_key_group() instead?",
|
||||
plugin->info.name);
|
||||
}
|
||||
}
|
||||
if (plugin->key_group->count == 0)
|
||||
{
|
||||
plugin->key_group = NULL; /* Ignore the group (maybe the plugin has optional KB) */
|
||||
return;
|
||||
}
|
||||
|
||||
plugin->key_group->label = plugin->info.name;
|
||||
|
||||
g_ptr_array_add(keybinding_groups, plugin->key_group);
|
||||
}
|
||||
|
||||
|
||||
@ -512,6 +507,7 @@ plugin_init(Plugin *plugin)
|
||||
g_module_symbol(plugin->module, "plugin_fields", (void *) &plugin_fields);
|
||||
if (plugin_fields)
|
||||
*plugin_fields = &plugin->fields;
|
||||
read_key_group(plugin);
|
||||
|
||||
/* start the plugin */
|
||||
g_return_if_fail(plugin->init);
|
||||
@ -539,10 +535,6 @@ plugin_init(Plugin *plugin)
|
||||
if (callbacks)
|
||||
add_callbacks(plugin, callbacks);
|
||||
|
||||
g_module_symbol(plugin->module, "plugin_key_group", (void *) &plugin->key_group);
|
||||
if (plugin->key_group)
|
||||
add_kb_group(plugin);
|
||||
|
||||
/* remember which plugins are active */
|
||||
active_plugin_list = g_list_append(active_plugin_list, plugin);
|
||||
|
||||
@ -692,8 +684,11 @@ plugin_cleanup(Plugin *plugin)
|
||||
remove_callbacks(plugin);
|
||||
|
||||
if (plugin->key_group)
|
||||
{
|
||||
g_free(plugin->key_group->keys);
|
||||
g_ptr_array_remove_fast(keybinding_groups, plugin->key_group);
|
||||
|
||||
setptr(plugin->key_group, NULL);
|
||||
}
|
||||
widget = plugin->toolbar_separator.widget;
|
||||
if (widget)
|
||||
gtk_widget_destroy(widget);
|
||||
|
||||
@ -27,11 +27,15 @@
|
||||
* These functions all take the @ref geany_plugin symbol as their first argument. */
|
||||
|
||||
#include "geany.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "pluginutils.h"
|
||||
#include "pluginprivate.h"
|
||||
|
||||
#include "ui_utils.h"
|
||||
#include "toolbar.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
/** Insert a toolbar item before the Quit button, or after the previous plugin toolbar item.
|
||||
@ -125,3 +129,51 @@ void plugin_signal_connect(GeanyPlugin *plugin,
|
||||
}
|
||||
|
||||
|
||||
/** Setup or resize a keybinding group for the plugin.
|
||||
* You should then call keybindings_set_item() 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 callback Unused, must be @c NULL.
|
||||
* @return The plugin's keybinding group.
|
||||
* @since 0.19. */
|
||||
GeanyKeyGroup *plugin_set_key_group(GeanyPlugin *plugin,
|
||||
const gchar *section_name, gsize count, GeanyKeyGroupCallback callback)
|
||||
{
|
||||
GeanyKeyGroup *group;
|
||||
GeanyPluginPrivate *priv = plugin->priv;
|
||||
|
||||
g_return_val_if_fail(section_name, NULL);
|
||||
g_return_val_if_fail(count, NULL);
|
||||
g_return_val_if_fail(!callback, NULL);
|
||||
|
||||
if (!priv->key_group)
|
||||
priv->key_group = g_new0(GeanyKeyGroup, 1);
|
||||
group = priv->key_group;
|
||||
|
||||
group->name = section_name;
|
||||
|
||||
if (!group->keys || count > group->count)
|
||||
{
|
||||
group->keys = g_renew(GeanyKeyBinding, group->keys, count);
|
||||
memset(group->keys + group->count, 0, (count - group->count) * sizeof(GeanyKeyBinding));
|
||||
}
|
||||
group->count = count;
|
||||
|
||||
if (!NZV(group->name))
|
||||
{
|
||||
geany_debug("Plugin \"%s\" has not set the name field for its keybinding group"
|
||||
" - ignoring all keybindings!",
|
||||
priv->info.name);
|
||||
return NULL;
|
||||
}
|
||||
/* prevent conflict with core bindings */
|
||||
g_return_val_if_fail(! g_str_equal(group->name, keybindings_keyfile_group_name), NULL);
|
||||
|
||||
group->label = priv->info.name;
|
||||
|
||||
g_ptr_array_add(keybinding_groups, group);
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
#ifndef PLUGINUTILS_H
|
||||
#define PLUGINUTILS_H
|
||||
|
||||
#include "plugindata.h"
|
||||
#include "plugindata.h" /* GeanyPlugin */
|
||||
|
||||
void plugin_add_toolbar_item(GeanyPlugin *plugin, GtkToolItem *item);
|
||||
|
||||
@ -36,4 +36,11 @@ void plugin_signal_connect(GeanyPlugin *plugin,
|
||||
GObject *object, gchar *signal_name, gboolean after,
|
||||
GCallback callback, gpointer user_data);
|
||||
|
||||
|
||||
/** Function pointer type used for keybinding group callbacks. */
|
||||
typedef gboolean (*GeanyKeyGroupCallback) (guint key_id);
|
||||
|
||||
struct GeanyKeyGroup *plugin_set_key_group(GeanyPlugin *plugin,
|
||||
const gchar *section_name, gsize count, GeanyKeyGroupCallback callback);
|
||||
|
||||
#endif /* PLUGINUTILS_H */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user