Multi-page Editor template
Default Multi-page Editor template behavior
By default, the Multi-page Editor template generates a trivial editor with three pages and a wizard for generating files associated with the editor. The editor enables text to be typed in a text editor on the first page (see Figure 26), previewed on the third in a read-only format with each word on a separate line and ordered alphabetically (see Figure 27), and the ability to control the font of the third page from the second (see Figure 28).
The first page of the Multi-page Editor shown in Figure 26 is a simple text editor. You can use it to type anything you want. The text can then be previewed on the third, or Preview, page shown in Figure 27.
Figure 26. First page of the Multi-page Editor
Notice in Figure 27, the sentence from Figure 26 has been reordered so the sentence is now in alphabetical order. In addition, each word is separated on its own line.
Figure 27. Third page of the Multi-page Editor
The second, or Properties, page shown in Figure 28 only has as a single property to set -- the font of the third or Preview page.
Figure 28. Second page of the Multi-page Editor
Default Multi-page Editor template behavior, continued
This Multi-page Editor is associated with a file extension of *.mpe, which stands for
Multi-page Editor. To create a new file of this type, the Multi-page Editor generates the Multi-page Editor file
wizard, shown in figures 29 and 30.
Figure 29 shows the new Multi-page Editor file wizard located in the Sample Wizards category.
Figure 29. Multi-page Editor File wizard
Figure 30 shows that the Multi-page editor collects two pieces of information. First is the Container, or Folder. The second is the name of the file. By default, the new file will contain the sentence in Figure 26.
Figure 30. Multi-page Editor File wizard
Run Multi-page Editor template behavior
To add the Multi-page Editor and Multi-page Editor File wizard to an existing project:
- Open the Plug-in Manifest Editor by double-clicking the plugin.xml file
- Select the Extensions tab at the bottom of the editor
- On the Extensions tab, click Add
- On the New Extension dialog, select Extension Wizards
- Select Extension Templates
- Select the Multi-page Editor template and click Next
- Click Finish to accept the defaults shown in Figure 31
Figure 31. Sample Multi-Page Editor
- Click Add again
- On the New Extension dialog, select Extension Wizards
- Select Extension Templates
- Select the New File Wizard template and click Next
- Click Finish to accept the defaults shown in Figure 32
Figure 32. New wizard options
Review Multi-page Editor template behavior results
The Multi-page Editor generates four Java files. Two are related to the editor and the other two are responsible
for the wizard. The two editor classes are the MultiPageEditor and the
MultiPageEditorContributor, which are the editor itself and the class responsible for managing
global actions for the editor. The two wizard classes are the SampleNewWizard and a page in that
wizard, SampleNewWizardPage.
The code for the MultiPageEditor class:
package com.ibm.tutorial.templates.editors;
import java.io.StringWriter;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FontDialog;
import org.eclipse.ui.*;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.ide.IDE;
/**
* An example showing how to create a multi-page editor.
* This example has 3 pages:
* <ul>
* <li>page 0 contains a nested text editor.
* <li>page 1 allows you to change the font used in page 2
* <li>page 2 shows the words in page 0 in sorted order
* </ul>
*/
public class MultiPageEditor
extends MultiPageEditorPart
implements IResourceChangeListener
{
/** The text editor used in page 0. */
private TextEditor editor;
/** The font chosen in page 1. */
private Font font;
/** The text widget used in page 2. */
private StyledText text;
/**
* Creates a multi-page editor example.
*/
public MultiPageEditor() {
super();
ResourcesPlugin.getWorkspace().
addResourceChangeListener(this);
}
/**
* Creates page 0 of the multi-page editor,
* which contains a text editor.
*/
void createPage0() {
try {
editor = new TextEditor();
int index = addPage(editor, getEditorInput());
setPageText(index, editor.getTitle());
} catch (PartInitException e) {
ErrorDialog.openError(
getSite().getShell(),
"Error creating nested text editor",
null,
e.getStatus());
}
}
/**
* Creates page 1 of the multi-page editor,
* which allows you to change the font used in page 2.
*/
void createPage1() {
Composite composite =
new Composite(getContainer(), SWT.NONE);
GridLayout layout = new GridLayout();
composite.setLayout(layout);
layout.numColumns = 2;
Button fontButton = new Button(composite, SWT.NONE);
GridData gd = new GridData(GridData.BEGINNING);
gd.horizontalSpan = 2;
fontButton.setLayoutData(gd);
fontButton.setText("Change Font...");
fontButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
setFont();
}
});
int index = addPage(composite);
setPageText(index, "Properties");
}
/**
* Creates page 2 of the multi-page editor,
* which shows the sorted text.
*/
void createPage2() {
Composite composite =
new Composite(getContainer(), SWT.NONE);
FillLayout layout = new FillLayout();
composite.setLayout(layout);
text = new StyledText(
composite, SWT.H_SCROLL | SWT.V_SCROLL);
text.setEditable(false);
int index = addPage(composite);
setPageText(index, "Preview");
}
/**
* Creates the pages of the multi-page editor.
*/
protected void createPages() {
createPage0();
createPage1();
createPage2();
}
/**
* The <code>MultiPageEditorPart</code> implementation of
* this <code>IWorkbenchPart</code> method disposes all
* nested editors.
* Subclasses may extend.
*/
public void dispose() {
ResourcesPlugin.getWorkspace().
removeResourceChangeListener(this);
super.dispose();
}
/**
* Saves the multi-page editor's document.
*/
public void doSave(IProgressMonitor monitor) {
getEditor(0).doSave(monitor);
}
/**
* Saves the multi-page editor's document as another
* file.
* Also updates the text for page 0's tab, and updates
* this multi-page editor's input
* to correspond to the nested editor's.
*/
public void doSaveAs() {
IEditorPart editor = getEditor(0);
editor.doSaveAs();
setPageText(0, editor.getTitle());
setInput(editor.getEditorInput());
}
/* (non-Javadoc)
* Method declared on IEditorPart
*/
public void gotoMarker(IMarker marker) {
setActivePage(0);
IDE.gotoMarker(getEditor(0), marker);
}
/**
* The <code>MultiPageEditorExample</code> implementation
* of this method
* checks that the input is an instance of
* <code>IFileEditorInput</code>.
*/
public void init(
IEditorSite site, IEditorInput editorInput)
throws PartInitException
{
if (!(editorInput instanceof IFileEditorInput))
throw new PartInitException(
"Invalid Input: Must be IFileEditorInput");
super.init(site, editorInput);
}
/* (non-Javadoc)
* Method declared on IEditorPart.
*/
public boolean isSaveAsAllowed() {
return true;
}
/**
* Calculates the contents of page 2 when the it is
* activated.
*/
protected void pageChange(int newPageIndex) {
super.pageChange(newPageIndex);
if (newPageIndex == 2) {
sortWords();
}
}
/**
* Closes all project files on project close.
*/
public void resourceChanged(
final IResourceChangeEvent event)
{
if(event.getType() == IResourceChangeEvent.PRE_CLOSE){
Display.getDefault().asyncExec(new Runnable(){
public void run(){
IWorkbenchPage[] pages =
getSite().getWorkbenchWindow().getPages();
for (int i = 0; i<pages.length; i++){
if(((FileEditorInput)editor.getEditorInput())
.getFile().getProject().equals(
event.getResource()))
{
IEditorPart editorPart =
pages[i].findEditor(
editor.getEditorInput());
pages[i].closeEditor(editorPart,true);
}
}
}
});
}
}
/**
* Sets the font related data to be applied to the text
* in page 2.
*/
void setFont() {
FontDialog fontDialog =
new FontDialog(getSite().getShell());
fontDialog.setFontList(text.getFont().getFontData());
FontData fontData = fontDialog.open();
if (fontData != null) {
if (font != null)
font.dispose();
font = new Font(text.getDisplay(), fontData);
text.setFont(font);
}
}
/**
* Sorts the words in page 0, and shows them in page 2.
*/
void sortWords() {
String editorText =
editor.getDocumentProvider().getDocument(
editor.getEditorInput()).get();
StringTokenizer tokenizer =
new StringTokenizer(editorText,
" \t\n\r\f!@#\u0024%^&*()-" +
"_=+`~[]{};:'\",.<>/?|\\");
ArrayList editorWords = new ArrayList();
while (tokenizer.hasMoreTokens()) {
editorWords.add(tokenizer.nextToken());
}
Collections.sort(editorWords, Collator.getInstance());
StringWriter displayText = new StringWriter();
for (int i = 0; i < editorWords.size(); i++) {
displayText.write(((String) editorWords.get(i)));
displayText.write(
System.getProperty("line.separator"));
}
text.setText(displayText.toString());
}
}
|
The code for the MultiPageEditorContributor.java class:
package com.ibm.tutorial.templates.editors;
import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.IDEActionFactory;
import org.eclipse.ui.part.MultiPageEditorActionBarContributor;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
/**
* Manages the installation/deinstallation of global
* actions for multi-page editors.
* Responsible for the redirection of global actions to
* the active editor.
* Multi-page contributor replaces the contributors for the
* individual editors in the multi-page editor.
*/
public class MultiPageEditorContributor
extends MultiPageEditorActionBarContributor
{
private IEditorPart activeEditorPart;
private Action sampleAction;
/**
* Creates a multi-page contributor.
*/
public MultiPageEditorContributor() {
super();
createActions();
}
/**
* Returns the action registed with the given
* text editor.
* @return IAction or null if editor is null.
*/
protected IAction getAction(
ITextEditor editor, String actionID)
{
return (editor == null ?
null : editor.getAction(actionID));
}
/* (non-Javadoc)
* Method declared in
* AbstractMultiPageEditorActionBarContributor.
*/
public void setActivePage(IEditorPart part) {
if (activeEditorPart == part)
return;
activeEditorPart = part;
IActionBars actionBars = getActionBars();
if (actionBars != null) {
ITextEditor editor = (part instanceof ITextEditor) ?
(ITextEditor) part : null;
actionBars.setGlobalActionHandler(
ActionFactory.DELETE.getId(),
getAction(
editor, ITextEditorActionConstants.DELETE));
actionBars.setGlobalActionHandler(
ActionFactory.UNDO.getId(),
getAction(
editor, ITextEditorActionConstants.UNDO));
actionBars.setGlobalActionHandler(
ActionFactory.REDO.getId(),
getAction(
editor, ITextEditorActionConstants.REDO));
actionBars.setGlobalActionHandler(
ActionFactory.CUT.getId(),
getAction(
editor, ITextEditorActionConstants.CUT));
actionBars.setGlobalActionHandler(
ActionFactory.COPY.getId(),
getAction(
editor, ITextEditorActionConstants.COPY));
actionBars.setGlobalActionHandler(
ActionFactory.PASTE.getId(),
getAction(
editor, ITextEditorActionConstants.PASTE));
actionBars.setGlobalActionHandler(
ActionFactory.SELECT_ALL.getId(),
getAction(
editor,
ITextEditorActionConstants.SELECT_ALL));
actionBars.setGlobalActionHandler(
ActionFactory.FIND.getId(),
getAction(
editor, ITextEditorActionConstants.FIND));
actionBars.setGlobalActionHandler(
IDEActionFactory.BOOKMARK.getId(),
getAction(
editor, IDEActionFactory.BOOKMARK.getId()));
actionBars.updateActionBars();
}
}
private void createActions() {
sampleAction = new Action() {
public void run() {
MessageDialog.openInformation(
null,
"Templates Plug-in",
"Sample Action Executed");
}
};
sampleAction.setText("Sample Action");
sampleAction.setToolTipText("Sample Action tool tip");
sampleAction.setImageDescriptor(
PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(
IDE.SharedImages.IMG_OBJS_TASK_TSK));
}
public void contributeToMenu(IMenuManager manager) {
IMenuManager menu = new MenuManager("Editor &Menu");
manager.prependToGroup(
IWorkbenchActionConstants.MB_ADDITIONS, menu);
menu.add(sampleAction);
}
public void contributeToToolBar(IToolBarManager manager)
{
manager.add(new Separator());
manager.add(sampleAction);
}
}
|
This code is a contributor, which demonstrates how to customize the menu and toolbar based on a particular editor
in the methods createActions, contributeToMenu, and contributeToToolBar. In
this example, it adds a Sample Action to the toolbar and to a new Editor Menu. It also demonstrates in
setActionPage and getAction methods how to disable global actions based on the editor's
state. In this example, global actions related to copy, paste, undo, etc. are disabled for the second and third
page of the wizard.
The code for the SampleNewWizard.javaclass:
package com.ibm.tutorial.templates.wizards;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.operation.*;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import java.io.*;
import org.eclipse.ui.*;
import org.eclipse.ui.ide.IDE;
/**
* This is a sample new wizard. Its role is to create a new
* file resource in the provided container. If the
* container resource (a folder or a project) is selected
* in the workspace when the wizard is opened, it will
* accept it as the target container. The wizard creates
* one file with the extension "mpe". If a sample multi-
* page editor (also available as a template) is registered
* for the same extension, it will be able to open it.
*/
public class SampleNewWizard extends Wizard
implements INewWizard
{
private SampleNewWizardPage page;
private ISelection selection;
/**
* Constructor for SampleNewWizard.
*/
public SampleNewWizard() {
super();
setNeedsProgressMonitor(true);
}
/**
* Adding the page to the wizard.
*/
public void addPages() {
page = new SampleNewWizardPage(selection);
addPage(page);
}
/**
* This method is called when 'Finish' button is pressed
* in the wizard. We will create an operation and run it
* using wizard as execution context.
*/
public boolean performFinish() {
final String containerName = page.getContainerName();
final String fileName = page.getFileName();
IRunnableWithProgress op = new IRunnableWithProgress()
{
public void run(IProgressMonitor monitor)
throws InvocationTargetException
{
try {
doFinish(containerName, fileName, monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
} finally {
monitor.done();
}
}
};
try {
getContainer().run(true, false, op);
} catch (InterruptedException e) {
return false;
} catch (InvocationTargetException e) {
Throwable realException = e.getTargetException();
MessageDialog.openError(
getShell(),
"Error", realException.getMessage());
return false;
}
return true;
}
/**
* The worker method. It will find the container, create
* the file if missing or just replace its contents, and
* open the editor on the newly created file.
*/
private void doFinish(
String containerName,
String fileName,
IProgressMonitor monitor)
throws CoreException
{
// create a sample file
monitor.beginTask("Creating " + fileName, 2);
IWorkspaceRoot root =
ResourcesPlugin.getWorkspace().getRoot();
IResource resource = root.findMember(
new Path(containerName));
if (!resource.exists() ||
!(resource instanceof IContainer))
{
throwCoreException("Container \"" +
containerName + "\" does not exist.");
}
IContainer container = (IContainer) resource;
final IFile file =
container.getFile(new Path(fileName));
try {
InputStream stream = openContentStream();
if (file.exists()) {
file.setContents(stream, true, true, monitor);
} else {
file.create(stream, true, monitor);
}
stream.close();
} catch (IOException e) {
}
monitor.worked(1);
monitor.setTaskName("Opening file for editing...");
getShell().getDisplay().asyncExec(new Runnable() {
public void run() {
IWorkbenchPage page =
PlatformUI.getWorkbench().
getActiveWorkbenchWindow().getActivePage();
try {
IDE.openEditor(page, file, true);
} catch (PartInitException e) {
}
}
});
monitor.worked(1);
}
/**
* We will initialize file contents with a sample text.
*/
private InputStream openContentStream() {
String contents =
"This is the initial file contents for *.mpe " +
"file that should be word-sorted in the " +
"Preview page of the multi-page editor";
return new ByteArrayInputStream(contents.getBytes());
}
private void throwCoreException(String message)
throws CoreException
{
IStatus status =
new Status(IStatus.ERROR,
"com.ibm.tutorial.templates",
IStatus.OK, message, null);
throw new CoreException(status);
}
/**
* We will accept the selection in the workbench to see
* if we can initialize from it.
* @see IWorkbenchWizard#init(
* IWorkbench, IStructuredSelection)
*/
public void init(IWorkbench workbench,
IStructuredSelection selection)
{
this.selection = selection;
}
}
|
The wizard in this code is a standard wizard that displays pages, then generates code in the
doFinish method. What is most interesting about this class is that in performFinish,
it demonstrates how to interact with Eclipse's process monitor for tasks that take a long time.
SampleNewWizardPage.java class
The code for the SampleNewWizardPage.java class:
package com.ibm.tutorial.templates.wizards;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.Path;
import org.eclipse.swt.events.*;
import org.eclipse.ui.dialogs.ContainerSelectionDialog;
import org.eclipse.jface.viewers.*;
/**
* The "New" wizard page allows setting the container for
* the new file as well as the file name. The page
* will only accept file name without the extension OR
* with the extension that matches the expected one (mpe).
*/
public class SampleNewWizardPage extends WizardPage {
private Text containerText;
private Text fileText;
private ISelection selection;
/**
* Constructor for SampleNewWizardPage.
* @param pageName
*/
public SampleNewWizardPage(ISelection selection) {
super("wizardPage");
setTitle("Multi-page Editor File");
setDescription("This wizard creates a new file with " +
"*.mpe extension that can be opened by a " +
"multi-page editor.");
this.selection = selection;
}
/**
* @see IDialogPage#createControl(Composite)
*/
public void createControl(Composite parent) {
Composite container = new Composite(parent, SWT.NULL);
GridLayout layout = new GridLayout();
container.setLayout(layout);
layout.numColumns = 3;
layout.verticalSpacing = 9;
Label label = new Label(container, SWT.NULL);
label.setText("&Container:");
containerText =
new Text(container, SWT.BORDER | SWT.SINGLE);
GridData gd =
new GridData(GridData.FILL_HORIZONTAL);
containerText.setLayoutData(gd);
containerText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
dialogChanged();
}
});
Button button = new Button(container, SWT.PUSH);
button.setText("Browse...");
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
handleBrowse();
}
});
label = new Label(container, SWT.NULL);
label.setText("&File name:");
fileText =
new Text(container, SWT.BORDER | SWT.SINGLE);
gd = new GridData(GridData.FILL_HORIZONTAL);
fileText.setLayoutData(gd);
fileText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
dialogChanged();
}
});
initialize();
dialogChanged();
setControl(container);
}
/**
* Tests if the current workbench selection is a suitable
* container to use.
*/
private void initialize() {
if (selection!=null
&& selection.isEmpty()==false
&& selection instanceof IStructuredSelection)
{
IStructuredSelection ssel =
(IStructuredSelection)selection;
if (ssel.size()>1) return;
Object obj = ssel.getFirstElement();
if (obj instanceof IResource) {
IContainer container;
if (obj instanceof IContainer)
container = (IContainer)obj;
else
container = ((IResource)obj).getParent();
containerText.setText(
container.getFullPath().toString());
}
}
fileText.setText("new_file.mpe");
}
/**
* Uses the standard container selection dialog to
* choose the new value for the container field.
*/
private void handleBrowse() {
ContainerSelectionDialog dialog =
new ContainerSelectionDialog(
getShell(),
ResourcesPlugin.getWorkspace().getRoot(),
false,
"Select new file container");
if (dialog.open() == ContainerSelectionDialog.OK) {
Object[] result = dialog.getResult();
if (result.length == 1) {
containerText.setText(
((Path)result[0]).toOSString());
}
}
}
/**
* Ensures that both text fields are set.
*/
private void dialogChanged() {
String container = getContainerName();
String fileName = getFileName();
if (container.length() == 0) {
updateStatus("File container must be specified");
return;
}
if (fileName.length() == 0) {
updateStatus("File name must be specified");
return;
}
int dotLoc = fileName.lastIndexOf('.');
if (dotLoc != -1) {
String ext = fileName.substring(dotLoc + 1);
if (ext.equalsIgnoreCase("mpe") == false) {
updateStatus("File extension must be \"mpe\"");
return;
}
}
updateStatus(null);
}
private void updateStatus(String message) {
setErrorMessage(message);
setPageComplete(message == null);
}
public String getContainerName() {
return containerText.getText();
}
public String getFileName() {
return fileText.getText();
}
}
|
This code is a simple SWT wizard page. The most interesting characteristic of this class is in the
handleBrowse method. This method uses Eclipse's ContainerSelectionDialog for displaying the folders in the workspace.

