Extending Omea with New Resource Types

Registering Actions

Before we can proceed to implement any functionality for processing books, we need to be able to create Book resources. We could start by writing some code that will create book instances for testing, but in order to keep the sample shorter, we will develop the real user interface for creating books right away.

First of all, we need to create the class that will implement the book creation action. Invoking the action will open a nonmodal window where the user can enter the details of the book and click "Save" to complete the book creation process. The action does not depend on the selected resources or any other context, so we can make it available all the time.

All action classes need to implement the IAction interface, with two methods - Execute, which performs the action, and Update, which updates the state of the action. Because our action is always enabled, we don't need to do anything in the Update method. Therefore, we can inherit from SimpleAction, which provides a default empty implementation of Update, and only implement the Execute method. We will place the stub of the action implementation in the NewBookAction class.

Omea plugin new book

Before proceeding to implement the action, let's register it so that we can see it in the Omea user interface. The "File / New" submenu of the main menu contains several commands for creating new resources, so this looks like a right place for our action.

The placement of menu items, submenus and separators in the main menu is defined by action groups. An action group is a sequence of related actions which can be delimited from other action groups by a separator or placed in a submenu. The relative position of action groups, and of actions within an action group, is specified by list anchors. A list anchor (an instance of the ListAnchor class) specifies that an action or group should be placed first in the list (ListAnchor.First), last in the list (ListAnchor.Last), before or after another action or group.

We will add our action to the FileNewActions group, which is one of the action groups comprising the File / New submenu. We don't need any special positioning for the action, so we'll go with ListAnchor.Last.

If you run the plugin now, the "Book..." action will appear under "File / New...", but nothing will happen when you click it.

Creating and Editing Resources

Omea plugin edit book

To implement the book creation action, we will use the resource editing framework provided by Omea. The interface we will get as a result will be very similar to the interfaces for editing contacts and tasks in the standard Omea.

The standard resource editing window consists of two main parts: the frame with buttons and the validation label, provided by Omea, and the edit pane, which implements the resource type-specific editing interface. The edit pane is a UserControl derived from the AbstractEditPane base class. The frame is created when the resource to edit and the instance of the pane are passed to Core.UIManager.OpenResourceEditWindow().

For common tasks, only two methods of AbstractEditPane need to be overridden. The method EditResource() shows the values of properties of the specified resource in the user interface controls. The method Save() saves the data from the controls to the resource properties.

An important point for development of resource edit windows is the requirement to perform all operations which create or modify resources in the resource thread. This allows you to serialize resource modification operations and greatly reduces the possibility of deadlocks and modification conflicts. The simplest way to marshal a resource write operation to the correct thread is to use the ResourceProxy class. An instance of the class is created in any thread, either attached to an existing resource or marked to create a new resource of the specified type. Then it accumulates the changes that need to be performed to the resource. The modifications are executed in the resource thread either synchronously (if the EndUpdate() method is used) or asynchronously (EndUpdateAsync()).

However, if you study the example code closely, you will note that NewBookAction creates a new resource directly from the main (user interface) thread. This is actually not an error, because the method used to create the resource is NewResourceTransient(). That method creates a transient resource - a resource which exists only in the memory. If the resource needs to be actually saved (and not simply discarded for some reason), saving of the resource needs to be performed in the resource thread. This is done by running the IResource.EndUpdate method as a job in the resource thread, through Core.ResourceAP.RunJob. (The details of that can be found in the help file.)

In order to keep the user interface simple, we will implement editing the author list as a simple text string, containing a list of author names separated with commas. When the edit form is opened, we get all the links from a book to its authors and concatenate their names into a string which is shown in the edit box. The saving process is harder: we need to create the actual Contact resources to represent the authors and link them to the book resource. Thus, saving the book data will require modification of more than one resource, and it will be easier not to use ResourceProxy but to run the entire saving operation as a single method in the resource thread.

To create the contact resources, we use the system-provided IContactManager interface. It contains fairly complicated logic for parsing a single contact name string into components and for finding duplicate contacts, so we can simply give it a text string, and it will return an IContact (a wrapper around a contact resource) containing either a new contact or an existing contact with the same name. The only thing that remains for us to do is link the returned contact to the book resource.

Page « 1 2 3 4 5 »
Dmitry's photo

Dmitry Jemerov

The youngest of JetBrains project leads, Dmitry got his job after working on Syndirella, an open-source RSS reader. Dmitry always carries with him a Tablet PC with Omea running, and is always eager to demo it to anyone willing to listen. When not at work, he sometimes finds the time to ride his bike, play his bass and meet his friends for a role-playing game.

Contact Dmitry via email: dmitry(.)jemerov
(at) jetbrains.com