/**
*
*/
package org.gudy.azureus2.ui.swt;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
/**
* A Label with a twistie graphic at the beginning; every time this label is clicked the
* twistie graphic toggles between pointing to the right and pointing down.
*
* @author knguyen
*
*/
public class TwistieLabel
extends Composite
implements ITwistieConstants
{
private int style = NONE;
/**
* An array of points for a triangle pointing downward
*/
static final int[] points_for_expanded = {
0,
2,
8,
2,
4,
6
};
/**
* An array of points for a triangle pointing to the right
*
*/
static final int[] points_for_collapsed = {
2,
-1,
2,
8,
6,
4
};
/**
* <code>Label</code> to display the text for this twistie
*/
private Label titleLabel = null;
/**
* The <code>Color</code> to use for the twistie graphic itself;
* defaults to the same as the foreground color of the titleLabel
*/
private Color twistieColor = null;
/**
* The state of the control; callers can check this state by calling {@link #isCollapsed()}
*/
private boolean isCollapsed = true;
/**
* An optional Label to display the description
*/
private Label descriptionLabel = null;
private List listeners = new ArrayList();
/**
* Create a twistie Label with the given style bit.
* <p>Style bit can be one or more of:</p>
* <ul>
* <li> TwistieLabel.NONE</li> -- The default; does not show description and separator, and is collapsed
* <li> TwistieLabel.SHOW_DESCRIPTION</li> -- Show the description below the separator (or title if separator s not shown)
* <li> TwistieLabel.SHOW_SEPARATOR</li> -- Show a separator below the title
* <li> TwistieLabel.SHOW_EXPANDED</li> -- Show a separator below the title
* </ul>
*
* @param parent
* @param style
*/
public TwistieLabel(Composite parent, int style) {
super(parent, SWT.NONE);
setBackgroundMode(SWT.INHERIT_FORCE);
this.style = style;
GridLayout gLayout = new GridLayout();
gLayout.marginHeight = 0;
gLayout.marginWidth = 0;
gLayout.verticalSpacing = 0;
gLayout.horizontalSpacing = 0;
setLayout(gLayout);
titleLabel = new Label(this, SWT.NONE);
if ((this.style & SHOW_SEPARATOR) != 0) {
Label separator = new Label(this, SWT.SEPARATOR | SWT.HORIZONTAL);
GridData labelData = new GridData(SWT.FILL, SWT.CENTER, true, false);
labelData.horizontalIndent = 10;
separator.setLayoutData(labelData);
}
if ((this.style & SHOW_DESCRIPTION) != 0) {
descriptionLabel = new Label(this, SWT.WRAP);
GridData labelData = new GridData(SWT.FILL, SWT.FILL, true, false);
labelData.horizontalIndent = 10;
descriptionLabel.setLayoutData(labelData);
/*
* Change the font to be italic for the description
*/
Font initialFont = descriptionLabel.getFont();
FontData[] fontData = initialFont.getFontData();
for (int i = 0; i < fontData.length; i++) {
fontData[i].setStyle(fontData[i].getStyle() | SWT.ITALIC);
}
descriptionLabel.setFont(new Font(getDisplay(), fontData));
}
if ((this.style & SHOW_EXPANDED) != 0) {
isCollapsed = false;
}
/*
* Leaving a little margin on the left; this is where we draw the twistie graphic
*/
GridData labelData = new GridData(SWT.FILL, SWT.CENTER, true, false);
labelData.horizontalIndent = 10;
titleLabel.setLayoutData(labelData);
/*
* Add our mouse interceptor to the control and the title label
*/
MouseInterceptor interceptor = new MouseInterceptor();
super.addMouseListener(interceptor);
titleLabel.addMouseListener(interceptor);
/*
* Listens to the paint event and do the drawing here
*/
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
/*
* The graphic is drawn to the left of the titleLabel so define the offset from the titleLabel
*/
int offsetX = titleLabel.getBounds().x - 10;
int offsetY = titleLabel.getBounds().y + 3;
if (null != twistieColor) {
e.gc.setBackground(twistieColor);
} else {
e.gc.setBackground(getForeground());
}
if (true == isCollapsed) {
e.gc.fillPolygon(translate(points_for_collapsed, offsetX, offsetY));
} else {
e.gc.fillPolygon(translate(points_for_expanded, offsetX, offsetY));
}
}
});
}
/**
* Translates the twistie points array to compensate for the given x and y offset
* @param data
* @param x
* @param y
* @return
*/
private int[] translate(int[] data, int x, int y) {
int[] target = new int[data.length];
for (int i = 0; i < data.length; i += 2) {
target[i] = data[i] + x;
}
for (int i = 1; i < data.length; i += 2) {
target[i] = data[i] + y;
}
return target;
}
/**
* Add a mouse listener to the control and also the <code>titleLabel</code>
*/
public void addMouseListener(MouseListener listener) {
if (null != titleLabel) {
titleLabel.addMouseListener(listener);
}
super.addMouseListener(listener);
}
/**
* Remove the mouse listener from the control and also the <code>titleLabel</code>
*/
public void removeMouseListener(MouseListener listener) {
if (null != titleLabel) {
titleLabel.removeMouseListener(listener);
}
super.removeMouseListener(listener);
}
/**
* Sets the color to be used for drawing the twistie graphic
* @param color
*/
public void setTwistieForeground(Color color) {
twistieColor = color;
}
/**
* Sets the foreground color for the control and also all the text-base children
*/
public void setForeground(Color color) {
if (null != titleLabel && false == titleLabel.isDisposed()) {
titleLabel.setForeground(color);
}
if (null != descriptionLabel && false == descriptionLabel.isDisposed()) {
descriptionLabel.setForeground(color);
}
if (null == twistieColor) {
twistieColor = color;
}
super.setForeground(color);
}
/**
* Sets the background color for the control and also all the text-base children
*/
public void setBackground(Color color) {
if (null != titleLabel) {
titleLabel.setBackground(color);
}
if (null != descriptionLabel) {
descriptionLabel.setBackground(color);
}
super.setBackground(color);
}
/**
* Sets the text to display as the title
* @param string
*/
public void setTitle(String string) {
if (null != titleLabel) {
titleLabel.setText(string);
}
}
/**
* Sets the text to display as the description; this is not in effect unless the {@link #SHOW_DESCRIPTION} flag is also set
* @param string
*/
public void setDescription(String string) {
if (null != descriptionLabel) {
descriptionLabel.setText(string);
}
}
/**
* Sets the tooltip for the control and also all the text-base children
*/
public void setToolTipText(String string) {
if (null != titleLabel) {
titleLabel.setToolTipText(string);
}
if (null != descriptionLabel) {
descriptionLabel.setToolTipText(string);
}
super.setToolTipText(string);
}
/**
* Sets the enablement for the control and also all the text-base children
*/
public void setEnabled(boolean enabled) {
if (null != titleLabel) {
titleLabel.setEnabled(enabled);
}
super.setEnabled(enabled);
}
/**
* Returns whether this control is in a collapsed state
* @return
*/
public boolean isCollapsed() {
return isCollapsed;
}
/**
* Add a listener to be notified whenever this control is collapsed or expanded; listeners
* can check the collapsed/expanded state on the control and perform layout changes if need be.
* @param listener
*/
public void addTwistieListener(ITwistieListener listener) {
listeners.add(listener);
}
public void removeTwistieListener(ITwistieListener listener) {
listeners.remove(listener);
}
private void notifyTwistieListeners() {
for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
((ITwistieListener) iterator.next()).isCollapsed(isCollapsed());
}
}
/**
* A listener that intercepts mouseDown events from the control and the title label so we can
* fire a single event to the listener to signal that the control has been collapsed or expanded.
*
*
* @author knguyen
*
*/
private class MouseInterceptor
extends MouseAdapter
{
/*
* Listens to the mouse click and toggle the isCollapsed flag then force a redraw to update the control
*/
public void mouseDown(MouseEvent e) {
isCollapsed = !isCollapsed;
redraw();
notifyTwistieListeners();
}
}
}