/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.diagram.ui.editor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
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.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.teiid.designer.ui.common.graphics.GlobalUiColorManager;
import org.teiid.designer.ui.common.util.WidgetUtil;
/**
* DiagramViewForm was constructed to provide a ViewForm that took a toolbar on the left side instead of the "top". The code was
* started using the org.eclipse.swt.custom.ViewForm class. (see IBM comment above) Instances of this class implement a Composite
* that lays out three children vertically and allows programmatic control of layout and border parameters. ViewForm is used in
* the workbench to implement a diagram view's toolbar local bar.
* <p>
* Note that although this class is a subclass of <code>Composite</code>, it does not make sense to set a layout on it.
* </p>
* <p>
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>BORDER, FLAT</dd>
* <dt><b>Events:</b></dt>
* <dd>(None)</dd>
* </dl>
* <p>
* IMPORTANT: This class is <em>not</em> intended to be subclassed.
* </p>
*
* @since 8.0
*/
public class DiagramViewForm extends Composite {
private SashForm sash;
/**
* marginWidth specifies the number of pixels of horizontal margin that will be placed along the left and right edges of the
* form. The default value is 0.
*/
public int marginWidth = 0;
/**
* marginHeight specifies the number of pixels of vertical margin that will be placed along the top and bottom edges of the
* form. The default value is 0.
*/
public int marginHeight = 0;
/**
* Color of innermost line of drop shadow border.
*/
public static final RGB borderInsideRGB = new RGB(132, 130, 132);
/**
* Color of middle line of drop shadow border.
*/
public static final RGB borderMiddleRGB = new RGB(143, 141, 138);
/**
* Color of outermost line of drop shadow border.
*/
public static final RGB borderOutsideRGB = new RGB(171, 168, 165);
// SWT widgets
private ToolBar toolBar;
private Control content;
private Control control;
// Configuration and state info
private int drawLine2 = -1;
private boolean showBorder = false;
private int BORDER_TOP = 0;
private int BORDER_BOTTOM = 0;
private int BORDER_LEFT = 0;
private int BORDER_RIGHT = 0;
private Color borderColor1;
private Color borderColor2;
private Color borderColor3;
private Rectangle oldArea;
private static final int OFFSCREEN = -200;
/**
* Constructs a new instance of this class given its parent and a style value describing its behavior and appearance.
* <p>
* The style value is either one of the style constants defined in class <code>SWT</code> which is applicable to instances of
* this class, or must be built by <em>bitwise OR</em>'ing together (that is, using the <code>int</code> "|" operator) two or
* more of those <code>SWT</code> style constants. The class description lists the style constants that are applicable to the
* class. Style bits are also inherited from superclasses.
* </p>
*
* @param parent a widget which will be the parent of the new instance (cannot be null)
* @param style the style of widget to construct
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
* </ul>
* @see SWT#BORDER
* @see SWT#FLAT
* @see #getStyle
*/
public DiagramViewForm( Composite parent,
int style ) {
super(parent, checkStyle(style));
sash = new SashForm(this, SWT.HORIZONTAL);
RowLayout layout = new RowLayout();
sash.setLayout(layout);
setContent(sash);
borderColor1 = GlobalUiColorManager.getColor(borderInsideRGB);
borderColor2 = GlobalUiColorManager.getColor(borderMiddleRGB);
borderColor3 = GlobalUiColorManager.getColor(borderOutsideRGB);
setBorderVisible((style & SWT.BORDER) != 0);
addPaintListener(new PaintListener() {
@Override
public void paintControl( PaintEvent event ) {
onPaint(event.gc);
}
});
addControlListener(new ControlAdapter() {
@Override
public void controlResized( ControlEvent e ) {
onResize();
}
});
addListener(SWT.Dispose, new Listener() {
@Override
public void handleEvent( Event e ) {
onDispose();
}
});
}
/**
* Check the style bits to ensure that no invalid styles are applied.
*
* @private
*/
private static int checkStyle( int style ) {
int mask = SWT.FLAT;
return style & mask | SWT.NO_REDRAW_RESIZE;
}
@Override
public Point computeSize( int wHint,
int hHint,
boolean changed ) {
checkWidget();
// size of title bar area
Point toolBarSize = new Point(0, 0);
int wHintToolBar = 25;
if (toolBar != null) {
ToolItem firstItem = toolBar.getItem(0);
if (firstItem != null) {
wHintToolBar = firstItem.getWidth() + 4;
}
toolBarSize = toolBar.computeSize(wHintToolBar, hHint);
toolBarSize.x += 1; // +1 for highlight line
}
Point size = new Point(0, 0);
// calculate width of title bar
size.x = toolBarSize.x;
size.y = toolBarSize.y + 1; // +1 for highlight line
if (content != null) {
Point contentSize = new Point(0, 0);
contentSize = content.computeSize(SWT.DEFAULT, SWT.DEFAULT);
size.y = Math.max(size.y, contentSize.y);
size.x += contentSize.x + 1; // +1 for line bewteen content and toolbar
}
size.x += 2 * marginWidth;
size.y += 2 * marginHeight;
if (wHint != SWT.DEFAULT) size.x = wHint;
if (hHint != SWT.DEFAULT) size.y = hHint;
Rectangle trim = computeTrim(0, 0, size.x, size.y);
return new Point(trim.width, trim.height);
}
@Override
public Rectangle computeTrim( int x,
int y,
int width,
int height ) {
checkWidget();
int trimX = x - BORDER_LEFT;
int trimY = y - BORDER_TOP;
int trimWidth = width + BORDER_LEFT + BORDER_RIGHT;
int trimHeight = height + BORDER_TOP + BORDER_BOTTOM;
return new Rectangle(trimX, trimY, trimWidth, trimHeight);
}
@Override
public Rectangle getClientArea() {
checkWidget();
Rectangle clientArea = super.getClientArea();
clientArea.x += BORDER_LEFT;
clientArea.y += BORDER_TOP;
clientArea.width -= BORDER_LEFT + BORDER_RIGHT;
clientArea.height -= BORDER_TOP + BORDER_BOTTOM;
return clientArea;
}
/**
* Returns the content area.
*
* @return the control in the content area of the pane or null
*/
public Control getContent() {
return content;
}
/**
* Returns the content area.
*
* @return the control in the content area of the pane or null
*/
public Control getControllerControl() {
return control;
}
/**
* Returns the Control that appears in the top left corner of the pane. Typically this is a label such as CLabel.
*
* @return the control in the top left corner of the pane or null
*/
public Control getToolBar() {
return toolBar;
}
@Override
public void layout( boolean changed ) {
checkWidget();
Rectangle rect = getClientArea();
drawLine2 = -1;
int width = rect.x + marginWidth;
Point toolBarSize = new Point(0, 0);
if (toolBar != null && !toolBar.isDisposed()) {
int wHintToolBar = 30;
int count = toolBar.getItemCount();
if (count > 0) {
final ToolItem firstItem = toolBar.getItem(0);
if (firstItem != null) {
wHintToolBar = 30; //firstItem.getWidth();
}
}
toolBarSize = toolBar.computeSize(wHintToolBar, SWT.DEFAULT);
Rectangle rectFinal = new Rectangle(0, 0, 10, 10);
rectFinal.x = rect.x + marginWidth;
rectFinal.y = rect.y + 1 + marginHeight;
rectFinal.width = toolBarSize.x;
rectFinal.height = rect.height - 2 * marginHeight;
toolBar.setBounds(rectFinal);
drawLine2 = toolBarSize.x + 3;
width = toolBarSize.x + 4; // +1 for divider line
}
if (content != null && !content.isDisposed()) {
Rectangle rectFinal = new Rectangle(0, 0, 10, 10);
rectFinal.x = width;
rectFinal.y = rect.y + marginHeight;
rectFinal.width = rect.x + rect.width - width - marginWidth;
rectFinal.height = rect.height - 2 * marginHeight;
content.setBounds(rectFinal);
}
}
void onDispose() {
borderColor1 = null;
borderColor2 = null;
borderColor3 = null;
toolBar = null;
content = null;
oldArea = null;
}
/**
* Draws the focus border.
*/
void onPaint( GC gc ) {
Rectangle d = super.getClientArea();
if (showBorder) {
if ((getStyle() & SWT.FLAT) != 0) {
gc.setForeground(borderColor1);
gc.drawRectangle(d.x, d.y, d.x + d.width - 1, d.y + d.height - 1);
} else {
gc.setForeground(borderColor1);
gc.drawRectangle(d.x, d.y, d.x + d.width - 3, d.y + d.height - 3);
gc.setForeground(borderColor2);
gc.drawLine(d.x + 1, d.y + d.height - 2, d.x + d.width - 1, d.y + d.height - 2);
gc.drawLine(d.x + d.width - 2, d.y + 1, d.x + d.width - 2, d.y + d.height - 1);
gc.setForeground(borderColor3);
gc.drawLine(d.x + 2, d.y + d.height - 1, d.x + d.width - 2, d.y + d.height - 1);
gc.drawLine(d.x + d.width - 1, d.y + 2, d.x + d.width - 1, d.y + d.height - 2);
}
}
if (drawLine2 != -1) {
// content separator line
gc.setForeground(borderColor1);
int y1 = d.y + BORDER_TOP;
int y2 = d.y + d.height - BORDER_BOTTOM;
gc.drawLine(drawLine2, y1, drawLine2, y2);
}
// highlight on top
int x = drawLine2;
if (x != -1) {
gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
gc.drawLine(d.x + BORDER_LEFT + marginWidth, d.y + BORDER_TOP + marginHeight, x - 1, d.y + BORDER_TOP + marginHeight);
gc.drawLine(d.x + BORDER_LEFT + marginWidth,
d.y + BORDER_TOP + marginHeight,
d.x + BORDER_LEFT + marginWidth,
d.y + d.height - BORDER_BOTTOM - marginHeight - 1);
}
gc.setForeground(getForeground());
}
void onResize() {
toolBar.pack();
layout();
Rectangle area = super.getClientArea();
if (oldArea == null || oldArea.width == 0 || oldArea.height == 0) {
redraw();
} else {
int width = 0;
if (oldArea.width < area.width) {
width = area.width - oldArea.width + BORDER_RIGHT;
} else if (oldArea.width > area.width) {
width = BORDER_RIGHT;
}
redraw(area.x + area.width - width, area.y, width, area.height, false);
int height = 0;
if (oldArea.height < area.height) {
height = area.height - oldArea.height + BORDER_BOTTOM;
}
if (oldArea.height > area.height) {
height = BORDER_BOTTOM;
}
redraw(area.x, area.y + area.height - height, area.width, height, false);
}
oldArea = area;
}
/**
* Sets the content. Setting the content to null will remove it from the pane - however, the creator of the content must
* dispose of the content.
*
* @param c the control to be displayed in the content area or null
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setContent( Control content ) {
checkWidget();
if (this.content != null && !this.content.isDisposed()) {
this.content.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
}
this.content = content;
layout();
}
/**
* Sets the content. Setting the content to null will remove it from the pane - however, the creator of the content must
* dispose of the content.
*
* @param c the control to be displayed in the content area or null
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setControllerControl( Control newControl ) {
checkWidget();
if (this.control != null && !this.control.isDisposed()) {
this.control.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
}
this.control = newControl;
layout();
}
/**
* Set the widget font. This will apply the font to the toolBar, topRight and topCenter widgets.
*/
@Override
public void setFont( Font f ) {
super.setFont(f);
if (toolBar != null && !toolBar.isDisposed()) toolBar.setFont(f);
layout();
}
public void setInitialSashFormWeights() {
// make sure weight is never less than 30% of the other weight.
// if it is, make it 70/30
int[] weights = WidgetUtil.getSashFormWeights(sash);
if (weights.length == 2) {
double d = (double)weights[0] / (double)weights[1] * 100D;
if ((d < 30)) {
// left side is too small:
weights[0] = 30;
weights[1] = 70;
} else if (d > 40) {
// left side is too big:
weights[0] = 40;
weights[1] = 60;
} // endif
}
sash.setWeights(weights);
}
/**
* Sets the layout which is associated with the receiver to be the argument which may be null.
* <p>
* Note : ViewForm does not use a layout class to size and position its children.
* </p>
*
* @param the receiver's new layout or null
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
@Override
public void setLayout( Layout layout ) {
checkWidget();
return;
}
/**
* Set the control that appears in the top left corner of the pane. Typically this is a label such as CLabel. The topLeft is
* optional. Setting the top left control to null will remove it from the pane - however, the creator of the control must
* dispose of the control.
*
* @param c the control to be displayed in the top left corner or null
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setToolBar( Control c ) {
checkWidget();
if (c != null && c.getParent() != this) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (this.toolBar != null && !this.toolBar.isDisposed()) {
this.toolBar.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
}
this.toolBar = (ToolBar)c;
layout();
}
/**
* Specify whether the border should be displayed or not.
*
* @param show true if the border should be displayed
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setBorderVisible( boolean show ) {
checkWidget();
if (showBorder == show) return;
showBorder = show;
if (showBorder) {
if ((getStyle() & SWT.FLAT) != 0) {
BORDER_LEFT = BORDER_TOP = BORDER_RIGHT = BORDER_BOTTOM = 1;
} else {
BORDER_LEFT = BORDER_TOP = 1;
BORDER_RIGHT = BORDER_BOTTOM = 3;
}
} else {
BORDER_BOTTOM = BORDER_TOP = BORDER_LEFT = BORDER_RIGHT = 0;
}
layout();
redraw();
}
public Control getSashForm() {
return sash;
}
}