Add plugin_signal_connect() for connecting plugin signals at

runtime and also for connecting to any GObject signal.
Add 'Plugin Utility Functions' on main page.
Add foreach_array() macro.



git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@4041 ea778897-0a13-0410-b9d1-a72fbfd435f5
This commit is contained in:
Nick Treleaven 2009-07-29 17:40:20 +00:00
parent 264ac9186c
commit 69922305e0
12 changed files with 89 additions and 28 deletions

View File

@ -1,3 +1,15 @@
2009-07-29 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
* src/pluginprivate.h, src/utils.h, src/plugindata.h,
src/stash.c, src/pluginutils.c, src/plugins.c, src/pluginutils.h,
doc/pluginsymbols.c, doc/plugins.dox, plugins/geanyfunctions.h,
plugins/filebrowser.c:
Add plugin_signal_connect() for connecting plugin signals at
runtime and also for connecting to any GObject signal.
Add 'Plugin Utility Functions' on main page.
Add foreach_array() macro.
2009-07-28 Frank Lanitz <frank(at)frank(dot)uvena(dot)de>
* wscript: Fix compiling error with waf.

View File

@ -42,9 +42,10 @@
* To get started, see the @link howto Plugin Howto @endlink.
*
* Other pages:
* - @link plugindata.h Main Datatypes and Macros @endlink
* - @link pluginsymbols.c Plugin Symbols @endlink
* - @link plugindata.h Main Datatypes and Macros @endlink
* - @link signals Plugin Signals @endlink
* - @link pluginutils.c Plugin Utility Functions @endlink
* - @link guidelines Plugin Writing Guidelines @endlink
*
* @note Some of these pages are also listed in Related Pages.
@ -56,10 +57,13 @@
*
* @section Usage
*
* To use plugin signals in Geany, you simply create a PluginCallback array, list the signals
* you want to listen to and create the appropiate signal callbacks for each signal.
* The callback array is read @a after plugin_init() has been called.
* @note The PluginCallback array has to be ended with a final NULL entry.
* To use plugin signals in Geany, you have two options:
*
* -# Create a PluginCallback array with the @ref plugin_callbacks symbol. List the signals
* you want to listen to and create the appropiate signal callbacks for each signal.
* The callback array is read @a after plugin_init() has been called.
* -# Use plugin_signal_connect(), which can be called at any time and can also connect
* to non-Geany signals (such as GTK widget signals).
*
* The following code demonstrates how to use signals in Geany plugins. The code can be inserted
* in your plugin code at any desired position.
@ -76,6 +80,7 @@ PluginCallback plugin_callbacks[] =
{ NULL, NULL, FALSE, NULL }
};
* @endcode
* @note The PluginCallback array has to be ended with a final @c NULL entry.
*
* @section Signals
*

View File

@ -66,7 +66,8 @@ const GeanyFunctions *geany_functions;
PluginFields *plugin_fields;
/** An array for connecting GeanyObject events, which should be terminated with
* @c {NULL, NULL, FALSE, NULL}. See @link signals Signal documentation @endlink. */
* @c {NULL, NULL, FALSE, NULL}. See @link signals Signal documentation @endlink.
* @see plugin_signal_connect(). */
PluginCallback plugin_callbacks[];
/** Most plugins should use the PLUGIN_KEY_GROUP() macro to define it. However,

View File

@ -30,6 +30,7 @@
#include <gdk/gdkkeysyms.h>
GeanyPlugin *geany_plugin;
GeanyData *geany_data;
GeanyFunctions *geany_functions;
@ -87,12 +88,10 @@ static struct
} popup_items;
static void document_activate_cb(GObject *obj, GeanyDocument *doc, gpointer data);
static void project_change_cb(GObject *obj, GKeyFile *config, gpointer data);
PluginCallback plugin_callbacks[] =
{
{ "document-activate", (GCallback) &document_activate_cb, TRUE, NULL },
{ "project-open", (GCallback) &project_change_cb, TRUE, NULL },
{ "project-save", (GCallback) &project_change_cb, TRUE, NULL },
{ NULL, NULL, FALSE, NULL }
@ -1020,6 +1019,9 @@ void plugin_init(GeanyData *data)
0, 0, "focus_file_list", _("Focus File List"), NULL);
keybindings_set_item(plugin_key_group, KB_FOCUS_PATH_ENTRY, kb_activate,
0, 0, "focus_path_entry", _("Focus Path Entry"), NULL);
plugin_signal_connect(geany_plugin, NULL, "document-activate", TRUE,
(GCallback) &document_activate_cb, NULL);
}

View File

@ -14,6 +14,8 @@
geany_functions->p_plugin->add_toolbar_item
#define plugin_module_make_resident \
geany_functions->p_plugin->module_make_resident
#define plugin_signal_connect \
geany_functions->p_plugin->signal_connect
#define document_new_file \
geany_functions->p_document->new_file
#define document_get_current \

View File

@ -50,7 +50,7 @@
enum {
/** The Application Programming Interface (API) version, incremented
* whenever any plugin data types are modified or appended to. */
GEANY_API_VERSION = 149,
GEANY_API_VERSION = 150,
/** The Application Binary Interface (ABI) version, incremented whenever
* existing fields in the plugin data types have to be changed or reordered. */
@ -138,7 +138,7 @@ GeanyPlugin;
};
/** callback array entry */
/** Callback array entry type used with the @ref plugin_callbacks symbol. */
typedef struct PluginCallback
{
/** The name of signal, must be an existing signal. For a list of available signals,
@ -546,6 +546,9 @@ typedef struct PluginFuncs
{
void (*add_toolbar_item)(GeanyPlugin *plugin, GtkToolItem *item);
void (*module_make_resident) (GeanyPlugin *plugin);
void (*signal_connect) (GeanyPlugin *plugin,
GObject *object, gchar *signal_name, gboolean after,
GCallback callback, gpointer user_data);
}
PluginFuncs;

View File

@ -32,6 +32,7 @@ typedef struct GeanyPluginPrivate
{
GeanyAutoSeparator toolbar_separator;
gboolean resident;
GArray *signal_ids; /* gulong signal IDs to disconnect when unloading */
}
GeanyPluginPrivate;

