blob: 6cd08b7e30b7d65d2fd054df4fa2ec13db46e104 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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
* Michael Williamson (eclipse-bugs@magnaworks.com) - patch (see Bugzilla #92545)
*******************************************************************************/
package org.eclipse.ui.forms.widgets;
import java.io.File;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.forms.*;
import org.eclipse.ui.internal.forms.widgets.FormUtil;
/**
* The toolkit is responsible for creating SWT controls adapted to work in
* Eclipse forms. In addition to changing their presentation properties (fonts,
* colors etc.), various listeners are attached to make them behave correctly in
* the form context.
* <p>
* In addition to being the control factory, the toolkit is also responsible for
* painting flat borders for select controls, managing hyperlink groups and
* control colors.
* <p>
* The toolkit creates some of the most common controls used to populate Eclipse
* forms. Controls that must be created using their constructors,
* <code>adapt()</code> method is available to change its properties in the
* same way as with the supported toolkit controls.
* <p>
* Typically, one toolkit object is created per workbench part (for example, an
* editor or a form wizard). The toolkit is disposed when the part is disposed.
* To conserve resources, it is possible to create one color object for the
* entire plug-in and share it between several toolkits. The plug-in is
* responsible for disposing the colors (disposing the toolkit that uses shared
* color object will not dispose the colors).
* <p>
* FormToolkit is normally instantiated, but can also be subclassed if some of
* the methods needs to be modified. In those cases, <code>super</code> must
* be called to preserve normal behaviour.
*
* @since 3.0
*/
public class FormToolkit {
public static final String KEY_DRAW_BORDER = "FormWidgetFactory.drawBorder"; //$NON-NLS-1$
public static final String TREE_BORDER = "treeBorder"; //$NON-NLS-1$
public static final String TEXT_BORDER = "textBorder"; //$NON-NLS-1$
private int borderStyle = SWT.NULL;
private FormColors colors;
private int orientation = Window.getDefaultOrientation();
// private KeyListener deleteListener;
private BorderPainter borderPainter;
private BoldFontHolder boldFontHolder;
private HyperlinkGroup hyperlinkGroup;
/* default */
VisibilityHandler visibilityHandler;
/* default */
KeyboardHandler keyboardHandler;
private class BorderPainter implements PaintListener {
public void paintControl(PaintEvent event) {
Composite composite = (Composite) event.widget;
Control[] children = composite.getChildren();
for (int i = 0; i < children.length; i++) {
Control c = children[i];
boolean inactiveBorder = false;
boolean textBorder = false;
if (!c.isVisible())
continue;
if (c.getEnabled() == false && !(c instanceof CCombo))
continue;
if (c instanceof Hyperlink)
continue;
Object flag = c.getData(KEY_DRAW_BORDER);
if (flag != null) {
if (flag.equals(Boolean.FALSE))
continue;
if (flag.equals(TREE_BORDER))
inactiveBorder = true;
else if (flag.equals(TEXT_BORDER))
textBorder = true;
}
if (getBorderStyle() == SWT.BORDER) {
if (!inactiveBorder && !textBorder) {
continue;
}
if (c instanceof Text || c instanceof Table
|| c instanceof Tree || c instanceof TableTree)
continue;
}
if (!inactiveBorder
&& (c instanceof Text || c instanceof CCombo || textBorder)) {
Rectangle b = c.getBounds();
GC gc = event.gc;
gc.setForeground(c.getBackground());
gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1,
b.height + 1);
// gc.setForeground(getBorderStyle() == SWT.BORDER ? colors
// .getBorderColor() : colors.getForeground());
gc.setForeground(colors.getBorderColor());
if (c instanceof CCombo)
gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1,
b.height + 1);
else
gc.drawRectangle(b.x - 1, b.y - 2, b.width + 1,
b.height + 3);
} else if (inactiveBorder || c instanceof Table
|| c instanceof Tree || c instanceof TableTree) {
Rectangle b = c.getBounds();
GC gc = event.gc;
gc.setForeground(colors.getBorderColor());
gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1,
b.height + 1);
}
}
}
}
private static class VisibilityHandler extends FocusAdapter {
public void focusGained(FocusEvent e) {
Widget w = e.widget;
if (w instanceof Control) {
FormUtil.ensureVisible((Control) w);
}
}
}
private static class KeyboardHandler extends KeyAdapter {
public void keyPressed(KeyEvent e) {
Widget w = e.widget;
if (w instanceof Control) {
if (e.doit)
FormUtil.processKey(e.keyCode, (Control) w);
}
}
}
private class BoldFontHolder {
private Font normalFont;
private Font boldFont;
public BoldFontHolder() {
}
public Font getBoldFont(Font font) {
createBoldFont(font);
return boldFont;
}
private void createBoldFont(Font font) {
if (normalFont == null || !normalFont.equals(font)) {
normalFont = font;
dispose();
}
if (boldFont == null) {
boldFont = FormUtil.createBoldFont(colors.getDisplay(),
normalFont);
}
}
public void dispose() {
if (boldFont != null) {
boldFont.dispose();
boldFont = null;
}
}
}
/**
* Creates a toolkit that is self-sufficient (will manage its own colors).
*
*/
public FormToolkit(Display display) {
this(new FormColors(display));
}
/**
* Creates a toolkit that will use the provided (shared) colors. The toolkit
* will <b>not </b> dispose the provided colors.
*
* @param colors
* the shared colors
*/
public FormToolkit(FormColors colors) {
this.colors = colors;
initialize();
}
/**
* Creates a button as a part of the form.
*
* @param parent
* the button parent
* @param text
* an optional text for the button (can be <code>null</code>)
* @param style
* the button style (for example, <code>SWT.PUSH</code>)
* @return the button widget
*/
public Button createButton(Composite parent, String text, int style) {
Button button = new Button(parent, style | SWT.FLAT | orientation);
if (text != null)
button.setText(text);
adapt(button, true, true);
return button;
}
/**
* Creates the composite as a part of the form.
*
* @param parent
* the composite parent
* @return the composite widget
*/
public Composite createComposite(Composite parent) {
return createComposite(parent, SWT.NULL);
}
/**
* Creates the composite as part of the form using the provided style.
*
* @param parent
* the composite parent
* @param style
* the composite style
* @return the composite widget
*/
public Composite createComposite(Composite parent, int style) {
Composite composite = new LayoutComposite(parent, style | orientation);
adapt(composite);
return composite;
}
/**
* Creats the composite that can server as a separator between various parts
* of a form. Separator height should be controlled by setting the height
* hint on the layout data for the composite.
*
* @param parent
* the separator parent
* @return the separator widget
*/
public Composite createCompositeSeparator(Composite parent) {
final Composite composite = new Composite(parent, orientation);
composite.addListener(SWT.Paint, new Listener() {
public void handleEvent(Event e) {
if (composite.isDisposed())
return;
Rectangle bounds = composite.getBounds();
GC gc = e.gc;
gc.setForeground(colors.getColor(FormColors.SEPARATOR));
if (colors.getBackground() != null)
gc.setBackground(colors.getBackground());
gc.fillGradientRectangle(0, 0, bounds.width, bounds.height,
false);
}
});
if (parent instanceof Section)
((Section) parent).setSeparatorControl(composite);
return composite;
}
/**
* Creates a label as a part of the form.
*
* @param parent
* the label parent
* @param text
* the label text
* @return the label widget
*/
public Label createLabel(Composite parent, String text) {
return createLabel(parent, text, SWT.NONE);
}
/**
* Creates a label as a part of the form.
*
* @param parent
* the label parent
* @param text
* the label text
* @param style
* the label style
* @return the label widget
*/
public Label createLabel(Composite parent, String text, int style) {
Label label = new Label(parent, style | orientation);
if (text != null)
label.setText(text);
adapt(label, false, false);
return label;
}
/**
* Creates a hyperlink as a part of the form. The hyperlink will be added to
* the hyperlink group that belongs to this toolkit.
*
* @param parent
* the hyperlink parent
* @param text
* the text of the hyperlink
* @param style
* the hyperlink style
* @return the hyperlink widget
*/
public Hyperlink createHyperlink(Composite parent, String text, int style) {
Hyperlink hyperlink = new Hyperlink(parent, style | orientation);
if (text != null)
hyperlink.setText(text);
hyperlink.addFocusListener(visibilityHandler);
hyperlink.addKeyListener(keyboardHandler);
hyperlinkGroup.add(hyperlink);
return hyperlink;
}
/**
* Creates an image hyperlink as a part of the form. The hyperlink will be
* added to the hyperlink group that belongs to this toolkit.
*
* @param parent
* the hyperlink parent
* @param style
* the hyperlink style
* @return the image hyperlink widget
*/
public ImageHyperlink createImageHyperlink(Composite parent, int style) {
ImageHyperlink hyperlink = new ImageHyperlink(parent, style
| orientation);
hyperlink.addFocusListener(visibilityHandler);
hyperlink.addKeyListener(keyboardHandler);
hyperlinkGroup.add(hyperlink);
return hyperlink;
}
/**
* Creates a rich text as a part of the form.
*
* @param parent
* the rich text parent
* @param trackFocus
* if <code>true</code>, the toolkit will monitor focus
* transfers to ensure that the hyperlink in focus is visible in
* the form.
* @return the rich text widget
*/
public FormText createFormText(Composite parent, boolean trackFocus) {
FormText engine = new FormText(parent, SWT.WRAP | orientation);
engine.marginWidth = 1;
engine.marginHeight = 0;
engine.setHyperlinkSettings(getHyperlinkGroup());
adapt(engine, trackFocus, true);
engine.setMenu(parent.getMenu());
return engine;
}
/**
* Adapts a control to be used in a form that is associated with this
* toolkit. This involves adjusting colors and optionally adding handlers to
* ensure focus tracking and keyboard management.
*
* @param control
* a control to adapt
* @param trackFocus
* if <code>true</code>, form will be scrolled horizontally
* and/or vertically if needed to ensure that the control is
* visible when it gains focus. Set it to <code>false</code> if
* the control is not capable of gaining focus.
* @param trackKeyboard
* if <code>true</code>, the control that is capable of
* gaining focus will be tracked for certain keys that are
* important to the underlying form (for example, PageUp,
* PageDown, ScrollUp, ScrollDown etc.). Set it to
* <code>false</code> if the control is not capable of gaining
* focus or these particular key event are already used by the
* control.
*/
public void adapt(Control control, boolean trackFocus, boolean trackKeyboard) {
control.setBackground(colors.getBackground());
control.setForeground(colors.getForeground());
if (control instanceof ExpandableComposite) {
ExpandableComposite ec = (ExpandableComposite)control;
if (ec.toggle != null) {
if (trackFocus)
ec.toggle.addFocusListener(visibilityHandler);
if (trackKeyboard)
ec.toggle.addKeyListener(keyboardHandler);
}
if (ec.textLabel != null) {
if (trackFocus)
ec.textLabel.addFocusListener(visibilityHandler);
if (trackKeyboard)
ec.textLabel.addKeyListener(keyboardHandler);
}
return;
}
if (trackFocus)
control.addFocusListener(visibilityHandler);
if (trackKeyboard)
control.addKeyListener(keyboardHandler);
}
/**
* Adapts a composite to be used in a form associated with this toolkit.
*
* @param composite
* the composite to adapt
*/
public void adapt(Composite composite) {
composite.setBackground(colors.getBackground());
composite.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
((Control) e.widget).setFocus();
}
});
composite.setMenu(composite.getParent().getMenu());
}
/**
* A helper method that ensures the provided control is visible when
* ScrolledComposite is somewhere in the parent chain. If scroll bars are
* visible and the control is clipped, the client of the scrolled composite
* will be scrolled to reveal the control.
*
* @param c
* the control to reveal
*/
public static void ensureVisible(Control c) {
FormUtil.ensureVisible(c);
}
/**
* Creates a section as a part of the form.
*
* @param parent
* the section parent
* @param sectionStyle
* the section style
* @return the section widget
*/
public Section createSection(Composite parent, int sectionStyle) {
Section section = new Section(parent, orientation, sectionStyle);
section.setMenu(parent.getMenu());
adapt(section, true, true);
if (section.toggle != null) {
section.toggle.setHoverDecorationColor(colors
.getColor(FormColors.TB_TOGGLE_HOVER));
section.toggle.setDecorationColor(colors
.getColor(FormColors.TB_TOGGLE));
}
section.setFont(boldFontHolder.getBoldFont(parent.getFont()));
if ((sectionStyle & Section.TITLE_BAR) != 0
|| (sectionStyle & Section.SHORT_TITLE_BAR) != 0) {
colors.initializeSectionToolBarColors();
section.setTitleBarBackground(colors.getColor(FormColors.TB_GBG));
section.setTitleBarBorderColor(colors
.getColor(FormColors.TB_BORDER));
section.setTitleBarGradientBackground(colors
.getColor(FormColors.TB_GBG));
section.setTitleBarForeground(colors.getColor(FormColors.TB_FG));
}
return section;
}
/**
* Creates an expandable composite as a part of the form.
*
* @param parent
* the expandable composite parent
* @param expansionStyle
* the expandable composite style
* @return the expandable composite widget
*/
public ExpandableComposite createExpandableComposite(Composite parent,
int expansionStyle) {
ExpandableComposite ec = new ExpandableComposite(parent, orientation,
expansionStyle);
ec.setMenu(parent.getMenu());
adapt(ec, true, true);
ec.setFont(boldFontHolder.getBoldFont(ec.getFont()));
return ec;
}
/**
* Creates a separator label as a part of the form.
*
* @param parent
* the separator parent
* @param style
* the separator style
* @return the separator label
*/
public Label createSeparator(Composite parent, int style) {
Label label = new Label(parent, SWT.SEPARATOR | style | orientation);
label.setBackground(colors.getBackground());
label.setForeground(colors.getBorderColor());
return label;
}
/**
* Creates a table as a part of the form.
*
* @param parent
* the table parent
* @param style
* the table style
* @return the table widget
*/
public Table createTable(Composite parent, int style) {
Table table = new Table(parent, style | borderStyle | orientation);
adapt(table, false, false);
// hookDeleteListener(table);
return table;
}
/**
* Creates a text as a part of the form.
*
* @param parent
* the text parent
* @param value
* the text initial value
* @return the text widget
*/
public Text createText(Composite parent, String value) {
return createText(parent, value, SWT.SINGLE);
}
/**
* Creates a text as a part of the form.
*
* @param parent
* the text parent
* @param value
* the text initial value
* @param style
* the text style
* @return the text widget
*/
public Text createText(Composite parent, String value, int style) {
Text text = new Text(parent, borderStyle | style | orientation);
if (value != null)
text.setText(value);
text.setForeground(colors.getForeground());
text.addFocusListener(visibilityHandler);
return text;
}
/**
* Creates a tree widget as a part of the form.
*
* @param parent
* the tree parent
* @param style
* the tree style
* @return the tree widget
*/
public Tree createTree(Composite parent, int style) {
Tree tree = new Tree(parent, borderStyle | style | orientation);
adapt(tree, false, false);
// hookDeleteListener(tree);
return tree;
}
/**
* Creates a scrolled form widget in the provided parent. If you do not
* require scrolling because there is already a scrolled composite up the
* parent chain, use 'createForm' instead.
*
* @param parent
* the scrolled form parent
* @return the form that can scroll itself
* @see #createForm
*/
public ScrolledForm createScrolledForm(Composite parent) {
ScrolledForm form = new ScrolledForm(parent, SWT.V_SCROLL
| SWT.H_SCROLL | orientation);
form.setExpandHorizontal(true);
form.setExpandVertical(true);
form.setBackground(colors.getBackground());
form.setForeground(colors.getColor(FormColors.TITLE));
colors.initializeSectionToolBarColors();
form.getForm().setSeparatorColor(colors.getColor(FormColors.TB_BORDER));
form.setFont(JFaceResources.getHeaderFont());
return form;
}
/**
* Creates a form widget in the provided parent. Note that this widget does
* not scroll its content, so make sure there is a scrolled composite up the
* parent chain. If you require scrolling, use 'createScrolledForm' instead.
*
* @param parent
* the form parent
* @return the form that does not scroll
* @see #createScrolledForm
*/
public Form createForm(Composite parent) {
Form formContent = new Form(parent, orientation);
formContent.setBackground(colors.getBackground());
formContent.setForeground(colors.getColor(FormColors.TITLE));
colors.initializeSectionToolBarColors();
formContent.setSeparatorColor(colors.getColor(FormColors.TB_BORDER));
formContent.setFont(JFaceResources.getHeaderFont());
return formContent;
}
/**
* Creates a scrolled page book widget as a part of the form.
*
* @param parent
* the page book parent
* @param style
* the text style
* @return the scrolled page book widget
*/
public ScrolledPageBook createPageBook(Composite parent, int style) {
ScrolledPageBook book = new ScrolledPageBook(parent, style
| orientation);
adapt(book, true, true);
book.setMenu(parent.getMenu());
return book;
}
/**
* Disposes the toolkit.
*/
public void dispose() {
if (colors.isShared() == false) {
colors.dispose();
colors = null;
}
boldFontHolder.dispose();
}
/**
* Returns the hyperlink group that manages hyperlinks for this toolkit.
*
* @return the hyperlink group
*/
public HyperlinkGroup getHyperlinkGroup() {
return hyperlinkGroup;
}
/**
* Sets the background color for the entire toolkit. The method delegates
* the call to the FormColors object and also updates the hyperlink group so
* that hyperlinks and other objects are in sync.
*
* @param bg
* the new background color
*/
public void setBackground(Color bg) {
hyperlinkGroup.setBackground(bg);
colors.setBackground(bg);
}
/**
* Refreshes the hyperlink colors by loading from JFace settings.
*/
public void refreshHyperlinkColors() {
hyperlinkGroup.initializeDefaultForegrounds(colors.getDisplay());
}
/**
* Paints flat borders for widgets created by this toolkit within the
* provided parent. Borders will not be painted if the global border style
* is SWT.BORDER (i.e. if native borders are used). Call this method during
* creation of a form composite to get the borders of its children painted.
* Care should be taken when selection layout margins. At least one pixel
* pargin width and height must be chosen to allow the toolkit to paint the
* border on the parent around the widgets.
* <p>
* Borders are painted for some controls that are selected by the toolkit by
* default. If a control needs a border but is not on its list, it is
* possible to force border in the following way:
*
* <pre>
*
*
*
* widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
*
* or
*
* widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
*
*
*
* </pre>
*
* @param parent
* the parent that owns the children for which the border needs
* to be painted.
*/
public void paintBordersFor(Composite parent) {
// if (borderStyle == SWT.BORDER)
// return;
if (borderPainter == null)
borderPainter = new BorderPainter();
parent.addPaintListener(borderPainter);
}
/**
* Returns the colors used by this toolkit.
*
* @return the color object
*/
public FormColors getColors() {
return colors;
}
/**
* Returns the border style used for various widgets created by this
* toolkit. The intent of the toolkit is to create controls with styles that
* yield a 'flat' appearance. On systems where the native borders are
* already flat, we set the style to SWT.BORDER and don't paint the borders
* ourselves. Otherwise, the style is set to SWT.NULL, and borders are
* painted by the toolkit.
*
* @return the global border style
*/
public int getBorderStyle() {
return borderStyle;
}
/**
* Sets the border style to be used when creating widgets. The toolkit
* chooses the correct style based on the platform but this value can be
* changed using this method.
*
* @param style
* <code>SWT.BORDER</code> or <code>SWT.NULL</code>
* @see #getBorderStyle
*/
public void setBorderStyle(int style) {
this.borderStyle = style;
}
/**
* A utility method that ensures that the control is visible in the scrolled
* composite. The prerequisite for this method is that the control has a
* class that extends ScrolledComposite somewhere in the parent chain. If
* the control is partially or fully clipped, the composite is scrolled to
* set by setting the origin to the control origin.
*
* @param c
* the control to make visible
* @param verticalOnly
* if <code>true</code>, the scrolled composite will be
* scrolled only vertically if needed. Otherwise, the scrolled
* composite origin will be set to the control origin.
* @since 3.1
*/
public static void setControlVisible(Control c, boolean verticalOnly) {
ScrolledComposite scomp = FormUtil.getScrolledComposite(c);
if (scomp == null)
return;
Point location = FormUtil.getControlLocation(scomp, c);
scomp.setOrigin(location);
}
private void initialize() {
initializeBorderStyle();
hyperlinkGroup = new HyperlinkGroup(colors.getDisplay());
hyperlinkGroup.setBackground(colors.getBackground());
visibilityHandler = new VisibilityHandler();
keyboardHandler = new KeyboardHandler();
boldFontHolder = new BoldFontHolder();
}
private void initializeBorderStyle() {
String osname = System.getProperty("os.name"); //$NON-NLS-1$
if (osname.equals("Windows XP")) { //$NON-NLS-1$
String javaHome = System.getProperty("java.home"); //$NON-NLS-1$
File homeDir = new File(javaHome);
File binDir = new File(homeDir, "bin"); //$NON-NLS-1$
File manifest = new File(binDir, "javaw.exe.manifest"); //$NON-NLS-1$
if (manifest.exists()) {
// Skinned widgets used - check for Windows Classic
// If not used, set the style to BORDER
RGB rgb = colors.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
if (rgb.red != 212 && rgb.green != 208 && rgb.blue != 200)
borderStyle = SWT.BORDER;
}
} else if (osname.startsWith("Mac")) //$NON-NLS-1$
borderStyle = SWT.BORDER;
}
/**
* Returns the orientation that all the widgets created by this toolkit will
* inherit, if set. Can be <code>SWT.NULL</code>,
* <code>SWT.LEFT_TO_RIGHT</code> and <code>SWT.RIGHT_TO_LEFT</code>.
*
* @return orientation style for this toolkit, or <code>SWT.NULL</code> if
* not set. The default orientation is inherited from the Window
* default orientation.
* @see org.eclipse.jface.window.Window#getDefaultOrientation()
* @since 3.1
*/
public int getOrientation() {
return orientation;
}
/**
* Sets the orientation that all the widgets created by this toolkit will
* inherit. Can be <code>SWT.NULL</code>, <code>SWT.LEFT_TO_RIGHT</code>
* and <code>SWT.RIGHT_TO_LEFT</code>.
*
* @param orientation
* style for this toolkit.
* @since 3.1
*/
public void setOrientation(int orientation) {
this.orientation = orientation;
}
}