Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Authors: Moore B
Published year: 2004
|< Day Day Up >|
This sections covers the basic framework classes and concept of the Graphical Editing Framework.
We assume that you already have good knowledge and experience in Eclipse plug-in development. You should have understand the concepts of Eclipse views and editors.
The following articles from eclipse.org are very useful for understanding terms and concepts mentioned in this chapter:
Eclipse Platform Technical Overview
Notes on the Eclipse Plug-in Architecture by Azad Bolour
How to Use the Eclipse API by Jim des Rivieres
Creating an Eclipse View by Dave Springgay
Getting Started with the Graphical Editing Framework by Randy Hudson
You should have installed Eclipse SDK 2.1.1 including GEF SDK 2.1.1, and you should be familiar with Draw2D concepts and terms provided by the developers guides, which are available in the Eclipse online help.
EditParts are the central elements in GEF applications. They are the controllers that specify how model elements are mapped to visual figures and how these figures behave in different situations.
Usually you will have to create an EditPart class for every model element class so you will have likely the same class hierarchy for the EditParts as you have for your model. The process of creating EditPart instances is not covered here. It will be explained in a later section.
EditParts are defined through the interface org.eclipse.gef.EditPart. See org.eclipse.gef.AbstractEditPart for an abstract base implementation of this interface. We strongly recommend (as do the GEF development team) that you do not implement the interface yourself. Instead, subclass the provided abstract base class AbstractEditPart.
In general, you should always subclass a provided base implementation rather then implementing the interface yourself. This protects you from unexpected API changes and reduces your work in case of API changes (for example, when new methods are added to the interface). It also helps your software to stay compatible with future versions.
Actually there are three different types of EditParts. For now, in this section, we will focus on only two of them: GraphicalEditParts and ConnectionEditParts. GraphicalEditParts are those EditParts that provide a graphical representation for their model. These graphical representations are figures. ConnectionEditParts represent connections between GraphicalEditParts.
The third type, TreeEditParts, is only interesting for building trees of your model with SWT tree widgets. This is not the primary intention of a graphical editor, but is probably useful for the outline view. Our redbook sample application will show you an introduction into this.
The EditPart interfaces provide a lot of methods. Your are not expected to call them, except for get/setModel, when necessary. Nearly all methods are used by the Graphical Editing Framework to handle the edit parts . But you can add your own methods, and we encourage you to add your EditPart implementations to ease your access to model elements and properties.
We already know that EditParts will be created somewhere and somehow by a factory (details about the factory will be explained later in 3.5.3, "Creating EditParts" on page 137). We will now focus on the methods involved in EditPart life-cycles.
When an EditPart was created, it is not yet visible or active. It becomes active when the Graphical Editing Framework gets informed about it. If an EditPart becomes obsolete for some reason (either the editor is closed or the model object represented by the EditPart was deleted) and the framework no longer needs it, it will be deactivated. There are two methods, EditPart#activate and EditPart#deactivate, which will be called by the framework when the state of an EditPart changes. A third method, EditPart#isActive, always returns the current activation state.
Although the JavaDoc of these methods indicates that an EditPart may be reactivated after it was obsolete, we have not experienced such situations during our development.
We suggest not to develop with the reuse of EditParts in mind. You should not try to keep track of EditPart instances across editor sessions. If an EditPart gets deactivated, throw it away. Allow it to get garbage collected by the virtual machine. By handling EditPart instances this way, you do not need to worry about the memory overhead. It will be solved for you and you can enjoy the advantages of Java.
EditPart#deactivate is a good point to release resources used by your EditPart (for example SWT images or fonts). We strongly suggest that you read the section about SWT resource management 4.2.6, "Resource management" on page 149.
GraphicalEditParts have a figure that is the visual part of the model. The GraphicalEditPart need to create the figure, update it on model changes, and dispose it (if necessary) if the EditPart is deactivated.
The figure is created by AbstractGraphicalEditPart#createFigure only once and will be cached by the abstract base implementation. Remember that you should always inherit from abstract base implementations if possible. AbstractGraphicalEditPart#createFigure is called when a figure is requested via AbstractGraphicalEditPart#getFigure for the first time.
Updating the figure is done in AbstractEditPart#refreshVisuals. You need to overwrite this method and update your figure according to the model changes you encountered . We will explain this later.
More about figures can be found in the Draw2D developers guide (see the Eclipse online help) and in 3.2.3, "Figures" on page 95 of this book.
A ConnectionEditPart, which represents a connection between two EditParts, is nothing more than a GraphicalEditPart, which has a source and a target EditPart. Connections are connected to ConnectionAnchors. These anchors should be provided by the EditParts the ConnectionEditPart points to/comes from.
The recommended way is that each GraphicalEditPart that could be a source or a target for connections implements the NodeEditPart interface. It is the most common way of how application models work. Connections usually points to some locations of the figure, and this figure is provided by a GraphicalEditPart.
Requests are the communication objects used in the Graphical Editing Framework. They contain information that might be necessary for executing the request later. There are several type of requests available. The three main types that are used most often in typical GEF applications, are CreateRequests, GroupRequests and LocationRequests.
Figure 3-9 shows the typical communication chain of a request and the objects involved. As you can see, someone (typically a tool, an action or some drag or drop handler) creates a request. This request is forwarded to an EditPart. The EditPart doesn't process the request itself. Instead it delegates it to an EditPolicy, which understands the request. The EditPolicy itself creates a command for the request, which will be executed to fulfill the request.
Figure 3-9: Communication chain Request — EditPart — Command
CreateRequests are used everywhere a new model object should be created. For connections the subclass CreateConnectionRequest is used. A CreateRequest has a CreationFactory, which you have to provide. This CreationFactory is responsible for creating the new model objects.
Actually, your CreationFactory implementation does not need to create new model objects. We suggest only submitting the type of the new model object and creating it later in a Command.
GroupRequests are Requests that can span more than one EditPart into one single request. A typical GroupRequest is the ChangeBoundsRequests, which is responsible for moving and/or resizing EditParts.
LocationRequests are requests that keep track of a location — for example, the SelectionRequest, which is responsible for selecting an EditPart. You can always determine where the user clicked into an EditPart to select it. This allows you to provide special behavior for different locations inside your EditPart.
We already know that the communication inside the Graphical Editing Framework is done via requests and that these requests are forwarded to EditPolicies. What are EditPolicies, and why is this done in this way?
Actually, EditPolicies are those parts in the Graphical Editing Framework, which bring the editing functionallity into EditParts, This is done because it is a good object-oriented design.
An EditPolicy defines what can be done with an EditPart. EditParts without EditPolicies will do nothing. They won't even be selectable. EditPolicies are also responsible for feedback management (for example, what should be shown when an EditPart is moved or resized) and they are allowed to delegate work (forward requests) to other EditParts (for example, children).
EditPolicies are categorized into roles (see constants in interface org.eclipse.gef.EditPolicy) and EditParts are limited to have only one EditPolicy per role.
The component role is defined as EditPolicy#COMPONENT_ROLE, and the base class for these kind of EditPolicies is ComponentEditPolicy.
This is the main role for all fundamental operations that involve the model element of an EditPart directly (for example, deletion of the model element). Whenever a request has nothing to do with UI interaction and only does something on the model element, it is best handled by a command delivered from a ComponentEditPolicy.
The connection role is defined as EditPolicy#CONNECTION_ROLE, and the base class is ConnectionEditPolicy. It is the corresponding component role for ConnectionEditParts.
The container role (EditPolicy#CONTAINER_ROLE, ContainerEditPolicy) is responsible for operations typically performed on containers (for example, the creation of children). Each EditPart with children would have a ContainerEditPolicy.
The layout role (EditPolicy#LAYOUT_ROLE, LayoutEditPolicy) is responsible for containers that have a layout associated to them. It can calculate proper locations for requests and define where children should be placed.
There is some overlap between ContainerEditPolicy and LayoutEditPolicy. ContainerEditPolicy is intended to be used in simple environments where it does not matter how children are placed. There is not any location information available inside ContainerEditPolicy.
The tree container role (EditPolicy#TREE_CONTAINER_ROLE, TreeContainerEditPolicy) is the corresponding container role for TreeEditParts.
The graphical node role (EditPolicy#GRAPHICAL_NODE_ROLE, GraphicalNodeEditPolicy) is used for establishing and managing connections on EditParts. Whenever your EditPart deals with connections, it will need a GraphicalNodeEditPolicy.
The direct edit role (EditPolicy#DIRECT_EDIT_ROLE, DirectEditPolicy) is used to bring direct editing behavior into EditParts. Thus, when the user double-clicks an EditPart, he will be able to directly edit properties on the figure.
More documentation about additional roles is available in the GEF developers guide available in the Eclipse online help.
A command is the part that actually modifies your model. Commands simplify the way of modifying your model because they provide support for:
Undo and redo
Combining and chaining
There is nothing more to say about commands than what can be found in the JavaDoc. You need to implement them and you need to instantiate them. The abstract base class is org.eclipse.gef.commands.Command.
From the Draw2D developers guide, we know that figures are drawn by a LightweightSystem. But that is not all. There are some more components involved, which we do not want to take care of when we are developing our editor. That is why the Graphical Editing Framework provides the GraphicalViewer.
A GraphicalViewer provides a seamless (JFace-like integration of EditParts into the Eclipse workbench.
Typically, a JFace viewer only needs some content, a factory, and some configuration, and it is done. It already provides all necessary implementation for drag and drop support, event and update handling, and other complicated tasks . A GEF GraphicalViewer does exactly the same.
There are two GraphicalViewer implementations available: one that does support native scrolling (ScrollingGraphicalViewer) and one that does not (GraphicalViewerImpl). The most common case is to use a viewer that supports native scrolling. It is even possible to have a ScrollingGraphicalViewer never showing its scrollbars. Thus, we will focus on this implementation.
A GraphicalViewer can be created out of the box. It has a parameter-less constructor and provides the method createControl to create the SWT control of this viewer. You do not even need an editor for this. A GraphicalViewer can be used anywhere an SWT control is available.
After the viewer is instantiated and the control is created, you need only to attach a RootEditPart (EditPartViewer#setRootEditPart) and an EditPartFactory (EditPartViewer#setEditPartFactory) to it, and set the content (EditPartViewer#setContents). The content is a model element that is the root of your model.
Please do not be confused by RootEditPart and the root of your model. Both are completely different and have no relation to each other.
A GraphicalViewer maintains a registry of all EditParts it contains. This map can be accessed by EditPartViewer#getEditPartRegistry. We are responsible for the key and the registration process, but the Graphical Editing Framework provides a default implementation, which automatically registers and unregisters EditParts using the model element they represent as key.
If you want to change the key mapping, you need to look at AbstractEditPart#registerModel and AbstractEditPart#unregisterModel.
A RootEditPart is a special kind of an EditPart. It has absolutely no relation to your model and should not be understood as a typical EditPart.
The task of a RootEditPart is to provide a suitable and homogeneous environment for the real EditParts that represents your model. Thus, it can be understood as an interface between a GraphicalViewer and your model EditParts.
There are several implementations available, but actually only two of them should be used — ScalableRootEditPart and ScalableFreeformRootEditPart. All other implementations are either deprecated or provide only a subset of functionality of the implementations mentioned above.
ScalableRootEditPart and ScalableFreeformRootEditPart can only be used inside a ScrollingGraphicalViewer.
Both implementations provide the possibility of scalability (zoom) support and introduce several layers to a GraphicalViewer. The only difference is that the ScalableFreeformRootEditPart can be extended in all directions, which enables negative coordinates.
Figure 3-10 gives an overview of the layers introduced by ScalableRootEditPart and ScalableFreeformRootEditPart.
Figure 3-10: Layers of ScalableRootEditPart and ScalableFreeformRootEditPart
Layers are used to separate and/or group figures of EditParts to better control their overlapping. Actually, all figures are placed into the primary layer. Figures representing connections are placed on the connection layer and so they are always painted above the other figures. Special figures (like drag or drop feedback or handles) are painted into special layers above the scalable layers. This is important because if you ever want to paint something in the feedback or handle layers, you must be aware that you need to scale this manually before painting.
When using the ScalableFreeformRootEditPart, your editor can extend in all directions. Thus, it is even possible to have negative coordinates. The (non-freeform) ScalableRootEditPart only allows extension in positive directions.
When using the ScalableFreeformRootEditPart, the EditPart of your root model object must use a figure of type FreeformFigure.
|< Day Day Up >|
Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Authors: Moore B
Published year: 2004