Composite Groups

A CompositeGroup is a specialization of the Composite interface. Therefore, it is recommended that you read the documentation of the Composite interface first to acquire a basic understanding of this interfaces inherited responsibility.

As seen there a Composite contains and manages child Primitive objects. A CompositeGroup, on the other hand, is capable of containing other Composite and CompositeLine objects.

The CoordinateSystem of a CompositeGroup is always mutable and thus does not get pooled.

As you can see in the diagram above, the interface BaseCompositeContainer is the root of all composite groups - CompositeGroup and CompositeView are both subtypes of interface BaseCompositeContainer. This chapter focuses on the various subtypes of interface CompositeGroup.

BaseCompositeContainer

The BaseCompositeContainer interface is a collection container specification for objects which implement the BaseComposite interface. Such objects can be added to and removed from any instance of a class which implements this interface.

Figure 2.7. Class Diagram BaseCompositeContainer

Class Diagram BaseCompositeContainer

The following methods define the important interface aspects:

  • void addComposite(BaseComposite)

    Adds the given BaseComposite to this BaseCompositeContainer.

  • void addComposites(List)

    Adds the BaseComposites contained in the given list to this BaseCompositeContainer.

  • int getCompositeCount()

    Returns the number of BaseComposite actually added to the BaseCompositeContainer.

  • BaseCompositeIterator getCompositeIterator()

    Returns a BaseCompositeIterator that can be used to iterate over the BaseCompositeContainers list of BaseComposites.

  • void setComposites(List)

    Add the BaseComposites contained in the given List to this BaseCompositeContainer. All the BaseComposites that are part of the list of BaseComposite of the BaseCompositeContainer but not part of the List given by bcomposites will be removed from the list of BaseComposite of the BaseCompositeContainer.

  • void removeAllComposites()

    Removes all BaseComposites from the list of BaseComposite of the BaseCompositeContainer.

  • void removeComposite(BaseComposite)

    Removes the given BaseComposite from this BaseCompositeContainer.

  • void removeComposites(List)

    Removes the BaseComposites contained in the given list from this BaseCompositeContainer.

CompositeGroup

A CompositeGroup has the ability to manage BaseComposite objects. The methods to do so are derived from class BaseCompositeContainer. A CompositeGroup may be folded, which means that the BaseComposite objects added to the CompositeGroup are not drawn to the screen.

A CompositeGroup supports two folded states. These states are defined by the following constants:

  • FOLDSTATE_FOLDED

  • FOLDSTATE_UNFOLDED

The following methods can be used to affect the foldable state and appearance.

  • int getFoldState()

    Returns the state of folding of the CompositeGroup. If the CompositeGroup is not foldable FOLDSTATE_UNFOLDED is returned.

  • void setFoldState(int)

    Sets the CompositeGroup to the state of folding given by state. The supported states of folding are defined through constants defined in this interface. If the CompositeGroup is not foldable nothing happens. If the CompositeGroup is actually in the given state also nothing will happen.

  • Size getFoldStateButtonSize()

    Returns a Size instance that describes the extensions for the FoldStateButton. The values of the extensions should be given in inner coordinate system of the CompositeGroup. Notice that the FoldStateButton is the button that is used to toggle between the different folding states.

  • void setFoldStateButtonSize(Size)

    Sets the extensions of the FoldStateButton to the extensions of the Size instance given by size. The values of the extensions should be given in the inner coordinate system of the CompositeGroup. Notice that the FoldStateButton is the button that is used to toggle between the different folding states.

  • Size getFoldStateSize(int)

    Returns a Size instance that describes the extensions of a CompositeGroup when it is in a folded state defined by the constant given by foldstate. These extensions may vary for different states of folding. The values of the extensions are given in the coordinate system of the context the CompositeGroup lies within. Notice that at the actual state of development only the FOLDSTATE_FOLDED constant is supported.

  • void setFoldStateSize(int, Size)

    Sets the extensions of a CompositeGroup for the fold state defined by the constant given by foldstate to the extensions of the Size instance given by size. The values of the extensions should be given in the coordinate system of the context the CompositeGroup lies within. When setting the extensions for the defined fold state and the CompositeGroup is actually in that state creates a ResizeEvent if one of the new extensions is different from the current extensions for that state. Notice that at the actual state of development only the FOLDSTATE_FOLDED and FOLDSTATE_UNFOLDED constants are supported.

  • void setFoldable(boolean)

    Sets the state of foldability for this CompositeGroup to the value given by foldable. If the CompositeGroup is no longer foldable and it is in a fold state different from FOLDSTATE_UNFOLDED the fold state will be changed to FOLDSTATE_UNFOLDED.

  • boolean isFoldable()

    Returns a boolean that indicates whether this CompositeGroup is foldable or not.

  • void toggleFoldState()

    Toggles between the different folding states.

