Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2016 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 * Kai Nacke - Fix for Bug 202382 * Bryan Hunt - Fix for Bug 245457 * Didier Villevalois - Fix for Bug 178534 * Robin Stocker - Fix for Bug 193034 (tool tip also on text) * Alena Laskavaia - Bug 481604 *******************************************************************************/ package org.eclipse.ui.forms.widgets; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.ListenerList; import org.eclipse.osgi.service.environment.Constants; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.forms.events.ExpansionEvent; import org.eclipse.ui.forms.events.HyperlinkAdapter; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.events.IExpansionListener; import org.eclipse.ui.internal.forms.widgets.FormUtil; import org.eclipse.ui.internal.forms.widgets.FormsResources; /** * This composite is capable of expanding or collapsing a single client that is * its direct child. The composite renders an expansion toggle affordance * (according to the chosen style), and a title that also acts as a hyperlink * (can be selected and is traversable). The client is layed out below the title * when expanded, or hidden when collapsed. * <p> * The widget can be instantiated as-is, or subclassed to modify some aspects of * it. * * <p> * Since 3.1, left/right arrow keys can be used to control the expansion state. * If several expandable composites are created in the same parent, up/down * arrow keys can be used to traverse between them. Expandable text accepts * mnemonics and mnemonic activation will toggle the expansion state. * * <p> * While expandable composite recognize that different styles can be used to * render the title bar, and even defines the constants for these styles (<code>TITLE_BAR</code> * and <code>SHORT_TITLE_BAR</code> the actual painting is done in the * subclasses. * * @see Section * @since 3.0 */ public class ExpandableComposite extends Canvas { /** * If this style is used, a twistie will be used to render the expansion * toggle. */ public static final int TWISTIE = 1 << 1; /** * If this style is used, a tree node with either + or - signs will be used * to render the expansion toggle. */ public static final int TREE_NODE = 1 << 2; /** * If this style is used, the title text will be rendered as a hyperlink * that can individually accept focus. Otherwise, it will still act like a * hyperlink, but only the toggle control will accept focus. */ public static final int FOCUS_TITLE = 1 << 3; /** * If this style is used, the client origin will be vertically aligned with * the title text. Otherwise, it will start at x = 0. */ public static final int CLIENT_INDENT = 1 << 4; /** * If this style is used, computed size of the composite will take the * client width into consideration only in the expanded state. Otherwise, * client width will always be taken into acount. */ public static final int COMPACT = 1 << 5; /** * If this style is used, the control will be created in the expanded state. * This state can later be changed programmatically or by the user if * TWISTIE or TREE_NODE style is used. */ public static final int EXPANDED = 1 << 6; /** * If this style is used, title bar decoration will be painted behind the * text. */ public static final int TITLE_BAR = 1 << 8; /** * If this style is used, a short version of the title bar decoration will * be painted behind the text. This style is useful when a more descrete * option is needed for the title bar. * * @since 3.1 */ public static final int SHORT_TITLE_BAR = 1 << 9; /** * If this style is used, title will not be rendered. */ public static final int NO_TITLE = 1 << 12; /** * By default, text client is right-aligned. If this style is used, it will * be positioned after the text control and vertically centered with it. */ public static final int LEFT_TEXT_CLIENT_ALIGNMENT = 1 << 13; /** * By default, a focus box is painted around the title when it receives focus. * If this style is used, the focus box will not be painted. This style does * not apply when FOCUS_TITLE is used. * @since 3.5 */ public static final int NO_TITLE_FOCUS_BOX = 1 << 14; /** * Width of the margin that will be added around the control (default is 0). */ public int marginWidth = 0; /** * Height of the margin that will be added around the control (default is * 0). */ public int marginHeight = 0; /** * Vertical spacing between the title area and the composite client control * (default is 3). */ public int clientVerticalSpacing = 3; /** * Vertical spacing between the title area and the description control * (default is 0). The description control is normally placed at the new * line as defined in the font used to render it. This value will be added * to it. * * @since 3.3 */ public int descriptionVerticalSpacing = 0; /** * Horizontal margin around the inside of the title bar area when TITLE_BAR * or SHORT_TITLE_BAR style is used. This variable is not used otherwise. * * @since 3.3 */ public int titleBarTextMarginWidth = 6; /** * The toggle widget used to expand the composite. */ protected ToggleHyperlink toggle; /** * The text label for the title. */ protected Control textLabel; /** * @deprecated this variable was left as protected by mistake. It will be * turned into static and hidden in the future versions. Do not * use them and do not change its value. */ @Deprecated protected int VGAP = 3; /** * @deprecated this variable was left as protected by mistake. It will be * turned into static and hidden in the future versions. Do not * use it and do not change its value. */ @Deprecated protected int GAP = 4; static final int IGAP = 4; static final int IVGAP = 3; private static final Point NULL_SIZE = new Point(0, 0); private static final int VSPACE = 3; private static final int SEPARATOR_HEIGHT = 2; private int expansionStyle = TWISTIE | FOCUS_TITLE | EXPANDED; private boolean expanded; private Control textClient; private Control client; private ListenerList<IExpansionListener> listeners = new ListenerList<>(); private Color titleBarForeground; private class ExpandableLayout extends Layout implements ILayoutExtension { private SizeCache toggleCache = new SizeCache(); private SizeCache textClientCache = new SizeCache(); private SizeCache textLabelCache = new SizeCache(); private SizeCache descriptionCache = new SizeCache(); private SizeCache clientCache = new SizeCache(); private void initCache(boolean shouldFlush) { toggleCache.setControl(toggle); textClientCache.setControl(textClient); textLabelCache.setControl(textLabel); descriptionCache.setControl(getDescriptionControl()); clientCache.setControl(client); if (shouldFlush) { toggleCache.flush(); textClientCache.flush(); textLabelCache.flush(); descriptionCache.flush(); clientCache.flush(); } } @Override protected void layout(Composite parent, boolean changed) { initCache(changed); Rectangle clientArea = parent.getClientArea(); int thmargin = 0; int tvmargin = 0; if (hasTitleBar()) { thmargin = titleBarTextMarginWidth; tvmargin = IVGAP; } int x = marginWidth + thmargin; int y = marginHeight + tvmargin; Point tsize = NULL_SIZE; Point tcsize = NULL_SIZE; if (toggle != null) tsize = toggleCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); int twidth = clientArea.width - marginWidth - marginWidth - thmargin - thmargin; if (tsize.x > 0) twidth -= tsize.x + IGAP; if (textClient != null) { tcsize = textClientCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); } Point size = NULL_SIZE; if (textLabel != null) { if (tcsize.x > 0 && FormUtil.isWrapControl(textClient)) { size = textLabelCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); if (twidth < size.x + IGAP + tcsize.x) { twidth -= IGAP; if (textLabel instanceof Label) { GC gc = new GC(textLabel); size = FormUtil.computeWrapSize(gc, ((Label) textLabel).getText(), Math.round(twidth * (size.x / (float) (size.x + tcsize.x)))); gc.dispose(); } else size = textLabelCache.computeSize( Math.round(twidth * (size.x / (float) (size.x + tcsize.x))), SWT.DEFAULT); tcsize = textClientCache.computeSize(twidth - size.x, SWT.DEFAULT); } } else { if (tcsize.x > 0) twidth -= tcsize.x + IGAP; size = textLabelCache.computeSize(twidth, SWT.DEFAULT); } } if (textLabel instanceof Label) { Point defSize = textLabelCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); if (defSize.y == size.y) { // One line - pick the smaller of the two widths size.x = Math.min(defSize.x, size.x); } } if (toggle != null) { GC gc = new GC(ExpandableComposite.this); gc.setFont(getFont()); FontMetrics fm = gc.getFontMetrics(); int textHeight = fm.getHeight(); gc.dispose(); if (textClient != null && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) != 0) { textHeight = Math.max(textHeight, tcsize.y); } int ty = textHeight / 2 - tsize.y / 2 + 1; ty = Math.max(ty, 0); ty += marginHeight + tvmargin; toggle.setLocation(x, ty); toggle.setSize(tsize); x += tsize.x + IGAP; } if (textLabel != null) { int ty = y; if (textClient != null && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) != 0) { if (size.y < tcsize.y) ty = tcsize.y / 2 - size.y / 2 + marginHeight + tvmargin; } String os = System.getProperty("os.name"); //$NON-NLS-1$ if (Constants.OS_LINUX.equalsIgnoreCase(os)) { size.x += 1; // See Bug 342610 } textLabelCache.setBounds(x, ty, size.x, size.y); } if (textClient != null) { int tcx; if ((expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) != 0) { tcx = x + size.x + GAP; } else { tcx = clientArea.width - tcsize.x - marginWidth - thmargin; } textClientCache.setBounds(tcx, y, tcsize.x, tcsize.y); } int height = Math.max(tcsize.y, size.y); // max of label/text client height = Math.max(height, tsize.y); // or max of toggle y += height; if (hasTitleBar()) y += tvmargin; Control separatorControl = getSeparatorControl(); if (separatorControl != null) { y += VSPACE; separatorControl.setBounds(marginWidth, y, clientArea.width - marginWidth - marginWidth, SEPARATOR_HEIGHT); y += SEPARATOR_HEIGHT; } if (expanded && client != null) { int areaWidth = clientArea.width - marginWidth - thmargin; int cx = marginWidth + thmargin; if ((expansionStyle & CLIENT_INDENT) != 0) { cx = x; } areaWidth -= cx; Control desc = getDescriptionControl(); if (desc != null) { if (separatorControl != null) { y += VSPACE; } Point dsize = descriptionCache.computeSize(areaWidth, SWT.DEFAULT); y += descriptionVerticalSpacing; descriptionCache.setBounds(cx, y, areaWidth, dsize.y); y += dsize.y; } y += clientVerticalSpacing; int cwidth = areaWidth; int cheight = clientArea.height - marginHeight - marginHeight - y; clientCache.setBounds(cx, y, cwidth, cheight); } } @Override protected Point computeSize(Composite parent, int wHint, int hHint, boolean changed) { initCache(changed); int width = 0; Point tsize = NULL_SIZE; int twidth = 0; if (toggle != null) { tsize = toggleCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); twidth = tsize.x + IGAP; } int thmargin = 0; int tvmargin = 0; if (hasTitleBar()) { thmargin = titleBarTextMarginWidth; tvmargin = IVGAP; } int innerwHint = wHint; if (innerwHint != SWT.DEFAULT) innerwHint -= twidth + marginWidth + marginWidth + thmargin + thmargin; int innertHint = innerwHint; Point tcsize = NULL_SIZE; if (textClient != null) { tcsize = textClientCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); } Point size = NULL_SIZE; if (textLabel != null) { if (tcsize.x > 0 && FormUtil.isWrapControl(textClient)) { size = textLabelCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); if (innertHint != SWT.DEFAULT && innertHint < size.x + IGAP + tcsize.x) { innertHint -= IGAP; if (textLabel instanceof Label) { GC gc = new GC(textLabel); size = FormUtil.computeWrapSize(gc, ((Label) textLabel).getText(), Math.round(innertHint * (size.x / (float) (size.x + tcsize.x)))); gc.dispose(); } else size = textLabelCache.computeSize( Math.round(innertHint * (size.x / (float) (size.x + tcsize.x))), SWT.DEFAULT); tcsize = textClientCache.computeSize(innertHint - size.x, SWT.DEFAULT); } } else { if (innertHint != SWT.DEFAULT && tcsize.x > 0) innertHint -= IGAP + tcsize.x; size = textLabelCache.computeSize(innertHint, SWT.DEFAULT); } } if (textLabel instanceof Label) { Point defSize = textLabelCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); if (defSize.y == size.y) { // One line - pick the smaller of the two widths size.x = Math.min(defSize.x, size.x); } } if (size.x > 0) width = size.x; if (tcsize.x > 0) width += IGAP + tcsize.x; if (toggle != null) width += twidth; int height = Math.max(tcsize.y, size.y); // max of label/text client height = Math.max(height, tsize.y); // or max of toggle if (getSeparatorControl() != null) { height += VSPACE + SEPARATOR_HEIGHT; } // if (hasTitleBar()) // height += VSPACE; if ((expanded || (expansionStyle & COMPACT) == 0) && client != null) { int cwHint = wHint; int clientIndent = 0; if ((expansionStyle & CLIENT_INDENT) != 0) clientIndent = twidth; if (cwHint != SWT.DEFAULT) { cwHint -= marginWidth + marginWidth + thmargin + thmargin; if ((expansionStyle & CLIENT_INDENT) != 0) if (tcsize.x > 0) cwHint -= twidth; } Point dsize = null; Point csize = clientCache.computeSize(FormUtil.getWidthHint(cwHint, client), SWT.DEFAULT); if (getDescriptionControl() != null) { int dwHint = cwHint; if (dwHint == SWT.DEFAULT) { dwHint = csize.x; if ((expansionStyle & CLIENT_INDENT) != 0) dwHint -= twidth; } dsize = descriptionCache.computeSize(dwHint, SWT.DEFAULT); width = Math.max(width, dsize.x + clientIndent); if (expanded) { if (getSeparatorControl() != null) { height += VSPACE; } height += descriptionVerticalSpacing + dsize.y; } } width = Math.max(width, csize.x + clientIndent); if (expanded) { height += clientVerticalSpacing; height += csize.y; } } Point result = new Point(width + marginWidth + marginWidth + thmargin + thmargin, height + marginHeight + marginHeight + tvmargin + tvmargin); return result; } @Override public int computeMinimumWidth(Composite parent, boolean changed) { return computeSize(parent, 0, SWT.DEFAULT, changed).x; } @Override public int computeMaximumWidth(Composite parent, boolean changed) { return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x; } } /** * Creates an expandable composite using a TWISTIE toggle. * * @param parent * the parent composite * @param style * SWT style bits */ public ExpandableComposite(Composite parent, int style) { this(parent, style, TWISTIE); } /** * Creates the expandable composite in the provided parent. * * @param parent * the parent * @param style * the control style (as expected by SWT subclass) * @param expansionStyle * the style of the expansion widget (TREE_NODE, TWISTIE, * CLIENT_INDENT, COMPACT, FOCUS_TITLE, * LEFT_TEXT_CLIENT_ALIGNMENT, NO_TITLE) */ public ExpandableComposite(Composite parent, int style, int expansionStyle) { super(parent, style); this.expansionStyle = expansionStyle; if ((expansionStyle & TITLE_BAR) != 0) setBackgroundMode(SWT.INHERIT_DEFAULT); super.setLayout(new ExpandableLayout()); if (hasTitleBar()) { this.addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { onPaint(e); } }); } if ((expansionStyle & TWISTIE) != 0) toggle = new Twistie(this, SWT.NULL); else if ((expansionStyle & TREE_NODE) != 0) toggle = new TreeNode(this, SWT.NULL); else expanded = true; if ((expansionStyle & EXPANDED) != 0) expanded = true; if (toggle != null) { toggle.setExpanded(expanded); toggle.addHyperlinkListener(new HyperlinkAdapter() { @Override public void linkActivated(HyperlinkEvent e) { toggleState(); } }); toggle.addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { if (textLabel instanceof Label && !isFixedStyle()) textLabel.setForeground( toggle.hover ? toggle.getHoverDecorationColor() : getTitleBarForeground()); } }); toggle.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.ARROW_UP) { verticalMove(false); e.doit = false; } else if (e.keyCode == SWT.ARROW_DOWN) { verticalMove(true); e.doit = false; } } }); if ((getExpansionStyle() & FOCUS_TITLE) == 0) { toggle.paintFocus = false; toggle.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { if (textLabel != null) { textLabel.redraw(); } } @Override public void focusLost(FocusEvent e) { if (textLabel != null) { textLabel.redraw(); } } }); } } if ((expansionStyle & FOCUS_TITLE) != 0) { Hyperlink link = new Hyperlink(this, SWT.WRAP); link.addHyperlinkListener(new HyperlinkAdapter() { @Override public void linkActivated(HyperlinkEvent e) { programmaticToggleState(); } }); textLabel = link; } else if ((expansionStyle & NO_TITLE) == 0) { final Label label = new Label(this, SWT.WRAP); if (!isFixedStyle()) { label.setCursor(FormsResources.getHandCursor()); Listener listener = new Listener() { @Override public void handleEvent(Event e) { switch (e.type) { case SWT.MouseDown: if (toggle != null) toggle.setFocus(); break; case SWT.MouseUp: label.setCursor(FormsResources.getBusyCursor()); programmaticToggleState(); label.setCursor(FormsResources.getHandCursor()); break; case SWT.MouseEnter: if (toggle != null) { label.setForeground(toggle.getHoverDecorationColor()); toggle.hover = true; toggle.redraw(); } break; case SWT.MouseExit: if (toggle != null) { label.setForeground(getTitleBarForeground()); toggle.hover = false; toggle.redraw(); } break; case SWT.Paint: if (toggle != null && (getExpansionStyle() & NO_TITLE_FOCUS_BOX) == 0) { paintTitleFocus(e.gc); } break; } } }; label.addListener(SWT.MouseDown, listener); label.addListener(SWT.MouseUp, listener); label.addListener(SWT.MouseEnter, listener); label.addListener(SWT.MouseExit, listener); label.addListener(SWT.Paint, listener); } textLabel = label; } if (textLabel != null) { textLabel.setMenu(getMenu()); textLabel.addTraverseListener(new TraverseListener() { @Override public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_MNEMONIC) { // steal the mnemonic if (!isVisible() || !isEnabled()) return; if (FormUtil.mnemonicMatch(getText(), e.character)) { e.doit = false; if (!isFixedStyle()) { programmaticToggleState(); } setFocus(); } } } }); } } @Override public boolean forceFocus() { return false; } /** * Overrides 'super' to pass the menu to the text label. * * @param menu * the menu from the parent to attach to this control. */ @Override public void setMenu(Menu menu) { if (textLabel != null) textLabel.setMenu(menu); super.setMenu(menu); } /** * Prevents assignment of the layout manager - expandable composite uses its * own layout. */ @Override public final void setLayout(Layout layout) { } /** * Sets the background of all the custom controls in the expandable. */ @Override public void setBackground(Color bg) { super.setBackground(bg); if ((getExpansionStyle() & TITLE_BAR) == 0) { if (textLabel != null) textLabel.setBackground(bg); if (toggle != null) toggle.setBackground(bg); } } /** * Sets the foreground of all the custom controls in the expandable. */ @Override public void setForeground(Color fg) { super.setForeground(fg); if (textLabel != null) textLabel.setForeground(fg); if (toggle != null) toggle.setForeground(fg); } /** * Sets the color of the toggle control. * * @param c * the color object */ public void setToggleColor(Color c) { if (toggle != null) toggle.setDecorationColor(c); } /** * Sets the active color of the toggle control (when the mouse enters the * toggle area). * * @param c * the active color object */ public void setActiveToggleColor(Color c) { if (toggle != null) toggle.setHoverDecorationColor(c); } /** * Sets the fonts of all the custom controls in the expandable. */ @Override public void setFont(Font font) { super.setFont(font); if (textLabel != null) textLabel.setFont(font); if (toggle != null) toggle.setFont(font); } @Override public void setEnabled(boolean enabled) { if (textLabel != null) textLabel.setEnabled(enabled); if (toggle != null) toggle.setEnabled(enabled); super.setEnabled(enabled); } /** * Sets the client of this expandable composite. The client must not be * <samp>null </samp> and must be a direct child of this container. * * @param client * the client that will be expanded or collapsed */ public void setClient(Control client) { Assert.isTrue(client != null && client.getParent().equals(this)); this.client = client; } /** * Returns the current expandable client. * * @return the client control */ public Control getClient() { return client; } /** * Sets the title of the expandable composite. The title will act as a * hyperlink and activating it will toggle the client between expanded and * collapsed state. * * @param title * the new title string * @see #getText() */ public void setText(String title) { if (textLabel instanceof Label) { ((Label) textLabel).setText(title); } else if (textLabel instanceof Hyperlink) { ((Hyperlink) textLabel).setText(title); } else { return; } layout(); } @Override public void setToolTipText(String string) { super.setToolTipText(string); // Also set on label, otherwise it's just on the background without text. if (textLabel instanceof Label) { ((Label) textLabel).setToolTipText(string); } else if (textLabel instanceof Hyperlink) { ((Hyperlink) textLabel).setToolTipText(string); } } /** * Returns the title string. * * @return the title string * @see #setText(String) */ public String getText() { if (textLabel instanceof Label) return ((Label) textLabel).getText(); else if (textLabel instanceof Hyperlink) return ((Hyperlink) textLabel).getText(); else return ""; //$NON-NLS-1$ } /** * Tests the expanded state of the composite. * * @return <samp>true </samp> if expanded, <samp>false </samp> if collapsed. */ public boolean isExpanded() { return expanded; } /** * Returns the bitwise-ORed style bits for the expansion control. * * @return the bitwise-ORed style bits for the expansion control */ public int getExpansionStyle() { return expansionStyle; } /** * Programmatically changes expanded state. * * @param expanded * the new expanded state */ public void setExpanded(boolean expanded) { internalSetExpanded(expanded); if (toggle != null) toggle.setExpanded(expanded); } /** * Performs the expansion state change for the expandable control. * * @param expanded * the expansion state */ protected void internalSetExpanded(boolean expanded) { if (this.expanded != expanded) { this.expanded = expanded; if (getDescriptionControl() != null) getDescriptionControl().setVisible(expanded); if (client != null) client.setVisible(expanded); layout(); } } /** * Adds the listener that will be notified when the expansion state changes. * * @param listener * the listener to add */ public void addExpansionListener(IExpansionListener listener) { listeners.add(listener); } /** * Removes the expansion listener. * * @param listener * the listner to remove */ public void removeExpansionListener(IExpansionListener listener) { listeners.remove(listener); } /** * If TITLE_BAR or SHORT_TITLE_BAR style is used, title bar decoration will * be painted behind the text in this method. The default implementation * does nothing - subclasses are responsible for rendering the title area. * * @param e * the paint event */ protected void onPaint(PaintEvent e) { } /** * Returns description control that will be placed under the title if * present. * * @return the description control or <samp>null </samp> if not used. */ protected Control getDescriptionControl() { return null; } /** * Returns the separator control that will be placed between the title and * the description if present. * * @return the separator control or <samp>null </samp> if not used. */ protected Control getSeparatorControl() { return null; } /** * Computes the size of the expandable composite. * * @see org.eclipse.swt.widgets.Composite#computeSize */ @Override public Point computeSize(int wHint, int hHint, boolean changed) { checkWidget(); Point size; ExpandableLayout layout = (ExpandableLayout) getLayout(); if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) { size = layout.computeSize(this, wHint, hHint, changed); } else { size = new Point(wHint, hHint); } Rectangle trim = computeTrim(0, 0, size.x, size.y); return new Point(trim.width, trim.height); } /** * Returns <samp>true </samp> if the composite is fixed i.e. cannot be * expanded or collapsed. Fixed control will still contain the title, * separator and description (if present) as well as the client, but will be * in the permanent expanded state and the toggle affordance will not be * shown. * * @return <samp>true </samp> if the control is fixed in the expanded state, * <samp>false </samp> if it can be collapsed. */ protected boolean isFixedStyle() { return (expansionStyle & TWISTIE) == 0 && (expansionStyle & TREE_NODE) == 0; } /** * Returns the text client control. * * @return Returns the text client control if specified, or * <code>null</code> if not. */ public Control getTextClient() { return textClient; } /** * Sets the text client control. Text client is a control that is a child of * the expandable composite and is placed to the right of the text. It can * be used to place small image hyperlinks. If more than one control is * needed, use Composite to hold them. Care should be taken that the height * of the control is comparable to the height of the text. * * @param textClient * the textClient to set or <code>null</code> if not needed any * more. */ public void setTextClient(Control textClient) { if (this.textClient != null) this.textClient.dispose(); this.textClient = textClient; } /** * Returns the difference in height between the text and the text client (if * set). This difference can cause vertical alignment problems when two * expandable composites are placed side by side, one with and one without * the text client. Use this method obtain the value to add to either * <code>descriptionVerticalSpacing</code> (if you have description) or * <code>clientVerticalSpacing</code> to correct the alignment of the * expandable without the text client. * * @return the difference in height between the text and the text client or * 0 if no corrective action is needed. * @since 3.3 */ public int getTextClientHeightDifference() { if (textClient == null || textLabel == null) return 0; int theight = textLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; int tcheight = textClient.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; return Math.max(tcheight - theight, 0); } /** * Tests if this expandable composite renders a title bar around the text. * * @return <code>true</code> for <code>TITLE_BAR</code> or * <code>SHORT_TITLE_BAR</code> styles, <code>false</code> * otherwise. */ protected boolean hasTitleBar() { return (getExpansionStyle() & TITLE_BAR) != 0 || (getExpansionStyle() & SHORT_TITLE_BAR) != 0; } /** * Sets the color of the title bar foreground when TITLE_BAR style is used. * * @param color * the title bar foreground */ public void setTitleBarForeground(Color color) { if (hasTitleBar()) titleBarForeground = color; if (textLabel != null) textLabel.setForeground(color); } /** * Returns the title bar foreground when TITLE_BAR style is used. * * @return the title bar foreground */ public Color getTitleBarForeground() { return titleBarForeground; } // end of APIs private void toggleState() { boolean newState = !isExpanded(); fireExpanding(newState, true); internalSetExpanded(newState); fireExpanding(newState, false); if (newState) FormUtil.ensureVisible(this); } private void fireExpanding(boolean state, boolean before) { int size = listeners.size(); if (size == 0) return; ExpansionEvent e = new ExpansionEvent(this, state); for (IExpansionListener listener : listeners) { if (before) listener.expansionStateChanging(e); else listener.expansionStateChanged(e); } } private void verticalMove(boolean down) { Composite parent = getParent(); Control[] children = parent.getChildren(); for (int i = 0; i < children.length; i++) { Control child = children[i]; if (child == this) { ExpandableComposite sibling = getSibling(children, i, down); if (sibling != null && sibling.toggle != null) { sibling.setFocus(); } break; } } } private ExpandableComposite getSibling(Control[] children, int index, boolean down) { int loc = down ? index + 1 : index - 1; while (loc >= 0 && loc < children.length) { Control c = children[loc]; if (c instanceof ExpandableComposite && c.isVisible()) return (ExpandableComposite) c; loc = down ? loc + 1 : loc - 1; } return null; } private void programmaticToggleState() { if (toggle != null) toggle.setExpanded(!toggle.isExpanded()); toggleState(); } private void paintTitleFocus(GC gc) { Point size = textLabel.getSize(); gc.setBackground(textLabel.getBackground()); gc.setForeground(textLabel.getForeground()); if (toggle.isFocusControl()) gc.drawFocus(0, 0, size.x, size.y); } }