File: plugins.dox

package info (click to toggle)
geany 1.37.1-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 27,828 kB
  • sloc: ansic: 97,030; cpp: 60,187; vhdl: 5,924; sh: 4,596; makefile: 1,384; cs: 1,233; javascript: 1,023; f90: 537; sql: 503; python: 483; fortran: 389; php: 251; ruby: 163; asm: 131; java: 130; perl: 119; cobol: 88; erlang: 73; xml: 66; ada: 50; lisp: 48; tcl: 42; pascal: 15; haskell: 6
file content (1119 lines) | stat: -rw-r--r-- 47,577 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
/*
 * plugins.dox - this file is part of Geany, a fast and lightweight IDE
 *
 * Copyright 2008 The Geany contributors
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * This file contains additional plugin documentation like the signal system
 * and a small howto. It is best viewed when filetype is set to C or C++.
 */


/**

@mainpage Geany Plugin API Documentation

@author Enrico Tröger, Nick Treleaven, Frank Lanitz, Matthew Brush

@section Intro
This is the Geany API documentation. It should be considered work in progress.
We will try to document as many functions and structs as possible.

@warning Do not use any symbol not in the documentation - it may change.
@warning Except for exceptions stated in the documentation for geany_load_module(), no API function
may be called if the plugin is not enabled (between the calls to their GeanyFuncs::init and
GeanyFuncs::cleanup functions).

@section pluginsupport Plugin Support
- @link howto Plugin HowTo @endlink - get started
- @ref proxy
- @ref legacy
- @link plugindata.h Plugin Datatypes and Macros @endlink
- @link pluginsignals.c Plugin Signals @endlink
- @link pluginutils.h Plugin Utility Functions @endlink
- @link guidelines Plugin Writing Guidelines @endlink
- <b>plugins/demoplugin.c</b> - in Geany's source, bigger than the howto example

@section common Common API files
- @link dialogs.h @endlink
- @link document.h @endlink
- @link editor.h @endlink
- @link filetypes.h @endlink
- @link keybindings.h @endlink
- @link msgwindow.h @endlink
- @link project.h @endlink
- @link sciwrappers.h Scintilla Wrapper Functions @endlink
- @link spawn.h Spawning programs @endlink
- @link stash.h Stash Pref/Setting Functions @endlink
- @link utils.h General Utility Functions @endlink
- @link ui_utils.h Widget Utility Functions @endlink

@section More
- All API functions and types - see <b>Files</b> link at the top
- Deprecated symbols - see <b>Related Pages</b> link at the top

@note See the HACKING file for information about developing the plugin API and
other useful notes.

@page guidelines Plugin Writing Guidelines

@section intro_guidelines Introduction

The following hints and guidelines are only recommendations. Nobody is forced to follow
them at all.

@section general General notes

@subsection ideas Getting a plugin idea

If you want to write a plugin but don't know yet what it should do, have a look at
https://www.geany.org/Support/PluginWishlist to get an idea about what users wish.

@subsection code Managing the source code

For authors of plugins for Geany, we created a dedicated @a geany-plugins project
on Sourceforge and GitHub to ease development of plugins and help new authors.
All information about this project you can find at http://plugins.geany.org/

To add a new plugin to this project, get in touch with the people on the
geany-devel-mailing list and create a fork of the geany-plugins project
at https://github.com/geany/geany-plugins.
Beside of adding a new plugin, geany-devel-mailing list is also the place
to discuss development related questions.
However, once you have done your fork of geany-plugins you can develop
your plugin until you think it is the right time to publish it. At this point,
create a pull request for adding your patch set into the master branch of the main
geany-plugins repository.

Of course, you don't need to use GitHub - any Git is fine. But GitHub
is making it way easier for review, merging and get in touch with you for
comments.

If you don't want your plugin to be part of the geany-plugins project it is also fine.
Just skip the part about forking geany-plugins and sending a pull request.
In this case it is of course also a good idea to post some kind of announcement
to geany-devel and maybe to the main geany mailing list -- it's up to you.
You can also ask for your plugin to be listed on the http://plugins.geany.org/
website as a third party plugin, helping Geany user to know about your plugin.

At time of writing, there are some plugins already available in the
repositories. Feel free to use any of these plugins as a start for your own,
maybe by copying the directory structure and the autotools files
(Makefile.am, configure.in, ...). Most of the available plugins are also ready for
i18n support, just for reference.

We encourage authors using this service to only commit changes to their
own plugin and not to others' plugins. Instead just send patches to
geany-devel at uvena.de or the plugin author directly.

@section paths Installation paths

- The plugin binary (@c pluginname.so) should be installed in Geany's libdir. This is
  necessary so that Geany can find the plugin.
  An easy way to retrieve Geany's libdir is to use the pkg-config tool, e.g. @code
  `$PKG_CONFIG --variable=libdir geany`/ geany
  @endcode
- If your plugin creates other binary files like helper programs or helper libraries,
  they should go into @c $prefix/bin (for programs, ideally prefixed with @a geany),
  additional libraries should be installed in Geany's libdir, maybe in a subdirectory.
- Plugins should install their documentation files (README, NEWS, ChangeLog, licences and
  other documentation files) into the common documentation directory
  @c $prefix/share/doc/geany-plugins/$pluginname/
- Translation files should be installed normally into @c $prefix/share/locale. There is no
  need to use Geany's translation directory. To set up translation support properly and
  for additional information, see main_locale_init().
- Do @a never install anything into a user's home directory like installing
  the plugin binary in @c ~/.config/geany/plugins/.


@page howto Plugin HowTo

@section intro_howto Introduction

Since Geany 0.12 there is a plugin interface to extend Geany's functionality and
add new features. This document gives a brief overview about how to add new
plugins by writing a simple "Hello World" plugin in C or C++.


@section buildenv Build environment

To be able to write plugins for Geany, you need the source code and some development
packages for GTK and its dependencies. The following will only describe the way to compile and
build plugins on Unix-like systems [1].
If you already have the Geany source code and compiled it from them, you can skip the
following.

First you need to have Geany installed. Then install the development files for GTK
and its dependencies. The easiest way to do this is to use your distribution's package
management system, e.g. on Debian and Ubuntu systems you can use
@code apt-get install libgtk2.0-dev intltool @endcode
This will install all necessary files to be able to compile plugins for Geany. On other
distributions, the package names and commands to use may differ.

Basically, you are done at this point and could continue with writing the plugin code.

[1] For Windows, it is basically the same but you might have some more work on setting up
the general build environment (compiler, GTK development files, ...). This is described on
Geany's website at https://www.geany.org/Support/BuildingOnWin32.

@section helloworld "Hello World"

@note This section describes the new entry points for plugins introduced with Geany 1.26. A short
summary of the legacy entry points is given at page @ref legacy but they are deprecated should not
be used any more.

When writing a plugin you will find a couple of functions which are mandatory and some which can be
implemented optionally for implementing some useful features once your plugin becomes more
powerful. For example to provide a configuration or help dialog.

@subsection beginning First steps for any Plugin

You should start your plugin with including <geanyplugin.h> and exporting a function named @a
geany_load_module(). In this function you must fill in basic information that Geany uses to learn
more about your plugin and present it to the user. You also must define some hooks that enable
Geany to actually execute your code.

Please also do not forget about license headers which are by convention at the start of source
files. You can use templates provided by Geany to get started. Without a proper license it will be
difficult for packagers to pick up and distribute your plugin.

As mentioned above, start with the very fundamental header that gets you all goodies of Geany's
plugin API. @a geanyplugin.h includes all of the Geany API and also the necessary GTK header
files so there is no need to include @a gtk/gtk.h yourself. In fact it includes a utility header
that helps supporting GTK+2 and GTK+3 in the same source.

@code
#include <geanyplugin.h>
@endcode

@note If you use autoconf then config.h must be included even before that as usual.

Now you can go on and write your first lines for your new plugin. As mentioned before, you will
need to implement a couple of functions. The first mandatory one is @a geany_load_module(). Geany
uses the presence of this function to identify a library as a plugin. When Geany scans the
pre-defined and user-configured plugin directories, it will take a look at each shared library (or
DLL on Windows) to see if it exports a @a geany_load_module() symbol. Files lacking these will be
ignored. The second mandatory one is an initialization function that is only called when the plugin
becomes actually enabled (by the user or at startup).

@subsection register Registering a Plugin

Geany will always invoke this geany_load_module(), regardless of whether the user activates your
plugin. In fact its purpose to probe if the plugin should even be presented to the user. Therefore
you must use this function to register your plugin. Geany will pass a pointer to a GeanyPlugin
instance which acts as a unique handle to your plugin. Use this pointer for registering and later
API calls. It won't change for the life time of the plugin. Registering the plugin consists of a
number of steps:

  1. Filling in GeanyPlugin::info with metadata that is shown to the user.
    - @ref PluginInfo::name : The name of your plugin
    - @ref PluginInfo::description : A brief description.
    - @ref PluginInfo::version : The plugin's version.
    - @ref PluginInfo::author : Your contact information, preferably in the form "Name <email>".
    .
    Filling in all of them is recommended to provide the best user experience, but only the name is
    truly mandatory. Since all of the strings are shown to the user they should be human readable.
  2. Filling in GeanyPlugin::funcs with function pointers that are called by Geany.
    - @ref GeanyPluginFuncs::init : an initialization function
    - @ref GeanyPluginFuncs::cleanup : a finalization function
    - @ref GeanyPluginFuncs::configure : a function that provides configuration (optional)
    - @ref GeanyPluginFuncs::help : a function that provides help (optional)
    - @ref GeanyPluginFuncs::callbacks : a pointer to an array of PluginCallback (optional).
    .
    @a init and @a cleanup are mandatory, the other ones depend on how advanced your plugin is.
    Furthermore, @a init is called on startup and when the user activates your plugin in the Plugin
    Manager, and @a cleanup is called on exit and when the user deactivates it. So use these to do
    advanced initialization and teardown as to not waste resources when the plugin is not even
    enabled.
  3. Actually registering by calling GEANY_PLUGIN_REGISTER() or GEANY_PLUGIN_REGISTER_FULL().
   - Usually you should use GEANY_PLUGIN_REGISTER() to register your plugin, passing the
    GeanyPlugin pointer that you received and filled out as above. GEANY_PLUGIN_REGISTER() also
    takes the minimum API version number you want to support (see @ref versions for details). Please
    also <b>check the return value</b>. Geany may refuse to load your plugin due to
    incompatibilities, you should then abort any extra setup. GEANY_PLUGIN_REGISTER() is a macro
    wrapping geany_plugin_register() which takes additional the API and ABI that you should not pass
    manually.
   - If you require a plugin-specific context or state to be passed to your GeanyPlugin::funcs then
    use GEANY_PLUGIN_REGISTER_FULL() to register. This one takes additional parameters for adding
    user data to your plugin. That user data pointer is subsequently passed back to your functions.
    It allows, for example, to set instance pointer to objects in case your plugin isn't written in
    pure C, enabling you to use member functions as plugin functions. You may also set such data
    later on, for example in your @ref GeanyPluginFuncs::init routine to defer costly allocations
    to when the plugin is actually activated by the user. However, you then have to call
    geany_plugin_set_data().


@subsection versions On API and ABI Versions
As previously mentioned @a geany_plugin_register() takes a number of versions as arguments:
  1. api_version
  2. min_api_version
  3. abi_version

These refer to Geany's versioning scheme to manage plugin compatibility. The following rules apply:
  - Plugins are compiled against a specific Geany version on the build machine. This version of
    Geany has specific ABI and API versions, which will be compiled into the plugin. Both are
    managed automatically, by calling GEANY_PLUGIN_REGISTER().
  - The Geany version that loads the plugin may be different, possibly even have different API and
    ABI versions.
  - The ABI version is the primary plugin compatibility criteria. The ABI version of the running
    Geany and the one that's compiled into the plugin must match exactly (==). In case of mismatch,
    the affected plugins need to be recompiled (generally without source code changes) against the
    running Geany. The ABI is usually stable even across multiple releases of Geany.
  - The API version is secondary. It doesn't have to match exactly, however a plugin can report
    a minimum API version that it requires to run. Geany will check if its own API is larger than
    that (>=) and will otherwise refuse to load the plugin. The API version is incremented when
    functions or variables are added to the API which often happens more than once within a release
    cycle.
  - The API version the plugin is compiled against is still relevant for enabling compatibility
    code inside Geany (for cases where incrementing the ABI version could be avoided).

Instead of calling geany_plugin_register() directly it is very highly recommended to use
GEANY_PLUGIN_REGISTER(). This is a convenient way to pass Geany's current API and ABI versions
without requiring future code changes whenever either one changes. In fact, the promise that
plugins need to be just recompiled on ABI change can hold if the plugins use this macro. You still
want to pass the API version needed at minimum to run your plugin. The value is defined in
plugindata.h by @ref GEANY_API_VERSION. In most cases this should be your minimum. Nevertheless when
setting this value, you should choose the lowest possible version here to make the plugin
compatible with a bigger number of versions of Geany. The absolute minimum is 225 which introduced
the new plugin entry points.

To increase your flexibility the API version of the running Geany is passed to geany_load_module().
You can use this information to toggle API-specific code. This comes handy, for example to enable
optional code that requires a recent API version without raising your minimum required API version.
This enables running the plugin against more Geany versions, although perhaps at reduced
functionality.

@subsection example Example

Going back to our "Hello World" plugin here is example code that properly adds the HelloWorld
plugin to Geany.

@code
/* License blob */

