Gedit New MDI plugin tutor.
-Muthiah Annamalai
Warning
Written for Gedit 2-new-mdi branch of CVS Please get if from cvs.gnome.org using
$export CVSROOT=:pserver:anonymous@anoncvs.gnome.org:/cvs/gnome $cvs update -r 'new_mdi' gedit $cvs update -r 'new_mdi' gedit
Gedit is a very simple text editor. Its architecture is centered around a single gedit-window, that is a document viewer, and helps view text, and provides controls.
We can add new functionality to Gedit, using plugins. For making a plugin we have to do the following. Note: $plugin refers to the name of your plugin. eg os, changecase
0: make a new folder called $plugin in /gedit/plugins/$plugin 1: write your source file in c. $plugin/$plugin.c write your $plugin.gedit-plugin.desktop.in write your $plugin.gedit-plugin 2: write a Makefile.am there. 3: Edit /gedit/configure.in, and add $plugin/Makefile in the AC_OUTPUT variable list. Edit /gedit/plugins/Makefile.am and add $plugin to DISTDIR SUBDIR variables. 4: compile gedit sources 5: install the module at $HOME/.gnome2/gedit/plugins/ This means the $plugin.gedit-plugin file, lib$plugin.la and the lib$plugin.so files. See the default plugins present at $prefix/lib/gedit-2/plugins/savecopy.gedit-plugin eg, /usr/lib/gedit-2/plugins/savecopy.gedit-plugin or /opt/lib/gedit-2/plugins/savecopy.gedit-plugin
The rest of this article is about step 1, writing your plugin.
New Plugin API
The plugin API is undergone a change in the new_MDI branch of gedit. This API uses a GObject as an underlying mechanism for plugin operation, and gives a more OO API, and lets us think in terms of Obejcts, overriding, inheritance etc.
Intro about GObject.
See the gnome developer documentation at http://developer.gnome.org/gobject/stable/index.html
Plugin Structure
Plugin structure is defined in gedit sources at /gedit/gedit-plugin.h and its made of a structure GeditPlugin which has the first element as the GObject.
/* * Main object structure */ typedef struct _GeditPlugin GeditPlugin; struct _GeditPlugin { GObject parent; };
If you lookinto the class structure, GeditPluginClass you see
/* * Class definition */ typedef struct _GeditPluginClass GeditPluginClass; struct _GeditPluginClass { GObjectClass parent_class; /* Virtual public methods */ void (*activate) (GeditPlugin *plugin, GeditWindow *window); void (*deactivate) (GeditPlugin *plugin, GeditWindow *window); void (*update_ui) (GeditPlugin *plugin, GeditWindow *window); GtkWidget *(*create_configure_dialog) (GeditPlugin *plugin); /* TODO: add place holders */ };
again, inheritance [substructures], and lots of pointer to functions which are in C++ terms, member functions .
If you know the GObject system, and API you will find that each GObject {and so also its derivative} have to be created, using g_object_new(TYPE_CODE,arg1,arg2,etc); where TYPE_CODE is the unique number for your $plugin_type when you register with the GType system.
Now g_object_new() calls the following functions in order, *
- g_$plugin_get_type()
- This function executes once, and registers, is a singleton style. Every other time its called, it can only return the earlier got type value.
- g_$plugin_class_init()
- This is called only once, to set/override static type members, and member functions.
- g_$plugin_instance_init()
- This is called everytime a g_object_new() is invoked on the $plugin type. Here you will setup your member variables, and values. This is C++ equivalent of a constructor.
Thus you have a brand new object (GObject) in your hands!
When the object's reference count drops to zero, we call g_$plugin_destroy().
g_$plugin_destroy()
- This function delete's the instance, like a C++ destructor. It works for every object, when its ref count falls to 0.
When the no object of $plugin types exits we call g_$plugin_finalize().
g_$plugin_finalize()
- If you ever had an oppurtunity to write a type of C++ runtime, or MONO, you will write class unloaders: but help yourself, this cleans up functions and static data allocations when a type is removed from the GType system. As you guessed, this is invoked only once. You cant cleanup plugins twice! Once you wipe them clean wheres all the dirt [footprint]?
Now you read the 'Public Methods' things given below, you get to see the pattern running into this GeditPlugin class.
/* * Public methods */ GType gedit_plugin_get_type (void) G_GNUC_CONST; void gedit_plugin_activate (GeditPlugin *plugin, GeditWindow *window); void gedit_plugin_deactivate (GeditPlugin *plugin, GeditWindow *window); void gedit_plugin_update_ui (GeditPlugin *plugin, GeditWindow *window); gboolean gedit_plugin_is_configurable (GeditPlugin *plugin); GtkWidget *gedit_plugin_create_configure_dialog
For more gyann[Hindi for enlightenment], you may read gedit-plugins-engine.h, on how gedit loads the plugin modules.
Now this gedit-plugins-engine loads the variables resembling
gchar Module*; gchar *IAge; gchar *Name; gchar *Desc; gchar *Author; gchar *Copyright; gchar *Website;
into the a structure, from your $plugin.gedit-plugin file which are used in Gedit preferences manager, to popup an About dialog for the plugin widget.
A typical $plugin.gedit-plugin file would look like this.
[Gedit Plugin] Module=sample IAge=2 _Name=User name _Description=Inserts the user name at the cursor position. Authors=Paolo Maggi <paolo@gnome.org> Copyright=Copyright © 2002-2005 Paolo Maggi Website=http://www.gedit.org
Now why this $plugin.gedit-plugin.desktop.in exists is, for makefile.in to convert on conditional basis.
The rest of the $pluginClass structure's members are given below. These, you will define in your Gedit $plugin.c file and do your programming within these methods.
void (*activate) (GeditPlugin *plugin, GeditWindow *window); void (*deactivate) (GeditPlugin *plugin, GeditWindow *window); void (*update_ui) (GeditPlugin *plugin, GeditWindow *window); GtkWidget *(*create_configure_dialog) (GeditPlugin *plugin);
Init
will be performed within your $plugin_class_init function, which is to setup your $plugin for the whole Gedit application. Some static data can be loaded into plugin->private_data field using the g_type_class_add_private (object_class, sizeof ($plugin_private)); and set its value in the $plugin_init() method passed to your
- g_type_register_static within the $plugin_get_type() function.
Generally all these details are hidden by your, GEDIT_PLUGIN_REGISTER_TYPE is
/* * Utility macro used to register plugins * * use: GEDIT_PLUGIN_REGISTER_TYPE(GeditSamplePlugin, gedit_sample_plugin) */ #define GEDIT_PLUGIN_REGISTER_TYPE(PluginName, plugin_name) \ \ static GType plugin_name##_type = 0; \ \ GType \ plugin_name##_get_type (void) \ { \ return plugin_name##_type; \ } \ \ static void plugin_name##_init (PluginName *self); \ static void plugin_name##_class_init (PluginName##Class *klass); \ static gpointer plugin_name##_parent_class = NULL; \ static void plugin_name##_class_intern_init (gpointer klass) \ { \ plugin_name##_parent_class = g_type_class_peek_parent (klass); \ plugin_name##_class_init ((PluginName##Class *) klass); \ } \ \ G_MODULE_EXPORT GType \ register_gedit_plugin (GTypeModule *module) \ { \ static const GTypeInfo our_info = \ { \ sizeof (PluginName##Class), \ NULL, /* base_init */ \ NULL, /* base_finalize */ \ (GClassInitFunc) plugin_name##_class_intern_init, \ NULL, \ NULL, /* class_data */ \ sizeof (PluginName), \ 0, /* n_preallocs */ \ (GInstanceInitFunc) plugin_name##_init \ }; \ \ gedit_debug_message (DEBUG_PLUGINS, "Registering " #PluginName); \ \ /* Initialise the i18n stuff */ \ bindtextdomain (GETTEXT_PACKAGE, GEDIT_LOCALEDIR); \ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); \ \ plugin_name##_type = g_type_module_register_type (module, \ GEDIT_TYPE_PLUGIN, \ #PluginName, \ &our_info, \ 0); \ return plugin_name##_type; \ } which fills the GTypeInfo structure of glib-$version/gobject/gtype.h struct _GTypeInfo { /* interface types, classed types, instantiated types */ guint16 class_size; GBaseInitFunc base_init; GBaseFinalizeFunc base_finalize; /* interface types, classed types, instantiated types */ GClassInitFunc class_init; GClassFinalizeFunc class_finalize; gconstpointer class_data; /* instantiated types */ guint16 instance_size; guint16 n_preallocs; GInstanceInitFunc instance_init; /* value handling */ const GTypeValueTable *value_table; };
and fills the functions of *
- $plugin_intern_init
- $plugin_class_init
Activate
is to add your plugin thing into the menubar of all gedit toplevel windows. It can be copy-paste'd from other plugins. You must replace the menu's "activate" signal callback to your plugin's function. This is the actual starting point of whatever functionality youre going to add to gedit, the rest all is plain boiler-plate code.
Deactivate
remove the menus from toplevel windows. It can also be copy-paste'd from other plugins.
create_configure_dialog
this is to give the users the choice of plugin functionality. The 'time' plugin in /gedit/plugin/time allows users to choose a time format they want. Its kinda cool. Instead of running main-loop like in earlier versions of Gedit plugins, we just need to set dialog callbacks, and set the plugin to react later. We need to return a pointer to a widget dialog.
Update UI
update ui is for enabling you plugin-menu-item to sensitive or insensitive, depending of doc is readonly or not. It can also be copy-paste'd from other plugins.
Destroy
Destroy is implemented in the $plugin_finalize() method. This is generally set in the $plugin_class_init() method by overriding the parent instances finalize handler. we may destroy the statically allocated priv->data and anyother
- plugin inited resources can be uninitialized.
We may a entry priv to our a custom structure, which inherits, the GeditPlugin's features, so you can load your plugin[instance] specific data, like this:
typedef struct _GeditSamplePlugin GeditSamplePlugin; struct _GeditSamplePlugin { GeditPlugin parent_instance; /*< private >*/ GeditSamplePluginPrivate *priv; }
Boiler Plate Generation
Thats it! Oops I think you mustve been demoralized by this story? No take heart, most of this code is boiler plate and Paolo Maggi, has written a script to generate a plugin-template, so you just need to save the files, and compile it.
Go to gedit/tools/ You see a tool called generate-plugin.py Run this from the command like like this,
$ ./generate-plugin.py Hello
For generating a 'Hello' plugin template, with UI menus etc. Now move this ./Hello folder to gedit/plugins/Hello and follow steps 0-5.
Well for more details dive into a full plugin like /gedit/plugins/sample and get started.
Muthiah Annamalai.
License
LICENSE: You may distribute this under GNU FDL.