blob: a6dc4eaad369f97e6d56da6fa19f4fd2ec895468 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2018, 2020 Dirk Fauth.
*
* 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:
* Dirk Fauth <dirk.fauth@googlemail.com> - Initial API and implementation
*****************************************************************************/
package org.eclipse.nebula.widgets.nattable.hideshow.indicator;
import org.eclipse.nebula.widgets.nattable.hierarchical.HierarchicalTreeLayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
import org.eclipse.nebula.widgets.nattable.layer.LayerUtil;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
/**
* Specialization of the {@link HideIndicatorOverlayPainter} that renders the
* hide indicator in the level header columns of a
* {@link HierarchicalTreeLayer}. The identification of the level header columns
* is done via the cell label {@link HierarchicalTreeLayer#LEVEL_HEADER_CELL}.
*
* @since 1.6
*/
public class HierarchicalHideIndicatorOverlayPainter extends HideIndicatorOverlayPainter {
protected HierarchicalTreeLayer treeLayer;
/**
* Creates a {@link HierarchicalHideIndicatorOverlayPainter} that renders
* the hide indicator in the given given column header layer and the
* {@link HierarchicalTreeLayer} level header columns.
*
* @param columnHeaderLayer
* The layer in the column header that should be used to
* determine the height of the hidden column indicator. Should be
* the top most layer in the column header region, e.g. the
* FilterRowHeaderComposite in case filtering is included. Can be
* <code>null</code> to avoid rendering of hidden column
* indicators.
*/
public HierarchicalHideIndicatorOverlayPainter(ILayer columnHeaderLayer, HierarchicalTreeLayer treeLayer) {
this(columnHeaderLayer, null, treeLayer);
}
/**
* Creates a {@link HierarchicalHideIndicatorOverlayPainter} that renders
* the hide indicator in the given header layers and the
* {@link HierarchicalTreeLayer} level header columns.
*
* @param columnHeaderLayer
* The layer in the column header that should be used to
* determine the height of the hidden column indicator. Should be
* the top most layer in the column header region, e.g. the
* FilterRowHeaderComposite in case filtering is included. Can be
* <code>null</code> to avoid rendering of hidden column
* indicators.
* @param rowHeaderLayer
* The layer in the row header that should be used to determine
* the width of the hidden row indicator. Should be the top most
* layer in the row header region. Can be <code>null</code> to
* avoid rendering of hidden row indicators.
*/
public HierarchicalHideIndicatorOverlayPainter(ILayer columnHeaderLayer, ILayer rowHeaderLayer, HierarchicalTreeLayer treeLayer) {
super(columnHeaderLayer, rowHeaderLayer);
this.treeLayer = treeLayer;
}
@Override
protected void paintHiddenColumnIndicator(ILayer layer, GC gc, int xOffset, int yOffset, Rectangle rectangle) {
if (this.columnHeaderLayer != null) {
int lineAdjustment = gc.getLineWidth() % 2;
int height = this.columnHeaderLayer.getHeight();
for (int col = 0; col < layer.getColumnCount(); col++) {
LabelStack configLabels = layer.getConfigLabelsByPosition(col, this.columnHeaderLayer.getRowCount());
if (configLabels.hasLabel(HideIndicatorConstants.COLUMN_LEFT_HIDDEN)) {
// ensure that the current column and the left column belong
// to the same level
int currentLevel = this.treeLayer.getLevelByColumnIndex(layer.getColumnIndexByPosition(col));
int leftLevel = this.treeLayer.getLevelByColumnIndex(layer.getColumnIndexByPosition(col - 1));
if (currentLevel == leftLevel) {
int x = layer.getStartXOfColumnPosition(col);
if (this.rowHeaderLayer == null || x >= this.rowHeaderLayer.getWidth()) {
int start = rectangle.y;
for (int i = 0; i < this.columnHeaderLayer.getRowCount(); i++) {
ILayerCell cell = layer.getCellByPosition(col, i);
int cellStart = layer.getStartXOfColumnPosition(cell.getOriginColumnPosition());
if (cellStart < x
&& ((this.rowHeaderLayer != null && x > this.rowHeaderLayer.getWidth())
|| (this.rowHeaderLayer == null && x > 0))) {
start += layer.getRowHeightByPosition(i);
}
}
gc.drawLine(x - lineAdjustment, start, x - lineAdjustment, height);
}
}
}
if (configLabels.hasLabel(HideIndicatorConstants.COLUMN_RIGHT_HIDDEN)) {
// ensure that the current column and the right column
// belong to the same level
int currentLevel = this.treeLayer.getLevelByColumnIndex(layer.getColumnIndexByPosition(col));
int rightPos = LayerUtil.convertColumnPosition(layer, col + 1, this.treeLayer);
int rightIndex = layer.getColumnIndexByPosition(rightPos);
int rightLevel = this.treeLayer.getLevelByColumnIndex(rightIndex);
if (currentLevel == rightLevel || (rightIndex < 0 && !this.treeLayer.isLevelHeaderColumn(rightPos))) {
// render the line on the right side of the last column
int x = layer.getStartXOfColumnPosition(col) + layer.getColumnWidthByPosition(col);
// adjust the rendering for the whole line width to
// avoid overlapping
if (col == layer.getColumnCount() - 1) {
lineAdjustment = (gc.getLineWidth() / 2) + lineAdjustment;
}
if (this.rowHeaderLayer == null || x >= this.rowHeaderLayer.getWidth()) {
int start = rectangle.y;
for (int i = 0; i < this.columnHeaderLayer.getRowCount(); i++) {
ILayerCell cell = layer.getCellByPosition(col + 1, i);
if (cell != null
&& cell.getOriginColumnPosition() < cell.getColumnPosition()
&& x < (cell.getBounds().x + cell.getBounds().width)) {
start += layer.getRowHeightByPosition(i);
}
}
gc.drawLine(x - lineAdjustment, start, x - lineAdjustment, height - 1);
}
}
}
}
}
}
@Override
protected void paintHiddenRowIndicator(ILayer layer, GC gc, int xOffset, int yOffset, Rectangle rectangle) {
// just in case there is a composition with a row header layer and a
// HierarchicalTreeLayer
super.paintHiddenRowIndicator(layer, gc, xOffset, yOffset, rectangle);
int lineAdjustment = gc.getLineWidth() % 2;
int startRow = this.columnHeaderLayer == null ? 0 : this.columnHeaderLayer.getRowCount();
for (int col = 0; col < layer.getColumnCount(); col++) {
LabelStack labels = layer.getConfigLabelsByPosition(col, startRow);
if (labels.hasLabel(HierarchicalTreeLayer.LEVEL_HEADER_CELL)) {
for (int row = startRow; row < layer.getRowCount(); row++) {
// get labels for column + 1 to reach the body without
// the level header
LabelStack configLabels = layer.getConfigLabelsByPosition(col + 1, row);
if (configLabels.hasLabel(HideIndicatorConstants.ROW_TOP_HIDDEN)) {
int y = layer.getStartYOfRowPosition(row);
if (this.columnHeaderLayer == null || y >= this.columnHeaderLayer.getHeight()) {
ILayerCell cell = layer.getCellByPosition(col, row);
int cellStart = layer.getStartYOfRowPosition(cell.getOriginRowPosition());
if (cellStart == y) {
int startX = layer.getStartXOfColumnPosition(col);
gc.drawLine(
startX,
y - lineAdjustment,
startX + layer.getColumnWidthByPosition(col),
y - lineAdjustment);
}
}
}
if (configLabels.hasLabel(HideIndicatorConstants.ROW_BOTTOM_HIDDEN)) {
int y = layer.getStartYOfRowPosition(row) + layer.getRowHeightByPosition(row);
// adjust the rendering for the whole line width to
// avoid overlapping
if (row == layer.getRowCount() - 1) {
lineAdjustment = (gc.getLineWidth() / 2) + lineAdjustment;
}
if (this.columnHeaderLayer == null || y >= this.columnHeaderLayer.getHeight()) {
ILayerCell cell = layer.getCellByPosition(col, row);
int cellStart = layer.getStartYOfRowPosition(cell.getOriginRowPosition());
if (cellStart == y) {
int startX = layer.getStartXOfColumnPosition(col);
gc.drawLine(
startX,
y - lineAdjustment,
startX + layer.getColumnWidthByPosition(col),
y - lineAdjustment);
}
}
}
}
}
}
}
}