#include <geanyplugin.h>


static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
	printf("Hello World from plugin!\n");

	/* Perform advanced set up here */

	return TRUE;
}


static void hello_cleanup(GeanyPlugin *plugin, gpointer pdata)
{
	printf("Bye World :-(\n");
}


G_MODULE_EXPORT
void geany_load_module(GeanyPlugin *plugin)
{
	/* Step 1: Set metadata */
	plugin->info->name = "HelloWorld";
	plugin->info->description = "Just another tool to say hello world";
	plugin->info->version = "1.0";
	plugin->info->author = "John Doe <john.doe@example.org>";

	/* Step 2: Set functions */
	plugin->funcs->init = hello_init;
	plugin->funcs->cleanup = hello_cleanup;

	/* Step 3: Register! */
	GEANY_PLUGIN_REGISTER(plugin, 225);
	/* alternatively:
	GEANY_PLUGIN_REGISTER_FULL(plugin, 225, data, free_func); */
}
@endcode

If you think this plugin seems not to implement any functionality right now and only wastes
some memory, you are right. But it should compile and load/unload in Geany nicely.
Now you have the very basic layout of a new plugin. Great, isn't it?

If you would rather write the plugin in C++, you can do that by marking @a geany_load_module()
as <c> extern "C" </c>, for example:

