Developer Guidelines
- Developer Guidelines
Introduction
These notes are designed to detail best practice in using the various APIs available in Dawn as a programmer. With Dawn one can either add new features using python scripts with pydev, using workflows or with Java in the form of osgi plugins. The python API and workflows are user level since any user may extend Dawn this way without changing Dawn. The Java programmer will normally need to contribute to the github repos and will need to rebuild Dawn before the feature is available in Dawn or will be using Dawn APIs in their own project, such as GDAfor instance. This guide is designed for Java programmers and explains some of the APIs available and when to use them.
If you are familiar with eclipse and buckminster, this quick start guide should get you up and running with developing DAWN. In order to push your changes, please complete a ‘Pull Request’ on github. There are four simple points to follow to get up and running:
1. Eclipse and Buckminster
If not already installed you will need the JDK installed. You will need to install a version of eclipse for ‘RCP and RAP developers’
and also add the Buckminster update site, something
like: http://download.eclipse.org/tools/buckminster/updates-4.3
depending on version. (It is not required to install git as you can
develop using egit inside eclipse acceptably well.)
Alternatively, you can Install
an eclipse bundle put together at Diamond that comes with all the
necessary plugins (eGit, Buckminster etc) which can be downloaded here.
2. Clean workspace
Create a folder to be the workspace. Inside this folder expand the most recent zip file from here: https://alfred.diamond.ac.uk/buckminster/templates/
This ensures that the defaults and target are going to be the same for all developers of DAWN.
3. CQuery
Open eclipse with the folder you created as the workspace folder. We are now going to run a Buckminster query to pull over all the DAWN source code. From the file menu in eclipse choose
‘Open a component query’ and enter the URI https://alfred.diamond.ac.uk/buckminster/base/dawn-master.cquery. Then enter the ‘eclipse.feature’ of org.dawnsci.base.site. On the properties of the CQuery, change “download.location” to “public” and if you don’t have ssh keys for github setup, set “github.authentication” to “anonymous”
Then press the ‘Resolve and Materialize’ button.
Figure 1 CQuery used to materialize a DAWN workspace
DAWN development environment will now materialize to your folder and a folder at the same level appended with ‘_git’ for the git repos.
4. Compile and Run
Please turn on ‘Build Automatically’ from the project menu. DAWN will now compile, don’t worry if there seem to be errors at this stage. When we run the product these will resolve.
Go to the plugin ‘org.dawnsci.product.plugin’ and open the org.dawnsci.base.product. Start DAWN from the toolbar at the top which contains a ‘Launch an Eclipse application in Debug mode’ action.
Figure 2 Compile and run DAWN
Congratulations, you are now able to develop and debug DAWN. Happy hacking…
Plotting
The plotting is a feature rich and elegant API which as well as plotting data can manage tools and selection regions.
@Deprecated Do not use DataSetPlotter for plotting. This class has now been deprecated and it will not be supported after Raptor.
Getting started with IPlottingSystem
The plotting system in Dawn is encapsulated behind an interface (feature rich and well documented, see javadocs). This interface is called IPlottingSystem. If you are doing any plotting in Dawn, you should use this interface. For Dawn 1.0 and 1.1 the interface includes 1D and 2D plotting, multiple axes, multiple regions (replaces overlays in the old system), configuration of almost everything, real time / interactive plots, advanced histogramming etc. From Dawn Raptor onwards, 3D will be included.
Unlike the plotting system in SDA, the default implementation of IPlottingSystem (known as ‘Light Weight Plotting System’) will pass over NX clients and other remote client technologies. In addition it manages the toolbar actions automatically (these can be manipulated in createPartControl of your part as well). Also it is no longer necessary to manage the low level drawing outside the plotting package for regions. A feature rich set of regions are provided in the API.
A Minimum View to Plot Something in DAWN
public class AViewToPlotSomething extends ViewPart {
private IPlottingSystem plotting;
private Logger logger = LoggerFactory.getLogger(AViewToPlotSomething.class);
public AViewToPlotSomething() {
try {
this.plotting = PlottingFactory.createPlottingSystem();
} catch (Exception e) {
logger.error("Cannot create a plotting system!", e);
}
}
@Override
public void createPartControl(Composite parent) {
plotting.createPlotPart(parent, "My Plot Name", getViewSite().getActionBars(), PlotType.IMAGE, this);
}
@Override
public void setFocus() {
plotting.setFocus();
}
@Override
public Object getAdapter(final Class clazz) {
if (IPlottingSystem.class == clazz)
return plotting;
if (IToolPageSystem.class == clazz)
return plotting;
return super.getAdapter(clazz);
}
}
Other examples can be found in the project org.dawnsci.plotting.examples in repo dawn-ui.
Plotting Something
When you would like to plot some data there are thread safe convenience methods the full ITrace API. The ITrace API allows more control but must be completed in the UI thread. There are numerous calls to the thread safe and ITrace based methods throughout Dawn which can be used as example code. Also some simple cases are detailed below. (Other examples can be found in the project org.dawnsci.plotting.examples in repo dawn-ui.)
Thread Safe Convenience Methods
@see IPlottingSystem.createPlot1D(...) // Add new plot
@see IPlottingSystem.updatePlot1D(...) // Update trace with same name if there or create new if not
@see IPlottingSystem.createPlot2D(...) // Add new plot
@see IPlottingSystem.updatePlot2D(...) // Update trace with same name if there or create new if not
//(From Raptor onwards)
@see IPlottingSystem.createPlot3D(...) // Add new plot
@see IPlottingSystem.updatePlot3D(...) // Update trace with same name if there or create new if not
Example:
IPlottingSystem system = ...
// Later in a job or thread:
// Make some random data
final LongDataset ls = createRandomDataset(2048);
AbstractDataset x = AbstractDataset.arange(ls.getSize(),AbstractDataset.INT32);
// Plot something
system.createPlot1D(indices, Arrays.asList(ls), monitor); // monitor may be null.
ITrace API
The low level or ‘Trace’ API allows all plots, in 1D, 2D or 3D to be dealt with using the ITrace object. Each ITrace object can be created, modified, added and removed. This must all be done from the UI thread. The order operation is generally:
- createXXX(…) // e.g. lineTrace = createLineTrace(…);
- configure // e.g. lineTrace.setData(x,y); lineTrace.setTraceColor(ColorConstants.red);
- addXXX() // e.g. system.add(lineTrace);
Line Traces for 1D
The plots for 1D can be dealt with by the ILineTrace class. An example of using this follows:
final String traceName = "fred";
final ILineTrace trace = getPlottingSystem().createLineTrace(traceName);
trace.setUserObject(...); // Some data I kept to know that it was my plot, optional
trace.setData(getXdata(), getYdata()); // AbstractDatasets x and y
trace.setTraceColor(new Color(null, getPlotColour()));
system.addTrace(trace);
system.repaint(); // optional, can be done at end of adding many.
Image Traces for 2D
Plotting images can be done in a similar way to plotting lines, using the standard ‘create, configure, add’ methodology. An example is:
final IImageTrace image = system.createImageTrace("fred");
image.setMax(400);
image.setMin(2);
image.setMask(...); // AbstractDataset of same size as data.
image.setData(AbstractDataset, List<AbstractDataset> axes, false);
system.addTrace(image);
Making Image Traces Update Fast
This rate is around ~20 ms / 2k image i.e. ~50 Hz / 2k image on an intel i7. The image is downsampled so a larger image would be measuring the speed of the downsampling algorithm.
To get this speed you need these lines on IImageTrace:
imageTrace.setDownsampleType(DownsampleType._POINT_); // Fast!
imageTrace.setRescaleHistogram(**false**); // Fast!
Then to send the data you need, this must be done in the UI thread.
imageTrace.setData(data, null, false);
Surface Traces for 2D/3D
From Raptor onwards Dawn will begin to support more 3D operations in the plotting API. An example of this is the new ISurfaceTrace which is accessible using system.createSurfaceTrace(…).
Listeners
The system is event based - meaning that when almost anything changes in the plotting it can be listened to. For instance new traces plotted by the user, new regions added, regions dragging etc. Some useful listeners and their function are explained below:
Listener Function
ITraceListener Notification of when traces are about to be plotted, have been added, have been deleted etc. The TraceWillPlot event allows the data of the trace to be modified which can be a useful advanced feature.
IRegionListener Notification of IRegions being created, add and deleted.
IROIListener Added to IRegions this listener notifies of location. Do not do work in the drag notification which takes a lot of CPU or your drag will be very slow!
IPaletteListener Can be added to an IImageTrace to be notified of a range of image properties being changed.
IToolChangeListener Notification of tools being changed by the user.
IAxisListener Changes to the ranges of Axes. Added to an IAxis.
ICoordinateSystemListener Added to an ICoordinateSystem to be notified of coordinate changes.
Services
There are a number of useful services which are available in Dawn. These can be retrieved in eclipse 3.x using:
IDawnService service = (IDawnService)PlaformUI.getWorkbench().getService(IDawnService.class);
The following table explains their usage (@see plugin org.dawb.common.services for all the other services available):
Service Function
IImageService Service for getting Images (ImageData) for any 2D AbstractDataset.
ILoaderService Service for loading data (using LoaderFactory)
IPaletteService Service for getting registered palettes (there is an extension point for registering an Image palette).
IThumbnailService Service for getting a thumbnail image of an dataset.
ISystemService Service for getting plotting systems backed by PlottingFactory.
For example:
final ISystemService<IPlottingSystem> service = (...)PlatformUI.getWorkbench().getService(ISystemService.class);
IPersistenceService Service for getting an IPersistentFile used to save and load various data. This service allows the saving of data, regions, masks to the HDF5 file format. (OSGI service only, PlaformUI.getWorkbench().getService(…) cannot be used.)
IConversionService Service for converting/exporting files of one format to another. For instance export a 3D hdf5/nexus stack to a directory of TIFF images.(OSGI service only, PlaformUI.getWorkbench().getService(…) cannot be used.)
Loader Service / Loader Factory
The ILoaderService is backed by LoaderFactory and either the ILoaderService or the LoaderFactory (which has static methods and more options for loading) are acceptable to use for loading data into Dawn. Please do not use loader classes (e.g. SRSLoader) directly either in Jython or Java code. It is much better to use the factory because it can determine the right loader automatically and it has a soft data cache for caching recently used data. This approach has a significant speed advantage.
Adding a FileLoader
Adding a file loader to be picked up by ILoaderService (and LoaderFactory) can be done using an extension point in Dawn 1.x.
To do this create a plugin for your loader and use the extension point ”uk.ac.diamond.scisoft.analysis.io.loader” to provide a class and a file extension for loading a given format. You can also set the priority. Since multiple file loaders for a given file extension are allowed, the priority provides a way of specifying when the loader should be tried, usually set to high for a custom file loader. Your loader should fail throwing an exception for invalid files, as soon as possible. This will make the LoaderService perform in a faster more robust way.
For example:
<extension point="uk.ac.diamond.scisoft.analysis.io.loader">
<loader class="org.dawb.gda.extensions.loaders.H5Loader" file_extension="h5, nxs, hdf5, hd5, hdf, nexus" high_priority="true">
</loader>
...
</extension>
Your new file loader class must extend AbstractFileLoader (uk.ac.diamond.scisoft.analysis.io) and optionally (usually) implement IMetaLoader and IDataSetLoader. If the file format can load information about the data faster than parsing all the data, it is a very good idea to implement IMetaLoader. @see org.dawb.gda.extensions.loaders.H5Loader
Thumbnail Service
The thumbnail service should be used when pulling out large numbers of thumbnails from a directory of images. It has been optimized in speed and memory for doing this task.
Persistence Service
The persistence service can be used to save and load data, regions, masks to and from HDF5 files. The persistence service can be obtained like the following:
IPersistenceService service = (IPersistenceService)ServiceManager.getService(IPersistence.class);
Then in order to save data the method service.createPersistenceFile(filePath) needs to be called.It returns an IPersistentFile that will be used save data using setters. To read data, service.getPersistenceFile(filePath) has to be used with the getters available to the IPersistentFile.
Once the reading/saving is done, the IPersistentFile needs to be closed:
Reading ROIs Example:
IPersistentFile file = service.createPersistenceFile(filePath);
... = file.getROIs();
file.close();
Regions
One of the more important concepts in Dawn are regions. These replace the old overlays in SDA and integrate elegantly with the region of interest (or ROI) code from the mathematics plugin of Dawn (uk.ac.diamond.scisoft.analysis). The regions follow the same methodology for creation programmatically:
region = createRegion(...);
// The plotting system mouse will change to show that clicking in the plot will start to add the region, or you can continue and add the region programmatically
- configure // region.setROI(new RectangularROI(0,0,100,100); region.setRegionColor(…) etc.
- addRegion(region) // It will now appear as visible to the users.
Common region types:
Region Type | Default color | ROI preferred |
---|---|---|
LINE(“Line”, | ColorConstants.cyan, | LinearROI.class), |
POLYLINE(“Polyline”, | ColorConstants.cyan, | PolylineROI.class), |
POLYGON(“Polygon”, | ColorConstants.cyan, | PolygonalROI.class), |
BOX(“Box”, | ColorConstants.green, | RectangularROI.class), |
GRID(“Grid”, | ColorConstants.lightGray, | GridROI.class), |
CIRCLE(“Circle”, | darkYellow, | CircularROI.class), |
SECTOR(“Sector”, | ColorConstants.red, | SectorROI.class), |
POINT(“Point”, | darkMagenta, | PointROI.class), |
ELLIPSE(“Ellipse”, | ColorConstants.lightGreen, | EllipticalROI.class), |
ELLIPSEFIT(“Ellipse fit”, | ColorConstants.lightGreen, | EllipticalFitROI.class), |
RING(“Ring”, | darkYellow, | SectorROI.class), |
XAXIS(“X-Axis”, | ColorConstants.blue, | RectangularROI.class), |
YAXIS(“Y-Axis”, | ColorConstants.blue, | RectangularROI.class), |
XAXIS_LINE(“X-Axis Line”, | ColorConstants.blue, | RectangularROI.class), |
YAXIS_LINE(“Y-Axis Line”, | ColorConstants.blue, | RectangularROI.class), |
FREE_DRAW(“Free draw”, | darkYellow, | PolylineROI.class); |
Tools
Tool Pages
Tools can be added to Dawn using the extension point “org.dawb.common.ui.toolPage”. You should declare a class extending AbstractToolPage and some auxiliary information such as if it is a 1D or 2D tool and the icon to use.
There is an example tool in the package org.dawb.workbench.plotting.tools.profile, the version of ExampleTool at the time of writing is shown below.
package org.dawb.workbench.plotting.tools.profile;
import org.dawb.common.ui.plot.tool.AbstractToolPage;
import org.dawb.common.ui.util.GridUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
public class ExampleTool extends AbstractToolPage {
private Composite control;
public ExampleTool() {
// Create your listeners to the main plotting
// Perhaps create a plotting system here from the PlottingFactory which is your side plot.
}
@Override
public ToolPageRole getToolPageRole() {
return ToolPageRole.ROLE_1D;
}
@Override
public void createControl(Composite parent) {
this.control = new Composite(parent, SWT.NONE);
control.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
control.setLayout(new GridLayout(1, false));
GridUtils.removeMargins(control);
// User interface shown in a page to the side of the plot.
// ... For instance: a side plot, a Viewer part
}
@Override
public Control getControl() {
return control;
}
@Override
public void setFocus() {
// If you have a table or tree in your tool, set focus here.
}
@Override
public void activate() {
super.activate();
// Now add any listeners to the plotting providing getPlottingSystem()!=null
}
@Override
public void deactivate() {
super.deactivate();
// Now remove any listeners to the plotting providing getPlottingSystem()!=null
}
@Override
public void dispose() {
super.dispose();
// Anything to kill off? This page is part of a view which is now disposed and will not be used again.
}
}
Tool Actions
It is possible to contribute actions to tools either directly when programming the tool but also through eclipse extension points. The extension point for contributing actions to a tool is called “org.dawb.common.ui.toolPageAction”. The action requires the id of the tool and the id of the command that the action should run. Other options such as Icon and where to place the tool are possible. NOTE this system is separate to the eclipse system for adding actions by extension point.
Tools and Perspectives
Tools are noramlly chosen by the user via the plotting menu to open a tool. Users may also choose to keep a tool open while opening other tools by opening the tool in a dedicated view. Another option is for the creator of a perpective to open the tool by adding it directly to a perspective. This is done in the IPerspectiveFactory implementation by opening a view with an id of “org.dawb.workbench.plotting.views.toolPageView.fixed” and a secondary id equal to the tool concerned.
For instance to open the diffraction tool fixed in a perspective (as is done in the diffraction perspective we have:
IFolderLayout toolPageLayout = layout.createFolder(....);
toolPageLayout.addView("org.dawb.workbench.plotting.views.toolPageView.fixed:org.dawb.
workbench.plotting.tools.diffraction.Diffraction");
Another example to open the region editor in a view would be:
toolPageLayout.addView("org.dawb.workbench.plotting.views.toolPageView.fixed:org.dawb.workbench.plotting.tools.region.editor");
Slicing
Any workbench part containing plotting may also slice data from files with multi-dimensional datasets (like nexus/df5 for instance). Here is the recipe for doing this:
- Your view or editor with plotting must implement ISlicablePlottingPart. It has one method:
void updatePlot(final ITransferableDataObject[] selections, final ISliceSystem sliceSystem, final boolean useTask);
Where selections is a list of data which has already been sliced and is ready to plot.
Implement the method to use getDataset(…) on ITransferableDataObject to obtain a reference of the IDataset required for plotting.
- You must implement the following one or two clauses in your IWorkbenchPart.getAdpater(…)
public Object getAdapter(@SuppressWarnings("rawtypes") final Class clazz) {
if (clazz == Page.class) {
return PlotDataPage._getPageFor_(this);
}
// If your part is not an IEditorPart
if (clazz == IFile.class) {
return ...;// the IFile which contains the data
}
return super.getAdapter(clazz);
}
- Include the ‘Data’ view in your perspective, id = ”org.dawb.workbench.views.dataSetView”
Conclusion
You can now slice any data that your part can access using the standard DAWN slicing.
Figure 3 Slicing UI in DAWN
If this UI is too complex we can create simpler modes for the slice component as required.
HDF5 Libraries
Simple HDF5 Reading
The standard reading uses ILoaderService or LoaderFactory if you are on the GDA Server (and OSGI is not available).
final ILoaderService service = (ILoaderService)PlatformUI.getWorkbench().getService(ILoaderService.class);
IDataHolder holder = service.getData(filePath, new ProgressMonitorWrapper( monitor));
ILazyDataset lz = holder.getLazyDataset("/entry/path/to/my/data");
The data can now be sliced to get parts of the data. Also lz.getSlice() will read all of the data (use with caution).
Using the Factory (Expert use only)
There is a HierarchicalDataFactory for getting read ability of an HDF5 File from any thread. This attempts to deal with any threading issues with reading the file in a multi-threaded environment. DAWN and GDA user interfaces are multi-threaded and this is an issue because background threads may run to decorate or pull out other information from HDF5 files while the workbench is running.
HDF5 has a number of complexities in getting working with Java programs. Firstly there are raw HDF5 APIs (two, one high level and one low level) and there is a Nexus API including some JNI for the nexus format.
The Nexus JNI should be avoided. It is adds an additional C++ JNI layer which can result in the system crashing, it is better to do more work in the Java layer using the HDF5 apis. In addition it forces some types of read to be blocking when the HDF5 API is non-blocking. In further addition, its version lags behind HDF5 which has regular releases with often quite powerful features (such as the forthcoming multi-threaded write).
HDF5 can read in a multi-threaded way however only one file handle must exist per h5 file being read (threads can share one file handle for a file but cannot have many handles for the same file). The solution to this is not to use synchronization on the read but to use caching of paths and synchronized access to the file handle. Synchronization on the read results in a slower to read system with is not acceptable for data analysis.
The recommended HDF5 API is IHierarchicalDataFile. This supports multi-threaded read and single threaded write. It supports writing of nexus attributes. It uses the high level HDF5 API from HDFView so is well tested. It exposes the low level HDF5 API for more advanced things such as chunking.
Examples:
Example 1 pull out data set names of a given type:
final IHierarchicalDataFile hFile = HierarchicalDataFactory._getReader_(h5.getAbsolutePath());
try {
final List<String> names = hFile.getDatasetNames(IHierarchicalDataFile._NUMER_ARRAY_);
if (names.size()!=noResults)
throw new Exception("Unexpected data sets from test file '"+afile+"' in hdf5 file '"+h5.getName()+"' they were: "+names);
// ...
} finally {
hFile.close();
}
}
Example 2 use the factory to get low level HDF5 objects:
IHierarchicalDataFile file = null;
try {
file = HierarchicalDataFactory.getReader(getFilePath());
Group grp = (Group)file.getData(FUNCTION_ENTRY);
if (grp==null)
throw new Exception("Reading Exception: " +FUNCTION_ENTRY+ " entry does not exist in the file " + filePath);
List<HObject> children = grp.getMemberList();
if (names==null) names = new ArrayList<String>(children.size());
for (HObject hObject : children) {
names.add(hObject.getName());
}
} finally {
if (file!=null) file.close();
}
Example 3 Write datasets to an HDF5 file:
IHierarchicalDataFile file = null;
try {
file = HierarchicalDataFactory.getReader(getFilePath());
final Group entry = file.group("entry"); // Creates a group if needed, also file.group(entry, "child")
file.setNexusAttribute(entry, Nexus._ENTRY_);
final AbstractDataset a = ...;
final Datatype d = H5Utils.getDatatype(a);
final long[] shape = H5Utils.getLong(a.getShape());
final Dataset s = file.createDataset(a.getName(), d, shape, a.getBuffer(), entry);
file.setNexusAttribute(s, Nexus.SDS);
// There is also file.appendDataset(...) for adding to a H5 stack
} finally {
if (file!=null)
hFile.close();
}
Using the Low Level API (God use only)
You can really get problems with using or copying code that uses the low level API. This is because we are talking about JNI here and if you use the calls in the wrong order or fail to close an entry in a try {} finally{} you can force the Java VM to exit. If you have to use the low level API, here is a top tip wrap you code in a try {} finally{} in the following way:
// Lock for low level in case anyone else reads:
try {
HierarchicalDataFactory.acquireLowLevelReadingAccess(absolutePath);
// Open in the low level using something like:
int fapl = H5.H5Pcreate_(HDF5Constants.H5P_FILE_ACCESS);
//.. etc
try {
// Do your evil low level access
} finally {
// Do all your low level closing in a finally
H5._H5Pclose_(fapl);
}
} finally {
HierarchicalDataFactory.releaseLowLevelReadingAccess(absolutePath);
}
Connecting Plot to Python
If you want any IPlottingSystem to connect to python to be scriptable, you should use the ScriptConnection class from
the plugin uk.ac.diamond.scisoft.analysis.plotclient. To use it in a workbench part do the following:
- Where you create your plotting system and after you have attached most of your listeners etc. (for instance createPartControl(…) ), put the lines:
this.connection = new ScriptingConnection(getPartName()); // Or whatever name the scripting should use connection.setPlottingSystem(plottingSystem);
- Remember to dispose the connection:
@Override public void dispose() { connection.dispose(); if (plottingSystem!=null) plottingSystem.dispose(); super.dispose(); }
Unit/Regression Testing
Automated testing in Dawn takes three forms currently:
- Junit tests
- Junit plugin tests
- Squish UI tests
The junit and squish tests are run on the Jenkins build system. If a developer checks in code to Dawn or changes the build to break these tests, it is expected that they will also investigate why this has occurred and take appropriate action. A change may be reverted if it breaks the tests for a prolonged period of time.
The junit plugin tests are not currently run by Jenkins. These tests are run by hand before a release. They include memory leak tests and should all pass before release.
General Eclipse RCP stuff
How to contribute to the DAWN welcome/intro page
Coming soon
Vertical and horizontal scroll bars on DAWN views
When creating a new view, it is important to take into account how the users will end up using this view. The machine which DAWN will be running on is also an important issue. Indeed if DAWN is being used on a laptop, chances are that most perspectives won’t be properly shown given the small dimension of the screen. To make sure that the user experience is not downgraded it is recommended to implement views with vertical and horizontal scrollbars so that main UI components such as buttons, text fields or other widgets won’t disappear if the available screen space is too small.
To do so a ScrolledComposite can be implemented:
final ScrolledComposite scrollComposite = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
final Composite content = new Composite(scrollComposite, SWT.NONE);
content.setLayout(new GridLayout(1, false));
content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// some widget with content as a parent composite
scrollComposite.setContent(content);
scrollComposite.setExpandVertical(true);
scrollComposite.setExpandHorizontal(true);
scrollComposite.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Rectangle r = scrollComposite.getClientArea();
int height = content.computeSize(r.width, SWT.DEFAULT).y;
scrollComposite.setMinHeight(height);
scrollComposite.setMinWidth(content.computeSize(SWT.DEFAULT, r.height).x);
}
});