RSS

Search Engine

Monday, June 14, 2010

Working with Editors, View Interaction and Model Updates

9.1. Overview

In the following we create a project which demonstrate the usage of editors and its interaction with an view.

We will create a view which shows several person. Once the user double-clicks on the name an editor is opened in which the user can edit the address of this person.

The following data model makes the assumption that the last name of a person is unique. This assumption is of course not true in the real world.

9.2. Create project

Create a new RCP project "de.vogella.rcp.intro.editor" . Use the "RCP application with a view" as a template.

9.3. Create and prepare the domain model

Create a package "de.vogella.rcp.intro.editor.model". Create the following classes in this package.

    
package de.vogella.rcp.intro.editor.model;

public class Address {

private String street;
private String number;
private String postalCode;
private String city;
private String country;

public String getStreet() {
return street;
}

public void setStreet(String street) {
this.street = street;
}

public String getNumber() {
return number;
}

public void setNumber(String number) {
this.number = number;
}

public String getPostalCode() {
return postalCode;
}

public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public String getCountry() {
return country;
}

public void setCountry(String country) {
this.country = country;
}

public String toString() {
return street + " " + number + " " + postalCode + " " + city + " "
+ country;
}
}

    
package de.vogella.rcp.intro.editor.model;

public class Person {
private String firstName;
private String lastName;
private Address address;

public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

@Override
public String toString() {
return firstName + " " + lastName;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result
+ ((lastName == null) ? 0 : lastName.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
return true;
}

}

    
package de.vogella.rcp.intro.editor.model;

import java.util.ArrayList;
import java.util.List;

public class MyModel {

private List persons = new ArrayList();

public List getPersons() {
return persons;
}

public MyModel() {
// Just for testing we hard-code the persons here:
Person person = new Person("Lars", "Vogel");
person.setAddress(new Address());
person.getAddress().setCountry("Germany");
persons.add(person);
person = new Person("Jim", "Knopf");
person.setAddress(new Address());
person.getAddress().setCountry("Germany");
persons.add(person);
}
}

Tip

Please note that we override the equals (and the hashcode method). This allows us later to identify the right editor for each person.

9.4. Content and Label provider

Create a new package "de.vogella.rcp.intro.editor.provider"

Create a new class "MyContentProvider" which implements the interface IStructuredContentProvider.

Adjust the coding according to the following example:

    package de.vogella.rcp.intro.editor.provider;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.Viewer;

import de.vogella.rcp.intro.editor.model.MyModel;

public class MyContentProvider implements IStructuredContentProvider,
PropertyChangeListener {

private final Viewer viewer;

public MyContentProvider(Viewer viewer) {
this.viewer = viewer;
}

@Override
public Object[] getElements(Object inputElement) {
MyModel content = (MyModel) inputElement;
return content.getPersons().toArray();
}

@Override
public void dispose() {
}

@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}

@Override
public void propertyChange(PropertyChangeEvent arg0) {
viewer.refresh();
}

}

Create a new class "MyLabelProvider" which implements the interface ILabelProvider. Use the following code.

    
package de.vogella.rcp.intro.editor.provider;

import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;

import de.vogella.rcp.intro.editor.model.Person;

public class MyLabelProvider implements ILabelProvider {

@Override
public Image getImage(Object element) {
return PlatformUI.getWorkbench().getSharedImages().getImage(
ISharedImages.IMG_OBJ_ELEMENT);
}

@Override
public String getText(Object element) {
Person person = (Person) element;
return (person.getLastName());
}

@Override
public void addListener(ILabelProviderListener listener) {
}

@Override
public void dispose() {
}

@Override
public boolean isLabelProperty(Object element, String property) {
return false;
}

@Override
public void removeListener(ILabelProviderListener listener) {
}
}

9.5. Use the domain model in the view

Change the class View to use your new content providers.

    
package de.vogella.rcp.intro.editor;

import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;

