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.

Apps/Gedit/Attic/NewMDIPluginHowTo (last edited 2020-04-14 14:29:17 by SébastienWilmet)