A CompositeGroup object creates additional events above and beyond a Composite. To be notified about the occurrence of these events, one has to register an object that implements the CompositeGroupListener interface.

Some of the events generated by a CompositeGroup are vetoable, which means that the processing of an event can be programmatically stopped. In order to veto an event, you have to register a VetoableCompositeGroupListener. For more information about this interface, please take a closer look at the documentation for VetoableCompositeGroupListener.

For more information about the CompositeGroupListener, please see interface CompositeGroupListener.

The methods a CompositeGroup provides to support the event mechanism are:

  • void addCompositeGroupListener(CompositeGroupListener)

    Adds the given CompositeGroupListener to the CompositeGroups internal structure, such that the listener is informed about every event that occurs.

  • void addVetoableCompositeGroupListener(VetoableCompositeGroupListener)

    Adds the given VetoableCompositeGroupListener to the CompositeGroups internal structure, such that the listener is informed about every event that occurs.

  • void removeCompositeGroupListener(CompositeGroupListener)

    Removes the given CompositeGroupListener from the CompositeGroups internal structure, such that the listener is no longer informed about occuring events.

  • void removeVetoableCompositeGroupListener(VetoableCompositeGroupListener)

    Removes the given VetoableCompositeGroupListener from the CompositeGroups internal structure, such that the listener is no longer informed about occuring events.

CompositeTable

This interface represents the definition of a CompositeGroup that behaves like a 2-dimensional table, complete with rows and columns. This table allows a Composite object to be placed inside any cell which may span multiple columns and rows. The indices of the columns and rows are 1-based, such that the upper-left cell has the position [1,1]. Columns and rows can be resized, moved, added, removed and uniquely identified by their headers.

A CompositeTable is made up of a View and a Model. Embedded Composite objects and properties, such as row and column count, are part of the model. The view holds visual data, such as widths and selections. The following lists show to which group the different classes belong:

View

  • CompositeTable

  • CompositeTableGrid

  • SelectionItems

  • AbstractTableHeaderVisualizer

  • DefaultTableHeaderVisualizer

  • CompositeTableSelectionEvent

  • CompositeTableSelectionListener

Model

  • CompositeTableModel

  • DefaultCompositeTableModel

The following figure shows a CompositeTable containing 4 labels positioned in cells [2,2] (with horizontal spanning), [2,6], [4,2] and cell [4,4] (with a horizontal spanning).

Figure 2.8. Screenshot CompositeTable

Screenshot CompositeTable

Package table contains classes that build up the base functionality of the CompositeTable.

There are three types of events and corresponding listeners that are related to a CompositeTable:

  • Model events: Fired when changes are made to the model of the table (for example columns are added).

  • Composite events: Fired when Composite objects are added to or removed from the model.

  • Selection events: Fired when a selection change on the table occurs (for example, a column is selected or a cell is deselected).

The following shows all table-related events and listeners and summarizes their purposes.

Table 2.1. Events and Listeners

Class or InterfacePurpose
  • CompositeTableModelEvent

  • CompositeTableModelCompositeEvent

  • CompositeTableModelListener

This is the listener interface for receiving CompositeTableModel and CompositeTableModelComposite events. All registered listeners are informed whenever a change in the table model occurs. For example, an event of this listener type is received whenever columns and rows are added and removed.

  • CompositeTableSelectionEvent

  • CompositeTableSelectionListener

All registered CompositeTableSelectionListener objects will be informed whenever a CompositeTableSelectionEvent occurs, which indicates that the selection state of a CompositeTable has changed.

CompositeTable Events

The events fired by a CompositeTable can be divided into two parts: events concerning the model (changes to column or row count, composites added, etc.) and events concerning the view (cells selected, etc.) part of the table.

Detecting Selections

To get notified about changes to selected elements (cells, columns, rows, inlying objects) of a CompositeTable, you must register an instance of type CompositeTableSelectionListener with the CompositeTable.

This interface defines just one method:

  • void tableSelectionChanged(CompositeTableSelectionEvent)

    Called on all registered listeners whenever a CompositeTableSelectionEvent occurs on the CompositeTable.

A CompositeTableSelectionEvent is fired when a cell, column or row selection in the CompositeTable has occurred.

The following selection types are distinguished:

  • int CELL_DESELECTED

    A cell was deselected.

  • int CELL_SELECTED

    A cell was selected.

  • int COLUMN_DESELECTED

    A column was deselected.

  • int COLUMN_SELECTED

    A column was selected.

  • int NOTHING_SELECTED

    Indicating that nothing is selected.

  • int ROW_DESELECTED

    A row was deselected.

  • int ROW_SELECTED

    A column was selected.

