Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.
Step 1: Defining the editor
Create a new Plug-in Project called com.codeandme.multiparteditor.
I already showed how to reuse the XML source editor, so I will keep the editor definition part rather short: Define an editor and a content type in your plugin.xml. It should look like this afterwards:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.eclipse.ui.editors">
<editor
class="com.codeandme.multiparteditor.SampleEditor"
default="false"
id="com.codeandme.editor.sampleEditor"
name="Sample Editor">
<contentTypeBinding
contentTypeId="com.codeandme.contenttype.sample">
</contentTypeBinding>
</editor>
</extension>
<extension
point="org.eclipse.core.contenttype.contentTypes">
<content-type
base-type="org.eclipse.core.runtime.xml"
file-extensions="sample"
id="com.codeandme.contenttype.sample"
name="Sample File"
priority="normal">
</content-type>
</extension>
</plugin>
Switch to the Dependencies tab and add following dependencies:- org.eclipse.core.resources
- org.eclipse.core.runtime
- org.eclipse.text
- org.eclipse.ui
- org.eclipse.ui.editors
- org.eclipse.ui.forms
- org.eclipse.ui.ide
- org.eclipse.wst.sse.ui
- org.eclipse.wst.xml.core
Step 2: Implementing the editor
Create a new Class com.codeandme.multiparteditor.SampleEditor
package com.codeandme.multiparteditor;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.wst.sse.ui.StructuredTextEditor;
public class SampleEditor extends FormEditor {
private StructuredTextEditor fSourceEditor;
private int fSourceEditorIndex;
@Override
public void init(final IEditorSite site, final IEditorInput input) throws PartInitException {
super.init(site, input);
// TODO: load your model here
}
@Override
protected void addPages() {
fSourceEditor = new StructuredTextEditor();
fSourceEditor.setEditorPart(this);
try {
// add form pages
addPage(new FirstForm(this, "firstID", "First Page"));
// add source page
fSourceEditorIndex = addPage(fSourceEditor, getEditorInput());
setPageText(fSourceEditorIndex, "Source");
} catch (final PartInitException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void doSaveAs() {
// not allowed
}
@Override
public boolean isSaveAsAllowed() {
return false;
}
@Override
public void doSave(IProgressMonitor monitor) {
throw new RuntimeException("To be implemented");
}
}
That's all you need for your very first FormEditor.Step 3: Add form pages
Create additional FormPages and add them to your editor. This is best done using WindowBuilder by using the New Wizard: WindowBuilder/SWT Designer/Forms/FormPage.
There are lots of tutorials out there, see here, here, or here to get you going.
Step 4: Keeping your model consistent
As we have multiple parts within our editor that modify the same source, we need some synchronization mechanism. The source editor already operates on a Document and updates its content accordingly. So our FormPages could operate on the same Document but this would result in lots of XML parsing. There are great tools for this out there, however in this example we will use another approach:
The main editor will maintain a model and inform and update an EditorPart before it is activated. That model can import and export its state to XML, which we will use to synchronize model and source editor.
All form pages should modify the model directly. When the editor page is changed, the activated page will be notified to update itself regarding new model data.
Open the SampleEditor and add following code at the end of the addPages() method:
// add listener for changes of the document source
getDocument().addDocumentListener(new IDocumentListener() {
@Override
public void documentAboutToBeChanged(final DocumentEvent event) {
// nothing to do
}
@Override
public void documentChanged(final DocumentEvent event) {
fSourceDirty = true;
}
});
The listener will be notified whenever the source editor is editing the document. We use an internal flag to indicate that our form pages need an update. Additionally add/exchange following methods:
/** Keeps track of dirty code from source editor. */
private boolean fSourceDirty = false;
@Override
public void doSave(final IProgressMonitor monitor) {
if (getActivePage() != fSourceEditorIndex)
updateSourceFromModel();
fSourceEditor.doSave(monitor);
}
@Override
protected void pageChange(final int newPageIndex) {
// check for update from the source code
if ((getActivePage() == fSourceEditorIndex) && (fSourceDirty))
updateModelFromSource();
// check for updates to be propagated to the source code
if (newPageIndex == fSourceEditorIndex)
updateSourceFromModel();
// switch page
super.pageChange(newPageIndex);
// update page if needed
final IFormPage page = getActivePageInstance();
if (page != null) {
// TODO update form page with new model data
page.setFocus();
}
}
private void updateModelFromSource() {
// TODO update source code for source viewer using new model data
fSourceDirty = false;
}
private void updateSourceFromModel() {
// TODO update source page from model
// getDocument().set("new source code");
fSourceDirty = false;
}
private IDocument getDocument() {
final IDocumentProvider provider = fSourceEditor.getDocumentProvider();
return provider.getDocument(getEditorInput());
}
private IFile getFile() {
final IEditorInput input = getEditorInput();
if (input instanceof FileEditorInput)
return ((FileEditorInput) input).getFile();
return null;
}
private String getContent() {
return getDocument().get();
}
doSave() on line 5 uses the source page save routine. Therefore we need to make sure, the source code is up to date.
pageChange() on line 13 first checks if we need to either update the model from the source code or the source code from the model. This happens whenever we switch from/to the source view. After the page is switched we check whether the new page is a form page. If so, we need to update the form page (see line 28). This could be done by calling page.setFocus(); and overwrite setFocus() in the FormPage implementation.
Methods getDocument(), getFile(), getContent() are there for your convenience. You can delete the latter two if you don't need them.




