The Matrix widget is designed with a separation of concerns in mind. It does not imply any data storage model for the visual attributes of the matrix elements. Instead it concentrates on efficient matrix layout calculation, painting, and event handling.


The diagram below presents the basic concepts of the Matrix layout.

Diagram 1


Axis is one of the two dimensions of the two-dimensional matrix. Axis names are appended with X and Y in order to make them as short as possible, even in length and staying next to each other when sorted.

Axis axisX = matrix.getAxisX();
Axis axisY = matrix.getAxisY();
is faster to type and more readable then
Axis columnAxis = matrix.getColumnAxis();
Axis rowAxis = matrix.getRowAxis();

Axis is composed of sections (at least one) and is indexed by a specific sub-type of the java.lang.Number class. By default it is Integer class. It can be switched to BigInteger for example by creating the Axis manually:

Axis axisX = new Axis(Integer.class, 2);    // Two sections indexed by Integer class
Axis axisY = new Axis(BigInteger.class, 3); // Three sections indexed by BigInteger class
final Matrix matrix = new Matrix(shell, SWT.V_SCROLL, axisX, axisY);


Section is a continuous segment of a matrix axis containing a number of items. Section can serve as a header, body, footer, filter area, etc. By default each axis has two sections: header with one item and body with zero items. The number of sections can be specified by creating the axis manually:

Axis axisX = new Axis(Integer.class, 2);    // Two sections indexed by Integer class
Axis axisY = new Axis(BigInteger.class, 3); // Three sections indexed by BigInteger class
final Matrix matrix = new Matrix(shell, SWT.V_SCROLL, axisX, axisY);

On Diagram 1 row axis has a header section with 1 item and the body section with 10 items, while the column axis has header section with 1 item and body section with 5 items.

This approach may cause some confusion, because in order to show the labels for the columns one must set the header section on the row axis visible, for example:



Zone constitutes a region of the matrix where a section from axis X and a section from the axis aY intersect with each other. There are four zones on the diagram below:

  • body
at intersection of axisX body section and axisY body section
  • column header
at intersection of axisX body section and axisY header section
  • row header
at intersection of axisX header section and axisY body section
  • top left
at intersection of axisX header section and axisY header section


Zones are created automatically by the matrix to cover all intersections of axis sections.

Each zone has its own collection of painters and key/mouse bindings.


Cell is identified by indexes of two section items from perpendicular axises. Cell area does not include lines, so whenever the line width changes the cell content painting algorithm can stay untouched. Painting lines separately provides also speed optimization advantages.

The line at index n belongs logically to section n-th item as well as cell at index n, so whenever the n-th item is hidden or moved so is the n-th line. The exception is line with index equal to section.getCount(), which is not bound to any cell.


Painter is responsible to draw everything that appears on the matrix canvas: background, images, text, lines. The design of painting mechanism allows 100% customization of the matrix appearance.

Painting order

Both matrix and each zone has an individual list of painters that defines the order in which the painters paint method is executed. The painter list can be manipulated by the addPainter, removePainter, setPainter, replacePainter methods.

The default structure of painters is following:

Frozen area painter is responsible for painting separately one of 9 matrix areas formed by the head and tail freeze dividers on both axises:

Painter Scope

The painter type specifies the scope in which it paints. The zone painting mechanism feeds the paint method with with values appropriate for the given scope.

Only zone painters can handle various painter scopes. Matrix works with only one type SCOPE_SINGLE, so the actual painter type is ignored.

Painter Properties

Painter has a set of public properties that are investigated by the paint method to determine what should be drawn on the canvas. The most significant ones are image, text and style. The same style object can be used in many painters. It has attributes like:

In order to alter properties depending on the cell indexes one should replace the default painter, for instance (Snippet_0017):