@code

extern "C" void geany_load_module(GeanyPlugin *plugin)
{
}

@endcode

You can also create an instance of a class and set that as data pointer (with
GEANY_PLUGIN_REGISTER_FULL()). With small wrappers that shuffle the parameters you can even use
member functions for @ref GeanyPlugin::funcs etc.

@section building Building

First make plugin.o:

@code gcc -c plugin.c -fPIC `pkg-config --cflags geany` @endcode

Then make the plugin library plugin.so (or plugin.dll on Windows):

@code gcc plugin.o -o plugin.so -shared `pkg-config --libs geany` @endcode

If all went OK, put the library into one of the paths Geany looks for plugins,
e.g. $prefix/lib/geany. See @ref paths "Installation paths" for details.

If you are writing the plugin in C++, then you will need to use your C++
compiler here, for example @c g++.

@section realfunc Adding functionality

Let's go on and implement some real functionality.

As mentioned before, GeanyPluginFuncs::init() will be called when the plugin is activated by
Geany. So it should implement everything that needs to be done during startup. In this case, we'd
like to add a menu item to Geany's Tools menu which runs a dialog printing "Hello World".

@code
static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
	GtkWidget *main_menu_item;

	// Create a new menu item and show it
	main_menu_item = gtk_menu_item_new_with_mnemonic("Hello World");
	gtk_widget_show(main_menu_item);

	// Attach the new menu item to the Tools menu
	gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu),
		main_menu_item);

	// Connect the menu item with a callback function
	// which is called when the item is clicked
	g_signal_connect(main_menu_item, "activate",
		G_CALLBACK(item_activate_cb), NULL);

	return TRUE;
}
@endcode

