Branch: refs/heads/master Author: Thomas Martitz kugel@rockbox.org Committer: Thomas Martitz kugel@rockbox.org Date: Sun, 23 Aug 2015 13:23:20 UTC Commit: 721009e262e9bc279122566a60e14af0048d9094 https://github.com/geany/geany/commit/721009e262e9bc279122566a60e14af0048d90...
Log Message: ----------- plugins: plugin loader redesign
The old plugin loader has a number of deficiencies:
- plugins need to export a couple of callback functions into the global namespace - plugins need to export data pointers, that are written by Geany - the exported functions have no user_data param, so there is no way to pass context/state back to the plugin (it needs global storage for that) - plugin registration is implicit, plugins have no way to not register themselves (it may want that due to missing runtime dependencies) - plugins perform the ABI/API verification, and even though we provide a convinience wrapper, it may get that wrong
As a result, I designed a new loader with the following design principles - semantics of callbacks should not change, but they they shouldn't be mess with the global namespace - each callback receives a self-identifying param (the GeanyPlugin instance) and a plugin-defined data pointer for their own use - explicit registration through a new API function - in-core API/ABI checks
The following principles shall be left unchanged: - The scan is done on startup and when the PM dialog is opened - Geany allocates GeanyPluginPrivate for each plugin, and GeanyPlugin is a member of it - Geany initially probes for the validity of the plugin, including file type and API/ABI check, thus Geany has the last word in determining what a plugin is - the PM dialog is updated with the proper, translated plugin information - the PM dialog GUI and user interaction in general is unchanged
With the redesign, plugins export a single function: geany_load_module(). This is called when the GModule is loaded. The main purpose of this function is to call geany_plugin_register() (new API function) to register the plugin. This is the only function that is learned about through g_module_symbol(). Within this call the plugin should a) set the localized info fields of GeanyPlugin::info b) pass compiled-against and minimum API version as well as compiled-against ABI version, to allow Geany to verify compatibility c) pass a pointer to an instance of GeanyPluginFuncs which holds pointers to enhanced versions of the known callbacks (except configure_single which is dropped). d) optionally pass a plugin-private data pointer for later callbacks
Enhanced means that all callbacks receive the GeanyPlugin pointer as the first and a pdata pointer as the last. pdata is private to the plugin and is set by geany_plugin_register().
The callbacks need (should) not be globally defined anymore, and the global GeanyData, GeanyPlugin and GeanyFunctions pointers are ignored and not set anymore. GeanyData is available through GeanyPlugin::geany_data.
Modified Paths: -------------- src/plugindata.h src/pluginprivate.h src/plugins.c src/pluginutils.c
Modified: src/plugindata.h 72 lines changed, 61 insertions(+), 11 deletions(-) =================================================================== @@ -108,17 +108,6 @@ typedef struct PluginInfo PluginInfo;
-/** Basic information for the plugin and identification. - * @see geany_plugin. */ -typedef struct GeanyPlugin -{ - PluginInfo *info; /**< Fields set in plugin_set_info(). */ - - struct GeanyPluginPrivate *priv; /* private */ -} -GeanyPlugin; - - /** Sets the plugin name and some other basic information about a plugin. * * @note If you want some of the arguments to be translated, see @ref PLUGIN_SET_TRANSLATABLE_INFO() @@ -248,6 +237,16 @@ GeanyData;
#define geany geany_data /**< Simple macro for @c geany_data that reduces typing. */
+/** Basic information for the plugin and identification. + * @see geany_plugin. */ +typedef struct GeanyPlugin +{ + PluginInfo *info; /**< Fields set in plugin_set_info(). */ + GeanyData *geany_data; /**< Pointer to global GeanyData intance */ + + struct GeanyPluginPrivate *priv; /* private */ +} +GeanyPlugin;
#ifndef GEANY_PRIVATE
@@ -263,8 +262,59 @@ void plugin_configure_single(GtkWidget *parent); void plugin_help(void); void plugin_cleanup(void);
+/** Called by Geany when a plugin library is loaded. + * + * This is the original entry point. Implement and export this function to be loadable at all. + * Then fill in GeanyPlugin::info and GeanyPlugin::funcs of the passed @p plugin. Finally + * GEANY_PLUGIN_REGISTER() and specify a minimum supported API version. + * + * @param plugin The unique plugin handle to your plugin. You must set some fields here. + * + * @since 1.26 (API 225) + */ +gboolean geany_load_module(GeanyPlugin *plugin); + #endif
+/** Callback functions that need to be implemented for every plugin. + * + * These callbacks should be registered by the plugin within Geany's call to + * geany_load_module() by calling geany_plugin_register() with an instance of this type. + * + * Geany will then call the callbacks at appropriate times. Each gets passed the + * plugin-defined data pointer as well as the corresponding GeanyPlugin instance + * pointer. + * + * @since 1.26 (API 225) + **/ +typedef struct GeanyPluginFuncs +{ + /** Array of plugin-provided signal handlers @see PluginCallback */ + PluginCallback *callbacks; + /** Called to initialize the plugin, when the user activates it (must not be @c NULL) */ + void (*init) (GeanyPlugin *plugin, gpointer pdata); + /** plugins configure dialog, optional (can be @c NULL) */ + GtkWidget* (*configure) (GeanyPlugin *plugin, GtkDialog *dialog, gpointer pdata); + /** Called when the plugin should show some help, optional (can be @c NULL) */ + void (*help) (GeanyPlugin *plugin, gpointer pdata); + /** Called when the plugin is disabled or when Geany exits (must not be @c NULL) */ + void (*cleanup) (GeanyPlugin *plugin, gpointer pdata); +} +GeanyPluginFuncs; + +gboolean geany_plugin_register(GeanyPlugin *plugin, gint api_version, gint min_api_version, + gint abi_version, GeanyPluginFuncs *cbs, gpointer pdata); + +/** Convinience macro to register a plugin. + * + * It simply calls geany_plugin_register() with GEANY_API_VERSION and GEANY_ABI_VERSION. + * + * @since 1.26 (API 225) + **/ +#define GEANY_PLUGIN_REGISTER(plugin, min_api_version) \ + geany_plugin_register((plugin), GEANY_API_VERSION, \ + (min_api_version), GEANY_ABI_VERSION) + /* Deprecated aliases */ #ifndef GEANY_DISABLE_DEPRECATED
Modified: src/pluginprivate.h 36 lines changed, 31 insertions(+), 5 deletions(-) =================================================================== @@ -32,6 +32,8 @@
G_BEGIN_DECLS
+typedef struct GeanyData GeanyData; + typedef struct SignalConnection { GObject *object; @@ -39,6 +41,22 @@ typedef struct SignalConnection } SignalConnection;
+typedef struct _GeanyPluginFuncsLegacy +{ + void (*init) (GeanyData *data); /* Called when the plugin is enabled */ + GtkWidget* (*configure) (GtkDialog *dialog); /* plugins configure dialog, optional */ + void (*configure_single) (GtkWidget *parent); /* plugin configure dialog, optional */ + void (*help) (void); /* Called when the plugin should show some help, optional */ + void (*cleanup) (void); /* Called when the plugin is disabled or when Geany exits */ + void (*set_info) (PluginInfo *info); /* Called to let the plugin provide metadata for the PM dialog */ +} +GeanyPluginFuncsLegacy; + +typedef enum _LoadedFlags { + LOADED_OK = 0x01, + IS_LEGACY = 0x02, +} +LoadedFlags;
typedef struct GeanyPluginPrivate { @@ -47,11 +65,13 @@ typedef struct GeanyPluginPrivate PluginInfo info; /* plugin name, description, etc */ GeanyPlugin public; /* fields the plugin can read */
- void (*init) (GeanyData *data); /* Called when the plugin is enabled */ - GtkWidget* (*configure) (GtkDialog *dialog); /* plugins configure dialog, optional */ - void (*configure_single) (GtkWidget *parent); /* plugin configure dialog, optional */ - void (*help) (void); /* Called when the plugin should show some help, optional */ - void (*cleanup) (void); /* Called when the plugin is disabled or when Geany exits */ + union { + GeanyPluginFuncs n; /* new-style callbacks, set by geany_plugin_register() + * NULL for legacy plugins (they do not call + * geany_plugin_register()) */ + GeanyPluginFuncsLegacy l; /* old callbacks, complete with set_info(), version_check() + * and configure_single. Deprecated */ + } cbs;
/* extra stuff */ PluginFields fields; @@ -59,9 +79,15 @@ typedef struct GeanyPluginPrivate GeanyAutoSeparator toolbar_separator; GArray *signal_ids; /* SignalConnection's to disconnect when unloading */ GList *sources; /* GSources to destroy when unloading */ + + gpointer cb_data; /* user data passed back to functions in GeanyPluginFuncs */ + LoadedFlags flags; /* bit-or of LoadedFlags */ } GeanyPluginPrivate;
+#define PLUGIN_LOADED_OK(p) (((p)->flags & LOADED_OK) != 0) +#define PLUGIN_IS_LEGACY(p) (((p)->flags & IS_LEGACY) != 0) + typedef GeanyPluginPrivate Plugin; /* shorter alias */
Modified: src/plugins.c 349 lines changed, 226 insertions(+), 123 deletions(-) =================================================================== @@ -162,37 +162,26 @@ static Plugin *find_active_plugin_by_name(const gchar *filename) }
+/* Mimics plugin_version_check() of legacy plugins for use with plugin_check_version() below */ +#define PLUGIN_VERSION_CODE(api, abi) ((abi) != GEANY_ABI_VERSION ? -1 : (api)) + static gboolean -plugin_check_version(GModule *module) +plugin_check_version(Plugin *plugin, int plugin_version_code) { - gint (*version_check)(gint) = NULL; - - g_module_symbol(module, "plugin_version_check", (void *) &version_check); - - if (G_UNLIKELY(! version_check)) + GModule *module = plugin->module; + if (plugin_version_code < 0) { - geany_debug("Plugin "%s" has no plugin_version_check() function - ignoring plugin!", - g_module_name(module)); + msgwin_status_add(_("The plugin "%s" is not binary compatible with this " + "release of Geany - please recompile it."), g_module_name(module)); + geany_debug("Plugin "%s" is not binary compatible with this " + "release of Geany - recompile it.", g_module_name(module)); return FALSE; } - else + if (plugin_version_code > GEANY_API_VERSION) { - gint result = version_check(GEANY_ABI_VERSION); - - if (result < 0) - { - msgwin_status_add(_("The plugin "%s" is not binary compatible with this " - "release of Geany - please recompile it."), g_module_name(module)); - geany_debug("Plugin "%s" is not binary compatible with this " - "release of Geany - recompile it.", g_module_name(module)); - return FALSE; - } - if (result > GEANY_API_VERSION) - { - geany_debug("Plugin "%s" requires a newer version of Geany (API >= v%d).", - g_module_name(module), result); - return FALSE; - } + geany_debug("Plugin "%s" requires a newer version of Geany (API >= v%d).", + g_module_name(module), plugin_version_code); + return FALSE; } return TRUE; } @@ -269,56 +258,164 @@ static gint cmp_plugin_names(gconstpointer a, gconstpointer b) }
-static void -plugin_load(Plugin *plugin) +/** Register a plugin to Geany. + * + * The plugin will show up in the plugin manager. The user can interact with + * it based on the callbacks it provides and installed GUI elements. + * + * The return value must be checked. It may be FALSE if the plugin failed to register which can + * mainly happen for two reasons (future Geany versions may add new failure conditions): + * - Not all mandatory fields of GeanyPlugin have been set. + * - The ABI or API versions reported by the plugin are incompatible with the running Geany. + * + * Do not call this directly. Use GEANY_PLUGIN_REGISTER() instead which automatically + * handles @a api_version and @a abi_version. + * + * @param plugin The plugin provided by Geany + * @param api_version The API version the plugin is compiled against (pass GEANY_API_VERSION) + * @param min_api_version The minimum API version required by the plugin + * @param abi_version The exact ABI version the plugin is compiled against (pass GEANY_ABI_VERSION) + * @param cbs A statically allocated @ref GeanyPluginFuncs structure filled with callbacks + * @param pdata A data pointer to store plugin-specific data, will be passed to the plugin's callbacks + * + * @return TRUE if the plugin was successfully registered. Otherwise FALSE. + * + * @since 1.26 (API 225) + * @see GEANY_PLUGIN_REGISTER() + **/ +GEANY_API_SYMBOL +gboolean geany_plugin_register(GeanyPlugin *plugin, gint api_version, gint min_api_version, + gint abi_version, GeanyPluginFuncs *cbs, gpointer pdata) { - GeanyPlugin **p_geany_plugin; - PluginCallback *callbacks; - PluginInfo **p_info; - PluginFields **plugin_fields; - - /* set these symbols before plugin_init() is called - * we don't set geany_data since it is set directly by plugin_new() */ - g_module_symbol(plugin->module, "geany_plugin", (void *) &p_geany_plugin); - if (p_geany_plugin) - *p_geany_plugin = &plugin->public; - g_module_symbol(plugin->module, "plugin_info", (void *) &p_info); - if (p_info) - *p_info = &plugin->info; - 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); - plugin->init(&geany_data); - - /* store some function pointers for later use */ - g_module_symbol(plugin->module, "plugin_configure", (void *) &plugin->configure); - g_module_symbol(plugin->module, "plugin_configure_single", (void *) &plugin->configure_single); - if (app->debug_mode && plugin->configure && plugin->configure_single) - g_warning("Plugin '%s' implements plugin_configure_single() unnecessarily - " - "only plugin_configure() will be used!", - plugin->info.name); + Plugin *p;
- g_module_symbol(plugin->module, "plugin_help", (void *) &plugin->help); - g_module_symbol(plugin->module, "plugin_cleanup", (void *) &plugin->cleanup); - if (plugin->cleanup == NULL) + g_return_val_if_fail(plugin != NULL, FALSE); + + p = plugin->priv; + /* Prevent registering incompatible plugins. */ + if (! plugin_check_version(p, PLUGIN_VERSION_CODE(api_version, abi_version))) + return FALSE; + /* If it ever becomes necessary we can save the api version in Plugin + * and apply compat code on a per-plugin basis, because we learn about + * the requested API version here. Also if we add to GeanyPluginFuncs then + * we have to inspect the plugin's api so that we don't misinterpret + * function pointers the plugin doesn't know anything about. */ + p->cbs.n = *cbs; + p->cb_data = pdata; + + /* Only init and cleanup callbacks are truly mandatory. */ + if (! cbs->init || ! cbs->cleanup) + { + geany_debug("Plugin '%s' has no %s function - ignoring plugin!", + cbs->init ? "cleanup" : "init", g_module_name(p->module)); + } + else { - if (app->debug_mode) + /* Yes, name is checked again later on, however we want return FALSE here + * to signal the error back to the plugin (but we don't print the message twice) */ + if (! EMPTY(p->info.name)) + p->flags = LOADED_OK; + } + + return PLUGIN_LOADED_OK(p); +} + + +/* This function is the equivalent of geany_plugin_register() for legacy-style + * plugins which we continue to load for the time being. */ +static void register_legacy_plugin(Plugin *plugin, GModule *module) +{ + gint (*p_version_check) (gint abi_version); + void (*p_set_info) (PluginInfo *info); + GeanyData **p_geany_data; + +#define CHECK_FUNC(__x, p) \ + if (! g_module_symbol(module, "plugin_" #__x, (void *) (p))) \ + { \ + geany_debug("Plugin "%s" has no plugin_" #__x "() function - ignoring plugin!", \ + g_module_name(plugin->module)); \ + return; \ + } + CHECK_FUNC(version_check, &p_version_check); + CHECK_FUNC(set_info, &p_set_info); + CHECK_FUNC(init, &plugin->cbs.l.init); +#undef CHECK_FUNC + + /* We must verify the version first. If the plugin has become incompatible any + * further actions should be considered invalid and therefore skipped. */ + if (! plugin_check_version(plugin, p_version_check(GEANY_ABI_VERSION))) + return; + + /* Since the version check passed we can proceed with setting basic fields and + * calling its set_info() (which might want to call Geany functions already). */ + g_module_symbol(module, "geany_data", (void *) &p_geany_data); + if (p_geany_data) + *p_geany_data = &geany_data; + /* Read plugin name, etc. name is mandatory but that's enforced in the common code. */ + p_set_info(&plugin->info); + + /* If all went well we can set the remaining callbacks and let it go for good. */ + g_module_symbol(module, "plugin_configure", (void *) &plugin->cbs.l.configure); + g_module_symbol(module, "plugin_configure_single", (void *) &plugin->cbs.l.configure_single); + g_module_symbol(module, "plugin_help", (void *) &plugin->cbs.l.help); + g_module_symbol(module, "plugin_cleanup", (void *) &plugin->cbs.l.cleanup); + + if (app->debug_mode) + { + if (plugin->cbs.l.configure && plugin->cbs.l.configure_single) + g_warning("Plugin '%s' implements plugin_configure_single() unnecessarily - " + "only plugin_configure() will be used!", + plugin->info.name); + if (plugin->cbs.l.cleanup == NULL) g_warning("Plugin '%s' has no plugin_cleanup() function - there may be memory leaks!", plugin->info.name); }
- /* now read any plugin-owned data that might have been set in plugin_init() */ + plugin->flags = LOADED_OK | IS_LEGACY; +} + + +static void +plugin_load(Plugin *plugin) +{ + PluginCallback *callbacks; + + if (PLUGIN_IS_LEGACY(plugin)) + { + GeanyPlugin **p_geany_plugin; + PluginInfo **p_info; + PluginFields **plugin_fields; + /* set these symbols before plugin_init() is called + * we don't set geany_data since it is set directly by plugin_new() */ + g_module_symbol(plugin->module, "geany_plugin", (void *) &p_geany_plugin); + if (p_geany_plugin) + *p_geany_plugin = &plugin->public; + g_module_symbol(plugin->module, "plugin_info", (void *) &p_info); + if (p_info) + *p_info = &plugin->info; + g_module_symbol(plugin->module, "plugin_fields", (void *) &plugin_fields); + if (plugin_fields) + *plugin_fields = &plugin->fields; + read_key_group(plugin); + + /* start the plugin */ + plugin->cbs.l.init(&geany_data); + + /* now read any plugin-owned data that might have been set in plugin_init() */ + if (plugin->fields.flags & PLUGIN_IS_DOCUMENT_SENSITIVE) + { + ui_add_document_sensitive(plugin->fields.menu_item); + }
- if (plugin->fields.flags & PLUGIN_IS_DOCUMENT_SENSITIVE) + g_module_symbol(plugin->module, "plugin_callbacks", (void *) &callbacks); + } + else { - ui_add_document_sensitive(plugin->fields.menu_item); + plugin->cbs.n.init(&plugin->public, plugin->cb_data); + callbacks = plugin->cbs.n.callbacks; }
- g_module_symbol(plugin->module, "plugin_callbacks", (void *) &callbacks); + /* new-style plugins set their callbacks in geany_load_module() */ if (callbacks) add_callbacks(plugin, callbacks);
@@ -327,8 +424,7 @@ plugin_load(Plugin *plugin) * sorted by plugin name */ active_plugin_list = g_list_insert_sorted(active_plugin_list, plugin, cmp_plugin_names);
- geany_debug("Loaded: %s (%s)", plugin->filename, - FALLBACK(plugin->info.name, "<Unknown>")); + geany_debug("Loaded: %s (%s)", plugin->filename, plugin->info.name); }
@@ -342,8 +438,7 @@ plugin_new(const gchar *fname, gboolean load_plugin, gboolean add_to_list) { Plugin *plugin; GModule *module; - GeanyData **p_geany_data; - void (*plugin_set_info)(PluginInfo*); + gboolean (*p_geany_load_module)(GeanyPlugin *);
g_return_val_if_fail(fname, NULL); g_return_val_if_fail(g_module_supported(), NULL); @@ -387,60 +482,42 @@ plugin_new(const gchar *fname, gboolean load_plugin, gboolean add_to_list) return NULL; }
- if (! plugin_check_version(module)) + plugin = g_new0(Plugin, 1); + plugin->module = module; + plugin->filename = g_strdup(fname); + plugin->public.geany_data = &geany_data; + plugin->public.priv = plugin; + /* Fields of plugin->info must to be initialized by the plugin */ + plugin->public.info = &plugin->info; + + g_module_symbol(module, "geany_load_module", (void *) &p_geany_load_module); + if (p_geany_load_module) { - if (! g_module_close(module)) - g_warning("%s: %s", fname, g_module_error()); - return NULL; + /* This is a new style plugin. It should fill in plugin->info and then call + * geany_plugin_register() in its geany_load_module() to successfully load. + * The ABI and API checks are performed by geany_plugin_register() (i.e. by us). + * We check the LOADED_OK flag separately to protect us against buggy plugins + * who ignore the result of geany_plugin_register() and register anyway */ + p_geany_load_module(&plugin->public); } - - g_module_symbol(module, "plugin_set_info", (void *) &plugin_set_info); - if (plugin_set_info == NULL) + else { - geany_debug("No plugin_set_info() defined for "%s" - ignoring plugin!", fname); - - if (! g_module_close(module)) - g_warning("%s: %s", fname, g_module_error()); - return NULL; + /* This is the legacy / deprecated code path. It does roughly the same as + * geany_load_module() and geany_plugin_register() together for the new ones */ + register_legacy_plugin(plugin, module); }
- plugin = g_new0(Plugin, 1); - - /* set basic fields here to allow plugins to call Geany functions in set_info() */ - g_module_symbol(module, "geany_data", (void *) &p_geany_data); - if (p_geany_data) - *p_geany_data = &geany_data; - - /* read plugin name, etc. */ - plugin_set_info(&plugin->info); - if (G_UNLIKELY(EMPTY(plugin->info.name))) + if (! PLUGIN_LOADED_OK(plugin)) { - geany_debug("No plugin name set in plugin_set_info() for "%s" - ignoring plugin!", - fname); - - if (! g_module_close(module)) - g_warning("%s: %s", fname, g_module_error()); - g_free(plugin); - return NULL; + geany_debug("Failed to load "%s" - ignoring plugin!", fname); + goto err; }
- g_module_symbol(module, "plugin_init", (void *) &plugin->init); - if (plugin->init == NULL) + if (EMPTY(plugin->info.name)) { - geany_debug("Plugin '%s' has no plugin_init() function - ignoring plugin!", - plugin->info.name); - - if (! g_module_close(module)) - g_warning("%s: %s", fname, g_module_error()); - g_free(plugin); - return NULL; + geany_debug("No plugin name set for "%s" - ignoring plugin!", fname); + goto err; } - /*geany_debug("Initializing plugin '%s'", plugin->info.name);*/ - - plugin->filename = g_strdup(fname); - plugin->module = module; - plugin->public.info = &plugin->info; - plugin->public.priv = plugin;
if (load_plugin) plugin_load(plugin); @@ -449,6 +526,13 @@ plugin_new(const gchar *fname, gboolean load_plugin, gboolean add_to_list) plugin_list = g_list_prepend(plugin_list, plugin);
return plugin; + +err: + if (! g_module_close(module)) + g_warning("%s: %s", fname, g_module_error()); + g_free(plugin->filename); + g_free(plugin); + return NULL; }
@@ -529,8 +613,11 @@ plugin_cleanup(Plugin *plugin) { GtkWidget *widget;
- if (plugin->cleanup) - plugin->cleanup(); + /* With geany_plugin_register() cleanup is mandatory */ + if (! PLUGIN_IS_LEGACY(plugin)) + plugin->cbs.n.cleanup(&plugin->public, plugin->cb_data); + else if (plugin->cbs.l.cleanup) + plugin->cbs.l.cleanup();
remove_callbacks(plugin); remove_sources(plugin); @@ -851,7 +938,12 @@ gboolean plugins_have_preferences(void) foreach_list(item, active_plugin_list) { Plugin *plugin = item->data; - if (plugin->configure != NULL || plugin->configure_single != NULL) + gboolean result; + if (! PLUGIN_IS_LEGACY(plugin)) + result = plugin->cbs.n.configure != NULL; + else + result = plugin->cbs.l.configure != NULL || plugin->cbs.l.configure_single != NULL; + if (result) return TRUE; }
@@ -892,17 +984,23 @@ static PluginManagerWidgets pm_widgets;
static void pm_update_buttons(Plugin *p) { - gboolean is_active = FALSE; gboolean has_configure = FALSE; gboolean has_help = FALSE; gboolean has_keybindings = FALSE;
- if (p != NULL) + if (p != NULL && is_active_plugin(p)) { - is_active = is_active_plugin(p); - has_configure = (p->configure || p->configure_single) && is_active; - has_help = p->help != NULL && is_active; - has_keybindings = p->key_group && p->key_group->plugin_key_count > 0 && is_active; + if (PLUGIN_IS_LEGACY(p)) + { + has_configure = p->cbs.l.configure || p->cbs.l.configure_single; + has_help = p->cbs.l.help != NULL; + } + else + { + has_configure = p->cbs.n.configure != NULL; + has_help = p->cbs.n.help != NULL; + } + has_keybindings = p->key_group && p->key_group->plugin_key_count; }
gtk_widget_set_sensitive(pm_widgets.configure_button, has_configure); @@ -1239,8 +1337,13 @@ static void pm_on_plugin_button_clicked(G_GNUC_UNUSED GtkButton *button, gpointe { if (GPOINTER_TO_INT(user_data) == PM_BUTTON_CONFIGURE) plugin_show_configure(&p->public); - else if (GPOINTER_TO_INT(user_data) == PM_BUTTON_HELP && p->help != NULL) - p->help(); + else if (GPOINTER_TO_INT(user_data) == PM_BUTTON_HELP) + { + if (PLUGIN_IS_LEGACY(p)) + p->cbs.l.help(); + else + p->cbs.n.help(&p->public, p->cb_data); + } else if (GPOINTER_TO_INT(user_data) == PM_BUTTON_KEYBINDINGS && p->key_group && p->key_group->plugin_key_count > 0) keybindings_dialog_show_prefs_scroll(p->info.name); }
Modified: src/pluginutils.c 20 lines changed, 13 insertions(+), 7 deletions(-) =================================================================== @@ -310,7 +310,7 @@ GeanyKeyGroup *plugin_set_key_group(GeanyPlugin *plugin,
static void on_pref_btn_clicked(gpointer btn, Plugin *p) { - p->configure_single(main_widgets.window); + p->cbs.l.configure_single(main_widgets.window); }
@@ -318,10 +318,16 @@ static GtkWidget *create_pref_page(Plugin *p, GtkWidget *dialog) { GtkWidget *page = NULL; /* some plugins don't have prefs */
- if (p->configure) + if (!PLUGIN_IS_LEGACY(p)) { - page = p->configure(GTK_DIALOG(dialog)); + if (p->cbs.n.configure) + page = p->cbs.n.configure(&p->public, GTK_DIALOG(dialog), p->cb_data); + } + else if (p->cbs.l.configure) + page = p->cbs.l.configure(GTK_DIALOG(dialog));
+ if (page) + { if (! GTK_IS_WIDGET(page)) { geany_debug("Invalid widget returned from plugin_configure() in plugin "%s"!", @@ -338,7 +344,7 @@ static GtkWidget *create_pref_page(Plugin *p, GtkWidget *dialog) gtk_box_pack_start(GTK_BOX(page), align, TRUE, TRUE, 0); } } - else if (p->configure_single) + else if (PLUGIN_IS_LEGACY(p) && p->cbs.l.configure_single) { GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0); GtkWidget *btn; @@ -421,12 +427,12 @@ void plugin_show_configure(GeanyPlugin *plugin) } p = plugin->priv;
- if (p->configure) + if (!PLUGIN_IS_LEGACY(p) || p->cbs.l.configure) configure_plugins(p); else { - g_return_if_fail(p->configure_single); - p->configure_single(main_widgets.window); + g_return_if_fail(p->cbs.l.configure_single); + p->cbs.l.configure_single(main_widgets.window); } }
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).