final Color color = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
matrix.getBody().replacePainter(new Painter(Painter.NAME_CELLS, Painter.SCOPE_CELLS) {
    public void setup(Integer indexX, Integer indexY) {
        style.background = indexY % 2 + indexX % 2 == 1 ? color : null;


Cell Merging

Cell merging can be done by (Snippet S0210):

zone.setMerged(startX, countX, startY, countY);

If the range defined by setMerging contains a cell that is merged, then all cells merged with that cell will be unmerged. This behavior is the same as in spreadsheets.

The merged area is attached to the cell with (startX, startY) coordinates, and is not attached the cell at the end of the merged area, so the cells can be moved in and out the merged region - this opertion does not change the merged area size. The area size changes when the cells inside of it get hidden or shown.

The maximum size of the merged area can be set by:

zone.setMergeLimit(X limitX, Y limitY);

It is introduced to prevent performance problems with layout calculation which in case of merged cells must go beyond viewport area. The default value of the merge limit is 1000.

Cell can be checked for being merged by:

zone.isMerged(indexX, indexY);

Event handling

Command Binding

Typical commands are bound to keyboard and mouse gestures. The default bindings are enlisted here. The more complex action triggering conditions includes moving by select axis items and start dragging from one of them.

The bindings can be added or removed by the bind and unbind methods. For example the default matrix binding to move the focus cell to the beginning of line can be altered by (Snippet_0901):

matrix.unbind(Matrix.CMD_FOCUS_MOST_LEFT, SWT.KeyDown, SWT.HOME);    // Windows standard
matrix.bind(Matrix.CMD_FOCUS_MOST_LEFT, SWT.KeyDown, SWT.MOD1 | SWT.ARROW_LEFT); // Mac OS standard

Zones also have the bind and unbind methods.

Secondary Events

A SelectionEvent event of selecting cells by user can be handled with the Zone.addSelectionListener method and selecting section items by the user can be handled with the Section.addSelectionListener method. Also a ControlEvent can be handled by Section.addControlListener method. See Snippet_0902.


Editing of a matrix cells is facilitated by the ZoneEditor class, for each <code>Zone</code> separately. The simplest way to make the matrix editable is to instiate the ZoneEditor class with a concrete implementation of the setModelValue method to apply the modified value to the model (see Snippet_0401):

data = new ArrayList<Object[]>
new ZoneEditor(matrix.getBody()) {
    public void setModelValue(Number indexX, Number indexY, Object value) {
        data.get(indexY.intValue())[indexX.intValue()] = value;

The default cell editor control for the above is Text and the value to edit is taken from the Painter.getText(Number, Number) method of the "cells" painter of the zone being edited.

Besides Text the ZoneEditor also supports other cell editor controls: native Combo, DateTime controls and emulated, native looking check boxes.

In order to use other controls then Text the createControl(Number, Number) method of ZoneEditor must be overriden to return the desired control for the specified cells. For example:

    public Control createControl(Number indexX, Number indexY, Composite parent) {
        if (indexX.intValue() == 2) {
            Combo combo = new Combo(parent, SWT.BORDER);
            return combo;
        else if (indexX.intValue() == 3) {
            return new DateTime(matrix, SWT.BORDER);
        else {
             return super.createControl(index0, index1);

Since DateTime control is suited to Date values and check boxes to Boolean values thus the getModelValue(Number, Number) method provides a mechanism to get a proper type of value suited to the cell editor control. For example:

    public Object getModelValue(Number indexX, Number indexY) {
        return data.get(indexY.intValue())[indexX.intValue()];

There is also method to set value in the cell editor control after it is activated setEditorValue(Control, Object) and getEditorValue(Control) method to get the value from the cell editor control after the apply action has been triggered. They need to be overridden in order to handle custom control types other then the built-in ones. For example:

    public void setEditorValue(Control control, Object value) {
        if (control instanceof List) {
            List list = ((List) control);
            int position = list.indexOf((String) value);
            if (position != -1) {
        } else {
            super.setEditorValue(control, value);
    public Object getEditorValue(Control control) {
        if (control instanceof List) {
            List list = (List) control;
            int position = list.getSelectionIndex();
            return position == -1 ? null : list.getItem(position);
        } else {
            return super.getEditorValue(control);

Embedded Controls

Edit controls can be be embedded in the cells by returning true from the hasEmbeddedControl(Number, Number) method, for example (Snippet_0402):

    public boolean hasEmbeddedControl(Number indexX, Number indexY) {
        Object value = data.get(indexY.intValue())[indexX.intValue()];
        return value instanceof Boolean;
    protected Control createControl(Number indexX, Number indexY, Composite parent) {
        Object value = data.get(indexY.intValue())[indexX.intValue()];
        if (value instanceof Boolean) {
            return new Button(parent, SWT.CHECK);
        return super.createControl(indexX, indexY);

Checkbox Emulation

Embedding controls in cells hurts performance. Check boxes can be emulated by returning a value from the getCheckboxEmulation(Number, Number) method, for example:

    public Object[] getCheckboxEmulation(Number indexX, Number indexY) {
        Object value = data.get(indexX.intValue())[indexY.intValue()];
        return value instanceof Boolean ? getDefaultCheckBoxImages() : null;

The images are taken from the path specified by the setEmulationPath(String) method.

The images of the check boxes in the current system theme can be created by then snapControlImages(String) the method.

Clipboard Operations

MatrixEditor is also responcible for clipboard operations.

The copy() method uses the format(Number, Number) function to get the String values for the individual cell. By default it is the value returned from the Painter.getText(Number, Number) method of the "cells" painter of the zone being edited.

The cut() method sets null value to the selected cells after invoking copy() method.

The paste() method uses the parse(Number, Number, String) function to parse the values for the individual cells.

The clipboard operations can be customized by overriding any of the above mentioned methods.


The default command bindings are listed below:

Command Matrix Edit Control
CMD_EDIT SWT.KeyDown SWT.F2 or SWT.MouseDoubleClick or
(for check buttons only) SWT.KeyDown ' '

SWT.CR or SWT.FocusOut
CMD_CUT SWT.KeyDown SWT.MOD1 | 'x'