/******************************************************************************* * Copyright (c) 2012, 2015 Original authors and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Original authors and others - initial API and implementation ******************************************************************************/ package org.eclipse.nebula.widgets.nattable.painter.cell.decorator; import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter; import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes; import org.eclipse.nebula.widgets.nattable.style.CellStyleUtil; import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeEnum; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Rectangle; /** * Decorates a cell painter with another cell painter. */ public class CellPainterDecorator implements ICellPainter { /** * The base {@link ICellPainter} that is decorated. */ private ICellPainter baseCellPainter; /** * The edge of the cell at which the decoration is applied. */ private CellEdgeEnum cellEdge; /** * The {@link ICellPainter} that is used to render the decoration. */ private ICellPainter decoratorCellPainter; /** * The spacing to use between base painter and decoration painter. Note: If * you want to add <b>padding</b> between the decoration and the cell border * you need to add a PaddingDecorator to your painter stack. */ private int spacing; /** * Flag to specify whether the base painter should render dependent to the * decoration painter or not. This will have effect on the boundary * calculation. Setting this flag to <code>true</code> the bounds of the * base painter will be modified regarding the bounds of the decoration * painter. This means that the starting coordinates for the base painter * are moving e.g. if the base painter renders centered the text will move * to the left because the decoration consumes space. If this flag is set to * <code>false</code> you can think of the decoration painter painting on * top of the base painter, possibly painting over the base painter. */ private boolean paintDecorationDependent; /** * Flag to specify whether this decorator should paint the background or * not. Typically it will paint the background to avoid rendering gaps for * the configured spacing. Using a default background this will work well as * all painters will use the same background color. Using e.g. the * GradientBackgroundPainter, this flag needs to be <code>false</code> as * otherwise there will be no gradient background because it gets * overpainted by the internal background painting. */ private boolean paintBg; /** * Creates a {@link CellPainterDecorator} without any configuration to * support initialization afterwards. * * @since 1.4 */ public CellPainterDecorator() { this(null, null, 0, null, true, true); } /** * Will create a {@link CellPainterDecorator} with the default spacing of 2 * between base and decoration painter, where the base painter is rendered * dependent to the decoration. * * @param baseCellPainter * The base {@link ICellPainter} that should be decorated * @param cellEdge * The edge of the cell at which the decoration should be applied * @param decoratorCellPainter * The {@link ICellPainter} that should be used to render the * decoration. */ public CellPainterDecorator( ICellPainter baseCellPainter, CellEdgeEnum cellEdge, ICellPainter decoratorCellPainter) { this(baseCellPainter, cellEdge, 2, decoratorCellPainter); } /** * Will create a {@link CellPainterDecorator} with the default spacing of 2 * between base and decoration painter. If paintDecorationDependent is set * to <code>false</code>, the spacing will be ignored. * * @param baseCellPainter * The base {@link ICellPainter} that should be decorated * @param cellEdge * The edge of the cell at which the decoration should be applied * @param decoratorCellPainter * The {@link ICellPainter} that should be used to render the * decoration. * @param paintDecorationDependent * Flag to specify whether the base painter should render * dependent to the decoration painter or not. */ public CellPainterDecorator( ICellPainter baseCellPainter, CellEdgeEnum cellEdge, ICellPainter decoratorCellPainter, boolean paintDecorationDependent) { this(baseCellPainter, cellEdge, 2, decoratorCellPainter, paintDecorationDependent); } /** * Will create a {@link CellPainterDecorator} with the given amount of * pixels as spacing between base and decoration painter, where the base * painter is rendered dependent to the decoration. * * @param baseCellPainter * The base {@link ICellPainter} that should be decorated * @param cellEdge * The edge of the cell at which the decoration should be applied * @param spacing * The amount of pixels that should be used as spacing between * decoration and base painter * @param decoratorCellPainter * The {@link ICellPainter} that should be used to render the * decoration. */ public CellPainterDecorator( ICellPainter baseCellPainter, CellEdgeEnum cellEdge, int spacing, ICellPainter decoratorCellPainter) { this(baseCellPainter, cellEdge, spacing, decoratorCellPainter, true); } /** * Will create a {@link CellPainterDecorator} with the given amount of * pixels as spacing between base and decoration painter. If * paintDecorationDependent is set to <code>false</code>, the spacing will * be ignored while the decoration is mainly rendered over the base painter. * * @param baseCellPainter * The base {@link ICellPainter} that should be decorated * @param cellEdge * The edge of the cell at which the decoration should be applied * @param decoratorCellPainter * The {@link ICellPainter} that should be used to render the * decoration. * @param paintDecorationDependent * Flag to specify whether the base painter should render * dependent to the decoration painter or not. */ public CellPainterDecorator( ICellPainter baseCellPainter, CellEdgeEnum cellEdge, int spacing, ICellPainter decoratorCellPainter, boolean paintDecorationDependent) { this(baseCellPainter, cellEdge, spacing, decoratorCellPainter, paintDecorationDependent, true); } /** * Will create a {@link CellPainterDecorator} with the given amount of * pixels as spacing between base and decoration painter. If * paintDecorationDependent is set to <code>false</code>, the spacing will * be ignored while the decoration is mainly rendered over the base painter. * * @param baseCellPainter * The base {@link ICellPainter} that should be decorated * @param cellEdge * The edge of the cell at which the decoration should be applied * @param decoratorCellPainter * The {@link ICellPainter} that should be used to render the * decoration. * @param paintDecorationDependent * Flag to specify whether the base painter should render * dependent to the decoration painter or not. * @param paintBg * <code>true</code> if the PaddingDecorator should paint the * background, <code>false</code> if not. */ public CellPainterDecorator( ICellPainter baseCellPainter, CellEdgeEnum cellEdge, int spacing, ICellPainter decoratorCellPainter, boolean paintDecorationDependent, boolean paintBg) { this.baseCellPainter = baseCellPainter; this.cellEdge = cellEdge; this.spacing = spacing; this.decoratorCellPainter = decoratorCellPainter; this.paintDecorationDependent = paintDecorationDependent; this.paintBg = paintBg; } /** * @param paintDecorationDependent * <code>true</code> if the base painter should render dependent * to the decoration painter, <code>false</code> if the * decoration should be rendered over the base painter. */ public void setPaintDecorationDependent(boolean paintDecorationDependent) { this.paintDecorationDependent = paintDecorationDependent; } /** * Configure whether this CellPainterDecorator should paint the background * or not. By default it will paint the background to ensure the spacing is * also painted in the configured background color. This will only cause * issues in case another background painter like the * GradientBackroundPainter should be used. * * @param paintBg * <code>true</code> if this CellPainterDecorator should also * paint the background, <code>false</code> if the background * should not be painted. */ public void setPaintBackground(boolean paintBg) { this.paintBg = paintBg; } /** * * @deprecated use setPaintDecorationDependent() instead, note that the * semantic is different */ @Deprecated public void setBaseCellPainterSpansWholeCell(boolean interiorPainterToSpanFullWidth) { this.paintDecorationDependent = !interiorPainterToSpanFullWidth; } @Override public int getPreferredWidth(ILayerCell cell, GC gc, IConfigRegistry configRegistry) { int baseWidth = (this.baseCellPainter != null ? this.baseCellPainter.getPreferredWidth(cell, gc, configRegistry) : 0); int decorationWidth = (this.decoratorCellPainter != null ? this.decoratorCellPainter.getPreferredWidth(cell, gc, configRegistry) : 0); switch (this.cellEdge) { case TOP_LEFT: case TOP_RIGHT: case BOTTOM_LEFT: case BOTTOM_RIGHT: case BOTTOM: case TOP: return this.spacing + Math.max(baseWidth, decorationWidth); default: break; } return baseWidth + this.spacing + decorationWidth; } @Override public int getPreferredHeight(ILayerCell cell, GC gc, IConfigRegistry configRegistry) { int baseHeight = (this.baseCellPainter != null ? this.baseCellPainter.getPreferredHeight(cell, gc, configRegistry) : 0); int decorationHeight = (this.decoratorCellPainter != null ? this.decoratorCellPainter.getPreferredHeight(cell, gc, configRegistry) : 0); switch (this.cellEdge) { case TOP_LEFT: case TOP_RIGHT: case BOTTOM_LEFT: case BOTTOM_RIGHT: case LEFT: case RIGHT: return this.spacing + Math.max(baseHeight, decorationHeight); default: break; } return baseHeight + this.spacing + decorationHeight; } @Override public void paintCell(ILayerCell cell, GC gc, Rectangle adjustedCellBounds, IConfigRegistry configRegistry) { Rectangle baseCellPainterBounds = this.paintDecorationDependent ? getBaseCellPainterBounds(cell, gc, adjustedCellBounds, configRegistry) : adjustedCellBounds; Rectangle decoratorCellPainterBounds = getDecoratorCellPainterBounds(cell, gc, adjustedCellBounds, configRegistry); if (this.paintBg) { Color originalBg = gc.getBackground(); gc.setBackground(CellStyleUtil .getCellStyle(cell, configRegistry) .getAttributeValue(CellStyleAttributes.BACKGROUND_COLOR)); gc.fillRectangle(adjustedCellBounds); gc.setBackground(originalBg); } if (this.baseCellPainter != null) { this.baseCellPainter.paintCell(cell, gc, baseCellPainterBounds, configRegistry); } if (this.decoratorCellPainter != null) { this.decoratorCellPainter.paintCell(cell, gc, decoratorCellPainterBounds, configRegistry); } } /** * * @return The Rectangle which can be used by the base cell painter. */ public Rectangle getBaseCellPainterBounds( ILayerCell cell, GC gc, Rectangle adjustedCellBounds, IConfigRegistry configRegistry) { int preferredDecoratorWidth = (this.decoratorCellPainter != null ? this.decoratorCellPainter.getPreferredWidth(cell, gc, configRegistry) : 0); int preferredDecoratorHeight = (this.decoratorCellPainter != null ? this.decoratorCellPainter.getPreferredHeight(cell, gc, configRegistry) : 0); // grab any extra space: int grabbedPreferredWidth = adjustedCellBounds.width - preferredDecoratorWidth - this.spacing; int grabbedPreferredHeight = adjustedCellBounds.height - preferredDecoratorHeight - this.spacing; switch (this.cellEdge) { case LEFT: return new Rectangle( adjustedCellBounds.x + preferredDecoratorWidth + this.spacing, adjustedCellBounds.y, grabbedPreferredWidth, adjustedCellBounds.height) .intersection(adjustedCellBounds); case RIGHT: return new Rectangle( adjustedCellBounds.x, adjustedCellBounds.y, grabbedPreferredWidth, adjustedCellBounds.height) .intersection(adjustedCellBounds); case TOP: return new Rectangle( adjustedCellBounds.x, adjustedCellBounds.y + preferredDecoratorHeight + this.spacing, adjustedCellBounds.width, grabbedPreferredHeight) .intersection(adjustedCellBounds); case BOTTOM: return new Rectangle( adjustedCellBounds.x, adjustedCellBounds.y, adjustedCellBounds.width, grabbedPreferredHeight) .intersection(adjustedCellBounds); case TOP_LEFT: return new Rectangle( adjustedCellBounds.x + preferredDecoratorWidth + this.spacing, adjustedCellBounds.y + preferredDecoratorHeight + this.spacing, grabbedPreferredWidth, grabbedPreferredHeight) .intersection(adjustedCellBounds); case TOP_RIGHT: return new Rectangle( adjustedCellBounds.x, adjustedCellBounds.y + preferredDecoratorHeight + this.spacing, grabbedPreferredWidth, grabbedPreferredHeight) .intersection(adjustedCellBounds); case BOTTOM_LEFT: return new Rectangle( adjustedCellBounds.x + preferredDecoratorWidth + this.spacing, adjustedCellBounds.y, grabbedPreferredWidth, grabbedPreferredHeight) .intersection(adjustedCellBounds); case BOTTOM_RIGHT: return new Rectangle( adjustedCellBounds.x, adjustedCellBounds.y, grabbedPreferredWidth, grabbedPreferredHeight) .intersection(adjustedCellBounds); case NONE: break; } return null; } /** * @return The Rectangle to paint the decoration. */ public Rectangle getDecoratorCellPainterBounds( ILayerCell cell, GC gc, Rectangle adjustedCellBounds, IConfigRegistry configRegistry) { int preferredDecoratorWidth = (this.decoratorCellPainter != null ? this.decoratorCellPainter.getPreferredWidth(cell, gc, configRegistry) : 0); int preferredDecoratorHeight = (this.decoratorCellPainter != null ? this.decoratorCellPainter.getPreferredHeight(cell, gc, configRegistry) : 0); switch (this.cellEdge) { case LEFT: return new Rectangle( adjustedCellBounds.x, adjustedCellBounds.y + ((adjustedCellBounds.height - preferredDecoratorHeight) / 2), preferredDecoratorWidth, preferredDecoratorHeight); case RIGHT: return new Rectangle( adjustedCellBounds.x + adjustedCellBounds.width - preferredDecoratorWidth, adjustedCellBounds.y + ((adjustedCellBounds.height - preferredDecoratorHeight) / 2), preferredDecoratorWidth, preferredDecoratorHeight); case TOP: return new Rectangle( adjustedCellBounds.x + ((adjustedCellBounds.width - preferredDecoratorWidth) / 2), adjustedCellBounds.y, preferredDecoratorWidth, preferredDecoratorHeight); case BOTTOM: return new Rectangle( adjustedCellBounds.x + ((adjustedCellBounds.width - preferredDecoratorWidth) / 2), adjustedCellBounds.y + adjustedCellBounds.height - preferredDecoratorHeight, preferredDecoratorWidth, preferredDecoratorHeight); case TOP_LEFT: return new Rectangle( adjustedCellBounds.x, adjustedCellBounds.y, preferredDecoratorWidth, preferredDecoratorHeight); case TOP_RIGHT: return new Rectangle( adjustedCellBounds.x + adjustedCellBounds.width - preferredDecoratorWidth, adjustedCellBounds.y, preferredDecoratorWidth, preferredDecoratorHeight); case BOTTOM_LEFT: return new Rectangle( adjustedCellBounds.x, adjustedCellBounds.y + adjustedCellBounds.height - preferredDecoratorHeight, preferredDecoratorWidth, preferredDecoratorHeight); case BOTTOM_RIGHT: return new Rectangle( adjustedCellBounds.x + adjustedCellBounds.width - preferredDecoratorWidth, adjustedCellBounds.y + adjustedCellBounds.height - preferredDecoratorHeight, preferredDecoratorWidth, preferredDecoratorHeight); case NONE: break; } return null; } @Override public ICellPainter getCellPainterAt( int x, int y, ILayerCell cell, GC gc, Rectangle adjustedCellBounds, IConfigRegistry configRegistry) { Rectangle decoratorCellPainterBounds = getDecoratorCellPainterBounds(cell, gc, adjustedCellBounds, configRegistry); if (this.decoratorCellPainter != null && decoratorCellPainterBounds.contains(x, y)) { return this.decoratorCellPainter.getCellPainterAt(x, y, cell, gc, decoratorCellPainterBounds, configRegistry); } else { Rectangle baseCellPainterBounds = this.paintDecorationDependent ? getBaseCellPainterBounds(cell, gc, adjustedCellBounds, configRegistry) : adjustedCellBounds; if (this.baseCellPainter != null && baseCellPainterBounds.contains(x, y)) { return this.baseCellPainter.getCellPainterAt(x, y, cell, gc, baseCellPainterBounds, configRegistry); } } return this; } /** * @return The base {@link ICellPainter} that is decorated. */ public ICellPainter getBaseCellPainter() { return this.baseCellPainter; } /** * @param baseCellPainter * The base {@link ICellPainter} that should be decorated. */ public void setBaseCellPainter(ICellPainter baseCellPainter) { this.baseCellPainter = baseCellPainter; } /** * @return The {@link ICellPainter} that is used to render the decoration. */ public ICellPainter getDecoratorCellPainter() { return this.decoratorCellPainter; } /** * @param decoratorCellPainter * The {@link ICellPainter} that should be used to render the * decoration. */ public void setDecoratorCellPainter(ICellPainter decoratorCellPainter) { this.decoratorCellPainter = decoratorCellPainter; } /** * @return The edge of the cell at which the decoration is applied. */ public CellEdgeEnum getCellEdge() { return this.cellEdge; } /** * @param cellEdge * The edge of the cell at which the decoration should be * applied. */ public void setCellEdge(CellEdgeEnum cellEdge) { this.cellEdge = cellEdge; } /** * @return The spacing that is used between base painter and decoration * painter. */ public int getSpacing() { return this.spacing; } /** * @param spacing * The spacing that should be used between base painter and * decoration painter. */ public void setSpacing(int spacing) { this.spacing = spacing; } }