This will add an item to the Tools menu and connect this item to a function which implements what
should be done when the menu item is activated by the user. This is done by g_signal_connect(). The
Tools menu can be accessed with plugin->geany_data->main_widgets->tools_menu. The structure
GeanyMainWidgets contains pointers to all main GUI elements in Geany.

Geany has a simple API for showing message dialogs. So our function contains
only a few lines:
@code
static void item_activate_cb(GtkMenuItem *menuitem, gpointer user_data)
{
	dialogs_show_msgbox(GTK_MESSAGE_INFO, "Hello World");
}
@endcode

For the moment you don't need to worry about the parameters of that function.

Now we need to clean up properly when the plugin is unloaded.

To remove the menu item from the Tools menu you can use gtk_widget_destroy().

First you should add gtk_widget_destroy() to your GeanyPluginFuncs::cleanup() function. The
argument for gtk_widget_destroy() is the widget object you created earlier in
GeanyPluginFuncs::init(). To be able to access this pointer in GeanyPluginFuncs::cleanup() you can
use geany_plugin_set_data() to set plugin-defined data pointer to the widget. Alternatively, you
can store the pointer in some global variable so its visibility will increase and it can be
accessed in all functions.

@code
/* alternative: global variable:
static GtkWidget *main_menu_item;
*/
// ...
static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
	GtkWidget *main_menu_item;

	// Create a new menu item and show it
	main_menu_item = gtk_menu_item_new_with_mnemonic("Hello World");
	gtk_widget_show(main_menu_item);
// ...
	geany_plugin_set_data(plugin, main_menu_item, NULL);
	return TRUE;
}

static void hello_cleanup(GeanyPlugin *plugin, gpointer pdata)
{
	GtkWidget *main_menu_item = (GtkWidget *) pdata;
// ...
	gtk_widget_destroy(main_menu_item);
}
@endcode

This will ensure your menu item is removed from the Tools menu as well as from
memory once your plugin is unloaded, so you don't leave any memory leaks.
Once this is done, your first plugin is ready. Congratulations!

@section listing Complete listing (without comments)

@code

#include <geanyplugin.h>

static void item_activate_cb(GtkMenuItem *menuitem, gpointer user_data)
{
	dialogs_show_msgbox(GTK_MESSAGE_INFO, "Hello World");
}


static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
	GtkWidget *main_menu_item;

	// Create a new menu item and show it
	main_menu_item = gtk_menu_item_new_with_mnemonic("Hello World");
	gtk_widget_show(main_menu_item);
	gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu),
			main_menu_item);
	g_signal_connect(main_menu_item, "activate",
			G_CALLBACK(item_activate_cb), NULL);

	geany_plugin_set_data(plugin, main_menu_item, NULL);
	return TRUE;
}


static void hello_cleanup(GeanyPlugin *plugin, gpointer pdata)
{
	GtkWidget *main_menu_item = (GtkWidget *) pdata;

	gtk_widget_destroy(main_menu_item);
}


G_MODULE_EXPORT
void geany_load_module(GeanyPlugin *plugin)
{
	plugin->info->name = "HelloWorld";
	plugin->info->description = "Just another tool to say hello world";
	plugin->info->version = "1.0";
	plugin->info->author = "John Doe <john.doe@example.org>";

	plugin->funcs->init = hello_init;
	plugin->funcs->cleanup = hello_cleanup;

	GEANY_PLUGIN_REGISTER(plugin, 225);
}
@endcode


Now you might like to look at Geany's source code for core plugins such as
@a plugins/demoplugin.c.

@section furtherimprovements Further Improvements and next steps
@subsection translatable_plugin_information Translatable plugin information

After having written our first plugin, there is still room for improvement.

By default, @ref geany_load_module() is not prepared to allow translation of the basic plugin
information, except plugins which are shipped with Geany's core distribution, because custom
gettext catalogs are not setup. Since most plugins are not shipped with Geany's core, it makes
sense to setup gettext when the plugin is loaded so that it gets translated inside Geany's Plugin
Manager. The solution is to call the API function main_locale_init() inside @ref
geany_load_module() and then use gettext's _() as usual.

The invocation will most probably look similar to this:
@code
// ...
	main_locale_init(LOCALEDIR, GETTEXT_PACKAGE);
	plugin->info->name = _("HelloWorld");
	plugin->info->description = _("Just another tool to say hello world");
	plugin->info->version = "1.0";
	plugin->info->author = "John Doe <john.doe@example.org>";
@endcode

The @a LOCALEDIR and the @a GETTEXT_PACKAGE parameters are usually set inside the build system.

As you can see the author's information is not marked as translatable in
this example.  The community has agreed that the best practice here is to
use, if possible, the latin version of the author's name followed by the
native spelling inside parenthesis, where applicable.

@subsection plugin_i18n Using i18n/l10n inside Plugin


You can (and should) also mark other strings beside the plugin's meta information as translatable.
Strings used in menu entries, information boxes or configuration dialogs should be translatable as
well.

@code
static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
	main_locale_init(LOCALEDIR, GETTEXT_PACKAGE);
	main_menu_item = gtk_menu_item_new_with_mnemonic(_("Hello World"));
// ...
}
@endcode

@page legacy Porting guide from legacy entry points to the current ones

@section intro_legacy Introduction

