blob: 448c567ebe0c07bbc513cc897911a1b683672f8c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 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.forms.widgets;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.IMessage;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.eclipse.ui.internal.forms.widgets.FormHeading;
import org.eclipse.ui.internal.forms.widgets.FormUtil;
/**
* Form is a custom control that renders a title and an optional background
* image above the body composite. It can be used alone when part of parents
* that are scrolled. If scrolling is required, use <code>ScrolledForm</code>
* instead because it has an instance of <code>Form</code> and adds scrolling
* capability.
* <p>
* Form can have a title if set. If not set, title area will not be left empty -
* form body will be resized to fill the entire form. In addition, an optional
* title image can be set and is rendered to the left of the title (since 3.2).
* <p>
* Form can have a title drop down menu if the menu bar manager is not empty
* (since 3.3).
* <p>
* Form title can support drag and drop if drag and drop support methods are
* invoked. When used, additional decoration is rendered behind the title to
* reinforce the drag and drop ability (since 3.3).
* <p>
* The form supports status messages. These messages can have various severity
* (error, warning, info or none). If status hyperlink handler is specified, the
* messages with the specified severity other than none will be rendered as
* hyperlinks.
* <p>
* Form can have a background image behind the title text. The image is tiled as
* many times as needed to fill the title area. Alternatively, gradient
* background can be painted vertically or horizontally.
* <p>
* Form can be put in a 'busy' state. While in this state, title image is
* replaced with an animation that lasts as long as the 'busy' state is active.
* <p>
* It is possible to create an optional head client control. When created, this
* control is placed in the form heading as a second row.
* <p>
* Form has a custom layout manager that is wrap-enabled. If a form is placed in
* a composite whose layout manager implements ILayoutExtension, the body of the
* form will participate in wrapping as long as its layout manager implements
* ILayoutExtension as well.
* <p>
* Children of the form should typically be created using FormToolkit to match
* the appearance and behaviour. When creating children, use the form body as a
* parent by calling 'getBody()' on the form instance. Example:
*
* <pre>
* FormToolkit toolkit = new FormToolkit(parent.getDisplay());
* Form form = toolkit.createForm(parent);
* form.setText(&quot;Sample form&quot;);
* form.getBody().setLayout(new GridLayout());
* toolkit.createButton(form.getBody(), &quot;Checkbox&quot;, SWT.CHECK);
* </pre>
*
* <p>
* No layout manager has been set on the body. Clients are required to set the
* desired layout manager explicitly.
* <p>
* Although the class is not final, it should not be subclassed.
*
* @since 3.0
*/
public class Form extends Composite {
private FormHeading head;
private Composite body;
private SizeCache bodyCache = new SizeCache();
private SizeCache headCache = new SizeCache();
private FormText selectionText;
private class FormLayout extends Layout implements ILayoutExtension {
public int computeMinimumWidth(Composite composite, boolean flushCache) {
return computeSize(composite, 5, SWT.DEFAULT, flushCache).x;
}
public int computeMaximumWidth(Composite composite, boolean flushCache) {
return computeSize(composite, SWT.DEFAULT, SWT.DEFAULT, flushCache).x;
}
public Point computeSize(Composite composite, int wHint, int hHint,
boolean flushCache) {
if (flushCache) {
bodyCache.flush();
headCache.flush();
}
bodyCache.setControl(body);
headCache.setControl(head);
int width = 0;
int height = 0;
Point hsize = headCache.computeSize(FormUtil.getWidthHint(wHint,
head), SWT.DEFAULT);
width = Math.max(hsize.x, width);
height = hsize.y;
boolean ignoreBody=getData(FormUtil.IGNORE_BODY)!=null;
Point bsize;
if (ignoreBody)
bsize = new Point(0,0);
else
bsize = bodyCache.computeSize(FormUtil.getWidthHint(wHint,
body), SWT.DEFAULT);
width = Math.max(bsize.x, width);
height += bsize.y;
return new Point(width, height);
}
protected void layout(Composite composite, boolean flushCache) {
if (flushCache) {
bodyCache.flush();
headCache.flush();
}
bodyCache.setControl(body);
headCache.setControl(head);
Rectangle carea = composite.getClientArea();
Point hsize = headCache.computeSize(carea.width, SWT.DEFAULT);
headCache.setBounds(0, 0, carea.width, hsize.y);
bodyCache
.setBounds(0, hsize.y, carea.width, carea.height - hsize.y);
}
}
/**
* Creates the form content control as a child of the provided parent.
*
* @param parent
* the parent widget
*/
public Form(Composite parent, int style) {
super(parent, SWT.NO_BACKGROUND | style);
super.setLayout(new FormLayout());
head = new FormHeading(this, SWT.NULL);
head.setMenu(parent.getMenu());
body = new LayoutComposite(this, SWT.NULL);
body.setMenu(parent.getMenu());
}
/**
* Passes the menu to the form body.
*
* @param menu
* the parent menu
*/
public void setMenu(Menu menu) {
super.setMenu(menu);
head.setMenu(menu);
body.setMenu(menu);
}
/**
* Fully delegates the size computation to the internal layout manager.
*/
public final Point computeSize(int wHint, int hHint, boolean changed) {
return ((FormLayout) getLayout()).computeSize(this, wHint, hHint,
changed);
}
/**
* Prevents from changing the custom control layout.
*/
public final void setLayout(Layout layout) {
}
/**
* Returns the title text that will be rendered at the top of the form.
*
* @return the title text
*/
public String getText() {
return head.getText();
}
/**
* Returns the title image that will be rendered to the left of the title.
*
* @return the title image or <code>null</code> if not set.
* @since 3.2
*/
public Image getImage() {
return head.getImage();
}
/**
* Sets the foreground color of the form. This color will also be used for
* the body.
*
* @param fg
* the foreground color
*/
public void setForeground(Color fg) {
super.setForeground(fg);
head.setForeground(fg);
body.setForeground(fg);
}
/**
* Sets the background color of the form. This color will also be used for
* the body.
*
* @param bg
* the background color
*/
public void setBackground(Color bg) {
super.setBackground(bg);
head.setBackground(bg);
body.setBackground(bg);
}
/**
* Sets the font of the header text.
*
* @param font
* the new font
*/
public void setFont(Font font) {
super.setFont(font);
head.setFont(font);
}
/**
* Sets the text to be rendered at the top of the form above the body as a
* title.
* <p>
* <strong>Note:</strong> Mnemonics are indicated by an '&amp;' that causes
* the next character to be the mnemonic. Mnemonics are not applicable in
* the case of the form title but need to be taken into acount due to the
* usage of the underlying widget that renders mnemonics in the title area.
* The mnemonic indicator character '&amp;' can be escaped by doubling it in
* the string, causing a single '&amp;' to be displayed.
* </p>
*
* @param text
* the title text
*/
public void setText(String text) {
head.setText(text);
layout();
redraw();
}
/**
* Sets the image to be rendered to the left of the title. This image will
* be temporarily hidden in two cases:
*
* <ol>
* <li>When the form is busy - replaced with a busy animation</li>
* <li>When the form has message set - replaced with the image indicating
* message severity</li>
* </ol>
*
* @param image
* the title image or <code>null</code> to show no image.
* @since 3.2
*/
public void setImage(Image image) {
head.setImage(image);
layout();
redraw();
}
/**
* Sets the background colors to be painted behind the title text in a
* gradient. Note that this method will reset color previously set by
* {@link #setBackground(Color)}. This is necessary for the simulated
* transparency of the heading in all of its children control.
*
* @param gradientColors
* the array of colors that form the gradient
* @param percents
* the partition of the overall space between the gradient colors
* @param vertical
* of <code>true</code>, the gradient will be rendered
* vertically, if <code>false</code> the orientation will be
* horizontal.
*/
public void setTextBackground(Color[] gradientColors, int[] percents,
boolean vertical) {
head.setTextBackground(gradientColors, percents, vertical);
}
/**
* Returns the optional background image of the form head.
*
* @return the background image or <code>null</code> if not specified.
*/
public Image getBackgroundImage() {
return head.getHeadingBackgroundImage();
}
/**
* Sets the optional background image to be rendered behind the title
* starting at the position 0,0. If the image is smaller than the container
* in any dimension, it will be tiled.
*
* @since 3.2
*
* @param backgroundImage
* the head background image.
*
*/
public void setBackgroundImage(Image backgroundImage) {
head.setHeadingBackgroundImage(backgroundImage);
}
/**
* Returns the tool bar manager that is used to manage tool items in the
* form's title area.
*
* @return form tool bar manager
*/
public IToolBarManager getToolBarManager() {
return head.getToolBarManager();
}
/**
* Sets the tool bar vertical alignment relative to the header. Can be
* useful when there is more free space at the second row (with the head
* client).
*
* @param alignment
* SWT.TOP or SWT.BOTTOM
* @since 3.3
*/
public void setToolBarVerticalAlignment(int alignment) {
head.setToolBarAlignment(alignment);
}
/**
* Returns the current tool bar alignment (if used).
*
* @return SWT.TOP or SWT.BOTTOM
* @since 3.3
*/
public int getToolBarVerticalAlignment() {
return head.getToolBarAlignment();
}
/**
* Returns the menu manager that is used to manage title area drop-down menu
* items.
*
* @return title area drop-down menu manager
* @since 3.3
*/
public IMenuManager getMenuManager() {
return head.getMenuManager();
}
/**
* Updates the local tool bar manager if used. Does nothing if local tool
* bar manager has not been created yet.
*/
public void updateToolBar() {
head.updateToolBar();
}
/**
* Returns the container that occupies the head of the form (the form area
* above the body). Use this container as a parent for the head client.
*
* @return the head of the form.
* @since 3.2
*/
public Composite getHead() {
return head;
}
/**
* Returns the optional head client if set.
*
* @return the head client or <code>null</code> if not set.
* @see #setHeadClient(Control)
* @since 3.2
*/
public Control getHeadClient() {
return head.getHeadClient();
}
/**
* Sets the optional head client. Head client is placed after the form
* title. This option causes the tool bar to be placed in the second raw of
* the header (below the head client).
* <p>
* The head client must be a child of the composite returned by
* <code>getHead()</code> method.
*
* @param headClient
* the optional child of the head
* @since 3.2
*/
public void setHeadClient(Control headClient) {
head.setHeadClient(headClient);
layout();
}
/**
* Returns the container that occupies the body of the form (the form area
* below the title). Use this container as a parent for the controls that
* should be in the form. No layout manager has been set on the form body.
*
* @return Returns the body of the form.
*/
public Composite getBody() {
return body;
}
/**
* Tests if the background image is tiled to cover the entire area of the
* form heading.
*
* @return <code>true</code> if heading background image is tiled,
* <code>false</code> otherwise.
*/
public boolean isBackgroundImageTiled() {
return head.isBackgroundImageTiled();
}
/**
* Sets whether the header background image is repeated to cover the entire
* heading area or not.
*
* @param backgroundImageTiled
* set <code>true</code> to tile the image, or
* <code>false</code> to paint the background image only once
* at 0,0
*/
public void setBackgroundImageTiled(boolean backgroundImageTiled) {
head.setBackgroundImageTiled(backgroundImageTiled);
}
/**
* Returns the background image alignment.
*
* @deprecated due to the underlying widget limitations, background image is
* either painted at 0,0 and/or tiled.
* @return SWT.LEFT
*/
public int getBackgroundImageAlignment() {
return SWT.LEFT;
}
/**
* Sets the background image alignment.
*
* @deprecated due to the underlying widget limitations, background image is
* always tiled and alignment cannot be controlled.
* @param backgroundImageAlignment
* The backgroundImageAlignment to set.
* @since 3.1
*/
public void setBackgroundImageAlignment(int backgroundImageAlignment) {
}
/**
* Tests if background image is clipped.
*
* @deprecated due to the underlying widget limitations, background image is
* always clipped.
* @return true
* @since 3.1
*/
public boolean isBackgroundImageClipped() {
return true;
}
/**
* Sets whether the background image is clipped.
*
* @deprecated due to the underlying widget limitations, background image is
* always clipped.
* @param backgroundImageClipped
* the value to set
* @since 3.1
*/
public void setBackgroundImageClipped(boolean backgroundImageClipped) {
}
/**
* Tests if the form head separator is visible.
*
* @return <code>true</code> if the head/body separator is visible,
* <code>false</code> otherwise
* @since 3.2
*/
public boolean isSeparatorVisible() {
return head.isSeparatorVisible();
}
/**
* If set, adds a separator between the head and body. Since 3.3, the colors
* that are used to render it are {@link IFormColors#H_BOTTOM_KEYLINE1} and
* {@link IFormColors#H_BOTTOM_KEYLINE2}.
*
* @param addSeparator
* <code>true</code> to make the separator visible,
* <code>false</code> otherwise.
* @since 3.2
*/
public void setSeparatorVisible(boolean addSeparator) {
head.setSeparatorVisible(addSeparator);
}
/**
* Returns the color used to render the optional head separator. If gradient
* text background is used additional colors from the gradient will be used
* to render the separator.
*
* @return separator color or <code>null</code> if not set.
* @since 3.2
* @deprecated use <code>getHeadColor(IFormColors.H_BOTTOM_KEYLINE2)</code>
*/
public Color getSeparatorColor() {
return head.getColor(IFormColors.H_BOTTOM_KEYLINE2);
}
/**
* Sets the color to be used to render the optional head separator.
*
* @param separatorColor
* the color to render the head separator or <code>null</code>
* to use the default color.
* @since 3.2
* @deprecated use
* <code>setHeadColor(IFormColors.H_BOTTOM_KEYLINE2, separatorColor)</code>
*/
public void setSeparatorColor(Color separatorColor) {
head.putColor(IFormColors.H_BOTTOM_KEYLINE2, separatorColor);
}
/**
* Sets the color used to paint an aspect of the form heading.
*
* @param key
* a valid form heading color key as defined in
* {@link IFormColors}. Relevant keys all start with an H_
* prefix.
* @param color
* the color to use for the provided key
* @since 3.3
*/
public void setHeadColor(String key, Color color) {
head.putColor(key, color);
}
/**
* Returns the color that is currently use to paint an aspect of the form
* heading, or <code>null</code> if not defined.
*
* @param key
* the color key
* @return the color object or <code>null</code> if not set.
* @since 3.3
*/
public Color getHeadColor(String key) {
return head.getColor(key);
}
/**
* Sets the message for this form. Message text is rendered in the form head
* when shown.
*
* @param message
* the message, or <code>null</code> to clear the message
* @see #setMessage(String, int)
* @since 3.2
*/
public void setMessage(String message) {
this.setMessage(message, 0, null);
}
/**
* Sets the message for this form with an indication of what type of message
* it is.
* <p>
* The valid message types are one of <code>NONE</code>,
* <code>INFORMATION</code>,<code>WARNING</code>, or
* <code>ERROR</code> defined in IMessageProvider interface.
* </p>
*
* @param newMessage
* the message, or <code>null</code> to clear the message
* @param newType
* the message type
* @see org.eclipse.jface.dialogs.IMessageProvider
* @since 3.2
*/
public void setMessage(String newMessage, int newType) {
this.setMessage(newMessage, newType, null);
}
/**
* Sets the message for this form with an indication of what type of message
* it is.
* <p>
* The valid message types are one of <code>NONE</code>,
* <code>INFORMATION</code>,<code>WARNING</code>, or
* <code>ERROR</code> defined in IMessageProvider interface.
* </p>
* <p>
* In addition to the summary message, this method also sets an array of
* individual messages.
*
*
* @param newMessage
* the message, or <code>null</code> to clear the message
* @param newType
* the message type
* @param children
* the individual messages that contributed to the overall
* message
* @see org.eclipse.jface.dialogs.IMessageProvider
* @since 3.3
*/
public void setMessage(String newMessage, int newType, IMessage[] children) {
head.showMessage(newMessage, newType, children);
layout();
}
/**
* Adds a message hyperlink listener. If at least one listener is present,
* messages will be rendered as hyperlinks.
*
* @param listener
* @see #removeMessageHyperlinkListener(IHyperlinkListener)
* @since 3.3
*/
public void addMessageHyperlinkListener(IHyperlinkListener listener) {
head.addMessageHyperlinkListener(listener);
}
/**
* Remove the message hyperlink listener.
*
* @param listener
* @see #addMessageHyperlinkListener(IHyperlinkListener)
* @since 3.3
*/
public void removeMessageHyperlinkListener(IHyperlinkListener listener) {
head.removeMessageHyperlinkListener(listener);
}
/**
* Tests if the form is in the 'busy' state. Busy form displays 'busy'
* animation in the area of the title image.
*
* @return <code>true</code> if busy, <code>false</code> otherwise.
* @since 3.2
*/
public boolean isBusy() {
return head.isBusy();
}
/**
* Sets the form's busy state. Busy form will display 'busy' animation in
* the area of the title image.
*
* @param busy
* the form's busy state
* @since 3.2
*/
public void setBusy(boolean busy) {
head.setBusy(busy);
}
/**
* Adds support for dragging items out of the form title area via a user
* drag-and-drop operation.
*
* @param operations
* a bitwise OR of the supported drag and drop operation types (
* <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
* <code>DROP_MOVE</code>)
* @param transferTypes
* the transfer types that are supported by the drag operation
* @param listener
* the callback that will be invoked to set the drag data and to
* cleanup after the drag and drop operation finishes
* @see org.eclipse.swt.dnd.DND
* @since 3.3
*/
public void addTitleDragSupport(int operations, Transfer[] transferTypes,
DragSourceListener listener) {
head.addDragSupport(operations, transferTypes, listener);
}
/**
* Adds support for dropping items into the form title area via a user
* drag-and-drop operation.
*
* @param operations
* a bitwise OR of the supported drag and drop operation types (
* <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
* <code>DROP_MOVE</code>)
* @param transferTypes
* the transfer types that are supported by the drop operation
* @param listener
* the callback that will be invoked after the drag and drop
* operation finishes
* @see org.eclipse.swt.dnd.DND
* @since 3.3
*/
public void addTitleDropSupport(int operations, Transfer[] transferTypes,
DropTargetListener listener) {
head.addDropSupport(operations, transferTypes, listener);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.IMessageProvider#getMessage()
*/
public String getMessage() {
return head.getMessage();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType()
*/
public int getMessageType() {
return head.getMessageType();
}
/**
* Returns the children messages that the cause of the summary message
* currently set on the form.
*
* @return an array of children messages or <code>null</code> if not set.
* @see #setMessage(String, int, IMessage[])
* @since 3.3
*/
public IMessage[] getChildrenMessages() {
return head.getChildrenMessages();
}
void setSelectionText(FormText text) {
if (selectionText != null && selectionText != text) {
selectionText.clearSelection();
}
this.selectionText = text;
}
}