Mercurial > projects > dwt-addons
view dwtx/ui/forms/widgets/ExpandableComposite.d @ 192:c3583c6ec027
Added missing default cases for switch statements
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 03 Nov 2008 22:52:26 +0100 |
parents | 56fea7e5f0f9 |
children |
line wrap: on
line source
/******************************************************************************* * 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 * Kai Nacke - Fix for Bug 202382 * Port to the D programming language: * Frank Benoit <benoit@tionex.de> *******************************************************************************/ module dwtx.ui.forms.widgets.ExpandableComposite; import dwtx.ui.forms.widgets.ToggleHyperlink; import dwtx.ui.forms.widgets.ILayoutExtension; import dwtx.ui.forms.widgets.SizeCache; import dwtx.ui.forms.widgets.Twistie; import dwtx.ui.forms.widgets.TreeNode; import dwtx.ui.forms.widgets.Hyperlink; import dwt.DWT; import dwt.events.FocusEvent; import dwt.events.FocusListener; import dwt.events.KeyAdapter; import dwt.events.KeyEvent; import dwt.events.PaintEvent; import dwt.events.PaintListener; import dwt.events.TraverseEvent; import dwt.events.TraverseListener; import dwt.graphics.Color; import dwt.graphics.Font; import dwt.graphics.FontMetrics; import dwt.graphics.GC; import dwt.graphics.Point; import dwt.graphics.Rectangle; import dwt.widgets.Canvas; import dwt.widgets.Composite; import dwt.widgets.Control; import dwt.widgets.Event; import dwt.widgets.Label; import dwt.widgets.Layout; import dwt.widgets.Listener; import dwt.widgets.Menu; import dwtx.core.runtime.Assert; import dwtx.core.runtime.ListenerList; import dwtx.ui.forms.events.ExpansionEvent; import dwtx.ui.forms.events.HyperlinkAdapter; import dwtx.ui.forms.events.HyperlinkEvent; import dwtx.ui.forms.events.IExpansionListener; import dwtx.ui.internal.forms.widgets.FormUtil; import dwtx.ui.internal.forms.widgets.FormsResources; import dwt.dwthelper.utils; /** * 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 : Canvas { /** * If this style is used, a twistie will be used to render the expansion * toggle. */ public static const 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 const 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 const 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 const 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 const 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 const int EXPANDED = 1 << 6; /** * If this style is used, title bar decoration will be painted behind the * text. */ public static const 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 const int SHORT_TITLE_BAR = 1 << 9; /** * If this style is used, title will not be rendered. */ public static const 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 const int LEFT_TEXT_CLIENT_ALIGNMENT = 1 << 13; /** * 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; package ToggleHyperlink toggle_package(){ return toggle; } package ToggleHyperlink toggle_package(ToggleHyperlink t){ return (toggle = t); } /** * The text label for the title. */ protected Control textLabel; package Control textLabel_package(){ return textLabel; } package Control textLabel_package(Control t){ return (textLabel = t); } /** * @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. */ 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. */ protected int GAP = 4; static const int IGAP = 4; static const int IVGAP = 3; private static Point NULL_SIZE_; private static Point NULL_SIZE(){ if( NULL_SIZE_ is null ){ synchronized(ExpandableComposite.classinfo ){ if( NULL_SIZE_ is null ){ NULL_SIZE_ = new Point(0, 0); } } } return NULL_SIZE_; } private static const int VSPACE = 3; private static const int SEPARATOR_HEIGHT = 2; private int expansionStyle = TWISTIE | FOCUS_TITLE | EXPANDED; private bool expanded; private Control textClient; private Control client; private ListenerList listeners; private Color titleBarForeground; private class ExpandableLayout : Layout, ILayoutExtension { private SizeCache toggleCache; private SizeCache textClientCache; private SizeCache textLabelCache; private SizeCache descriptionCache; private SizeCache clientCache; this(){ toggleCache = new SizeCache(); textClientCache = new SizeCache(); textLabelCache = new SizeCache(); descriptionCache = new SizeCache(); clientCache = new SizeCache(); } private void initCache(bool 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(); } } protected void layout(Composite parent, bool 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 !is null) tsize = toggleCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); int twidth = clientArea.width - marginWidth - marginWidth - thmargin - thmargin; if (tsize.x > 0) twidth -= tsize.x + IGAP; if (textClient !is null) { tcsize = textClientCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); } Point size = NULL_SIZE; if (textLabel !is null) { if (tcsize.x > 0 && FormUtil.isWrapControl(textClient)) { size = textLabelCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); if (twidth < size.x + IGAP + tcsize.x) { twidth -= IGAP; if (null !is cast(Label)textLabel ) size = FormUtil.computeWrapSize(new GC(textLabel), (cast(Label)textLabel).getText(), cast(int) Math.round(twidth*(size.x/cast(float)(size.x+tcsize.x)))); else size = textLabelCache.computeSize(cast(int) Math.round(twidth*(size.x/cast(float)(size.x+tcsize.x))), DWT.DEFAULT); tcsize = textClientCache.computeSize(twidth-size.x, DWT.DEFAULT); } } else { if (tcsize.x > 0) twidth -= tcsize.x + IGAP; size = textLabelCache.computeSize(twidth, DWT.DEFAULT); } } if (null !is cast(Label)textLabel ) { Point defSize = textLabelCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); if (defSize.y is size.y) { // One line - pick the smaller of the two widths size.x = Math.min(defSize.x, size.x); } } if (toggle !is null) { GC gc = new GC(this.outer); gc.setFont(getFont()); FontMetrics fm = gc.getFontMetrics(); int textHeight = fm.getHeight(); gc.dispose(); if (textClient !is null && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) !is 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 !is null) { int ty = y; if (textClient !is null && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) !is 0) { if (size.y < tcsize.y) ty = tcsize.y / 2 - size.y / 2 + marginHeight + tvmargin; } textLabelCache.setBounds(x, ty, size.x, size.y); } if (textClient !is null) { int tcx; if ((expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) !is 0) { tcx = x + size.x + GAP; } else { tcx = clientArea.width - tcsize.x - marginWidth - thmargin; } textClientCache.setBounds(tcx, y, tcsize.x, tcsize.y); } int tbarHeight = 0; if (size.y > 0) tbarHeight = size.y; if (tcsize.y > 0) tbarHeight = Math.max(tbarHeight, tcsize.y); y += tbarHeight; if (hasTitleBar()) y += tvmargin; if (getSeparatorControl() !is null) { y += VSPACE; getSeparatorControl().setBounds(marginWidth, y, clientArea.width - marginWidth - marginWidth, SEPARATOR_HEIGHT); y += SEPARATOR_HEIGHT; if (expanded) y += VSPACE; } if (expanded) { int areaWidth = clientArea.width - marginWidth - marginWidth - thmargin - thmargin; int cx = marginWidth + thmargin; if ((expansionStyle & CLIENT_INDENT) !is 0) { cx = x; areaWidth -= x; } if (client !is null) { Point dsize = null; Control desc = getDescriptionControl(); if (desc !is null) { dsize = descriptionCache.computeSize(areaWidth, DWT.DEFAULT); y += descriptionVerticalSpacing; descriptionCache.setBounds(cx, y, areaWidth, dsize.y); y += dsize.y + clientVerticalSpacing; } else { y += clientVerticalSpacing; if (getSeparatorControl() !is null) y -= VSPACE; } int cwidth = areaWidth; int cheight = clientArea.height - marginHeight - marginHeight - y; clientCache.setBounds(cx, y, cwidth, cheight); } } } protected Point computeSize(Composite parent, int wHint, int hHint, bool changed) { initCache(changed); int width = 0, height = 0; Point tsize = NULL_SIZE; int twidth = 0; if (toggle !is null) { tsize = toggleCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); twidth = tsize.x + IGAP; } int thmargin = 0; int tvmargin = 0; if (hasTitleBar()) { thmargin = titleBarTextMarginWidth; tvmargin = IVGAP; } int innerwHint = wHint; if (innerwHint !is DWT.DEFAULT) innerwHint -= twidth + marginWidth + marginWidth + thmargin + thmargin; int innertHint = innerwHint; Point tcsize = NULL_SIZE; if (textClient !is null) { tcsize = textClientCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); } Point size = NULL_SIZE; if (textLabel !is null) { if (tcsize.x > 0 && FormUtil.isWrapControl(textClient)) { size = textLabelCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); if (innertHint !is DWT.DEFAULT && innertHint < size.x + IGAP + tcsize.x) { innertHint -= IGAP; if (null !is cast(Label)textLabel ) size = FormUtil.computeWrapSize(new GC(textLabel), (cast(Label)textLabel).getText(), cast(int) Math.round(innertHint*(size.x/cast(float)(size.x+tcsize.x)))); else size = textLabelCache.computeSize(cast(int) Math.round(innertHint*(size.x/cast(float)(size.x+tcsize.x))), DWT.DEFAULT); tcsize = textClientCache.computeSize(innertHint-size.x, DWT.DEFAULT); } } else { if (innertHint !is DWT.DEFAULT && tcsize.x > 0) innertHint -= IGAP + tcsize.x; size = textLabelCache.computeSize(innertHint, DWT.DEFAULT); } } if (null !is cast(Label)textLabel ) { Point defSize = textLabelCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); if (defSize.y is 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 !is null) width += twidth; height = tcsize.y > 0 ? Math.max(tcsize.y, size.y) : size.y; if (getSeparatorControl() !is null) { height += VSPACE + SEPARATOR_HEIGHT; if (expanded && client !is null) height += VSPACE; } // if (hasTitleBar()) // height += VSPACE; if ((expanded || (expansionStyle & COMPACT) is 0) && client !is null) { int cwHint = wHint; int clientIndent = 0; if ((expansionStyle & CLIENT_INDENT) !is 0) clientIndent = twidth; if (cwHint !is DWT.DEFAULT) { cwHint -= marginWidth + marginWidth + thmargin + thmargin; if ((expansionStyle & CLIENT_INDENT) !is 0) if (tcsize.x > 0) cwHint -= twidth; } Point dsize = null; Point csize = clientCache.computeSize(FormUtil.getWidthHint( cwHint, client), DWT.DEFAULT); if (getDescriptionControl() !is null) { int dwHint = cwHint; if (dwHint is DWT.DEFAULT) { dwHint = csize.x; if ((expansionStyle & CLIENT_INDENT) !is 0) dwHint -= twidth; } dsize = descriptionCache.computeSize(dwHint, DWT.DEFAULT); } if (dsize !is null) { width = Math.max(width, dsize.x + clientIndent); if (expanded) height += descriptionVerticalSpacing + dsize.y + clientVerticalSpacing; } else { height += clientVerticalSpacing; if (getSeparatorControl() !is null) height -= VSPACE; } width = Math.max(width, csize.x + clientIndent); if (expanded) height += csize.y; } if (toggle !is null) height = height - size.y + Math.max(size.y, tsize.y); Point result = new Point(width + marginWidth + marginWidth + thmargin + thmargin, height + marginHeight + marginHeight + tvmargin + tvmargin); return result; } public int computeMinimumWidth(Composite parent, bool changed) { return computeSize(parent, 0, DWT.DEFAULT, changed).x; } /* * (non-Javadoc) * * @see dwtx.ui.forms.parts.ILayoutExtension#computeMinimumWidth(dwt.widgets.Composite, * bool) */ public int computeMaximumWidth(Composite parent, bool changed) { return computeSize(parent, DWT.DEFAULT, DWT.DEFAULT, changed).x; } } /** * Creates an expandable composite using a TWISTIE toggle. * * @param parent * the parent composite * @param style * DWT style bits */ public this(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 DWT subclass) * @param expansionStyle * the style of the expansion widget (TREE_NODE, TWISTIE, * CLIENT_INDENT, COMPACT, FOCUS_TITLE, * LEFT_TEXT_CLIENT_ALIGNMENT, NO_TITLE) */ public this(Composite parent, int style, int expansionStyle) { listeners = new ListenerList(); super(parent, style); this.expansionStyle = expansionStyle; if ((expansionStyle & TITLE_BAR) !is 0) setBackgroundMode(DWT.INHERIT_DEFAULT); super.setLayout(new ExpandableLayout()); if (hasTitleBar()) { this.addPaintListener(new class PaintListener { public void paintControl(PaintEvent e) { onPaint(e); } }); } if ((expansionStyle & TWISTIE) !is 0) toggle = new Twistie(this, DWT.NULL); else if ((expansionStyle & TREE_NODE) !is 0) toggle = new TreeNode(this, DWT.NULL); else expanded = true; if ((expansionStyle & EXPANDED) !is 0) expanded = true; if (toggle !is null) { toggle.setExpanded(expanded); toggle.addHyperlinkListener(new class HyperlinkAdapter { public void linkActivated(HyperlinkEvent e) { toggleState(); } }); toggle.addPaintListener(new class PaintListener { public void paintControl(PaintEvent e) { if (null !is cast(Label)textLabel && !isFixedStyle()) textLabel.setForeground(toggle.hover_package ? toggle .getHoverDecorationColor() : getTitleBarForeground()); } }); toggle.addKeyListener(new class KeyAdapter { public void keyPressed(KeyEvent e) { if (e.keyCode is DWT.ARROW_UP) { verticalMove(false); e.doit = false; } else if (e.keyCode is DWT.ARROW_DOWN) { verticalMove(true); e.doit = false; } } }); if ((getExpansionStyle()&FOCUS_TITLE) is 0) { toggle.paintFocus=false; toggle.addFocusListener(new class FocusListener { public void focusGained(FocusEvent e) { textLabel.redraw(); } public void focusLost(FocusEvent e) { textLabel.redraw(); } }); } } if ((expansionStyle & FOCUS_TITLE) !is 0) { Hyperlink link = new Hyperlink(this, DWT.WRAP); link.addHyperlinkListener(new class HyperlinkAdapter { public void linkActivated(HyperlinkEvent e) { programmaticToggleState(); } }); textLabel = link; } else if ((expansionStyle & NO_TITLE) is 0) { final Label label = new Label(this, DWT.WRAP); if (!isFixedStyle()) { label.setCursor(FormsResources.getHandCursor()); // DWT FIXME: workaround for DMD anonymous class, nested function problem Listener listener = dgListener( &tst, label ); label.addListener(DWT.MouseDown, listener); label.addListener(DWT.MouseUp, listener); label.addListener(DWT.MouseEnter, listener); label.addListener(DWT.MouseExit, listener); label.addListener(DWT.Paint, listener); } textLabel = label; } if (textLabel !is null) { textLabel.setMenu(getMenu()); textLabel.addTraverseListener(new class TraverseListener { public void keyTraversed(TraverseEvent e) { if (e.detail is DWT.TRAVERSE_MNEMONIC) { // steal the mnemonic if (!isVisible() || !isEnabled()) return; if (FormUtil.mnemonicMatch(getText(), e.character)) { e.doit = false; if (!isFixedStyle()) { programmaticToggleState(); } setFocus(); } } } }); } } // DWT FIXME: workaround for DMD anonymous class, nested function problem void tst(Event e, Label label_) { switch (e.type) { case DWT.MouseDown: if (toggle !is null) toggle.setFocus(); break; case DWT.MouseUp: label_.setCursor(FormsResources.getBusyCursor()); programmaticToggleState(); label_.setCursor(FormsResources.getHandCursor()); break; case DWT.MouseEnter: if (toggle !is null) { label_.setForeground(toggle .getHoverDecorationColor()); toggle.hover_package = true; toggle.redraw(); } break; case DWT.MouseExit: if (toggle !is null) { label_.setForeground(getTitleBarForeground()); toggle.hover_package = false; toggle.redraw(); } break; case DWT.Paint: if (toggle !is null) { paintTitleFocus(e.gc); } break; default: } } /* (non-Javadoc) * @see dwt.widgets.Control#forceFocus() */ public bool 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. */ public void setMenu(Menu menu) { if (textLabel !is null) textLabel.setMenu(menu); super.setMenu(menu); } /** * Prevents assignment of the layout manager - expandable composite uses its * own layout. */ public final void setLayout(Layout layout) { } /** * Sets the background of all the custom controls in the expandable. */ public void setBackground(Color bg) { super.setBackground(bg); if ((getExpansionStyle() & TITLE_BAR) is 0) { if (textLabel !is null) textLabel.setBackground(bg); if (toggle !is null) toggle.setBackground(bg); } } /** * Sets the foreground of all the custom controls in the expandable. */ public void setForeground(Color fg) { super.setForeground(fg); if (textLabel !is null) textLabel.setForeground(fg); if (toggle !is null) toggle.setForeground(fg); } /** * Sets the color of the toggle control. * * @param c * the color object */ public void setToggleColor(Color c) { if (toggle !is 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 !is null) toggle.setHoverDecorationColor(c); } /** * Sets the fonts of all the custom controls in the expandable. */ public void setFont(Font font) { super.setFont(font); if (textLabel !is null) textLabel.setFont(font); if (toggle !is null) toggle.setFont(font); } /* * (non-Javadoc) * * @see dwt.widgets.Control#setEnabled(bool) */ public void setEnabled(bool enabled) { if (textLabel !is null) textLabel.setEnabled(enabled); if (toggle !is 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 !is null && client.getParent().opEquals(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 (null !is cast(Label)textLabel ) (cast(Label) textLabel).setText(title); else if (null !is cast(Hyperlink)textLabel ) (cast(Hyperlink) textLabel).setText(title); } /** * Returns the title string. * * @return the title string * @see #setText(String) */ public String getText() { if (null !is cast(Label)textLabel ) return (cast(Label) textLabel).getText(); else if (null !is cast(Hyperlink)textLabel ) return (cast(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 bool 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(bool expanded) { internalSetExpanded(expanded); if (toggle !is null) toggle.setExpanded(expanded); } /** * Performs the expansion state change for the expandable control. * * @param expanded * the expansion state */ protected void internalSetExpanded(bool expanded) { if (this.expanded !is expanded) { this.expanded = expanded; if (getDescriptionControl() !is null) getDescriptionControl().setVisible(expanded); if (client !is 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(cast(Object)listener); } /** * Removes the expansion listener. * * @param listener * the listner to remove */ public void removeExpansionListener(IExpansionListener listener) { listeners.remove(cast(Object)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 dwt.widgets.Composite#computeSize */ public Point computeSize(int wHint, int hHint, bool changed) { checkWidget(); Point size; ExpandableLayout layout = cast(ExpandableLayout) getLayout(); if (wHint is DWT.DEFAULT || hHint is DWT.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 bool isFixedStyle() { return (expansionStyle & TWISTIE) is 0 && (expansionStyle & TREE_NODE) is 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 !is 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 is null || textLabel is null) return 0; int theight = textLabel.computeSize(DWT.DEFAULT, DWT.DEFAULT).y; int tcheight = textClient.computeSize(DWT.DEFAULT, DWT.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 bool hasTitleBar() { return (getExpansionStyle() & TITLE_BAR) !is 0 || (getExpansionStyle() & SHORT_TITLE_BAR) !is 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) { titleBarForeground = color; 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() { bool newState = !isExpanded(); fireExpanding(newState, true); internalSetExpanded(newState); fireExpanding(newState, false); if (newState) FormUtil.ensureVisible(this); } private void fireExpanding(bool state, bool before) { int size = listeners.size(); if (size is 0) return; ExpansionEvent e = new ExpansionEvent(this, state); Object [] listenerList = listeners.getListeners(); for (int i = 0; i < size; i++) { IExpansionListener listener = cast(IExpansionListener) listenerList[i]; if (before) listener.expansionStateChanging(e); else listener.expansionStateChanged(e); } } private void verticalMove(bool down) { Composite parent = getParent(); Control[] children = parent.getChildren(); for (int i = 0; i < children.length; i++) { Control child = children[i]; if (child is this) { ExpandableComposite sibling = getSibling(children, i, down); if (sibling !is null && sibling.toggle !is null) { sibling.setFocus(); } break; } } } private ExpandableComposite getSibling(Control[] children, int index, bool down) { int loc = down ? index + 1 : index - 1; while (loc >= 0 && loc < children.length) { Control c = children[loc]; if (null !is cast(ExpandableComposite)c && c.isVisible()) return cast(ExpandableComposite) c; loc = down ? loc + 1 : loc - 1; } return null; } private void programmaticToggleState() { if (toggle !is 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); } }