This page briefly describes the deprecated, legacy plugin entry points. These have been in place
prior to Geany 1.26 and are still loadable and working for the time being. However, do not create
new plugins against these. For this reason, the actual description here is rather minimalistic and
concentrates on porting legacy plugins to the new interface. Basically its main purpose
is to give newcomers an idea of what they are looking at if they come across a legacy plugin.

@section overview Overview

The legacy entry points consist of a number of pre-defined symbols (functions and variables)
exported by plugins. There is no active registration procedure. It is implicit simply by exporting
the mandatory symbols. The entirety of the symbols is described at the page @link pluginsymbols.c
Plugin Symbols @endlink.

At the very least plugins must define the functions @a plugin_init(GeanyData *) and @a
plugin_version_check(gint). Additionally, an instance of the struct PluginInfo named plugin_info
must be exported as well, this contains the same metadata already known from GeanyPlugin::info. The
functions plugin_cleanup(), plugin_help(), plugin_configure(GtkDialog *) and
plugin_configure_single(GtkWidget *) are optional, however Geany prints a warning if
plugin_cleanup() is missing and only one of plugin_configure(GtkDialog *) and
plugin_configure_single(GtkWidget *) is used for any single plugin.

By convention, plugin_version_check() is implicitly defined through the use of PLUGIN_VERSION_CHECK(),
and similarly plugin_info is defined through PLUGIN_SET_INFO() or PLUGIN_SET_TRANSLATABLE_INFO().

The functions should generally perform the same tasks as their equivalents in GeanyPlugin::funcs.

Geany also recognized numerous variable fields if the plugin exported them globally, and actually
set a few of them inside the plugins data section.

@section porting Porting a Legacy Plugin

Given a legacy plugin it can be modified to use the new entry points without much effort. This
section gives a basic recipe that should work for most existing plugins. The transition should
be easy and painless so it is recommended that you adapt your plugin as soon as possible.

@note This guide is intentionally minimalistic (in terms of suggested code changes) in order to
allow adaption to the current entry points as quickly as possible and without a lot effort. It
should also work even for rather complex plugins comprising multiple source files. On the other hand
it does not make use of new features such as geany_plugin_set_data().

@subsection functions Functions

Probably the biggest hurdle is the dropped support of the long-deprecated
plugin_configure_single(). This means you first have to port the configuration dialog (if any) to
the combined plugin dialog. While you previously created a custom dialog you now attach the main
widget of that dialog to the combined plugin dialog simply by returning it from
GeanyPluginFuncs::configure. You don't actually add it, Geany will do that for you. The pointer to
the dialog is passed to @a configure simply to allow you to connect to its "response" or "close"
signals.

The following lists the function mapping of previous @a plugin_* functions to the new @a
GeanyPlugin::funcs. They are semantically the same, however the new functions receive more
parameters which you may use or not.

  - plugin_init() => GeanyPlugin->funcs->init
  - plugin_cleanup() => GeanyPlugin->funcs->cleanup
  - plugin_help() => GeanyPlugin->funcs->help
  - plugin_configure() => GeanyPlugin->funcs->configure

@note @ref GeanyPluginFuncs::init() should return a boolean value: whether or not the plugin loaded
successfully. Since legacy plugins couldn't fail in plugin_init() you should return @c TRUE
unconditionally.

@note Again, plugin_configure_single() is not supported anymore.

@subsection Variables

Exported global variables are not recognized anymore. They are replaced in the following ways:

@ref plugin_info is simply removed. Instead, you have to assign the values to GeanyPlugin::info
yourself, and it must be done inside your @a geany_load_module().

Example:

@code
PLUGIN_SET_INFO(
	"HelloWorld",
	"Just another tool to say hello world",
	"1.0", "John Doe <john.doe@example.org>");
@endcode

becomes

@code
G_MODULE_EXPORT
void geany_load_module(GeanyPlugin *plugin)
{
// ...
	plugin->info->name = "HelloWorld";
	plugin->info->description = "Just another tool to say hello world";
	plugin->info->version = "1.0";
	plugin->info->author = "John Doe <john.doe@example.org>";
// ...
}
@endcode
@note Refer to @ref translatable_plugin_information for i18n support for the metadata.


The @ref plugin_callbacks array is supported by assigning the GeanyPluginFuncs::callbacks to
the array.

@ref plugin_fields is not supported anymore. Use ui_add_document_sensitive() instead.
@ref PLUGIN_KEY_GROUP and @ref plugin_key_group are also not supported anymore. Use
plugin_set_key_group() and keybindings_set_item() respectively.

Additionally, Geany traditionally set a few variables. This is not the case anymore. @ref
geany_functions has been removed in 1.25 and since then existed only for compatibility and has been
empty. You can simply remove its declaration from your source code. @ref geany_plugin is passed to
each @ref GeanyPluginFuncs function. You need to store it yourself somewhere if you need it
elsewhere. @ref geany_data is now available as a member of GeanyPlugin.

@code
GeanyPlugin *geany_plugin;
GeanyData *geany_data;

static gboolean my_init(GeanyPlugin *plugin, gpointer pdata)
{
// ...
	geany_plugin = plugin;
	geany_data = plugin->geany_data;
	return TRUE;
}
@endcode

@ref geany_plugin is now also passed by default to the PluginCallback signal handlers as data
pointer if it's set to NULL.

@code
static PluginCallback plugin_callbacks[] = {
	{ "editor-notify", (GCallback) &on_editor_notify_cb, FALSE, NULL },
// ...
};

static gboolean on_editor_notify_cb(GObject *object, GeanyEditor *editor,
									SCNotification *nt, gpointer data)
{
	GeanyPlugin *plugin = data;
//...
}


G_MODULE_EXPORT
void geany_load_module(GeanyPlugin *plugin)
{
// ...
	plugin->funcs->callbacks = plugin_callbacks;
// ...
}
}
@endcode


@page proxy Proxy Plugin HowTo

@section proxy_intro Introduction

Geany has built-in support for plugins. These plugins can alter the way Geany operates in many
imaginable ways which leaves little to be desired.

However, there is one significant short-coming. Due to the infrastructure, Geany's built-in support
only covers plugins written in C, perhaps C++ and Vala. Basically all languages which can be
compiled into native shared libraries and can link GTK libraries. This excludes dynamic languages
such as Python.

Geany provides a mechanism to enable support for those languages. Native plugins can register as
proxy plugins by being a normal plugin to the Geany-side and by providing a bridge to write plugins
in another language on the other side.

These plugins are also called sub-plugins. This refers to the relation to their proxy.
To Geany they are first-class citizens.

@section proxy_protocol Writing a Proxy Plugin

The basic idea is that a proxy plugin provides methods to match, load and unload one or more
sub-plugin plugins in an abstract manner:

 - Matching consists of providing a list of supported file extensions for the sub-plugins and
   a mechanism to resolve file extension uncertainty or ambiguity. The matching makes the plugin
   visible to the user within the Plugin Manager.
 - Loading consists of loading the sub-plugin's file, passing the file to some form of interpreter
   and calling GEANY_PLUGIN_REGISTER() or GEANY_PLUGIN_REGISTER_FULL() on behalf of the sub-plugin
   at some point.
 - Unloading simply reverses the effect of loading.

For providing these methods, GeanyPlugin has a field GeanyProxyFuncs which contains three function
pointers which must be initialized prior to calling geany_plugin_register_proxy(). This should be
done in the GeanyPluginFuncs::init function of the proxy plugin.

 - In the call to geany_plugin_register_proxy() the proxy plugin passes a list of file extensions.
   When Geany scans through its plugin directories as usual it will also look for files with
   that extensions and consider found files as plugin candidate.
 - GeanyProxyFuncs::probe may be implemented to probe if a plugin candidate (that has one of the
   provided file extensions) is actually a plugin. This may depend on the plugin file itself in
   case of ambiguity or availability of runtime dependencies or even configuration.
   @ref GeanyProxyProbeResults constants should be returned. Not implementing GeanyProxyFuncs::probe
   at all is equivalent to always returning @ref GEANY_PROXY_MATCH.
 - GeanyProxyFuncs::load must be implemented to actually load the plugin. It is called by Geany
   when the user enables the sub-plugin. What "loading" means is entirely up to the proxy plugin and
   probably depends on the interpreter of the dynamic language that shall be supported. After
   setting everything up as necessary GEANY_PLUGIN_REGISTER() or GEANY_PLUGIN_REGISTER_FULL() must
   be called to register the sub-plugin.
 - GeanyProxyFuncs::unload must be implemented and is called when the user unchecks the sub-plugin
   or when Geany exits. Here, the proxy should release any references or memory associated to the
   sub-plugin. Note that if GeanyProxyFuncs::load didn't succeed, i.e. didn't successfully register
   the sub-plugin, then this function won't be called.

GeanyProxyFuncs::load and GeanyProxyFuncs::unload receive two GeanyPlugin pointers: One that
corresponds to the proxy itself and another that corresponds to the sub-plugin. The sub-plugin's
one may be used to call various API functions on behalf of the sub-plugin, including
GEANY_PLUGIN_REGISTER() and GEANY_PLUGIN_REGISTER_FULL().

GeanyProxyFuncs::load may return a pointer that is passed back to GeanyProxyFuncs::unload. This can
be used to store proxy-defined but sub-plugin-specific data required for unloading. However, this
pointer is not passed to the sub-plugin's GeanyPluginFuncs. To arrange for that, you want to call
GEANY_PLUGIN_REGISTER_FULL(). This method is the key to enable proxy plugins to wrap the
GeanyPluginFuncs of all sub-plugins and yet multiplex between multiple sub-plugin, for example by
storing a per-sub-plugin interpreter context.

@note If the pointer returned from GeanyProxyFuncs::load is the same that is passed to
GEANY_PLUGIN_REGISTER_FULL() then you must pass NULL as free_func, because that would be invoked
prior to unloading. Insert the corresponding code into GeanyProxyFuncs::unload.

@section proxy_compat_guideline Guideline for Checking Compatibility

Determining if a plugin candidate is compatible is not a single test. There are multiple levels and
each should be handled differently in order to give the user a consistent feedback.

Consider the 5 basic cases:

1) A candidate comes with a suitable file extension but is not a workable plugin file at all. For
example, your proxy supports plugins written in a shell script (.sh) but the shebang of that script
points to an incompatible shell (or even lacks a shebang). You should check for this in
GeanyProxyFuncs::probe() and return @ref GEANY_PROXY_IGNORE which hides that script from the Plugin
Manager and allows other enabled proxy plugins to pick it up. GeanyProxyFuncs::probe() returning
@ref GEANY_PROXY_IGNORE is an indication that the candidate is meant for another proxy, or the user
placed the file by accident in one of Geany's plugin directories. In other words the candidate
simply doesn't correspond to your proxy. Thus any noise by debug messages for this case is
undesirable.

2) A proxy plugin provides its own, versioned API to sub-plugin. The API version of the sub-plugin
is not compatible with the API exposed by the proxy. GeanyProxyFuncs::probe() should never perform
a version check because its sole purpose is to indicate a proxy's correspondence to a given
candidate. It should return @ref GEANY_PROXY_MATCH instead. Later, Geany will invoke the
GeanyProxyFuncs::load(), and this function is the right place for a version check. If it fails then
you simply do not call GEANY_PLUGIN_REGISTER(), but rather print a debug message. The result is
that the sub-plugin is not shown in the Plugin Manager at all. This is consistent with the
treatment of native plugins by Geany.

3) The sub-plugin is also depending on Geany's API version (whether it is or not depends on the
design of the proxy). In this case do not do anything special but forward the API version the
sub-plugin is written/compiled against to GEANY_PLUGIN_REGISTER(). Here, Geany will perform its own
compatibility check, allowing for a consistent user feedback. The result is again that the
sub-plugin is hidden from the Plugin Manager, like in case 2. But Geany will print a debug message
so you can skip that.


If you have even more cases try to fit it into case 1 or 2, depending on whether other proxy
plugins should get a chance to load the candidate or not.

@section proxy_dep_guideline Guideline for Runtime Errors

A sub-plugin might not be able to run even if it's perfectly compatible with its proxy. This
includes the case when it lacks certain runtime dependencies such as programs or modules but also
syntactic problems or other errors.

There are two basic classes:

1) Runtime errors that can be determined at load time. For example, the shebang of a script
indicates a specific interpreter version but that version is not installed on the system. Your proxy
should respond the same way as for version-incompatible plugins: don't register the plugin at
all, but leave a message the user suggesting what has to be installed in order to work. Handle
syntax errors in the scripts of sub-plugins the same way if possible.

2) Runtime errors that cannot be determined without actually running the plugin. An example would
be missing modules in Python scripts. If your proxy has no way of foreseeing the problem the plugin
will be registered normally. However, you can catch runtime errors by implementing
GeanyPluginFuncs::init() on the plugin's behalf. This is called after user activation and allows to
indicate errors by returning @c FALSE. However, allowing the user to enable a plugin and then
disabling anyway is a poor user experience.

Therefore, if possible, try to fail fast and disallow registration.

@section Proxy Plugin Example

In this section a dumb example proxy plugin is shown in order to give a practical starting point.
The sub-plugin are not actually code but rather a ini-style description of one or more menu items
that are added to Geany's tools menu and a help dialog. Real world sub-plugins would contain actual
code, usually written in a scripting language.

A sub-plugin file looks like this:

@code{.ini}
#!!PROXY_MAGIC!!

[Init]
item0 = Bam
item1 = Foo
item2 = Bar

[Help]
text = I'm a simple test. Nothing to see!

[Info]
name = Demo Proxy Tester
description = I'm a simple test. Nothing to see!
version = 0.1
author = The Geany developer team
@endcode

The first line acts as a verification that this file is truly a sub-plugin. Within the [Init] section
there is the menu items for Geany's tools menu. The [Help] section declares the sub-plugins help
text which is shown in its help dialog (via GeanyPluginFuncs::help). The [Info] section is
used as-is for filling the sub-plugins PluginInfo fields.

That's it, this dumb format is purely declarative and contains no logic. Yet we will create plugins
from it.

We start by registering the proxy plugin to Geany. There is nothing special to it compared to
normal plugins. A proxy plugin must also fill its own @ref PluginInfo and @ref GeanyPluginFuncs,
followed by registering through GEANY_PLUGIN_REGISTER().


@code{.c}

/* Called by Geany to initialize the plugin. */
static gboolean demoproxy_init(GeanyPlugin *plugin, gpointer pdata)
{
	// ...
}


/* Called by Geany before unloading the plugin. */
static void demoproxy_cleanup(GeanyPlugin *plugin, gpointer data)
{
	// ...
}


G_MODULE_EXPORT
void geany_load_module(GeanyPlugin *plugin)
{
	plugin->info->name = _("Demo Proxy");
	plugin->info->description = _("Example Proxy.");
	plugin->info->version = "0.1";
	plugin->info->author = _("The Geany developer team");

	plugin->funcs->init = demoproxy_init;
	plugin->funcs->cleanup = demoproxy_cleanup;

	GEANY_PLUGIN_REGISTER(plugin, 225);
}

@endcode

The next step is to actually register as a proxy plugin. This is done in demoproxy_init().
As previously mentioned, it needs a list of accepted file extensions and a set of callback
functions.

@code{.c}
static gboolean demoproxy_init(GeanyPlugin *plugin, gpointer pdata)
{
	const gchar *extensions[] = { "ini", "px", NULL };

	plugin->proxy_funcs->probe  = demoproxy_probe;
	plugin->proxy_funcs->load   = demoproxy_load;
	plugin->proxy_funcs->unload = demoproxy_unload;

	return geany_plugin_register_proxy(plugin, extensions);
}

@endcode

The callback functions deserve a closer look.

As already mentioned the file format includes a magic first line which must be present.
GeanyProxyFuncs::probe() verifies that it's present and avoids showing the sub-plugin in the
Plugin Manager if not.

@code{.c}
static gint demoproxy_probe(GeanyPlugin *proxy, const gchar *filename, gpointer pdata)
{
	/* We know the extension is right (Geany checks that). For demo purposes we perform an
	 * additional check. This is not necessary when the extension is unique enough. */
	gboolean match = FALSE;
	gchar linebuf[128];
	FILE *f = fopen(filename, "r");
	if (f != NULL)
	{
		if (fgets(linebuf, sizeof(linebuf), f) != NULL)
			match = utils_str_equal(linebuf, "#!!PROXY_MAGIC!!\n");
		fclose(f);
	}
	return match ? GEANY_PROXY_MATCH : GEANY_PROXY_IGNORE;
}
@endcode

GeanyProxyFuncs::load is a bit more complex. It reads the file, fills the sub-plugin's PluginInfo
fields and calls GEANY_PLUGIN_REGISTER_FULL(). Additionally, it creates a per-plugin context that
holds GKeyFile instance (a poor man's interpreter context). You can also see that it does not call
GEANY_PLUGIN_REGISTER_FULL() if g_key_file_load_from_file() found an error (probably a syntax
problem) which means the sub-plugin cannot be enabled.

It also installs wrapper functions for the sub-plugin's GeanyPluginFuncs as ini files aren't code.
It's very likely that your proxy needs something similar because you can only install function
pointers to native code.

@code{.c}
typedef struct {
	GKeyFile       *file;
	gchar          *help_text;
	GSList         *menu_items;
}
PluginContext;


static gboolean proxy_init(GeanyPlugin *plugin, gpointer pdata);
static void proxy_help(GeanyPlugin *plugin, gpointer pdata);
static void proxy_cleanup(GeanyPlugin *plugin, gpointer pdata);


static gpointer demoproxy_load(GeanyPlugin *proxy, GeanyPlugin *plugin,
                               const gchar *filename, gpointer pdata)
{
	GKeyFile *file;
	gboolean result;

	file = g_key_file_new();
	result = g_key_file_load_from_file(file, filename, 0, NULL);

	if (result)
	{
		PluginContext *data = g_new0(PluginContext, 1);
		data->file = file;

		plugin->info->name = g_key_file_get_locale_string(data->file, "Info", "name", NULL, NULL);
		plugin->info->description = g_key_file_get_locale_string(data->file, "Info", "description", NULL, NULL);
		plugin->info->version = g_key_file_get_locale_string(data->file, "Info", "version", NULL, NULL);
		plugin->info->author = g_key_file_get_locale_string(data->file, "Info", "author", NULL, NULL);

		plugin->funcs->init = proxy_init;
		plugin->funcs->help = proxy_help;
		plugin->funcs->cleanup = proxy_cleanup;

		/* Cannot pass g_free as free_func be Geany calls it before unloading, and since
		 * demoproxy_unload() accesses the data this would be catastrophic */
		GEANY_PLUGIN_REGISTER_FULL(plugin, 225, data, NULL);
		return data;
	}

	g_key_file_free(file);
	return NULL;
}
@endcode

demoproxy_unload() simply releases all resources acquired in demoproxy_load(). It does not have to
do anything else in for unloading.

@code{.c}
static void demoproxy_unload(GeanyPlugin *proxy, GeanyPlugin *plugin, gpointer load_data, gpointer pdata)
{
	PluginContext *data = load_data;

	g_free((gchar *)plugin->info->name);
	g_free((gchar *)plugin->info->description);
	g_free((gchar *)plugin->info->version);
	g_free((gchar *)plugin->info->author);

	g_key_file_free(data->file);
	g_free(data);
}
@endcode

Finally the demo_proxy's wrapper GeanyPluginFuncs. They are called for each possible sub-plugin and
therefore have to multiplex between each using the plugin-defined data pointer. Each is called by
Geany as if it were an ordinary, native plugin.

proxy_init() actually reads the sub-plugin's file using GKeyFile APIs. It prepares for the help
dialog and installs the menu items. proxy_help() is called when the user clicks the help button in
the Plugin Manager. Consequently, this fires up a suitable dialog, although with a dummy message.
proxy_cleanup() frees all memory allocated in proxy_init().

@code{.c}
static gboolean proxy_init(GeanyPlugin *plugin, gpointer pdata)
{
	PluginContext *data;
	gint i = 0;
	gchar *text;

	data = (PluginContext *) pdata;

	/* Normally, you would instruct the VM/interpreter to call into the actual plugin. The
	 * plugin would be identified by pdata. Because there is no interpreter for
	 * .ini files we do it inline, as this is just a demo */
	data->help_text = g_key_file_get_locale_string(data->file, "Help", "text", NULL, NULL);
	while (TRUE)
	{
		GtkWidget *item;
		gchar *key = g_strdup_printf("item%d", i++);
		text = g_key_file_get_locale_string(data->file, "Init", key, NULL, NULL);
		g_free(key);

		if (!text)
			break;

		item = gtk_menu_item_new_with_label(text);
		gtk_widget_show(item);
		gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), item);
		gtk_widget_set_sensitive(item, FALSE);
		data->menu_items = g_slist_prepend(data->menu_items, (gpointer) item);
		g_free(text);
	}

	return TRUE;
}


static void proxy_help(GeanyPlugin *plugin, gpointer pdata)
{
	PluginContext *data;
	GtkWidget *dialog;

	data = (PluginContext *) pdata;

	dialog = gtk_message_dialog_new(
		GTK_WINDOW(plugin->geany_data->main_widgets->window),
		GTK_DIALOG_DESTROY_WITH_PARENT,
		GTK_MESSAGE_INFO,
		GTK_BUTTONS_OK,
		"%s", data->help_text);
	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
		_("(From the %s plugin)"), plugin->info->name);

	gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);
}


static void proxy_cleanup(GeanyPlugin *plugin, gpointer pdata)
{
	PluginContext *data = (PluginContext *) pdata;

	g_slist_free_full(data->menu_items, (GDestroyNotify) gtk_widget_destroy);
	g_free(data->help_text);
}
@endcode


*/