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:

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:

  1. 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
  2. Message Composer
    • an action into the File menu and a copy of it on the toolbar
  3. 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:

  1. unzip the file somewhere
  2. enter the example-module/ directory
  3. create the build directory and enter it: $ mkdir _build && cd _build

  4. configure the project with cmake: $ cmake -G "Unix Makefiles" ..

  5. 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:

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.

Apps/Evolution/Extensions (last edited 2024-03-07 12:44:43 by MilanCrha)