Resize Modes

The table provides various resize modes that ordain how the resizing of a column or row via mouse drag is handled. The following modes are provided:

  • int RESIZE_LAST

    The size of the last column or row is adjusted using the same amount of space, but in the opposite direction.

  • int RESIZE_NEXT

    When the size of a column (respectively row) is adjusted, the size of the neighboring column (row) adjusted using the same amount of space, but in the opposite direction.

  • int RESIZE_NONE

    Just set the size without affecting anything. Used by interally method applyGeomtry and the CompositeTableBuilder.

  • int RESIZE_SUBSEQUENT

    Columns or rows are adjusted proportionally on a size change.

  • int RESIZE_TABLE

    The entire table becomes larger or smaller when a column or row is resize. Hence all non-affected columns and rows remain untouched.

These modes can be manipulated using these methods:

  • void setCellResizeEnabled(boolean)

    Enables / Disables the resizing of column- and rows via mouse interaction.

  • int getCellResizeMode()

    Returns the auto resize mode of the table. The default mode is RESIZE_SUBSEQUENT.

  • void setCellResizeMode(int)

    Sets the tables resize mode when table columns or rows are resized. When no mode is set explicitly the default mode RESIZE_SUBSEQUENT is used.

  • boolean isCellResizeEnabled()

    Determines whether the resizing of cells is allowed.

Interface CompositeTableModel

All data-related information of a CompositeTable is held in a model. This model is of type CompositeTableModel and specifies the methods a CompositeTable needs to update tabular data. This means that whenever table data needs to be updated (in contrast to its presentation) you have to do this by speaking to the model.

The default implementation for this interface is DefaultCompositeTableModel.

To get notified about changes in the model a CompositeTableModelListener can be registered (respectively removed) using the following methods:

  • void addTableModelListener(CompositeTableModelListener)

    Adds a listener to the list that is notified each time a change to the tables data model occurs.

  • void removeTableModelListener(CompositeTableModelListener)

    Removes the given listener from the list of listeners.

The following methods are provided to invoke inlying Composite objects:

  • void clear()

    Removes all cells from the table-model. All registered CompositeTableModelCompositeListeners will be informed about this change.

  • Composite getCompositeAt(int, int)

    Returns the composite corresponding the cell (if any), null if there is none. If a Composite spans multiple rows / columns and the given cell lies within this area, the "hit" Composite is returned.

  • Composite getCompositeAt(int, int, boolean)

    Returns the composite corresponding the cell (if any), null if there is none. If a Composite spans multiple rows / columns and the given cell lies within this area, the "hit" Composite is returned.

  • void setCompositeAt(int, int, Composite)

    Sets the given Composite into the specified cell. A CompositeTableException is thrown if there is a conflict with other set composites.

  • void setCompositeAt(int, int, int, int, Composite)

    Sets the given Composite into the specified area of cells. A CompositeTableException is thrown if there is a conflict with other set composites.

  • List getCompositesInRegion(int, int, int, int)

    Returns the Composites lying in the given area (if any). A composite must have at least one point inside the area to be in the the returned List. The area does include the column columnStop and the row rowStop.

  • void removeCompositeAt(int, int)

    Removes the composite located at column,row or lying within the area "hit" by the given column, row. All registered CompositeTableModelCompositeListeners will be informed. If there is no composite at this position, a CompositeTableException is thrown.

In summary, whenever you wish to

  • change the column or row count

  • set column or row spanning manually

  • set the value of the column or row header

  • affect the inlying objects of the table

do it using the table model.

Detecting Changes in the Table Model

If you would like to be informed about model changes, register a CompositeTableModelListener via method addTableModelListener() in the model. The listener interface defines two methods:

  • void tableModelChanged(CompositeTableModelEvent)

    Invoked whenever a change on the properties of the model occurs. At the time of writing this two changes are distinguished and used:

    • COLUMN_COUNT_CHANGED

    • ROW_COUNT_CHANGED

    The type can be retrieved by calling the method getType() on the delivered CompositeTableModelEvent.

Creating a Simple Table

