/*******************************************************************************
* Copyright (c) 2008 IBM Corporation 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:
* Michael Houston <schmeeky@gmail.com> - initial implementation
* fisherja@gmail.com - bugfix in 269563
*******************************************************************************/
package org.eclipse.nebula.widgets.grid.internal;
import org.eclipse.nebula.widgets.grid.AbstractRenderer;
import org.eclipse.nebula.widgets.grid.GridColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
/**
* Renders the tree branch hierarchy for a {@link GridColumn}
* @author Michael Houston <schmeeky@gmail.com>
*/
public class BranchRenderer extends AbstractRenderer {
private static final int[] LINE_STYLE = new int[] {1,1};
// Line segments
/** A full-width horizontal bar */
public static final int H_FULL = 1;
/** A horizontal bar from the centre to the right */
public static final int H_RIGHT = 1 << 1;
/** A horizontal bar from the centre to the toggle */
public static final int H_CENTRE_TOGGLE = 1 << 2;
/** A horizontal bar from the left to the toggle */
public static final int H_LEFT_TOGGLE = 1 << 3;
/** A full-height vertical bar */
public static final int V_FULL = 1 << 4;
/** A vertical bar from the top to the middle */
public static final int V_TOP = 1 << 5;
/** A vertical bar from the toggle to the bottom */
public static final int DESCENDER = 1 << 6;
/** A vertical bar from the top to the toggle */
public static final int ASCENDER = 1 << 7;
// Predefined combinations
/** Indicates that a branch should not be rendered. */
public static final int NONE = 0;
/** Indicates that a branch should be rendered as a 'T' shape. This
* is used for normal children with following siblings */
public static final int T = V_FULL | H_RIGHT;
/** Indicates that a branch should be rendered as an 'L' shape. This
* is used for the last child element */
public static final int L = V_TOP | H_RIGHT;
/** Indicates that a branch should be rendered as a 'I' shape. This
* is used for connecting children when intermediate children are shown. */
public static final int I = V_FULL;
/** Indicates that the toggle decoration for an expanded parent should be drawn */
public static final int NODE = DESCENDER;
/** Indicates that the decoration for a leaf node should be drawn */
public static final int LEAF = H_LEFT_TOGGLE;
/** Indicates that the decoration for a root node should be drawn */
public static final int ROOT = ASCENDER | DESCENDER;
/** Indicates that the decoration for the last root node should be drawn */
public static final int LAST_ROOT = ASCENDER;
/** A half-width T used on roots with no children */
public static final int SMALL_T = V_FULL | H_CENTRE_TOGGLE;
/** A half-width L used on roots with no children */
public static final int SMALL_L = V_TOP | H_CENTRE_TOGGLE;
private int indent;
private int[] branches;
private Rectangle toggleBounds;
/**
* {@inheritDoc}
*/
public Point computeSize(GC gc, int hint, int hint2, Object value) {
return new Point(getBounds().width, getBounds().height);
}
/**
* {@inheritDoc}
*/
public void paint(GC gc, Object value) {
Rectangle bounds = getBounds();
int xLeft = bounds.x;
int yTop = bounds.y - 1;
int yBottom = yTop + bounds.height;
int yMiddle = toggleBounds.y + toggleBounds.height / 2;
int yToggleBottom = toggleBounds.y + toggleBounds.height - 1;
int yToggleTop = toggleBounds.y;
int oldStyle = gc.getLineStyle();
Color oldForeground = gc.getForeground();
int[] oldLineDash = gc.getLineDash();
gc.setForeground(getDisplay().getSystemColor(
isSelected() ?
SWT.COLOR_LIST_SELECTION_TEXT
: SWT.COLOR_WIDGET_NORMAL_SHADOW));
int dy = bounds.y % 2;
// Set line style to dotted
gc.setLineDash(LINE_STYLE);
// Adjust line positions by a few pixels to create correct effect
yToggleTop --;
yTop ++;
yToggleBottom ++;
// Adjust full height
// If height is even, we shorten to an odd number of pixels, and start at the original y offset
if (bounds.height % 2 == 0) {
yBottom -= 1;
}
// If height is odd, we alternate based on the row offset
else {
yTop += dy;
yBottom -= dy;
}
// Adjust ascender and descender
yToggleBottom += dy;
if ((yToggleTop - yTop + 1) % 2 == 0)
yToggleTop -= 1;
if ((yToggleBottom - yBottom + 1) % 2 == 0)
yToggleBottom += dy == 1 ? -1 : 1;
for (int i = 0; i < branches.length; i++) {
// Calculate offsets for this branch
int xRight = xLeft + indent;
int xMiddle = xLeft + toggleBounds.width / 2;
int xMiddleBranch = xMiddle;
int xToggleRight = xLeft + toggleBounds.width;
int dx = 0;
xRight --;
xMiddleBranch += 2;
xToggleRight --;
if (indent % 2 == 0) {
xRight -= 1;
}
else {
dx = xLeft % 2;
xLeft += dx;
xRight -= dx;
}
// Render line segments
if ((branches[i] & H_FULL) == H_FULL)
gc.drawLine(xLeft, yMiddle, xRight, yMiddle);
if ((branches[i] & H_RIGHT) == H_RIGHT)
gc.drawLine(xMiddleBranch, yMiddle, xRight, yMiddle);
if ((branches[i] & H_CENTRE_TOGGLE) == H_CENTRE_TOGGLE)
gc.drawLine(xMiddleBranch, yMiddle, xToggleRight, yMiddle);
if ((branches[i] & H_LEFT_TOGGLE) == H_LEFT_TOGGLE)
gc.drawLine(xLeft, yMiddle, xToggleRight, yMiddle);
if ((branches[i] & V_FULL) == V_FULL)
gc.drawLine(xMiddle, yTop, xMiddle, yBottom);
if ((branches[i] & V_TOP) == V_TOP)
gc.drawLine(xMiddle, yTop, xMiddle, yMiddle);
if ((branches[i] & ASCENDER) == ASCENDER)
gc.drawLine(xMiddle, yTop, xMiddle, yToggleTop);
if ((branches[i] & DESCENDER) == DESCENDER)
gc.drawLine(xMiddle, yToggleBottom, xMiddle, yBottom);
xLeft += indent - dx;
}
gc.setLineDash(oldLineDash);
gc.setLineStyle(oldStyle);
gc.setForeground(oldForeground);
}
/**
* Sets the branches that will be used. The values are taken from the constants in this class such as
* I, L, T, NODE, LEAF and NONE, which represent the branch type to be used for each level.
* @param branches an array of branch types
*/
public void setBranches(int[] branches) {
this.branches = branches;
}
/**
* Sets the indent used for rendering the tree branches
* @param toggleIndent the indent used for the tree
*/
public void setIndent(int toggleIndent) {
this.indent = toggleIndent;
}
/**
* Sets bounds of the toggle control. This is used to position the downwards branches
* @param toggleBounds the bounds of the toggle control
*/
public void setToggleBounds(Rectangle toggleBounds) {
this.toggleBounds = toggleBounds;
}
}