This page moved to GitLab Wiki space
Contents
Evolution 2.32 introduces a new framework for extending individual Evolution object classes. It allows finer-grained control and far more flexibility than EPlugin hooks. The extension API is very simple and integrates well with GTypeModule.
Update:
- The base classes for the extension framework now live in libebackend. The examples below now reflect that.
See the API documentation for EExtensible and EExtension.
Making Classes Extensible
An Evolution object or widget class can be made extensible in two steps:
1. Add the EExtensible interface when registering the class type. There are no methods to implement; the interface basically acts like a tag for the class.
#include <libebackend/libebackend.h> G_DEFINE_TYPE_WITH_CODE ( ECustomWidget, e_custom_widget, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
2. Load extensions for the class at some point during instance initialization. Generally this should be done toward the end of the initialization code, so extensions get a fully initialized instance to work with.
static void e_custom_widget_constructed (GObject *object) { /* Initialization code goes here... */ e_extensible_load_extensions (E_EXTENSIBLE (object)); }
Writing Extensions
Writing a basic Evolution extension involves three steps:
1. Subclass EExtension. See the "hello world" extension below for a complete example.
2. In the class initialization function, specify the GType being extended. The GType must be a GObjectClass that implements the EExtensible interface as described above.
3. Register the extension's own GType. If the extension is to be loaded dynamically using GTypeModule, the type should be registered in the library module's e_module_load() function.
Example
The following is an example of a dynamically loaded extension for the EShell class. It prints a message when an EShell instance is created, and also when the EShell instance is finalized.
#include <shell/e-shell.h> #include <libebackend/libebackend.h> typedef struct _EHelloWorld EHelloWorld; typedef struct _EHelloWorldClass EHelloWorldClass; struct _EHelloWorld { EExtension parent; }; struct _EHelloWorldClass { EExtensionClass parent_class; }; /* Module Entry Points */ void e_module_load (GTypeModule *type_module); void e_module_unload (GTypeModule *type_module); /* Forward Declarations */ GType e_hello_world_get_type (void); G_DEFINE_DYNAMIC_TYPE (EHelloWorld, e_hello_world, E_TYPE_EXTENSION) static void e_hello_world_constructed (GObject *object) { EExtensible *extensible; /* This retrieves the EShell instance we're extending. */ extensible = e_extension_get_extensible (E_EXTENSION (object)); /* This prints "Hello world from EShell!" */ g_print ("Hello world from %s!\n", G_OBJECT_TYPE_NAME (extensible)); } static void e_hello_world_finalize (GObject *object) { g_print ("Goodbye cruel world!\n"); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_hello_world_parent_class)->finalize (object); } static void e_hello_world_class_init (EHelloWorldClass *class) { GObjectClass *object_class; EExtensionClass *extension_class; object_class = G_OBJECT_CLASS (class); object_class->constructed = e_hello_world_constructed; object_class->finalize = e_hello_world_finalize; /* Specify the GType of the class we're extending. * The class must implement the EExtensible interface. */ extension_class = E_EXTENSION_CLASS (class); extension_class->extensible_type = E_TYPE_SHELL; } static void e_hello_world_class_finalize (EHelloWorldClass *class) { /* This function is usually left empty. */ } static void e_hello_world_init (EHelloWorld *extension) { /* The EShell object we're extending is not available yet, * but we could still do some early initialization here. */ } G_MODULE_EXPORT void e_module_load (GTypeModule *type_module) { /* This registers our EShell extension class with the GObject * type system. An instance of our extension class is paired * with each instance of the class we're extending. */ e_hello_world_register_type (type_module); } G_MODULE_EXPORT void e_module_unload (GTypeModule *type_module) { /* This function is usually left empty. */ }
Extensible Evolution Classes
The following classes are extensible in Evolution 3.10. More to follow.
CompEditor
EAttachmentIconView (API Reference)
EAttachmentTreeView (API Reference)
ECalendarItem
ECalendarView
ECalModel
EClientCache (API Reference)
EDateEdit (API Reference)
EImportAssistant (API Reference)
EMailAccountStore
EMailBrowser
EMailConfigAssistant
EMailConfigConfirmPage
EMailConfigDefaultsPage
EMailConfigIdentityPage
EMailConfigNotebook
EMailConfigProviderPage
EMailConfigSecurityPage
EMailConfigServicePage
EMailConfigSummaryPage
EMailConfigWelcomePage
EMailExtensionRegistry (API Reference)
EMailFormatter (API Reference)
EMailPanedView
EMailPart (API Reference)
EMailSession
EMailUISession
EMeetingStore
EMeetingTimeSelector
EMsgComposer (API Reference)
EShell (API Reference)
EShellContent (API Reference)
EShellSearchbar (API Reference)
EShellSidebar (API Reference)
EShellSwitcher (API Reference)
EShellTaskbar (API Reference)
EShellView (API Reference)
EShellWindow (API Reference)
ESourceConfig (API Reference)
ESpellEntry (API Reference)
ETreeViewFrame (API Reference)
EWebView (API Reference)
EWeekdayChooser
Example Module
The attached example-module.zip contains a build-able source code to create a new module which extends three parts of the Evolution:
- Mail view
- an action in the context menu of the folder list, which is available only when a maildir folder is selected, like the one under On This Computer
- an action in the Message menu, which requires at least one message to be selected
- Message Composer
- an action into the File menu and a copy of it on the toolbar
- Calendar view
- an action into the context menu of a selected event
- an action into the Actions menu
The actions print an example data it can gather in respective contexts.
The project can be built using cmake like this:
- unzip the file somewhere
- enter the example-module/ directory
create the build directory and enter it: $ mkdir _build && cd _build
configure the project with cmake: $ cmake -G "Unix Makefiles" ..
build and install the project: $ make && make install
Make sure you've installed development packages for the evolution-data-server and evolution. The project requires evolution 3.36.0 (see the root CMakeLists.txt REQUIRE_EVOLUTION_VERSION variable), but it can be built with older versions too, with some changes in the composer code and libecal requirement.
Since Evolution 3.40.0 the module can be used with system-installed Evolution, without a need to have write access to the system directory. To build it use:
$ mkdir _build && cd _build $ cmake -DCMAKE_INSTALL_PREFIX=~/.local/share/evolution/modules \ -DFORCE_INSTALL_PREFIX=ON .. $ make && make install
then restart Evolution and it'll load also this module.
Preview and WebKit Editor plugins
Since 3.42.2 Evolution reads plugins in JavaScript for the preview from $PREFIX/share/evolution/webkit/preview-plugins/ and from ~/.local/share/evolution/preview-plugins/, similarly for the WebKit editor the plugins can be saved into $PREFIX/share/evolution/webkit/webkit-editor-plugins/ and ~/.local/share/evolution/webkit-editor-plugins/.
The plugin is an object, which contains two members:
name - a string with a name of the plugin
setup(doc) - a setup function
The argument of the setup function is a document, which can be either the main document or an iframe document. The function can be called multiple times for the same document. If the plugin is changing an event callback, then make sure it also calls the one being set there already, to have the things working properly.
An example plugin, which colors all links with an orange color in the preview panel looks like this:
'use strict'; var orgGnomeEvolutionTestPlugin = { name : "orgGnomeEvolutionTestPlugin", setup : function(doc) { var anchors, ii; anchors = doc.getElementsByTagName("A"); for (ii = 0; ii < anchors.length; ii++) { anchors[ii].style = "color: orange;"; } } }; Evo.RegisterPlugin(orgGnomeEvolutionTestPlugin);
Save it for example as ~/.local/share/evolution/preview-plugins/org.gnome.Evolution.test.js and restart Evolution.
Copy the file into ~/.local/share/evolution/webkit-editor-plugins/org.gnome.Evolution.test.js and change the last line from Evo.RegisterPlugin(orgGnomeEvolutionTestPlugin); to EvoEditor.RegisterPlugin(orgGnomeEvolutionTestPlugin); (the only change is to call the RegisterPlugin on an EvoEditor object for the WebKit editor, instead of the Evo object for the preview panel). Any link existing after the composer open in the message body will be colored orange.