import de.vogella.rcp.intro.editor.model.MyModel;
import de.vogella.rcp.intro.editor.provider.MyContentProvider;
import de.vogella.rcp.intro.editor.provider.MyLabelProvider;

public class View extends ViewPart {
public static final String ID = "de.vogella.rcp.intro.editor.view";

private TableViewer viewer;

/**
* This is a callback that will allow us to create the viewer and initialize
* it.
*/
public void createPartControl(Composite parent) {
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
| SWT.V_SCROLL);
viewer.setContentProvider(new MyContentProvider(viewer));
viewer.setLabelProvider(new MyLabelProvider());
viewer.setInput(new MyModel());
getSite().setSelectionProvider(viewer);
}

/**
* Passing the focus request to the viewer's control.
*/
public void setFocus() {
viewer.getControl().setFocus();
}
}

The view makes his viewer available as selection provider via the following line:" getSite().setSelectionProvider(viewer);". This make is possible for the command which opens the editor to get the selection of the view.

Tip

All workbench parts have a site, which can be accessed via the method getSite(). A site is a Facade which allows access to other parts of the workbench, e.g. the shell, the workbench window, etc. Whenever possible use the site to access Workbench objects.

Run your application. The view should now display the last names for your content provider. Currently nothing happens if you click on the names.

9.6. Editor area

Make the editor area visible by changing your Perspective.java.

    
package de.vogella.rcp.intro.editor;

import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPerspectiveFactory;

public class Perspective implements IPerspectiveFactory {

public void createInitialLayout(IPageLayout layout) {
String editorArea = layout.getEditorArea();
layout.setEditorAreaVisible(true);
layout.setFixed(true);
layout.addStandaloneView(View.ID, false, IPageLayout.LEFT, 1.0f,
editorArea);
}

}

9.7. Editor Input

In package "de.vogella.rcp.intro.editor.editors" create a new class "MyPersonEditorInput" which implements IEditorInput. IEditorInput serves as the model for the editor.

It is recommended to overwrite the method equals and hashcode in an implementation of IEditor. Based on the equals method the system will determine if the corresponding editor is already open or not.

Change the created code to the following.

    
package de.vogella.rcp.intro.editor.editors;

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPersistableElement;

import de.vogella.rcp.intro.editor.model.Person;

public class MyPersonEditorInput implements IEditorInput {

private final Person person;

public MyPersonEditorInput(Person person) {
this.person = person;
}

public Person getPerson() {
return person;
}

@Override
public boolean exists() {
return false;
}

@Override
public ImageDescriptor getImageDescriptor() {
return null;
}

@Override
public String getName() {
return person.toString();
}

@Override
public IPersistableElement getPersistable() {
return null;
}

@Override
public String getToolTipText() {
return person.toString();
}

@Override
public Object getAdapter(Class adapter) {
return null;
}

@Override
public boolean equals(Object obj) {
if (super.equals(obj)) {
return true;
}
if (obj instanceof MyPersonEditorInput) {
return person.equals(((MyPersonEditorInput) obj).getPerson());
}
return false;
}

@Override
public int hashCode() {
return person.hashCode();
}
}

9.8. Adding the editor

Go to plugin.xml and select the tab extensions. Add the extension org.eclipse.ui.editors. Do not use a template. Use the ID "de.vogella.rcp.intro.editor.editors.MyPersonEditor", any name you want and the class "de.vogella.rcp.intro.editor.editors.MyPersonEditor".

Tip

Make also sure to select an icon! Otherwise your editor will not work.

Click on the class hyperlink to create the class. Create the following class. Important is the class variable "ID" which we will later use to reference to this class. The variable ID must match the id defined in the editor extension.

    
package de.vogella.rcp.intro.editor.editors;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;

import de.vogella.rcp.intro.editor.model.Person;

public class MyPersonEditor extends EditorPart {
public static final String ID = "de.vogella.rcp.intro.editor.editors.MyPersonEditor";
private Person person;
private Text text2;

public MyPersonEditor() {
}

@Override
public void doSave(IProgressMonitor monitor) {
person.getAddress().setCountry(text2.getText());
}

@Override
public void doSaveAs() {
}

@Override
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
setSite(site);
setInput(input);
person = ((MyPersonEditorInput) input).getPerson();
setPartName(person.getFirstName());

}

@Override
public boolean isDirty() {
if (person.getAddress().getCountry().equals(text2.getText())) {
return false;
}
return true;
}

@Override
public boolean isSaveAsAllowed() {
return false;
}

@Override
public void createPartControl(Composite parent) {

GridLayout layout = new GridLayout();
layout.numColumns = 2;
parent.setLayout(layout);
Label label1 = new Label(parent, SWT.BORDER);
label1.setText("Person: ");
Label personName = new Label(parent, SWT.BORDER);
personName.setText(person.toString());
Label label2 = new Label(parent, SWT.BORDER);
label2.setText("Country");
text2 = new Text(parent, SWT.BORDER);
text2.setText(person.getAddress().getCountry());
}

@Override
public void setFocus() {
}

}

9.9. Creating a command for calling the editor

Create a command "de.vogella.rcp.intro.editor.callEditor" with the default handler "de.vogella.rcp.intro.editor.handler.CallEditor".

Create the following class "de.vogella.rcp.intro.editor.handler.CallEditor".

    
package de.vogella.rcp.intro.editor.handler;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.handlers.HandlerUtil;

import de.vogella.rcp.intro.editor.View;
import de.vogella.rcp.intro.editor.editors.MyPersonEditor;
import de.vogella.rcp.intro.editor.editors.MyPersonEditorInput;
import de.vogella.rcp.intro.editor.model.Person;

public class CallEditor extends AbstractHandler implements IHandler {

@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
// Get the view
IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
IWorkbenchPage page = window.getActivePage();
View view = (View) page.findView(View.ID);
// Get the selection
ISelection selection = view.getSite().getSelectionProvider()
.getSelection();
if (selection != null && selection instanceof IStructuredSelection) {
Object obj = ((IStructuredSelection) selection).getFirstElement();
// If we had a selection lets open the editor
if (obj != null) {
Person person = (Person) obj;
MyPersonEditorInput input = new MyPersonEditorInput(person);
try {
page.openEditor(input, MyPersonEditor.ID);

} catch (PartInitException e) {
System.out.println(e.getStackTrace());
}
}
}
return null;
}

}

9.10. Calling the editor

Add a double-click listener to your view which call the editor.

    
package de.vogella.rcp.intro.editor;

import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.part.ViewPart;

import de.vogella.rcp.intro.editor.provider.MyContentProvider;
import de.vogella.rcp.intro.editor.provider.MyLabelProvider;

public class View extends ViewPart {
public static final String ID = "de.vogella.rcp.intro.editor.view";

private TableViewer viewer;

/**
* This is a callback that will allow us to create the viewer and initialize
* it.
*/
public void createPartControl(Composite parent) {
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
| SWT.V_SCROLL);
viewer.setContentProvider(new MyContentProvider(viewer));
viewer.setLabelProvider(new MyLabelProvider());
viewer.setInput(new MyModel());
getSite().setSelectionProvider(viewer);
// New
hookDoubleClickCommand();
}

// New
private void hookDoubleClickCommand() {
viewer.addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
IHandlerService handlerService = (IHandlerService) getSite()
.getService(IHandlerService.class);
try {
handlerService.executeCommand(
"de.vogella.rcp.intro.editor.callEditor", null);
} catch (Exception ex) {
throw new RuntimeException(
"de.vogella.rcp.intro.editor.callEditor not found");
}
}
});
}

/**
* Passing the focus request to the viewer's control.
*/
public void setFocus() {
viewer.getControl().setFocus();
}
}

Run the application. If you double-click on an item in the view, the editor should open and the counry should get displayed. If you close the editor and re-open it, changes in the address should be displayed.

0 comments:

Post a Comment