Programming the Eclipse Workbench, Page 3
Editor's Object Model
While the editor's input is very simple — a list of unique strings — and could be represented by one of Java's collection classes, a feature is required that these classes don't provide — you want to be notified whenever the model changes (e.g., elements are added or removed). Thus, create a simple wrapper equipped with listener/event mechanism, which will allow any interested party to register themselves as a listener and be notified (via a custom event) whenever changes are made to the underlying list.
You would be correct in concluding that you could just as easily maintain an instance of ArrayList internal to the editor, provide access to it only via editor's public methods, and in those methods track any changes to the list. However, models accompanied by a listener/event notification mechanism are more flexible (e.g., may be used outside of an editor context) and their use is wide-spread throughout Eclipse.
Now, the content provider can be implemented, which both converts the object model into a list of string elements (suitable for displaying in the ListViewer) and updates the viewer whenever changes are detected in the model (this part is accomplished by registering the content provider as the model's listener whenever the input changes).
Finishing the Minimal Implementation
isSaveAsAllowedwill always be returned. Saving back to the original input, however, will be supported, and thus the method
doSave(IProgressMonitor)will be implemented. Its body is rather trivial — write the model's elements into a flat text file, each on its own line. However, one important protocol that you need to support here is the editor's "dirty" state; recall that an editor is in this state when it contains unpersisted data that will be lost when the application closes. Our editor implementation is required to maintain this state (and provide it via method
isDirty), and notify any interested listeners (via their
IPropertyListenerinterface) whenever this state changes. When a save is successful, the editor should no longer be dirty; thus, we should set our internal flag to
falseand fire a property change event (with ID
PROP_DIRTY) to indicate the change. The
doSavemethod may only be called when the editor is in fact dirty, and so we don't have to check if the state did in fact change.
Contributing Editor Actions
To implement the action bar contributor for the editor, you must go back to the Plug-in Manifest Editor and in the extension's details section, click the "contributorClass" label (which looks like a hyperlink). This class instantiates all actions in its constructor and contributes them to their respective action bars: the Add action is appended to the top-level Edit menu (since it already has similar Add actions), and the Delete, Undo, and Redo actions is set as "global action" handles. What this means is that whenever this contributor is active, it will provide its own implementations of the global Delete, Undo, and Redo actions defined by the Workbench and accessible in the Edit menu as well as through keyboard shortcuts.
Regardless of its particular function, each action class you implement
must be aware of its "target" editor instance; that is, the particular
editor with which it is to interact. The contributor is notified
whenever an editor instance becomes active in its
method; thus, you propagate this notification to each action instance and
allow it to respond accordingly.
Using Operation History
Now that you have the right infrastructure in place, you can implement
each action class. The Add action is the simplest — all it needs to do
is solicit input from the user (for example, using the JFace
InputDialog) and add it to the active editor's model, if appropriate.
Similarly, the Delete action needs to remove the elements currently
selected in the active editor. However, Delete should be disabled when
there are no selected elements. Thus, this action must be aware of the
active editor's selection changes. You can accomplish that by exposing
the editor's viewer selection via the
interface and registering the Delete action as a
on the active editor instance.
While you could implement the above actions to perform their functions directly on the active editor's model, you would run into a problem: how would Undo and Redo be implemented? These two actions don't provide any model-specific functionality; all they need to do is undo the last action, or redo the action that was last undone, whatever it may be. Ideally, they would be able to undo and redo multiple actions, not just one.
Because this is a frequently encountered problem in interactive
applications with editing capabilities, the Workbench provides
which is a service that allows the application to represent its user
invokable actions in a command-like pattern and use the
to execute, undo, and redo them. This service is rather flexible and I
won't go into much detail beyond what's necessary for this exercise. In
short, the functionality of Add and Delete is encapsulated in the
IUndoableOperation, which knows how to execute,
undo, and redo itself. The actions simply execute the operation in the
editor's undo context and allow the Undo and Redo actions to navigate
the operation history as required. As the last bit, you register the
editor itself as an operation history listener and mark it dirty
whenever there is an unsaved operation in its context.
Test-Running the Editor
To see the results, run the plug-in as an Eclipse Application. When the runtime workbench appears, switch to the Resource Perspective (though you can use any other perspective with a view that allows you to manipulate your Workspace). Create a new, simple project, and in it a simple file with extension ".list". This will cause a new, empty file to be created and opened in the List Editor we just developed. You should now be able to add elements using the "Add List Element..." action in the main Edit menu, and delete them (when selected) using the Del key. You can also save and re-open the modified files.
One oddity you may notice is that the Content Outline view, usually present in most perspectives by default, displays a message stating that the outline is not available. As its name indicates, this view is used to present a concise outline of the active editor's contents. In order to do this, the editor must provide a special adapter whose job it is to display an editor-specific outline. We did not develop one in this article, mostly because our content model cannot get any simpler than a list, and thus the outline itself would have to be nothing more than a list. For more information about this feature, see class
The Eclipse Workbench serves as a reusable foundation for developing desktop applications. It consists mainly of views and editors, commonly referred to as Workbench Parts. Views and editors expose their functionality as actions, which they contribute to their respective action bars. As an example, you were shown how to develop a simple editor for manipulating a list of elements, with support for global actions and undoable operations.
- Eclipse Help
- Inside the Workbench: A guide to the workbench internals
- Contributing Actions to the Eclipse Workbench
- The entire source code for the example developed in this article can be downloaded here.
About the Author
Peter Nehrer is an independent software consultant living in Toronto, Ontario. He specializes in Eclipse-based enterprise solutions and J2EE applications. His professional interests include development tools, model-driven software development, and information sharing. He is the author and contributor to several Eclipse-related Open Source projects. Peter is an IBM Certified Solutons Developer for XML and related technologies. He holds an M.S. in Computer Science from the University of Massachusetts at Amherst, MA.
Page 3 of 3