View File

@ -72,8 +72,6 @@ typedef struct Plugin
GeanyPlugin public; /* fields the plugin can read */
GeanyPluginPrivate priv; /* GeanyPlugin type private data, same as (*public.priv) */
gulong *signal_ids; /* signal IDs to disconnect when unloading */
gsize signal_ids_len;
GeanyKeyGroup *key_group;
void (*init) (GeanyData *data); /* Called when the plugin is enabled */
@ -100,7 +98,8 @@ static void pm_show_dialog(GtkMenuItem *menuitem, gpointer user_data);
static PluginFuncs plugin_funcs = {
&plugin_add_toolbar_item,
&plugin_module_make_resident
&plugin_module_make_resident,
&plugin_signal_connect
};
static DocumentFuncs doc_funcs = {
@ -455,16 +454,12 @@ static void add_callbacks(Plugin *plugin, PluginCallback *callbacks)
if (len == 0)
return;
plugin->signal_ids_len = len;
plugin->signal_ids = g_new(gulong, len);
for (i = 0; i < len; i++)
{
cb = &callbacks[i];
plugin->signal_ids[i] = (cb->after) ?
g_signal_connect_after(geany_object, cb->signal_name, cb->callback, cb->user_data) :
g_signal_connect(geany_object, cb->signal_name, cb->callback, cb->user_data);
plugin_signal_connect(&plugin->public, NULL, cb->signal_name, cb->after,
cb->callback, cb->user_data);
}
}
@ -686,14 +681,16 @@ plugin_new(const gchar *fname, gboolean init_plugin, gboolean add_to_list)
static void remove_callbacks(Plugin *plugin)
{
guint i;
GArray *signal_ids = plugin->priv.signal_ids;
gulong *i;
if (plugin->signal_ids == NULL)
if (signal_ids == NULL)
return;
for (i = 0; i < plugin->signal_ids_len; i++)
g_signal_handler_disconnect(geany_object, plugin->signal_ids[i]);
g_free(plugin->signal_ids);
foreach_array(gulong, i, signal_ids)
g_signal_handler_disconnect(geany_object, *i);
g_array_free(signal_ids, TRUE);
}

View File

@ -92,3 +92,33 @@ void plugin_module_make_resident(GeanyPlugin *plugin)
}
/** Connect a signal which will be disconnected on unloading the plugin, to prevent a possible segfault.
* @param plugin Must be @ref geany_plugin.
* @param object Object to connect to, or @c NULL when using @link signals Geany signals @endlink.
* @param signal_name The name of the signal. For a list of available
* signals, please see the @link signals Signal documentation @endlink.
* @param after Set to @c TRUE to call your handler after the main signal handlers have been called
* (if supported by @a signal_name).
* @param callback The function to call when the signal is emitted.
* @param user_data The user data passed to the signal handler.
* @see plugin_callbacks. */
void plugin_signal_connect(GeanyPlugin *plugin,
GObject *object, gchar *signal_name, gboolean after,
GCallback callback, gpointer user_data)
{
gulong id;
if (!object)
object = geany_object;
id = after ?
g_signal_connect_after(object, signal_name, callback, user_data) :
g_signal_connect(object, signal_name, callback, user_data);
if (!plugin->priv->signal_ids)
plugin->priv->signal_ids = g_array_new(FALSE, FALSE, sizeof(gulong));
g_array_append_val(plugin->priv->signal_ids, id);
}

View File

@ -32,4 +32,8 @@ void plugin_add_toolbar_item(GeanyPlugin *plugin, GtkToolItem *item);
void plugin_module_make_resident(GeanyPlugin *plugin);
void plugin_signal_connect(GeanyPlugin *plugin,
GObject *object, gchar *signal_name, gboolean after,
GCallback callback, gpointer user_data);
#endif /* PLUGINUTILS_H */

View File

@ -62,10 +62,7 @@
#include <gtk/gtk.h>
#include "stash.h"
#include "utils.h" /* only for utils_get_setting_*(). Stash should not depend on Geany. */
#define foreach_array(type, item, array) \
foreach_c_array(item, ((type*)(gpointer)array->data), array->len)
#include "utils.h" /* only for foreach_*, utils_get_setting_*(). Stash should not depend on Geany. */
struct GeanyPrefEntry

View File

@ -64,6 +64,13 @@
#define foreach_c_array(item, array, len) \
for (item = array; item < &array[len]; item++)
/** Iterates all items in @a array.
* @param type Type of @a item.
* @param item pointer to item in @a array.
* @param array @c GArray to traverse. */
#define foreach_array(type, item, array) \
foreach_c_array(item, ((type*)(gpointer)array->data), array->len)
/** Iterates all the pointers in @a ptr_array.
* @param item pointer in @a ptr_array.
* @param idx @c guint index into @a ptr_array.