blob: c2d588b065c28aebd2326a7810576412560a7259 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2020 Original authors and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Original authors and others - initial API and implementation
* Dirk Fauth <dirk.fauth@googlemail.com> - Bug 451152, 453055, 455949
* drgler <dsp@bdal.de> - Bug 473532
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.layer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;
import org.eclipse.nebula.widgets.nattable.command.AbstractRegionCommand;
import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.coordinate.Range;
import org.eclipse.nebula.widgets.nattable.layer.cell.AggregateConfigLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelProvider;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.layer.cell.TranslatedLayerCell;
import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
import org.eclipse.nebula.widgets.nattable.painter.layer.ILayerPainter;
import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
/**
* A composite layer is a layer that is made up of a number of underlying child
* layers. This class assumes that the child layers are laid out in a regular
* grid pattern where the child layers in each composite row all have the same
* number of rows and the same height, and the child layers in each composite
* column each have the same number of columns and the same width.
*/
public class CompositeLayer extends AbstractLayer {
private final int layoutXCount;
private final int layoutYCount;
private final HashMap<ILayer, String> childLayerToRegionNameMap = new HashMap<>();
private final HashMap<String, ILayer> regionNameToChildLayerMap = new HashMap<>();
private final HashMap<String, IConfigLabelAccumulator> regionNameToConfigLabelAccumulatorMap = new HashMap<>();
/** Data struct. for child Layers */
private final ILayer[][] childLayerLayout;
private final CompositeLayerPainter compositeLayerPainter = new CompositeLayerPainter();
public CompositeLayer(int layoutXCount, int layoutYCount) {
this.layoutXCount = layoutXCount;
this.layoutYCount = layoutYCount;
this.childLayerLayout = new ILayer[layoutXCount][layoutYCount];
setLayerPainter(this.compositeLayerPainter);
}
// Dispose
@Override
public void dispose() {
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
ILayer childLayer = this.childLayerLayout[layoutX][layoutY];
if (childLayer != null) {
childLayer.dispose();
}
}
}
}
// Persistence
@Override
public void saveState(String prefix, Properties properties) {
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
ILayer childLayer = this.childLayerLayout[layoutX][layoutY];
if (childLayer != null) {
String regionName = this.childLayerToRegionNameMap.get(childLayer);
childLayer.saveState(prefix + "." + regionName, properties); //$NON-NLS-1$
}
}
}
super.saveState(prefix, properties);
}
@Override
public void loadState(String prefix, Properties properties) {
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
ILayer childLayer = this.childLayerLayout[layoutX][layoutY];
if (childLayer != null) {
String regionName = this.childLayerToRegionNameMap.get(childLayer);
childLayer.loadState(prefix + "." + regionName, properties); //$NON-NLS-1$
}
}
}
super.loadState(prefix, properties);
}
// Configuration
@Override
public void configure(IConfigRegistry configRegistry, UiBindingRegistry uiBindingRegistry) {
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
this.childLayerLayout[layoutX][layoutY].configure(
configRegistry, uiBindingRegistry);
}
}
super.configure(configRegistry, uiBindingRegistry);
}
/**
* {@inheritDoc} Handle commands
*/
@Override
public boolean doCommand(ILayerCommand command) {
if (super.doCommand(command)) {
return true;
}
if (command instanceof AbstractRegionCommand
&& ((AbstractRegionCommand) command).label != null) {
AbstractRegionCommand arc = ((AbstractRegionCommand) command);
ILayer child = getChildLayerByRegionName(arc.label);
if (child != null) {
// execute and return true because the command was executed for
// a specific region that has been found, so nobody else should
// handle it further
child.doCommand(arc.cloneForRegion());
return true;
}
}
return doCommandOnChildLayers(command);
}
protected boolean doCommandOnChildLayers(ILayerCommand command) {
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
ILayer childLayer = this.childLayerLayout[layoutX][layoutY];
ILayerCommand childCommand = command.cloneCommand();
if (childLayer.doCommand(childCommand)) {
return true;
}
}
}
return false;
}
// Horizontal features
// Columns
/**
* @return total number of columns being displayed Note: Works off the
* header layers.
*/
@Override
public int getColumnCount() {
int columnCount = 0;
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
columnCount += this.childLayerLayout[layoutX][0].getColumnCount();
}
return columnCount;
}
@Override
public int getPreferredColumnCount() {
int preferredColumnCount = 0;
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
preferredColumnCount += this.childLayerLayout[layoutX][0].getPreferredColumnCount();
}
return preferredColumnCount;
}
/**
* @param compositeColumnPosition
* Column position in the {@link CompositeLayer}
* @return column index in the underlying layer.
*/
@Override
public int getColumnIndexByPosition(int compositeColumnPosition) {
int layoutX = getLayoutXByColumnPosition(compositeColumnPosition);
if (layoutX < 0) {
return -1;
}
ILayer childLayer = this.childLayerLayout[layoutX][0];
int childColumnPosition = compositeColumnPosition - getColumnPositionOffset(layoutX);
return childLayer.getColumnIndexByPosition(childColumnPosition);
}
@Override
public int localToUnderlyingColumnPosition(int localColumnPosition) {
int layoutX = getLayoutXByColumnPosition(localColumnPosition);
if (layoutX < 0) {
return -1;
}
return localColumnPosition - getColumnPositionOffset(layoutX);
}
@Override
public int underlyingToLocalColumnPosition(
ILayer sourceUnderlyingLayer, int underlyingColumnPosition) {
Point layoutCoordinate = getLayoutXYByChildLayer(sourceUnderlyingLayer);
if (layoutCoordinate == null) {
return -1;
}
return getColumnPositionOffset(layoutCoordinate.x) + underlyingColumnPosition;
}
@Override
public Collection<Range> underlyingToLocalColumnPositions(
ILayer sourceUnderlyingLayer, Collection<Range> underlyingColumnPositionRanges) {
Point layoutCoordinate = getLayoutXYByChildLayer(sourceUnderlyingLayer);
if (layoutCoordinate == null) {
return null;
}
Collection<Range> localColumnPositionRanges = new ArrayList<>(underlyingColumnPositionRanges.size());
int offset = getColumnPositionOffset(layoutCoordinate.x);
for (Range underlyingColumnPositionRange : underlyingColumnPositionRanges) {
localColumnPositionRanges.add(new Range(
offset + underlyingColumnPositionRange.start,
offset + underlyingColumnPositionRange.end));
}
return localColumnPositionRanges;
}
// Width
@Override
public int getWidth() {
int width = 0;
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
width += this.childLayerLayout[layoutX][0].getWidth();
}
return width;
}
@Override
public int getPreferredWidth() {
int preferredWidth = 0;
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
preferredWidth += this.childLayerLayout[layoutX][0].getPreferredWidth();
}
return preferredWidth;
}
@Override
public int getColumnWidthByPosition(int column) {
int layoutX = getLayoutXByColumnPosition(column);
if (layoutX < 0) {
return 0;
}
ILayer childLayer = this.childLayerLayout[layoutX][0];
return childLayer.getColumnWidthByPosition(column - getColumnPositionOffset(layoutX));
}
// Column resize
@Override
public boolean isColumnPositionResizable(int compositeColumnPosition) {
// Only looks at the header
int layoutX = getLayoutXByColumnPosition(compositeColumnPosition);
if (layoutX < 0) {
return false;
}
ILayer childLayer = this.childLayerLayout[layoutX][0];
int childColumnPosition = compositeColumnPosition - getColumnPositionOffset(layoutX);
return childLayer.isColumnPositionResizable(childColumnPosition);
}
// X
/**
* @param x
* pixel position - starts from 0
*/
@Override
public int getColumnPositionByX(int x) {
int layoutX = getLayoutXByPixelX(x);
if (layoutX < 0) {
return -1;
}
ILayer childLayer = this.childLayerLayout[layoutX][0];
int childX = x - getWidthOffset(layoutX);
int childColumnPosition = childLayer.getColumnPositionByX(childX);
return getColumnPositionOffset(layoutX) + childColumnPosition;
}
@Override
public int getStartXOfColumnPosition(int columnPosition) {
int layoutX = getLayoutXByColumnPosition(columnPosition);
if (layoutX < 0) {
return -1;
}
ILayer childLayer = this.childLayerLayout[layoutX][0];
int childColumnPosition = columnPosition - getColumnPositionOffset(layoutX);
return getWidthOffset(layoutX) + childLayer.getStartXOfColumnPosition(childColumnPosition);
}
// Underlying
@Override
public Collection<ILayer> getUnderlyingLayersByColumnPosition(int columnPosition) {
Collection<ILayer> underlyingLayers = new HashSet<>();
for (int layoutX = 0; layoutX < this.childLayerLayout.length; layoutX++) {
int columnPositionOffset = getColumnPositionOffset(layoutX);
if (columnPosition >= columnPositionOffset
&& columnPosition < columnPositionOffset + this.childLayerLayout[layoutX][0].getColumnCount()) {
for (int layoutY = 0; layoutY < this.childLayerLayout[layoutX].length; layoutY++) {
underlyingLayers.add(this.childLayerLayout[layoutX][layoutY]);
}
break;
}
}
return underlyingLayers;
}
// Vertical features
// Rows
@Override
public int getRowCount() {
int rowCount = 0;
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
rowCount += this.childLayerLayout[0][layoutY].getRowCount();
}
return rowCount;
}
@Override
public int getPreferredRowCount() {
int preferredRowCount = 0;
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
preferredRowCount += this.childLayerLayout[0][layoutY].getPreferredRowCount();
}
return preferredRowCount;
}
@Override
public int getRowIndexByPosition(int compositeRowPosition) {
int layoutY = getLayoutYByRowPosition(compositeRowPosition);
if (layoutY < 0) {
return -1;
}
ILayer childLayer = this.childLayerLayout[0][layoutY];
int childRowPosition = compositeRowPosition - getRowPositionOffset(layoutY);
return childLayer.getRowIndexByPosition(childRowPosition);
}
@Override
public int localToUnderlyingRowPosition(int localRowPosition) {
int layoutY = getLayoutYByRowPosition(localRowPosition);
if (layoutY < 0) {
return -1;
}
return localRowPosition - getRowPositionOffset(layoutY);
}
@Override
public int underlyingToLocalRowPosition(
ILayer sourceUnderlyingLayer, int underlyingRowPosition) {
Point layoutCoordinate = getLayoutXYByChildLayer(sourceUnderlyingLayer);
if (layoutCoordinate == null) {
return -1;
}
return getRowPositionOffset(layoutCoordinate.y) + underlyingRowPosition;
}
@Override
public Collection<Range> underlyingToLocalRowPositions(
ILayer sourceUnderlyingLayer, Collection<Range> underlyingRowPositionRanges) {
Point layoutCoordinate = getLayoutXYByChildLayer(sourceUnderlyingLayer);
if (layoutCoordinate == null) {
return null;
}
Collection<Range> localRowPositionRanges = new ArrayList<>(underlyingRowPositionRanges.size());
int offset = getRowPositionOffset(layoutCoordinate.y);
for (Range underlyingRowPositionRange : underlyingRowPositionRanges) {
localRowPositionRanges.add(new Range(
offset + underlyingRowPositionRange.start,
offset + underlyingRowPositionRange.end));
}
return localRowPositionRanges;
}
// Height
@Override
public int getHeight() {
int height = 0;
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
height += this.childLayerLayout[0][layoutY].getHeight();
}
return height;
}
@Override
public int getPreferredHeight() {
int preferredHeight = 0;
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
preferredHeight += this.childLayerLayout[0][layoutY].getPreferredHeight();
}
return preferredHeight;
}
@Override
public int getRowHeightByPosition(int row) {
int layoutY = getLayoutYByRowPosition(row);
if (layoutY < 0) {
return 0;
}
ILayer childLayer = this.childLayerLayout[0][layoutY];
return childLayer.getRowHeightByPosition(row - getRowPositionOffset(layoutY));
}
// Row resize
/**
* @return false if the row position is out of bounds
*/
@Override
public boolean isRowPositionResizable(int compositeRowPosition) {
int layoutY = getLayoutYByRowPosition(compositeRowPosition);
if (layoutY < 0) {
return false;
}
ILayer childLayer = this.childLayerLayout[0][layoutY];
int childRowPosition = compositeRowPosition - getRowPositionOffset(layoutY);
return childLayer.isRowPositionResizable(childRowPosition);
}
// Y
/**
* Get the <i>row</i> position relative to the layer the containing
* coordinate y.
*
* @param y
* Mouse event Y position.
*/
@Override
public int getRowPositionByY(int y) {
int layoutY = getLayoutYByPixelY(y);
if (layoutY < 0) {
return -1;
}
ILayer childLayer = this.childLayerLayout[0][layoutY];
int childY = y - getHeightOffset(layoutY);
int childRowPosition = childLayer.getRowPositionByY(childY);
return getRowPositionOffset(layoutY) + childRowPosition;
}
@Override
public int getStartYOfRowPosition(int rowPosition) {
int layoutY = getLayoutYByRowPosition(rowPosition);
if (layoutY < 0) {
return -1;
}
ILayer childLayer = this.childLayerLayout[0][layoutY];
int childRowPosition = rowPosition - getRowPositionOffset(layoutY);
return getHeightOffset(layoutY) + childLayer.getStartYOfRowPosition(childRowPosition);
}
@Override
public Collection<ILayer> getUnderlyingLayersByRowPosition(int rowPosition) {
Collection<ILayer> underlyingLayers = new HashSet<>();
for (int layoutY = 0; layoutY < this.childLayerLayout[0].length; layoutY++) {
int rowPositionOffset = getRowPositionOffset(layoutY);
if (rowPosition >= rowPositionOffset
&& rowPosition < rowPositionOffset + this.childLayerLayout[0][layoutY].getRowCount()) {
for (int layoutX = 0; layoutX < this.childLayerLayout.length; layoutX++) {
underlyingLayers.add(this.childLayerLayout[layoutX][layoutY]);
}
break;
}
}
return underlyingLayers;
}
// Cell features
@Override
public ILayerCell getCellByPosition(
int compositeColumnPosition, int compositeRowPosition) {
Point layoutCoordinate = getLayoutXYByPosition(
compositeColumnPosition, compositeRowPosition);
if (layoutCoordinate == null) {
return null;
}
ILayer childLayer = this.childLayerLayout[layoutCoordinate.x][layoutCoordinate.y];
int childColumnPosition = compositeColumnPosition - getColumnPositionOffset(layoutCoordinate.x);
int childRowPosition = compositeRowPosition - getRowPositionOffset(layoutCoordinate.y);
ILayerCell cell = childLayer.getCellByPosition(childColumnPosition, childRowPosition);
if (cell != null) {
cell = new TranslatedLayerCell(
cell,
this,
underlyingToLocalColumnPosition(childLayer, cell.getOriginColumnPosition()),
underlyingToLocalRowPosition(childLayer, cell.getOriginRowPosition()),
underlyingToLocalColumnPosition(childLayer, cell.getColumnPosition()),
underlyingToLocalRowPosition(childLayer, cell.getRowPosition()));
}
return cell;
}
@Override
public Rectangle getBoundsByPosition(
int compositeColumnPosition, int compositeRowPosition) {
Point layoutCoordinate = getLayoutXYByPosition(
compositeColumnPosition, compositeRowPosition);
if (layoutCoordinate == null) {
return null;
}
ILayer childLayer = this.childLayerLayout[layoutCoordinate.x][layoutCoordinate.y];
int childColumnPosition = compositeColumnPosition - getColumnPositionOffset(layoutCoordinate.x);
int childRowPosition = compositeRowPosition - getRowPositionOffset(layoutCoordinate.y);
final Rectangle bounds = childLayer.getBoundsByPosition(
childColumnPosition, childRowPosition);
if (bounds != null) {
bounds.x += getWidthOffset(layoutCoordinate.x);
bounds.y += getHeightOffset(layoutCoordinate.y);
}
return bounds;
}
@Override
public String getDisplayModeByPosition(
int compositeColumnPosition, int compositeRowPosition) {
Point layoutCoordinate = getLayoutXYByPosition(
compositeColumnPosition, compositeRowPosition);
if (layoutCoordinate == null) {
return super.getDisplayModeByPosition(
compositeColumnPosition, compositeRowPosition);
}
ILayer childLayer = this.childLayerLayout[layoutCoordinate.x][layoutCoordinate.y];
return childLayer.getDisplayModeByPosition(
compositeColumnPosition - getColumnPositionOffset(layoutCoordinate.x),
compositeRowPosition - getRowPositionOffset(layoutCoordinate.y));
}
@Override
public LabelStack getConfigLabelsByPosition(
int compositeColumnPosition, int compositeRowPosition) {
Point layoutCoordinate = getLayoutXYByPosition(compositeColumnPosition, compositeRowPosition);
if (layoutCoordinate == null) {
return new LabelStack();
}
ILayer childLayer = this.childLayerLayout[layoutCoordinate.x][layoutCoordinate.y];
int childColumnPosition = compositeColumnPosition - getColumnPositionOffset(layoutCoordinate.x);
int childRowPosition = compositeRowPosition - getRowPositionOffset(layoutCoordinate.y);
LabelStack configLabels = childLayer.getConfigLabelsByPosition(
childColumnPosition, childRowPosition);
String regionName = this.childLayerToRegionNameMap.get(childLayer);
IConfigLabelAccumulator configLabelAccumulator = this.regionNameToConfigLabelAccumulatorMap.get(regionName);
if (configLabelAccumulator != null) {
configLabelAccumulator.accumulateConfigLabels(
configLabels, childColumnPosition, childRowPosition);
}
configLabels.addLabel(regionName);
return configLabels;
}
@Override
public Object getDataValueByPosition(
int compositeColumnPosition, int compositeRowPosition) {
Point layoutCoordinate = getLayoutXYByPosition(
compositeColumnPosition, compositeRowPosition);
if (layoutCoordinate == null) {
return null;
}
ILayer childLayer = this.childLayerLayout[layoutCoordinate.x][layoutCoordinate.y];
return childLayer.getDataValueByPosition(
compositeColumnPosition - getColumnPositionOffset(layoutCoordinate.x),
compositeRowPosition - getRowPositionOffset(layoutCoordinate.y));
}
@Override
public ICellPainter getCellPainter(
int compositeColumnPosition, int compositeRowPosition,
ILayerCell cell, IConfigRegistry configRegistry) {
Point layoutCoordinate = getLayoutXYByPosition(
compositeColumnPosition, compositeRowPosition);
if (layoutCoordinate == null) {
return null;
}
ILayer childLayer = this.childLayerLayout[layoutCoordinate.x][layoutCoordinate.y];
return childLayer.getCellPainter(
compositeColumnPosition - getColumnPositionOffset(layoutCoordinate.x),
compositeRowPosition - getRowPositionOffset(layoutCoordinate.y),
cell, configRegistry);
}
// Child layer stuff
public void setChildLayer(String regionName, ILayer childLayer,
final int layoutX, final int layoutY) {
if (childLayer == null) {
throw new IllegalArgumentException("Cannot set null child layer"); //$NON-NLS-1$
}
this.childLayerToRegionNameMap.put(childLayer, regionName);
this.regionNameToChildLayerMap.put(regionName, childLayer);
childLayer.addLayerListener(this);
this.childLayerLayout[layoutX][layoutY] = childLayer;
childLayer.setClientAreaProvider(() -> getChildClientArea(layoutX, layoutY));
}
public IConfigLabelAccumulator getConfigLabelAccumulatorByRegionName(String regionName) {
return this.regionNameToConfigLabelAccumulatorMap.get(regionName);
}
/**
* Sets the IConfigLabelAccumulator for the given named region. Replaces any
* existing IConfigLabelAccumulator.
*
* @param regionName
* the region name.
* @param configLabelAccumulator
* the {@link IConfigLabelAccumulator} to set.
*/
public void setConfigLabelAccumulatorForRegion(
String regionName, IConfigLabelAccumulator configLabelAccumulator) {
this.regionNameToConfigLabelAccumulatorMap.put(regionName, configLabelAccumulator);
}
/**
* Adds the configLabelAccumulator to the existing label accumulators.
*
* @param regionName
* the region name.
* @param configLabelAccumulator
* the {@link IConfigLabelAccumulator} to add.
*/
public void addConfigLabelAccumulatorForRegion(
String regionName, IConfigLabelAccumulator configLabelAccumulator) {
IConfigLabelAccumulator existingConfigLabelAccumulator =
this.regionNameToConfigLabelAccumulatorMap.get(regionName);
AggregateConfigLabelAccumulator aggregateAccumulator;
if (existingConfigLabelAccumulator instanceof AggregateConfigLabelAccumulator) {
aggregateAccumulator = (AggregateConfigLabelAccumulator) existingConfigLabelAccumulator;
} else {
aggregateAccumulator = new AggregateConfigLabelAccumulator();
if (existingConfigLabelAccumulator != null) {
aggregateAccumulator.add(existingConfigLabelAccumulator);
}
this.regionNameToConfigLabelAccumulatorMap.put(regionName, aggregateAccumulator);
}
aggregateAccumulator.add(configLabelAccumulator);
}
private Rectangle getChildClientArea(final int layoutX, final int layoutY) {
final ILayer childLayer = this.childLayerLayout[layoutX][layoutY];
final Rectangle compositeClientArea = getClientAreaProvider().getClientArea();
if (childLayer.isDynamicSizeLayer()) {
// check if there are further sections to the bottom and reduce the
// height accordingly necessary in case the current child layer is
// a viewport layer that takes the whole space
if (this.childLayerLayout[layoutX].length > layoutY + 1) {
for (int bottomY = (layoutY + 1); bottomY < this.childLayerLayout[layoutX].length; bottomY++) {
ILayer bottomChildLayer = this.childLayerLayout[layoutX][bottomY];
int bottomHeight = bottomChildLayer.getPreferredHeight();
// if the layer to the bottom is bigger than the client area
// we assume it is the layer that takes the remaining space
// e.g. ViewportLayer
if (bottomHeight < compositeClientArea.height) {
compositeClientArea.height -= bottomHeight;
}
}
}
// check if there are further sections to the right and reduce the
// width accordingly necessary in case the current child layer is
// a viewport layer that takes the whole space
if (this.childLayerLayout.length > layoutX + 1) {
for (int rightX = (layoutX + 1); rightX < this.childLayerLayout.length; rightX++) {
ILayer rightChildLayer = this.childLayerLayout[rightX][layoutY];
int rightWidth = rightChildLayer.getPreferredWidth();
// if the layer to the right is bigger than the client area
// we assume it is the layer that takes the remaining space
// e.g. ViewportLayer
if (rightWidth < compositeClientArea.width) {
compositeClientArea.width -= rightWidth;
}
}
}
}
final Rectangle childClientArea = new Rectangle(
compositeClientArea.x + getWidthOffset(layoutX),
compositeClientArea.y + getHeightOffset(layoutY),
childLayer.getPreferredWidth(),
childLayer.getPreferredHeight());
return compositeClientArea.intersection(childClientArea);
}
/**
* @param layoutX
* col position in the CompositeLayer
* @param layoutY
* row position in the CompositeLayer
* @return child layer according to the Composite Layer Layout
*/
public ILayer getChildLayerByLayoutCoordinate(int layoutX, int layoutY) {
if (layoutX < 0
|| layoutX >= this.layoutXCount
|| layoutY < 0
|| layoutY >= this.layoutYCount) {
return null;
} else {
return this.childLayerLayout[layoutX][layoutY];
}
}
/**
* @param regionName
* The region name for which the child layer is requested.
* @return The child layer that is registered for the given region name.
*/
public ILayer getChildLayerByRegionName(String regionName) {
return this.regionNameToChildLayerMap.get(regionName);
}
/**
* @param x
* pixel position
* @param y
* pixel position
* @return Region which the given position is in
*/
@Override
public LabelStack getRegionLabelsByXY(int x, int y) {
Point layoutCoordinate = getLayoutXYByPixelXY(x, y);
if (layoutCoordinate == null) {
return null;
}
ILayer childLayer = this.childLayerLayout[layoutCoordinate.x][layoutCoordinate.y];
int childX = x - getWidthOffset(layoutCoordinate.x);
int childY = y - getHeightOffset(layoutCoordinate.y);
LabelStack regionLabels = childLayer.getRegionLabelsByXY(childX, childY);
String regionName = this.childLayerToRegionNameMap.get(childLayer);
regionLabels.addLabel(regionName);
return regionLabels;
}
@Override
public ILayer getUnderlyingLayerByPosition(int columnPosition, int rowPosition) {
Point layoutCoordinate = getLayoutXYByPosition(columnPosition, rowPosition);
if (layoutCoordinate != null) {
return this.childLayerLayout[layoutCoordinate.x][layoutCoordinate.y];
}
return null;
}
// Layout coordinate accessors
protected Point getLayoutXYByChildLayer(ILayer childLayer) {
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
if (this.childLayerLayout[layoutX][layoutY] == childLayer) {
return new Point(layoutX, layoutY);
}
}
}
return null;
}
protected int getLayoutXByPixelX(int x) {
int layoutX = 0;
while (layoutX < this.layoutXCount) {
ILayer childLayer = this.childLayerLayout[layoutX][0];
if (childLayer == null) {
return -1;
}
int widthOffset = getWidthOffset(layoutX);
if (x >= widthOffset && x < widthOffset + childLayer.getWidth()) {
return layoutX;
}
layoutX++;
}
return -1;
}
protected int getLayoutYByPixelY(int y) {
int layoutY = 0;
while (layoutY < this.layoutYCount) {
ILayer childLayer = getChildLayerByLayoutCoordinate(0, layoutY);
if (childLayer == null) {
return -1;
}
int heightOffset = getHeightOffset(layoutY);
if (y >= heightOffset && y < heightOffset + childLayer.getHeight()) {
return layoutY;
}
layoutY++;
}
return -1;
}
protected Point getLayoutXYByPixelXY(int x, int y) {
int layoutX = 0;
while (layoutX < this.layoutXCount) {
ILayer childLayer = this.childLayerLayout[layoutX][0];
if (childLayer == null) {
return null;
}
int widthOffset = getWidthOffset(layoutX);
if (x >= widthOffset && x < widthOffset + childLayer.getWidth()) {
break;
}
layoutX++;
}
int layoutY = 0;
while (layoutY < this.layoutYCount) {
ILayer childLayer = getChildLayerByLayoutCoordinate(layoutX, layoutY);
if (childLayer == null) {
return null;
}
int heightOffset = getHeightOffset(layoutY);
if (y >= heightOffset && y < heightOffset + childLayer.getHeight()) {
return new Point(layoutX, layoutY);
}
layoutY++;
}
return null;
}
protected int getLayoutXByColumnPosition(int compositeColumnPosition) {
int layoutX = 0;
while (layoutX < this.layoutXCount) {
ILayer childLayer = this.childLayerLayout[layoutX][0];
int columnPositionOffset = getColumnPositionOffset(layoutX);
if (compositeColumnPosition >= columnPositionOffset
&& compositeColumnPosition < columnPositionOffset + childLayer.getColumnCount()) {
return layoutX;
}
layoutX++;
}
return -1;
}
protected int getLayoutYByRowPosition(int compositeRowPosition) {
int layoutY = 0;
while (layoutY < this.layoutYCount) {
ILayer childLayer = this.childLayerLayout[0][layoutY];
int rowPositionOffset = getRowPositionOffset(layoutY);
if (compositeRowPosition >= rowPositionOffset
&& compositeRowPosition < rowPositionOffset + childLayer.getRowCount()) {
return layoutY;
}
layoutY++;
}
return -1;
}
protected Point getLayoutXYByPosition(int compositeColumnPosition,
int compositeRowPosition) {
int layoutX = 0;
while (layoutX < this.layoutXCount) {
ILayer childLayer = this.childLayerLayout[layoutX][0];
int columnPositionOffset = getColumnPositionOffset(layoutX);
if (compositeColumnPosition >= columnPositionOffset
&& compositeColumnPosition < columnPositionOffset + childLayer.getColumnCount()) {
break;
}
layoutX++;
}
if (layoutX >= this.layoutXCount) {
return null;
}
int layoutY = 0;
while (layoutY < this.layoutYCount) {
ILayer childLayer = this.childLayerLayout[layoutX][layoutY];
int rowPositionOffset = getRowPositionOffset(layoutY);
if (compositeRowPosition >= rowPositionOffset
&& compositeRowPosition < rowPositionOffset + childLayer.getRowCount()) {
return new Point(layoutX, layoutY);
}
layoutY++;
}
return null;
}
// Offsets
protected int getColumnPositionOffset(int layoutX) {
int offset = 0;
for (int x = 0; x < layoutX; x++) {
offset += this.childLayerLayout[x][0].getColumnCount();
}
return offset;
}
protected int getWidthOffset(int layoutX) {
int offset = 0;
for (int x = 0; x < layoutX; x++) {
offset += this.childLayerLayout[x][0].getWidth();
}
return offset;
}
protected int getRowPositionOffset(int layoutY) {
int offset = 0;
for (int y = 0; y < layoutY; y++) {
offset += this.childLayerLayout[0][y].getRowCount();
}
return offset;
}
protected int getHeightOffset(int layoutY) {
int offset = 0;
for (int y = 0; y < layoutY; y++) {
offset += this.childLayerLayout[0][y].getHeight();
}
return offset;
}
/**
* @return The number of column regions in this CompositeLayer.
*/
public int getLayoutXCount() {
return this.layoutXCount;
}
/**
* @return The number of row regions in this CompositeLayer.
*/
public int getLayoutYCount() {
return this.layoutYCount;
}
/**
*
* @return The internal data structure of the layer composition.
* @since 1.6
*/
protected ILayer[][] getChildLayerLayout() {
return this.childLayerLayout;
}
@Override
public boolean isDynamicSizeLayer() {
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
if (this.childLayerLayout[layoutX][layoutY].isDynamicSizeLayer()) {
return true;
}
}
}
return false;
}
@Override
public Collection<String> getProvidedLabels() {
Collection<String> labels = super.getProvidedLabels();
for (int layoutX = 0; layoutX < this.layoutXCount; layoutX++) {
for (int layoutY = 0; layoutY < this.layoutYCount; layoutY++) {
ILayer childLayer = this.childLayerLayout[layoutX][layoutY];
String regionName = this.childLayerToRegionNameMap.get(childLayer);
labels.add(regionName);
IConfigLabelAccumulator accumulator = this.regionNameToConfigLabelAccumulatorMap.get(regionName);
if (accumulator instanceof IConfigLabelProvider) {
labels.addAll(((IConfigLabelProvider) accumulator).getProvidedLabels());
}
labels.addAll(childLayer.getProvidedLabels());
}
}
return labels;
}
/**
* The {@link ILayerPainter} that is used to render compositions.
*
* @since 1.6
*/
public class CompositeLayerPainter implements ILayerPainter {
@Override
public void paintLayer(ILayer natLayer, GC gc, int xOffset,
int yOffset, Rectangle rectangle, IConfigRegistry configuration) {
int x = xOffset;
for (int layoutX = 0; layoutX < CompositeLayer.this.layoutXCount; layoutX++) {
int y = yOffset;
for (int layoutY = 0; layoutY < CompositeLayer.this.layoutYCount; layoutY++) {
ILayer childLayer = CompositeLayer.this.childLayerLayout[layoutX][layoutY];
Rectangle childLayerRectangle = new Rectangle(
x, y, childLayer.getWidth(), childLayer.getHeight());
childLayerRectangle = rectangle.intersection(childLayerRectangle);
Rectangle originalClipping = gc.getClipping();
gc.setClipping(childLayerRectangle);
childLayer.getLayerPainter().paintLayer(
natLayer, gc, x, y, childLayerRectangle, configuration);
gc.setClipping(originalClipping);
y += childLayer.getHeight();
}
x += CompositeLayer.this.childLayerLayout[layoutX][0].getWidth();
}
}
@Override
public Rectangle adjustCellBounds(int columnPosition, int rowPosition, Rectangle cellBounds) {
Point layoutCoordinate = getLayoutXYByPosition(columnPosition, rowPosition);
if (layoutCoordinate == null) {
return null;
}
ILayer childLayer = CompositeLayer.this.childLayerLayout[layoutCoordinate.x][layoutCoordinate.y];
if (childLayer == null) {
return null;
}
int widthOffset = getWidthOffset(layoutCoordinate.x);
int heightOffset = getHeightOffset(layoutCoordinate.y);
cellBounds.x -= widthOffset;
cellBounds.y -= heightOffset;
ILayerPainter childLayerPainter = childLayer.getLayerPainter();
int childColumnPosition = columnPosition - getColumnPositionOffset(layoutCoordinate.x);
int childRowPosition = rowPosition - getRowPositionOffset(layoutCoordinate.y);
Rectangle adjustedChildCellBounds = childLayerPainter.adjustCellBounds(
childColumnPosition, childRowPosition, cellBounds);
adjustedChildCellBounds.x += widthOffset;
adjustedChildCellBounds.y += heightOffset;
return adjustedChildCellBounds;
}
}
}