d54b65b9ac
The documentation provides a quite detailed description of the new loader In addition it adds a "how to transition" that briefly describes the old loader (for curious newcomers) and lots of hints for porting legacy plugins to the new loader.
725 lines
30 KiB
Plaintext
725 lines
30 KiB
Plaintext
/*
|
|
* plugins.dox - this file is part of Geany, a fast and lightweight IDE
|
|
*
|
|
* Copyright 2008-2011 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
|
|
* Copyright 2008-2011 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
|
|
* Copyright 2009-2012 Frank Lanitz <frank(at)frank(dot)uvena(dot)de>
|
|
* Copyright 2014 Matthew Brush <matt(at)geany(dot)org>
|
|
* Copyright 2015 Thomas Martitz <kugel(at)rockbox(dot)org>
|
|
*
|
|
* 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.
|
|
|
|
@section pluginsupport Plugin Support
|
|
- @link howto Plugin HowTo @endlink - get started
|
|
- @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 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
|
|
http://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 where
|
|
to discuss development related questions.
|
|
However, once you have done your fork of geany-plugins you can develop
|
|
your plugin until you think its 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 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 http://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 fo 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(), 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.
|
|
4. Optionally setting user data that is passed to GeanyPlugin::funcs with geany_plugin_set_data().
|
|
Here you can set a data pointer that is passed back to your functions called by Geany. This
|
|
allows to avoid global variables which may be useful. It also allows 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 call this function later on, for example in your @ref GeanyPluginFuncs::init
|
|
routine to defer costly allocations to when the plugin is actually activated by the user.
|
|
|
|
|
|
@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! */
|
|
if (GEANY_PLUGIN_REGISTER(plugin, 225))
|
|
return;
|
|
|
|
/* Step 4: call geany_plugin_set_data(), not done here
|
|
geany_plugin_set_data(plugin, 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_set_data()). 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 it's 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 implicitely 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 eqivalents 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
|
|
succesfully. 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
|
|
|
|
|
|
*/
|