1 /*
2  *      pluginutils.c - this file is part of Geany, a fast and lightweight IDE
3  *
4  *      Copyright 2005 The Geany contributors
5  *
6  *      This program is free software; you can redistribute it and/or modify
7  *      it under the terms of the GNU General Public License as published by
8  *      the Free Software Foundation; either version 2 of the License, or
9  *      (at your option) any later version.
10  *
11  *      This program is distributed in the hope that it will be useful,
12  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *      GNU General Public License for more details.
15  *
16  *      You should have received a copy of the GNU General Public License along
17  *      with this program; if not, write to the Free Software Foundation, Inc.,
18  *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 /** @file pluginutils.h
22  * Plugin utility functions.
23  * These functions all take the @ref geany_plugin symbol as their first argument. */
24 
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28 
29 #ifdef HAVE_PLUGINS
30 
31 #include "pluginutils.h"
32 
33 #include "app.h"
34 #include "geanyobject.h"
35 #include "keybindings.h"
36 #include "keybindingsprivate.h"
37 #include "plugindata.h"
38 #include "pluginprivate.h"
39 #include "plugins.h"
40 #include "support.h"
41 #include "toolbar.h"
42 #include "ui_utils.h"
43 #include "utils.h"
44 
45 
46 typedef struct
47 {
48 	gpointer data;
49 	GDestroyNotify free_func;
50 }
51 PluginDocDataProxy;
52 
53 
54 /** Returns the runtime API version Geany was compiled with.
55  *
56  * Unlike @ref GEANY_API_VERSION this version is the value of that
57  * define at the time when Geany itself was compiled. This allows to
58  * establish soft dependencies which are resolved at runtime depending
59  * on Geany's API version.
60  *
61  * @return Geany's API version
62  * @since 1.30 (API 231)
63  **/
64 GEANY_API_SYMBOL
geany_api_version(void)65 gint geany_api_version(void)
66 {
67 	return GEANY_API_VERSION;
68 }
69 
70 /** Inserts a toolbar item before the Quit button, or after the previous plugin toolbar item.
71  * A separator is added on the first call to this function, and will be shown when @a item is
72  * shown; hidden when @a item is hidden.
73  * @note You should still destroy @a item yourself, usually in @ref plugin_cleanup().
74  * @param plugin Must be @ref geany_plugin.
75  * @param item The item to add. */
76 GEANY_API_SYMBOL
plugin_add_toolbar_item(GeanyPlugin * plugin,GtkToolItem * item)77 void plugin_add_toolbar_item(GeanyPlugin *plugin, GtkToolItem *item)
78 {
79 	GtkToolbar *toolbar = GTK_TOOLBAR(main_widgets.toolbar);
80 	gint pos;
81 	GeanyAutoSeparator *autosep;
82 
83 	g_return_if_fail(plugin);
84 	autosep = &plugin->priv->toolbar_separator;
85 
86 	if (!autosep->widget)
87 	{
88 		GtkToolItem *sep;
89 
90 		pos = toolbar_get_insert_position();
91 
92 		sep = gtk_separator_tool_item_new();
93 		gtk_toolbar_insert(toolbar, sep, pos);
94 		autosep->widget = GTK_WIDGET(sep);
95 
96 		toolbar_item_ref(sep);
97 	}
98 	else
99 	{
100 		pos = gtk_toolbar_get_item_index(toolbar, GTK_TOOL_ITEM(autosep->widget));
101 		g_return_if_fail(pos >= 0);
102 	}
103 
104 	gtk_toolbar_insert(toolbar, item, pos + autosep->item_count + 1);
105 	toolbar_item_ref(item);
106 
107 	/* hide the separator widget if there are no toolbar items showing for the plugin */
108 	ui_auto_separator_add_ref(autosep, GTK_WIDGET(item));
109 }
110 
111 
112 /** Ensures that a plugin's module (*.so) will never be unloaded.
113  *  This is necessary if you register new GTypes in your plugin, e.g. when using own classes
114  *  using the GObject system.
115  *
116  * @param plugin Must be @ref geany_plugin.
117  *
118  *  @since 0.16
119  */
120 GEANY_API_SYMBOL
plugin_module_make_resident(GeanyPlugin * plugin)121 void plugin_module_make_resident(GeanyPlugin *plugin)
122 {
123 	g_return_if_fail(plugin);
124 	plugin_make_resident(plugin->priv);
125 }
126 
127 
128 /** @girskip
129  * Connects a signal which will be disconnected on unloading the plugin, to prevent a possible segfault.
130  * @param plugin Must be @ref geany_plugin.
131  * @param object @nullable Object to connect to, or @c NULL when using @link pluginsignals.c Geany signals @endlink.
132  * @param signal_name The name of the signal. For a list of available
133  * signals, please see the @link pluginsignals.c Signal documentation @endlink.
134  * @param after Set to @c TRUE to call your handler after the main signal handlers have been called
135  * (if supported by @a signal_name).
136  * @param callback The function to call when the signal is emitted.
137  * @param user_data The user data passed to the signal handler.
138  * @see plugin_callbacks.
139  *
140  * @warning Before version 1.25 (API < 218),
141  *          this should only be used on objects that outlive the plugin, never on
142  *          objects that will get destroyed before the plugin is unloaded.  For objects
143  *          created and destroyed by the plugin, you can simply use @c g_signal_connect(),
144  *          since all handlers are disconnected when the object is destroyed anyway.
145  *          For objects that may or may not outlive the plugin (like @link GeanyEditor.sci
146  *          a document's @c ScintillaObject @endlink, which is destroyed when the document
147  *          is closed), you currently have to manually handle both situations, when the
148  *          plugin is unloaded before the object is destroyed (and then, you have to
149  *          disconnect the signal on @c plugin_cleanup()), and when the object is destroyed
150  *          during the plugin's lifetime (in which case you cannot and should not disconnect
151  *          manually in @c plugin_cleanup() since it already has been disconnected and the
152  *          object has been destroyed), and disconnect yourself or not as appropriate.
153  * @note Since version 1.25 (API >= 218), the object lifetime is watched and so the above
154  *       restriction does not apply.  However, for objects destroyed by the plugin,
155  *       @c g_signal_connect() is safe and has lower overhead.
156  **/
157 GEANY_API_SYMBOL
plugin_signal_connect(GeanyPlugin * plugin,GObject * object,const gchar * signal_name,gboolean after,GCallback callback,gpointer user_data)158 void plugin_signal_connect(GeanyPlugin *plugin,
159 		GObject *object, const gchar *signal_name, gboolean after,
160 		GCallback callback, gpointer user_data)
161 {
162 	gulong id;
163 	SignalConnection sc;
164 
165 	g_return_if_fail(plugin != NULL);
166 	g_return_if_fail(object == NULL || G_IS_OBJECT(object));
167 
168 	if (!object)
169 		object = geany_object;
170 
171 	id = after ?
172 		g_signal_connect_after(object, signal_name, callback, user_data) :
173 		g_signal_connect(object, signal_name, callback, user_data);
174 
175 	if (!plugin->priv->signal_ids)
176 		plugin->priv->signal_ids = g_array_new(FALSE, FALSE, sizeof(SignalConnection));
177 
178 	sc.object = object;
179 	sc.handler_id = id;
180 	g_array_append_val(plugin->priv->signal_ids, sc);
181 
182 	/* watch the object lifetime to nuke our pointers to it */
183 	plugin_watch_object(plugin->priv, object);
184 }
185 
186 
187 typedef struct PluginSourceData
188 {
189 	Plugin		*plugin;
190 	GList		list_link;	/* element of plugin->sources cointaining this GSource */
191 	GSourceFunc	function;
192 	gpointer	user_data;
193 } PluginSourceData;
194 
195 
196 /* prepend psd->list_link to psd->plugin->sources */
psd_register(PluginSourceData * psd,GSource * source)197 static void psd_register(PluginSourceData *psd, GSource *source)
198 {
199 	psd->list_link.data = source;
200 	psd->list_link.prev = NULL;
201 	psd->list_link.next = psd->plugin->sources;
202 	if (psd->list_link.next)
203 		psd->list_link.next->prev = &psd->list_link;
204 	psd->plugin->sources = &psd->list_link;
205 }
206 
207 
208 /* removes psd->list_link from psd->plugin->sources */
psd_unregister(PluginSourceData * psd)209 static void psd_unregister(PluginSourceData *psd)
210 {
211 	if (psd->list_link.next)
212 		psd->list_link.next->prev = psd->list_link.prev;
213 	if (psd->list_link.prev)
214 		psd->list_link.prev->next = psd->list_link.next;
215 	else /* we were the first of the list, update the plugin->sources pointer */
216 		psd->plugin->sources = psd->list_link.next;
217 }
218 
219 
on_plugin_source_destroy(gpointer data)220 static void on_plugin_source_destroy(gpointer data)
221 {
222 	PluginSourceData *psd = data;
223 
224 	psd_unregister(psd);
225 	g_slice_free1(sizeof *psd, psd);
226 }
227 
228 
on_plugin_source_callback(gpointer data)229 static gboolean on_plugin_source_callback(gpointer data)
230 {
231 	PluginSourceData *psd = data;
232 
233 	return psd->function(psd->user_data);
234 }
235 
236 
237 /* adds the given source to the default GMainContext and to the list of sources to remove at plugin
238  * unloading time */
plugin_source_add(GeanyPlugin * plugin,GSource * source,GSourceFunc func,gpointer data)239 static guint plugin_source_add(GeanyPlugin *plugin, GSource *source, GSourceFunc func, gpointer data)
240 {
241 	guint id;
242 	PluginSourceData *psd = g_slice_alloc(sizeof *psd);
243 
244 	psd->plugin = plugin->priv;
245 	psd->function = func;
246 	psd->user_data = data;
247 
248 	g_source_set_callback(source, on_plugin_source_callback, psd, on_plugin_source_destroy);
249 	psd_register(psd, source);
250 	id = g_source_attach(source, NULL);
251 	g_source_unref(source);
252 
253 	return id;
254 }
255 
256 
257 /** @girskip
258  * Adds a GLib main loop timeout callback that will be removed when unloading the plugin,
259  * preventing it to run after the plugin has been unloaded (which may lead to a segfault).
260  *
261  * @param plugin Must be @ref geany_plugin.
262  * @param interval The time between calls to the function, in milliseconds.
263  * @param function The function to call after the given timeout.
264  * @param data The user data passed to the function.
265  * @return the ID of the event source (you generally won't need it, or better use g_timeout_add()
266  *   directly if you want to manage this event source manually).
267  *
268  * @see g_timeout_add()
269  * @since 0.21, plugin API 205.
270  */
271 GEANY_API_SYMBOL
plugin_timeout_add(GeanyPlugin * plugin,guint interval,GSourceFunc function,gpointer data)272 guint plugin_timeout_add(GeanyPlugin *plugin, guint interval, GSourceFunc function, gpointer data)
273 {
274 	return plugin_source_add(plugin, g_timeout_source_new(interval), function, data);
275 }
276 
277 
278 /** @girskip
279  * Adds a GLib main loop timeout callback that will be removed when unloading the plugin,
280  * preventing it to run after the plugin has been unloaded (which may lead to a segfault).
281  *
282  * @param plugin Must be @ref geany_plugin.
283  * @param interval The time between calls to the function, in seconds.
284  * @param function The function to call after the given timeout.
285  * @param data The user data passed to the function.
286  * @return the ID of the event source (you generally won't need it, or better use
287  *   g_timeout_add_seconds() directly if you want to manage this event source manually).
288  *
289  * @see g_timeout_add_seconds()
290  * @since 0.21, plugin API 205.
291  */
292 GEANY_API_SYMBOL
plugin_timeout_add_seconds(GeanyPlugin * plugin,guint interval,GSourceFunc function,gpointer data)293 guint plugin_timeout_add_seconds(GeanyPlugin *plugin, guint interval, GSourceFunc function,
294 		gpointer data)
295 {
296 	return plugin_source_add(plugin, g_timeout_source_new_seconds(interval), function, data);
297 }
298 
299 
300 /** @girskip
301  * Adds a GLib main loop IDLE callback that will be removed when unloading the plugin, preventing
302  * it to run after the plugin has been unloaded (which may lead to a segfault).
303  *
304  * @param plugin Must be @ref geany_plugin.
305  * @param function The function to call in IDLE time.
306  * @param data The user data passed to the function.
307  * @return the ID of the event source (you generally won't need it, or better use g_idle_add()
308  *   directly if you want to manage this event source manually).
309  *
310  * @see g_idle_add()
311  * @since 0.21, plugin API 205.
312  */
313 GEANY_API_SYMBOL
plugin_idle_add(GeanyPlugin * plugin,GSourceFunc function,gpointer data)314 guint plugin_idle_add(GeanyPlugin *plugin, GSourceFunc function, gpointer data)
315 {
316 	return plugin_source_add(plugin, g_idle_source_new(), function, data);
317 }
318 
319 
320 /** @girskip
321  * Sets up or resizes a keybinding group for the plugin.
322  * You should then call keybindings_set_item() for each keybinding in the group.
323  * @param plugin Must be @ref geany_plugin.
324  * @param section_name Name of the section used for this group in the keybindings configuration file, i.e. @c "html_chars".
325  * @param count Number of keybindings for the group.
326  * @param callback @nullable Group callback, or @c NULL if you only want individual keybinding callbacks.
327  * @return The plugin's keybinding group.
328  * @since 0.19.
329  **/
330 GEANY_API_SYMBOL
plugin_set_key_group(GeanyPlugin * plugin,const gchar * section_name,gsize count,GeanyKeyGroupCallback callback)331 GeanyKeyGroup *plugin_set_key_group(GeanyPlugin *plugin,
332 		const gchar *section_name, gsize count, GeanyKeyGroupCallback callback)
333 {
334 	Plugin *priv = plugin->priv;
335 
336 	priv->key_group = keybindings_set_group(priv->key_group, section_name,
337 		priv->info.name, count, callback);
338 	return priv->key_group;
339 }
340 
341 /** Sets up or resizes a keybinding group for the plugin
342  *
343  * You should then call keybindings_set_item() or keybindings_set_item_full() for each
344  * keybinding in the group.
345  * @param plugin Must be @ref geany_plugin.
346  * @param section_name Name of the section used for this group in the keybindings configuration file, i.e. @c "html_chars".
347  * @param count Number of keybindings for the group.
348  * @param cb @nullable New-style group callback, or @c NULL if you only want individual keybinding callbacks.
349  * @param pdata Plugin specific data, passed to the group callback @a cb.
350  * @param destroy_notify Function that is invoked to free the plugin data when not needed anymore.
351  * @return @transfer{none} The plugin's keybinding group.
352  *
353  * @since 1.26 (API 226)
354  * @see See keybindings_set_item
355  * @see See keybindings_set_item_full
356  **/
357 GEANY_API_SYMBOL
plugin_set_key_group_full(GeanyPlugin * plugin,const gchar * section_name,gsize count,GeanyKeyGroupFunc cb,gpointer pdata,GDestroyNotify destroy_notify)358 GeanyKeyGroup *plugin_set_key_group_full(GeanyPlugin *plugin,
359 		const gchar *section_name, gsize count,
360 		GeanyKeyGroupFunc cb, gpointer pdata, GDestroyNotify destroy_notify)
361 {
362 	GeanyKeyGroup *group;
363 
364 	group = plugin_set_key_group(plugin, section_name, count, NULL);
365 	group->cb_func = cb;
366 	group->cb_data = pdata;
367 	group->cb_data_destroy = destroy_notify;
368 
369 	return group;
370 }
371 
372 
on_pref_btn_clicked(gpointer btn,Plugin * p)373 static void on_pref_btn_clicked(gpointer btn, Plugin *p)
374 {
375 	p->configure_single(main_widgets.window);
376 }
377 
378 
create_pref_page(Plugin * p,GtkWidget * dialog)379 static GtkWidget *create_pref_page(Plugin *p, GtkWidget *dialog)
380 {
381 	GtkWidget *page = NULL;	/* some plugins don't have prefs */
382 
383 	if (p->cbs.configure)
384 	{
385 		page = p->cbs.configure(&p->public, GTK_DIALOG(dialog), p->cb_data);
386 		if (! GTK_IS_WIDGET(page))
387 		{
388 			geany_debug("Invalid widget returned from plugin_configure() in plugin \"%s\"!",
389 				p->info.name);
390 			return NULL;
391 		}
392 		else
393 		{
394 			GtkWidget *align = gtk_alignment_new(0.5, 0.5, 1, 1);
395 
396 			gtk_alignment_set_padding(GTK_ALIGNMENT(align), 6, 6, 6, 6);
397 			gtk_container_add(GTK_CONTAINER(align), page);
398 			page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
399 			gtk_box_pack_start(GTK_BOX(page), align, TRUE, TRUE, 0);
400 		}
401 	}
402 	else if (p->configure_single)
403 	{
404 		GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0);
405 		GtkWidget *btn;
406 
407 		gtk_alignment_set_padding(GTK_ALIGNMENT(align), 6, 6, 6, 6);
408 
409 		btn = gtk_button_new_from_stock(GTK_STOCK_PREFERENCES);
410 		g_signal_connect(btn, "clicked", G_CALLBACK(on_pref_btn_clicked), p);
411 		gtk_container_add(GTK_CONTAINER(align), btn);
412 		page = align;
413 	}
414 	return page;
415 }
416 
417 
418 /* multiple plugin configure dialog
419  * current_plugin can be NULL */
configure_plugins(Plugin * current_plugin)420 static void configure_plugins(Plugin *current_plugin)
421 {
422 	GtkWidget *dialog, *vbox, *nb;
423 	GList *node;
424 	gint cur_page = -1;
425 
426 	dialog = gtk_dialog_new_with_buttons(_("Configure Plugins"),
427 		GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
428 		GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
429 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
430 		GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
431 	gtk_widget_set_name(dialog, "GeanyDialog");
432 
433 	vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
434 	nb = gtk_notebook_new();
435 	gtk_notebook_set_scrollable(GTK_NOTEBOOK(nb), TRUE);
436 	gtk_box_pack_start(GTK_BOX(vbox), nb, TRUE, TRUE, 0);
437 
438 	foreach_list(node, active_plugin_list)
439 	{
440 		Plugin *p = node->data;
441 		GtkWidget *page = create_pref_page(p, dialog);
442 
443 		if (page)
444 		{
445 			GtkWidget *label = gtk_label_new(p->info.name);
446 			gint n = gtk_notebook_append_page(GTK_NOTEBOOK(nb), page, label);
447 
448 			if (p == current_plugin)
449 				cur_page = n;
450 		}
451 	}
452 	if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(nb)))
453 	{
454 		gtk_widget_show_all(vbox);
455 		if (cur_page >= 0)
456 			gtk_notebook_set_current_page(GTK_NOTEBOOK(nb), cur_page);
457 
458 		/* run the dialog */
459 		while (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_APPLY);
460 	}
461 	else
462 		utils_beep();
463 
464 	gtk_widget_destroy(dialog);
465 }
466 
467 
468 /** Shows the plugin's configure dialog.
469  * The plugin must implement one of the plugin_configure() or plugin_configure_single() symbols.
470  * @param plugin Must be @ref geany_plugin.
471  * @since 0.19. */
472 /* if NULL, show all plugins */
473 GEANY_API_SYMBOL
plugin_show_configure(GeanyPlugin * plugin)474 void plugin_show_configure(GeanyPlugin *plugin)
475 {
476 	Plugin *p;
477 
478 	if (!plugin)
479 	{
480 		configure_plugins(NULL);
481 		return;
482 	}
483 	p = plugin->priv;
484 
485 	if (p->cbs.configure)
486 		configure_plugins(p);
487 	else
488 	{
489 		g_return_if_fail(p->configure_single);
490 		p->configure_single(main_widgets.window);
491 	}
492 }
493 
494 
495 struct BuilderConnectData
496 {
497 	gpointer user_data;
498 	GeanyPlugin *plugin;
499 };
500 
501 
connect_plugin_signals(GtkBuilder * builder,GObject * object,const gchar * signal_name,const gchar * handler_name,GObject * connect_object,GConnectFlags flags,gpointer user_data)502 static void connect_plugin_signals(GtkBuilder *builder, GObject *object,
503 	const gchar *signal_name, const gchar *handler_name,
504 	GObject *connect_object, GConnectFlags flags, gpointer user_data)
505 {
506 	gpointer symbol = NULL;
507 	struct BuilderConnectData *data = user_data;
508 
509 	symbol = plugin_get_module_symbol(data->plugin->priv, handler_name);
510 
511 	plugin_signal_connect(data->plugin, object, signal_name, FALSE,
512 		G_CALLBACK(symbol) /*ub?*/, data->user_data);
513 }
514 
515 
516 /**
517  * Allows auto-connecting Glade/GtkBuilder signals in plugins.
518  *
519  * When a plugin uses GtkBuilder to load some UI from file/string,
520  * the gtk_builder_connect_signals() function is unable to automatically
521  * connect to the plugin's signal handlers. A plugin could itself use
522  * the gtk_builder_connect_signals_full() function to automatically
523  * connect to the signal handler functions by loading it's GModule
524  * and retrieving pointers to the handler functions, but rather than
525  * each plugin having to do that, this function handles it automatically.
526  *
527  * @code
528  * ...
529  * GeanyPlugin *geany_plugin;
530  *
531  * G_MODULE_EXPORT void
532  * myplugin_button_clicked(GtkButton *button, gpointer user_data)
533  * {
534  *   g_print("Button pressed\n");
535  * }
536  *
537  * void plugin_init(GeanyData *data)
538  * {
539  *   GtkBuilder *builder = gtk_builder_new();
540  *   gtk_builder_add_from_file(builder, "gui.glade", NULL);
541  *   plugin_builder_connect_signals(geany_plugin, builder, NULL);
542  *   ...
543  * }
544  * @endcode
545  *
546  * @note It's important that you prefix your callback handlers with
547  * a plugin-specific prefix to avoid clashing with other plugins since
548  * the function symbols will be exported process-wide.
549  *
550  * @param plugin Must be @ref geany_plugin.
551  * @param builder The GtkBuilder to connect signals with.
552  * @param user_data User data to pass to the connected signal handlers.
553  *
554  * @since 1.24, plugin API 217.
555  */
556 GEANY_API_SYMBOL
plugin_builder_connect_signals(GeanyPlugin * plugin,GtkBuilder * builder,gpointer user_data)557 void plugin_builder_connect_signals(GeanyPlugin *plugin,
558 	GtkBuilder *builder, gpointer user_data)
559 {
560 	struct BuilderConnectData data = { NULL };
561 
562 	g_return_if_fail(plugin != NULL && plugin->priv != NULL);
563 	g_return_if_fail(GTK_IS_BUILDER(builder));
564 
565 	data.user_data = user_data;
566 	data.plugin = plugin;
567 
568 	gtk_builder_connect_signals_full(builder, connect_plugin_signals, &data);
569 }
570 
571 
572 /** Get the additional data that corresponds to the plugin.
573  *
574  * @param plugin The plugin provided by Geany
575  * @return The data corresponding to the plugin or @c NULL if none set.
576  *
577  * @since 1.32 (API 234)
578  *
579  * @see geany_plugin_set_data()
580  */
geany_plugin_get_data(const GeanyPlugin * plugin)581 gpointer geany_plugin_get_data(const GeanyPlugin *plugin)
582 {
583 	g_return_val_if_fail (plugin != NULL, NULL);
584 	g_return_val_if_fail (PLUGIN_LOADED_OK (plugin->priv), NULL);
585 
586 	return plugin->priv->cb_data;
587 }
588 
589 
590 /** Add additional data that corresponds to the plugin.
591  *
592  * @p pdata is the pointer going to be passed to the individual plugin callbacks
593  * of GeanyPlugin::funcs. When the  plugin is cleaned up, @p free_func is invoked for the data,
594  * which connects the data to the time the plugin is enabled.
595  *
596  * One intended use case is to set GObjects as data and have them destroyed automatically
597  * by passing g_object_unref() as @a free_func, so that member functions can be used
598  * for the @ref GeanyPluginFuncs (via wrappers) but you can set completely custom data.
599  *
600  * Be aware that this can only be called once and only by plugins registered via
601  * @ref geany_plugin_register(). So-called legacy plugins cannot use this function.
602  *
603  * @note This function must not be called if the plugin was registered with
604  * geany_plugin_register_full().
605  *
606  * @param plugin The plugin provided by Geany
607  * @param pdata The plugin's data to associate, must not be @c NULL
608  * @param free_func The destroy notify
609  *
610  * @since 1.26 (API 225)
611  */
612 GEANY_API_SYMBOL
geany_plugin_set_data(GeanyPlugin * plugin,gpointer pdata,GDestroyNotify free_func)613 void geany_plugin_set_data(GeanyPlugin *plugin, gpointer pdata, GDestroyNotify free_func)
614 {
615 	Plugin *p = plugin->priv;
616 
617 	g_return_if_fail(PLUGIN_LOADED_OK(p));
618 	/* Do not allow calling this only to set a notify. */
619 	g_return_if_fail(pdata != NULL);
620 	/* The rationale to allow only setting the data once is the following:
621 	 * In the future we want to support proxy plugins (which bind non-C plugins to
622 	 * Geany's plugin api). These proxy plugins might need to own the data pointer
623 	 * on behalf of the proxied plugin. However, if not, then the plugin should be
624 	 * free to use it. This way we can make sure the plugin doesn't accidentally
625 	 * trash its proxy.
626 	 *
627 	 * Better a more limited API now that can be opened up later than a potentially
628 	 * wrong one that can only be replaced by another one. */
629 	if (p->cb_data != NULL || p->cb_data_destroy != NULL)
630 	{
631 		if (PLUGIN_HAS_LOAD_DATA(p))
632 			g_warning("Invalid call to %s(), geany_plugin_register_full() was used. Ignored!\n", G_STRFUNC);
633 		else
634 			g_warning("Double call to %s(), ignored!", G_STRFUNC);
635 		return;
636 	}
637 
638 	p->cb_data = pdata;
639 	p->cb_data_destroy = free_func;
640 }
641 
642 
plugin_doc_data_proxy_free(gpointer pdata)643 static void plugin_doc_data_proxy_free(gpointer pdata)
644 {
645 	PluginDocDataProxy *prox = pdata;
646 	if (prox != NULL)
647 	{
648 		if (prox->free_func)
649 			prox->free_func(prox->data);
650 		g_slice_free(PluginDocDataProxy, prox);
651 	}
652 }
653 
654 
655 /**
656  * Retrieve plugin-specific data attached to a document.
657  *
658  * @param plugin The plugin who attached the data.
659  * @param doc The document which the data was attached to.
660  * @param key The key name of the attached data.
661  *
662  * @return The attached data pointer or `NULL` if the key is not found
663  * for the given plugin.
664  *
665  * @since 1.29 (Plugin API 228)
666  * @see plugin_set_document_data plugin_set_document_data_full
667  */
668 GEANY_API_SYMBOL
plugin_get_document_data(struct GeanyPlugin * plugin,struct GeanyDocument * doc,const gchar * key)669 gpointer plugin_get_document_data(struct GeanyPlugin *plugin,
670 	struct GeanyDocument *doc, const gchar *key)
671 {
672 	gchar *real_key;
673 	PluginDocDataProxy *data;
674 
675 	g_return_val_if_fail(plugin != NULL, NULL);
676 	g_return_val_if_fail(doc != NULL, NULL);
677 	g_return_val_if_fail(key != NULL && *key != '\0', NULL);
678 
679 	real_key = g_strdup_printf("geany/plugins/%s/%s", plugin->info->name, key);
680 	data = document_get_data(doc, real_key);
681 	g_free(real_key);
682 
683 	return (data != NULL) ? data->data : NULL;
684 }
685 
686 
687 /**
688  * Attach plugin-specific data to a document.
689  *
690  * @param plugin The plugin attaching data to the document.
691  * @param doc The document to attach the data to.
692  * @param key The key name for the data.
693  * @param data The pointer to attach to the document.
694  *
695  * @since 1.29 (Plugin API 228)
696  * @see plugin_get_document_data plugin_set_document_data_full
697  */
698 GEANY_API_SYMBOL
plugin_set_document_data(struct GeanyPlugin * plugin,struct GeanyDocument * doc,const gchar * key,gpointer data)699 void plugin_set_document_data(struct GeanyPlugin *plugin, struct GeanyDocument *doc,
700 	const gchar *key, gpointer data)
701 {
702 	plugin_set_document_data_full(plugin, doc, key, data, NULL);
703 }
704 
705 
706 /**
707  * Attach plugin-specific data and a free function to a document.
708  *
709  * This is useful for plugins who want to keep some additional data with
710  * the document and even have it auto-released appropriately (see below).
711  *
712  * This is a simple example showing how a plugin might use this to
713  * attach a string to each document and print it when the document is
714  * saved:
715  *
716  * @code
717  * void on_document_open(GObject *unused, GeanyDocument *doc, GeanyPlugin *plugin)
718  * {
719  *     plugin_set_document_data_full(plugin, doc, "my-data",
720  *         g_strdup("some-data"), g_free);
721  * }
722  *
723  * void on_document_save(GObject *unused, GeanyDocument *doc, GeanyPlugin *plugin)
724  * {
725  *     const gchar *some_data = plugin_get_document_data(plugin, doc, "my-data");
726  *     g_print("my-data: %s\n", some_data);
727  * }
728  *
729  * gboolean plugin_init(GeanyPlugin *plugin, gpointer unused)
730  * {
731  *     plugin_signal_connect(plugin, NULL, "document-open", TRUE,
732  *         G_CALLBACK(on_document_open), plugin);
733  *     plugin_signal_connect(plugin, NULL, "document-new", TRUE,
734  *         G_CALLBACK(on_document_open), plugin);
735  *     plugin_signal_connect(plugin, NULL, "document-save", TRUE,
736  *         G_CALLBACK(on_document_save), plugin);
737  *     return TRUE;
738  * }
739  *
740  * void geany_load_module(GeanyPlugin *plugin)
741  * {
742  *   // ...
743  *   plugin->funcs->init = plugin_init;
744  *   // ...
745  * }
746  * @endcode
747  *
748  * The @a free_func can be used to tie the lifetime of the data to that
749  * of the @a doc and/or the @a plugin. The @a free_func will be called
750  * in any of the following cases:
751  *
752  *   - When a document is closed.
753  *   - When the plugin is unloaded.
754  *   - When the document data is set again using the same key.
755  *
756  * @param plugin The plugin attaching data to the document.
757  * @param doc The document to attach the data to.
758  * @param key The key name for the data.
759  * @param data The pointer to attach to the document.
760  * @param free_func The function to call with data when removed.
761  *
762  * @since 1.29 (Plugin API 228)
763  * @see plugin_get_document_data plugin_set_document_data
764  */
765 GEANY_API_SYMBOL
plugin_set_document_data_full(struct GeanyPlugin * plugin,struct GeanyDocument * doc,const gchar * key,gpointer data,GDestroyNotify free_func)766 void plugin_set_document_data_full(struct GeanyPlugin *plugin,
767 	struct GeanyDocument *doc, const gchar *key, gpointer data,
768 	GDestroyNotify free_func)
769 {
770 	PluginDocDataProxy *prox;
771 
772 	g_return_if_fail(plugin != NULL);
773 	g_return_if_fail(doc != NULL);
774 	g_return_if_fail(key != NULL);
775 
776 	prox = g_slice_new(PluginDocDataProxy);
777 	if (prox != NULL)
778 	{
779 		gchar *real_key = g_strdup_printf("geany/plugins/%s/%s", plugin->info->name, key);
780 		prox->data = data;
781 		prox->free_func = free_func;
782 		document_set_data_full(doc, real_key, prox, plugin_doc_data_proxy_free);
783 		g_free(real_key);
784 	}
785 }
786 
787 
788 #endif
789