blob: ba39ba256fdea7ac77d97572527a99c4acb9cd19 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.presentations.r21.widgets;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
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.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;
/**
* Instances of this class implement a Composite that lays out three
* children horizontally and allows programmatic control of layout and
* border parameters. ViewForm is used in the workbench to implement a
* view's label/menu/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>
*/
public class ViewForm extends Composite {
/**
* 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 RGB borderInsideRGB = new RGB(132, 130, 132);
/**
* Color of middle line of drop shadow border.
*/
public static RGB borderMiddleRGB = new RGB(143, 141, 138);
/**
* Color of outermost line of drop shadow border.
*/
public static RGB borderOutsideRGB = new RGB(171, 168, 165);
// SWT widgets
private Control topLeft;
private Control topCenter;
private Control topRight;
private Control content;
// Configuration and state info
private boolean separateTopCenter = false;
private int drawLine1 = -1;
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 ViewForm(Composite parent, int style) {
super(parent, checkStyle(style));
borderColor1 = new Color(getDisplay(), borderInsideRGB);
borderColor2 = new Color(getDisplay(), borderMiddleRGB);
borderColor3 = new Color(getDisplay(), borderOutsideRGB);
setBorderVisible((style & SWT.BORDER) != 0);
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
onPaint(event.gc);
}
});
addControlListener(new ControlAdapter() {
public void controlResized(ControlEvent e) {
onResize();
}
});
addListener(SWT.Dispose, new Listener() {
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 | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
return style & mask | SWT.NO_REDRAW_RESIZE;
}
public Point computeSize(int wHint, int hHint, boolean changed) {
checkWidget();
// size of title bar area
Point leftSize = new Point(0, 0);
if (topLeft != null) {
leftSize = topLeft.computeSize(SWT.DEFAULT, SWT.DEFAULT);
leftSize.x += 1; // +1 for highlight line
}
Point centerSize = new Point(0, 0);
if (topCenter != null) {
centerSize = topCenter.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
Point rightSize = new Point(0, 0);
if (topRight != null) {
rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
Point size = new Point(0, 0);
// calculate width of title bar
if (separateTopCenter
|| (wHint != SWT.DEFAULT && leftSize.x + centerSize.x
+ rightSize.x > wHint)) {
size.x = leftSize.x + rightSize.x;
size.x = Math.max(centerSize.x, size.x);
size.y = Math.max(leftSize.y, rightSize.y) + 1; // +1 for highlight line
if (topCenter != null) {
size.y += centerSize.y;
}
} else {
size.x = leftSize.x + centerSize.x + rightSize.x;
size.y = Math.max(leftSize.y, Math.max(centerSize.y, rightSize.y)) + 1; // +1 for highlight line
}
if (content != null) {
Point contentSize = new Point(0, 0);
contentSize = content.computeSize(SWT.DEFAULT, SWT.DEFAULT);
size.x = Math.max(size.x, contentSize.x);
size.y += contentSize.y + 1; // +1 for line bewteen content and header
}
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);
}
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);
}
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() {
//checkWidget();
return content;
}
/**
* Returns Control that appears in the top center of the pane.
* Typically this is a toolbar.
*
* @return the control in the top center of the pane or null
*/
public Control getTopCenter() {
//checkWidget();
return topCenter;
}
/**
* 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 getTopLeft() {
//checkWidget();
return topLeft;
}
/**
* Returns the control in the top right corner of the pane.
* Typically this is a Close button or a composite with a Menu and Close button.
*
* @return the control in the top right corner of the pane or null
*/
public Control getTopRight() {
//checkWidget();
return topRight;
}
public void layout(boolean changed) {
checkWidget();
Rectangle rect = getClientArea();
drawLine1 = -1;
drawLine2 = -1;
Point leftSize = new Point(0, 0);
if (topLeft != null && !topLeft.isDisposed()) {
leftSize = topLeft.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
Point centerSize = new Point(0, 0);
if (topCenter != null && !topCenter.isDisposed()) {
centerSize = topCenter.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
Point rightSize = new Point(0, 0);
if (topRight != null && !topRight.isDisposed()) {
rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
int minTopWidth = leftSize.x + centerSize.x + rightSize.x + 2
* marginWidth + 1; // +1 for highlight line
int height = rect.y + marginHeight;
boolean top = false;
if (separateTopCenter || minTopWidth > rect.width) {
int topHeight = Math.max(rightSize.y, leftSize.y);
if (topRight != null && !topRight.isDisposed()) {
top = true;
topRight.setBounds(rect.x + rect.width - marginWidth
- rightSize.x, rect.y + 1 + marginHeight, rightSize.x,
topHeight);
height += 1 + topHeight; // +1 for highlight line
}
if (topLeft != null && !topLeft.isDisposed()) {
top = true;
leftSize = topLeft.computeSize(rect.width - 2 * marginWidth
- rightSize.x - 1, SWT.DEFAULT);
topLeft.setBounds(rect.x + 1 + marginWidth, rect.y + 1
+ marginHeight, leftSize.x, topHeight);
height = Math
.max(height, rect.y + marginHeight + 1 + topHeight); // +1 for highlight line
}
if (topCenter != null && !topCenter.isDisposed()) {
top = true;
if (height > rect.y + marginHeight) {
drawLine1 = height;
height += 1; // +1 for divider line
}
centerSize = topCenter.computeSize(
rect.width - 2 * marginWidth, SWT.DEFAULT);
topCenter.setBounds(rect.x + rect.width - marginWidth
- centerSize.x, height, centerSize.x, centerSize.y);
height += centerSize.y;
}
} else {
int topHeight = Math.max(rightSize.y, Math.max(centerSize.y,
leftSize.y));
if (topRight != null && !topRight.isDisposed()) {
top = true;
topRight.setBounds(rect.x + rect.width - marginWidth
- rightSize.x, rect.y + marginHeight + 1, // +1 for highlight line
rightSize.x, topHeight);
height += 1 + topHeight; // +1 for highlight line
}
if (topCenter != null && !topCenter.isDisposed()) {
top = true;
topCenter.setBounds(rect.x + rect.width - marginWidth
- rightSize.x - centerSize.x,
rect.y + marginHeight + 1, // +1 for highlight line
centerSize.x, topHeight);
height = Math
.max(height, rect.y + marginHeight + 1 + topHeight); // +1 for highlight line
}
if (topLeft != null && !topLeft.isDisposed()) {
top = true;
leftSize = topLeft.computeSize(rect.width - 2 * marginWidth
- rightSize.x - centerSize.x - 1, topHeight);
topLeft.setBounds(rect.x + marginWidth + 1, // +1 for highlight line
rect.y + marginHeight + 1, // +1 for highlight line
leftSize.x, topHeight);
height = Math
.max(height, rect.y + marginHeight + 1 + topHeight); // +1 for highlight line
}
}
if (content != null && !content.isDisposed()) {
if (top) {
drawLine2 = height;
height += 1; // +1 for divider line
}
content
.setBounds(rect.x + marginWidth, height, rect.width - 2
* marginWidth, rect.y + rect.height - height
- marginHeight);
}
}
private void onDispose() {
if (borderColor1 != null) {
borderColor1.dispose();
}
borderColor1 = null;
if (borderColor2 != null) {
borderColor2.dispose();
}
borderColor2 = null;
if (borderColor3 != null) {
borderColor3.dispose();
}
borderColor3 = null;
topLeft = null;
topCenter = null;
topRight = null;
content = null;
oldArea = null;
}
/**
* Draws the focus border.
*/
private 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 (drawLine1 != -1) {
// top seperator line
gc.setForeground(borderColor1);
gc.drawLine(d.x + BORDER_LEFT, drawLine1, d.x + d.width
- BORDER_RIGHT, drawLine1);
}
if (drawLine2 != -1) {
// content separator line
gc.setForeground(borderColor1);
gc.drawLine(d.x + BORDER_LEFT, drawLine2, d.x + d.width
- BORDER_RIGHT, drawLine2);
}
// highlight on top
int y = drawLine1;
if (y == -1) {
y = drawLine2;
}
if (y != -1) {
gc.setForeground(getDisplay().getSystemColor(
SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
gc.drawLine(d.x + BORDER_LEFT + marginWidth, d.y + BORDER_TOP
+ marginHeight, d.x + BORDER_LEFT + marginWidth, y - 1);
gc.drawLine(d.x + BORDER_LEFT + marginWidth, d.y + BORDER_TOP
+ marginHeight, d.x + d.width - BORDER_RIGHT - marginWidth
- 1, d.y + BORDER_TOP + marginHeight);
}
gc.setForeground(getForeground());
}
private void onResize() {
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 content 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 (content != null && content.getParent() != this) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (this.content != null && !this.content.isDisposed()) {
this.content.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
}
this.content = content;
layout();
}
/**
* Set the widget font.
* This will apply the font to the topLeft, topRight and topCenter widgets.
*/
public void setFont(Font f) {
super.setFont(f);
if (topLeft != null && !topLeft.isDisposed())
topLeft.setFont(f);
if (topCenter != null && !topCenter.isDisposed())
topCenter.setFont(f);
if (topRight != null && !topRight.isDisposed())
topRight.setFont(f);
layout();
}
/**
* 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 layout 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>
*/
public void setLayout(Layout layout) {
checkWidget();
return;
}
/**
* Set the control that appears in the top center of the pane.
* Typically this is a toolbar.
* The topCenter is optional. Setting the topCenter to null will remove it from
* the pane - however, the creator of the topCenter must dispose of the topCenter.
*
* @param topCenter the control to be displayed in the top center 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 setTopCenter(Control topCenter) {
checkWidget();
if (topCenter != null && topCenter.getParent() != this) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (this.topCenter != null && !this.topCenter.isDisposed()) {
this.topCenter.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
}
this.topCenter = topCenter;
layout();
}
/**
* 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 setTopLeft(Control c) {
checkWidget();
if (c != null && c.getParent() != this) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (this.topLeft != null && !this.topLeft.isDisposed()) {
this.topLeft.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
}
this.topLeft = c;
layout();
}
/**
* Set the control that appears in the top right corner of the pane.
* Typically this is a Close button or a composite with a Menu and Close button.
* The topRight is optional. Setting the top right 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 right 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>
* <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this ViewForm</li>
* </ul>
*/
public void setTopRight(Control c) {
checkWidget();
if (c != null && c.getParent() != this) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (this.topRight != null && !this.topRight.isDisposed()) {
this.topRight.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
}
this.topRight = 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();
}
/**
* If true, the topCenter will always appear on a separate line by itself, otherwise the
* topCenter will appear in the top row if there is room and will be moved to the second row if
* required.
*
* @param show true if the topCenter will always appear on a separate line by itself
*
* @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 setTopCenterSeparate(boolean show) {
checkWidget();
separateTopCenter = show;
layout();
}
}