/******************************************************************************* * Copyright (c) 2000, 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: * 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.IMessageManager; import org.eclipse.ui.forms.events.IHyperlinkListener; import org.eclipse.ui.internal.forms.MessageManager; 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("Sample form"); * form.getBody().setLayout(new GridLayout()); * toolkit.createButton(form.getBody(), "Checkbox", 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 1.0 * @noextend This class is not intended to be subclassed by clients. */ 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 MessageManager messageManager; 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) { // RAP [rh] SWT.NO_BACKGROUND not implemented // super(parent, SWT.NO_BACKGROUND | style); super(parent, 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. */ 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 '&' 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 '&' can be escaped by doubling it in * the string, causing a single '&' 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. */ 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. * * @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 */ public void setToolBarVerticalAlignment(int alignment) { head.setToolBarAlignment(alignment); } /** * Returns the current tool bar alignment (if used). * * @return SWT.TOP or SWT.BOTTOM */ 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 */ 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. */ 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) */ 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 */ 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. */ 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 */ 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 */ 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 */ 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. */ 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. * @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. * @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 */ 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. */ 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) */ 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 */ 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 */ 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) */ public void addMessageHyperlinkListener(IHyperlinkListener listener) { head.addMessageHyperlinkListener(listener); } /** * Remove the message hyperlink listener. * * @param listener * @see #addMessageHyperlinkListener(IHyperlinkListener) */ 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. */ 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 */ 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[]) */ public IMessage[] getChildrenMessages() { return head.getChildrenMessages(); } void setSelectionText(FormText text) { if (selectionText != null && selectionText != text) { selectionText.clearSelection(); } this.selectionText = text; } /** * Returns the message manager that will keep track of messages in this * form. * * @return the message manager instance */ public IMessageManager getMessageManager() { if (messageManager == null) messageManager = new MessageManager(this); return messageManager; } }