Geany  dev
Plugin 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++.

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

apt-get install libgtk2.0-dev intltool

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.

"Hello World"

When writing a plugin, you will find a couple of functions or macros which are mandatory and some which are free to use for implementing some useful feature once your plugin becomes more powerful like including a configuration or help dialog.

You should start your plugin with including some of the needed C header files and defining some basic global variables which will help you to access all needed functions of the plugin API in a more comfortable way.

Let's start with the very basic headers and add more later if necessary.

#include <geanyplugin.h>

geanyplugin.h includes all of the Geany API and also the necessary GTK header files, so there is no need to include gtk/gtk.h yourself.

Note
plugindata.h contains the biggest part of the plugin API and provides some basic macros. geanyfunctions.h provides some macros for convenient access to plugin API functions.

Then you should define three basic variables which will give access to data fields and functions provided by the plugin API.

Now you can go on and write your first lines for your new plugin. As mentioned before, you will need to implement and fill out a couple of functions/macros to make the plugin work. So let's start with PLUGIN_VERSION_CHECK().

PLUGIN_VERSION_CHECK() is a convenient way to tell Geany which version of Geany's plugin API is needed at minimum to run your plugin. The value is defined in plugindata.h by 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.

For the next step, you will need to tell Geany some basic information about your plugin which will be shown in the plugin manager dialog.

To do this you should use the PLUGIN_SET_INFO() macro, which expects 4 parameters:

Based on this, the line could look like:

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

Once this is done, you will need to implement the function which will be executed when the plugin is loaded. Part of that function could be adding and removing of an item to Geany's Tools menu, setting up keybindings or registering some callbacks. Also you will need to implement the function that is called when your plugin is unloaded. These functions are called plugin_init() and plugin_cleanup(). Let's see what this looks like:

PLUGIN_SET_INFO("HelloWorld", "Just another tool to say hello world",
"1.0", "Joe Doe <joe.doe@example.org>");
{
}
void plugin_cleanup(void)
{
}

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?

Note

If you would rather write the plugin in C++, you can do that by marking the plugin functions that it implements as extern "C", for example:

extern "C" void plugin_init(GeanyData *data)
{
}
extern "C" void plugin_cleanup(void)
{
}

Building

First make plugin.o:

gcc -c plugin.c -fPIC `pkg-config --cflags geany`

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

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

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

Note

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

Adding functionality

Let's go on and implement some real functionality.

As mentioned before, plugin_init() will be called when the plugin is loaded in 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".

{
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(geany->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);
}

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 geany->main_widgets->tools_menu. The structure main_widgets contains pointers to the main GUI elements in Geany.

Geany has a simple API for showing message dialogs. So our function contains only a few lines:

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

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(). gtk_widget_destroy() expects a pointer to a GtkWidget object.

First you should add gtk_widget_destroy() to your plugin_cleanup() function. The argument for gtk_widget_destroy() is the widget object you created earlier in plugin_init(). To be able to access this pointer in plugin_cleanup(), you need to move its definition from plugin_init() into the global context so its visibility will increase and it can be accessed in all functions.

static GtkWidget *main_menu_item = NULL;
// ...
{
main_menu_item = gtk_menu_item_new_with_mnemonic("Hello World");
gtk_widget_show(main_menu_item);
// ...
}
void plugin_cleanup(void)
{
gtk_widget_destroy(main_menu_item);
}

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!

Complete listing (without comments)

#include <geanyplugin.h>
PLUGIN_SET_INFO("HelloWorld", "Just another tool to say hello world",
"1.0", "John Doe <john.doe@example.org>");
static GtkWidget *main_menu_item = NULL;
static void item_activate_cb(GtkMenuItem *menuitem, gpointer gdata)
{
dialogs_show_msgbox(GTK_MESSAGE_INFO, "Hello World");
}
{
main_menu_item = gtk_menu_item_new_with_mnemonic("Hello World");
gtk_widget_show(main_menu_item);
gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu),
main_menu_item);
g_signal_connect(main_menu_item, "activate",
G_CALLBACK(item_activate_cb), NULL);
}
void plugin_cleanup(void)
{
gtk_widget_destroy(main_menu_item);
}

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

Furter Improvements and next steps

Translatable plugin information

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

By default, PLUGIN_SET_INFO() does not allow translation of the basic plugin information for plugins which are not shipped with Geany's core distribution. Since most plugins are not shipped with Geany's core, it makes sense to enable translation when the plugin is loaded so that it gets translated inside Geany's Plugin Manager. As of Geany 0.19, the plugin API contains the PLUGIN_SET_TRANSLATABLE_INFO() macro which enables translation of the basic plugin details passed to PLUGIN_SET_INFO() when the plugin is loaded.

PLUGIN_SET_TRANSLATABLE_INFO() takes two more parameters than PLUGIN_SET_INFO(), for a total of six parameters.

The Localdir and the Gettextpackage parameters are usually set inside the build system. If this has been done, the call for example HelloWorld plugin could look like:

LOCALEDIR, GETTEXT_PACKAGE, _("Hello World"),
_("Just another tool to say hello world"),
"1.0", "John Doe <john.doe@example.org>");

When using this macro, you should use the gettext macro _() to mark the strings like name and the short description as translatable as well. You can see how this is done in the above example.

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.

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 also be translatable as well. Geany offers a way to enable this in the plugin's code using the main_locale_init() function provided by the plugin API. This function takes the same two parameters discussed in the previous section; GETTEXT_PACKAGE and LOCALEDIR.

The main_locale_init() function is best called during initialization in the plugin's plugin_init() function. Adding this to the HelloWorld example could look like:

{
main_locale_init(LOCALEDIR, GETTEXT_PACKAGE);
main_menu_item = gtk_menu_item_new_with_mnemonic("Hello World");
gtk_widget_show(main_menu_item);
gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu),
main_menu_item);
g_signal_connect(main_menu_item, "activate",
G_CALLBACK(item_activate_cb), NULL);
}
Note
If you've previously called the PLUGIN_SET_TRANSLATABLE_INFO() you do not need to call main_locale_init() yourself, as this has been already been done for you.