The CompositeFactory provides three different methods to create new CompositeTable instances:

  • CompositeTable newCompositeTable(CoordinateSystem)

    Creates a new CompositeTable using the CoordinateSystem given by parameter coordSystem.

  • CompositeTable newCompositeTable(CoordinateSystem, CompositeTableModel)

    Creates a new CompositeTable that uses the given CoordinateSystem and the given CompositeTableModel.

  • CompositeTable newCompositeTable(CoordinateSystem, int, int)

    Creates a new CompositeTable with the given number of columns and rows. The CoordinateSystem to be used is given by parameter coordSystem.

  • CompositeTable newCompositeTable(CoordinateSystem, Object[], Object[])

    Creates a new CompositeTable with the CoordinateSystem given by parameter coordSystem. The column headers of the table are specified with parameter columnHeaders, the row headers with the given array rowHeaders. The resulting table will have length() columns and length() rows.

Here is a code snippet showing how to set up a simple Table:

Example 2.1. Creating a Simple Table

CompositeFactory factory= CompositeFactory.newInstance();
try
{
    CoordinateSystem tmpCoords= 
        CoordinateSystemPool.parse(
            "x=Lin[min=0;max=5600],y=Lin[min=0;max=5400]");
    
    Object[] columnNames= { "C1", "C2", "C3"};
    Object[] rowNames= { "R1", "R2", "R3", "R4", "R5"};

    CompositeTable table= factory.newCompositeTable(tmpCoords,
        columnNames, rowNames);
    table.setBoundingBox(500, 4500, 500, 4500);
}
catch(CoordinateSystemParseException e)
{
    e.printStackTrace();
}

This table has 3 columns (using the Strings C1, C2, C3 as column headers) and 5 rows (headers R1, R2, R3, R4, R5). If you use one of the “model-free” constructors (as we did), a default CompositeTableModel will be created behind the scenes. See section “Interface CompositeTableModel” of this chapter for more detailed information.

Custom Headers

This section will show you how to set up a custom header for the table. In our domain, a Composite that is used as a table header is called a “visualizer.” Class AbstractTableHeaderVisualizer specifies two abstract methods which have to be implemented by a derived concrete class. These methods are:

  • AbstractTableHeaderVisualizer deepCopy()

    Returns a deep-copy of this visualizer.

  • Composite getHeaderVisualizer(Object, boolean, int)

    Returns a prepared Composite that will get used as a table header at the given index. Parameter value holds the value object to be visualized (normally an object of type String). The given boolean indicates whether this cell is selected or not.

The abstract class also provides some non-abstract methods to set properties common to all visualizer objects, such as setSelectedPaintFormat().

Now let us see how to implement a simple custom visualizer component. Our class has to extend class AbstractTableHeaderVisualizer and contain a Composite that holds two inlying Primitive objects of type Rect2D:

public class Ch02_TestTableCellVisualizer 
    extends AbstractTableHeaderVisualizer
{

    private Composite composite;
    private Rect2D backRect, foreRect;

    // default constructor
    public Ch02_TestTableCellVisualizer()
    {
        CompositeFactory factory= CompositeFactory.newInstance();
        try
        {
            composite= 
                factory.newComposite(
                    CoordinateSystemPool.parse(
                        "x=Lin[min=0;max=400],y=Lin[min=0;max=400]"));

            composite.addPrimitive(backRect= new Rect2D());
            backRect.set(0, 0, 400, 400);
            composite.addPrimitive(foreRect= new Rect2D());
        }
        catch(CoordinateSystemParseException e)
        {
            if(Debug.DISPLAY_THROWABLES)
                Debug.printThrowable(e);
        }
    }

    private Ch02_TestTableCellVisualizer(
        Ch02_TestTableCellVisualizer source)
    {
        this.composite= source.composite.deepCopy();
    }

    public AbstractTableHeaderVisualizer deepCopy()
    {
        return new Ch02_TestTableCellVisualizer(this);
    }

The only part missing is the implementation of method getHeaderVisualizer, which we show in this example:

public Composite getHeaderVisualizer(
    Object value,
    boolean selected,
    int index)
{
    if(index != 3) return null;
      
    if (selected)
    {
        backRect.setPaint(selectedPaintFormat);
        foreRect.setPaint(unselectedPaintFormat);
        foreRect.set(50, 50, 350, 350);
    }
    else
    {
        backRect.setPaint(unselectedPaintFormat);
        foreRect.setPaint(selectedPaintFormat);
        foreRect.set(100, 100, 300, 300);
    }
     
    return composite;
}

Now we set the visualizer so that it is used by the CompositeTable:

// assume table was created 
table.setColumnHeaderVisualizer(
    new Ch02_TestTableCellVisualizer());
            

This visualizer is very custom, indeed: it draws a mid-sized rectangle when not selected and a large one when selected. Notice that the Composite is only returned for column 3. If null is returned (as it is the case for all leftover columns), the default visualizer is used. This is what the table will look like:

  • Selected:

  • Unselected: