As with the previous chapter, a new TimeZoneTreeView
class will be created using the plugin.xml
editor, as an E4 view. This will show time zones, organized hierarchically by region.
com.packtpub.e4.clock.ui
project and navigate to Plug-in Tools | Open Manifest if it's not open already.org.eclipse.ui.views
entry. Right-click on this, navigate to New | e4view, and fill in the following:com.packtpub.e4.clock.ui.views.TimeZoneTreeView
Time Zone Tree View
com.packtpub.e4.clock.ui.views.TimeZoneTreeView
com.packtpub.e4.clock.ui
icons/sample.gif
plugin.xml
file that looks like:<e4view category="com.packtpub.e4.clock.ui" class="com.packtpub.e4.clock.ui.views.TimeZoneTreeView" icon="icons/sample.gif" id="com.packtpub.e4.clock.ui.views.TimeZoneTreeView" name="Time Zone Tree View" restorable="true"> </e4view>
org.eclipse.e4.ui.di
package, which adds the @Focus
annotation used by E4 views. The @Inject
and @PostConstruct
annotations are re-exported from the org.eclipse.ui
bundle.TimeZoneTreeView
in the com.packtpub.e4.clock.ui.views
package.create
method taking a Composite
parent and annotate it with @PostConstruct
. Inside the method, create an instance of a TreeViewer
, with the H_SCROLL
, V_SCROLL
, and MULTI
flags set, and store it in a field treeViewer
:package com.packtpub.e4.clock.ui.views; import javax.annotation.PostConstruct; public class TimeZoneTreeView { private TreeViewer treeViewer; @PostConstruct public void create(Composite parent) { treeViewer = new TreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI ); } }
TimeZoneLabelProvider
, which extends LabelProvider
(from the org.eclipse.jface.viewers
package). This has a method called getText
, which is passed an object and translates that into a textual representation. Instead of a toString
call here, return an appropriate value for a Map.Entry
or a ZoneId
:public class TimeZoneLabelProvider extends LabelProvider { @SuppressWarnings("rawtypes") public String getText(Object element) { if (element instanceof Map) { return "Time Zones"; } else if (element instanceof Map.Entry) { return ((Map.Entry) element).getKey().toString(); } else if (element instanceof ZoneId) { return ((ZoneId) element).getId().split("/")[1]; } else { return "Unknown type: " + element.getClass(); } } }
TreeViewer
can have multiple roots, the instanceof
Map
test is used to represent the top of the tree, called Time Zones.TimeZoneContentProvider
, which implements the ITreeContentProvider
interface. This requires the implementation of three of the six methods as follows, leaving the other three empty:hasChildren
: Returns true if the node has childrengetChildren
: Provides the children of a given nodegetElements
: Provides the top-level rootshasChildren
method will return true
if passed a non-empty Map
or Collection
; otherwise, recurse into the Map.Entry
value:@SuppressWarnings("rawtypes") public boolean hasChildren(Object element) { if (element instanceof Map) { return !((Map) element).isEmpty(); } else if (element instanceof Map.Entry) { return hasChildren(((Map.Entry)element).getValue()); } else if (element instanceof Collection) { return !((Collection) element).isEmpty(); } else { return false; } }
getChildren
implementation recurses into a Map
, Map.Entry
, or Collection
following the same pattern. Since the return of this function is an Object[]
, the entrySet
method in the Map
class can be used to convert the contents to an array:@SuppressWarnings("rawtypes") public Object[] getChildren(Object parentElement) { if (parentElement instanceof Map) { return ((Map) parentElement).entrySet().toArray(); } else if (parentElement instanceof Map.Entry) { return getChildren(((Map.Entry)parentElement).getValue()); } else if (parentElement instanceof Collection) { return ((Collection) parentElement).toArray(); } else { return new Object[0]; } }
The key to implementing an ITreeContentProvider
is to remember to keep the implementation of the getChildren
and hasChildren
methods in sync. One way of doing this is to implement the hasChildren
method as testing whether getChildren
returns an empty array, but this may not be performant if getChildren
is an expensive operation.
TreeViewer
can have multiple roots, there is a method to get the array of roots from the input element object. A bug in the JFace framework prevents the getElements
argument containing its own value; it is therefore conventional to pass in an array (containing a single element) and return it:public Object[] getElements(Object inputElement) { if (inputElement instanceof Object[]) { return (Object[]) inputElement; } else { return new Object[0]; } }
create
method of the TimeZoneTreeView
class:treeViewer.setLabelProvider(new TimeZoneLabelProvider()); treeViewer.setContentProvider(new TimeZoneContentProvider()); treeViewer.setInput(new Object[] {TimeZoneComparator.getTimeZones()});
focus
method with a @Focus
annotation, which sets the focus on the viewer's control
. The @Focus
annotation needs to be imported from the org.eclipse.e4.ui.di
package:import org.eclipse.e4.ui.di.Focus; ... @Focus public void focus() { treeViewer.getControl().setFocus(); }
The data for TreeViewer
was provided by the setInput
method, which is almost always an array of objects containing a single element.
To traverse the data structure, the ITreeContentProvider
interface provides two key methods: hasChildren
and getChildren
. These methods allow the data structure to be interrogated on demand as the user opens and closes nodes in the tree. The rationale for having two separate methods is that the calculation for getChildren
may be expensive; so the hasChildren
call is used to display the expandable icon on the node, but the getChildren
call is deferred until the user opens that specific node in the tree.
To render the labels in the tree, a LabelProvider
is used. This provides a label (and optional image) for each element. It is possible to present a different icon for each type of object; this is used by the Package View in the Java perspective to present a class icon for the classes, a package icon for the packages, and so on.
The LabelProvider
can render the text in different ways; for example, it could append the timezone
offset or only show the difference between that and GMT.
3.12.161.77