/**************************************************************************** * Copyright (c) 2005-2009 Jeremy Dowdall * 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 - SWT's CCombo was relied upon _heavily_ for example and reference * Jeremy Dowdall <jeremyd@aspencloud.com> - initial API and implementation *****************************************************************************/ package org.eclipse.nebula.cwt.base; import org.eclipse.nebula.cwt.animation.AnimationRunner; import org.eclipse.nebula.cwt.animation.effects.Resize; import org.eclipse.nebula.cwt.animation.movement.LinearInOut; import org.eclipse.nebula.cwt.v.VButton; import org.eclipse.nebula.cwt.v.VButtonPainter; import org.eclipse.nebula.cwt.v.VControl; import org.eclipse.nebula.cwt.v.VLayout; import org.eclipse.nebula.cwt.v.VNative; import org.eclipse.nebula.cwt.v.VPanel; import org.eclipse.nebula.cwt.v.VSimpleLayout; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionListener; 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.layout.GridData; 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.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; /** * The AbstractCombo is an abstract class which provides the basic functionality * for a button with a DROP_DOWN, or "popup", shell component. When the user * selects the button the shell is set visible and the SWT Components which have * been placed on the "content" Composite will be shown. */ public abstract class BaseCombo extends Canvas { /** * Special layout implementation to position the combo's drop-down Button * within its Text. */ protected class DropComboLayout extends VLayout { protected Point computeSize(VPanel panel, int wHint, int hHint, boolean flushCache) { Point size = text.computeSize(SWT.DEFAULT, SWT.DEFAULT); if(button.getVisible()) { size.x += size.y; } size.y += textMarginHeight; if (win32) { size.x += 8; } if(wHint != SWT.DEFAULT) { size.x = Math.min(size.x, wHint); } if(hHint != SWT.DEFAULT) { size.y = Math.min(size.y, hHint); } return size; } protected void layout(VPanel panel, boolean flushCache) { Rectangle cRect = panel.getClientArea(); Point tSize = text.computeSize(SWT.DEFAULT, SWT.DEFAULT); tSize.y += textMarginHeight; if (win32) { tSize.x += 8; } Point bSize = button.getVisible() ? new Point(tSize.y, tSize.y) : new Point(0, 0); if(leftAlign) { text.setBounds(cRect.x + bSize.x, cRect.y + (win32 ? getBorderWidth() : 0), cRect.width - bSize.x, tSize.y); button.setBounds(cRect.x, cRect.y, bSize.x, bSize.y); } else { text.setBounds( cRect.x+(win32 ? 1 : 0), cRect.y + (win32 ? 1 : 0), cRect.width-bSize.x-(win32 ? 2 : 0), tSize.y-(win32 ? 2 : 0) ); button.setBounds( win32 ? (cRect.x+cRect.width-cRect.height+1) : (cRect.x+cRect.width-bSize.x), cRect.y, win32 ? (cRect.height-1) : bSize.x, win32 ? cRect.height : bSize.y ); } } } /** * The value of {@link SWT#getVersion()} for the earliest known revision * that fixes the SWT bug mentioned in bug 185739. */ protected static int SWT_MODAL_FIX_VERSION = 3346; /** * true if the platform is carbon, false otherwise */ protected static final boolean carbon = "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$ /** * true if the platform is gtk, false otherwise */ protected static final boolean gtk = "gtk".equals(SWT.getPlatform()); //$NON-NLS-1$ /** * true if the platform is win32, false otherwise */ protected static final boolean win32 = "win32".equals(SWT.getPlatform()); //$NON-NLS-1$ /** * true if the platform is winXP, false otherwise */ protected static final boolean winxp = "win32".equals(SWT.getPlatform()) && "5.0".equals(System.getProperty("os.version")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ /** * true if the platform is win32, false otherwise */ protected static final boolean vista = "win32".equals(SWT.getPlatform()) && "6.0".equals(System.getProperty("os.version")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ /** * A constant value used to pad the computed height for this widget, so that * the combo's Button will fit without clipping its top and bottom borders. */ protected static final int textMarginHeight = win32 ? 4 : 0; /** * A style constant indicating that this combo will only have a drop down button, * rather than a button and a text box. */ protected static final int BUTTON_ONLY = 0; /** * A constant indicating that the drop down button is always visible. * Valid only when style is DROP_DOWN. * @see #BUTTON_AUTO * @see #BUTTON_NEVER */ protected static final int BUTTON_ALWAYS = 1; /** * A constant indicating that the drop down button is never visible. * Valid only when style is DROP_DOWN. * <p> * This setting may be useful if the subclass of this BaseCombo allows * programmatic opening and closing of the drop down shell via {@link #setOpen(boolean)}, * or a similar method. * </p> * @see #BUTTON_AUTO * @see #BUTTON_ALWAYS * @see #setOpen(boolean) * @see #setOpen(boolean, Runnable) */ protected static final int BUTTON_NEVER = 2; /** * A constant indicating that the drop down button is visible when the * text box has the focus, and is hidden otherwise. * Valid only when style is DROP_DOWN. * @see #BUTTON_ALWAYS * @see #BUTTON_NEVER */ protected static final int BUTTON_AUTO = 3; private static int checkStyle(int style) { int rstyle = SWT.NONE; if((style & SWT.BORDER) != 0) { if(win32 || (style & SWT.SIMPLE) != 0) { rstyle |= SWT.BORDER; } } if(win32) { rstyle |= SWT.DOUBLE_BUFFERED; } return rstyle; } /** * A recursive method to find out if a composite is an ancestor of a control. * @param control * @param composite * @return true if the composite is an ancestor, false otherwise. */ protected static boolean containsControl(Control control, Composite composite) { if(composite != null && !composite.isDisposed()) { Control[] children = composite.getChildren(); for(Control child : children) { if(!child.isDisposed()) { if(child == control) { return true; } else if(child instanceof Composite){ return containsControl(control, (Composite) child); } } } } return false; } /** * The VPanel that is the base of this widget. If the style is SIMPLE, this panel * will be the base of the content area, otherwise, it is the base of the text/button * area. */ protected VPanel panel = null; /** * The Button widget of a DROP_DOWN style combo. This value may be null -- * protect all references to this field with the checkButton() method. */ protected VButton button = null; /** * True if a default image should be used for the button; false otherwise - * as is the case when an image is set using {@link #setButtonImage(Image)} * * @see #setButtonImage(Image) */ protected boolean defaultButtonImage = true; /** * The Text widget of a DROP_DOWN style combo. This value may be null -- * protect all references to this field with the checkText() method. */ protected VNative<Text> text = null; /** * The popup Shell widget of a DROP_DOWN style combo. This value may be null -- * protect all references to this field with the checkContentShell() method. */ protected Shell contentShell = null; /** * The widget contents of the popup Shell in a DROP_DOWN combo or the full * contents of a SIMPLE combo. This value may be null -- protect all * references to this field with the checkContent() method. */ protected Control content; /** * The style bits requested. NOTE: this may not match the value returned by * {@link #getStyle()} if invalid bits were requested. */ protected int style; /** * Flag to indicate that this is a SIMPLE style combo. */ protected boolean simple; /** * Flag to indicate that this combo's BUTTON should be displayed on the left * side of its Text. */ protected boolean leftAlign = false; private int buttonVisibility; private Listener buttonVisibilityListener; private boolean dropDown; private boolean open = false; private boolean holdOpen = false; private VControl positionControl; private VControl stretchControl; private Listener textListener; private Listener shellListener; private Listener comboListener = new Listener() { public void handleEvent(Event event) { switch (event.type){ case SWT.Move: if(isOpen()) { setOpen(false); } break; case SWT.Resize: if(isOpen()) { setOpen(false); } layout(true); break; } } }; private Listener disposeListener = new Listener() { public void handleEvent(Event event) { if(!isDisposed()) { getShell().removeListener(SWT.Deactivate, comboListener); if(checkContentShell()) { contentShell.dispose(); } } } }; /** * Main constructor -- must be called by all subclasses in their own * constructors. * <p> * SWT.TOGGLE, SWT.PUSH, SWT.ARROW, SWT.FLAT, SWT.TRAIL, SWT.LEAD, SWT.BORDER, SWT.SIMPLE, SWT.DROP_DOWN * </p> * @param parent * the visual parent of this widget * @param style * the requested SWT style bitmask for this widget */ public BaseCombo(Composite parent, int style) { super(parent, checkStyle(style)); panel = new VPanel(this, SWT.NONE); panel.setWidget(this); init(style); } /** * Adds the listener to the collection of listeners who will be notified * when the receiver's text is modified, by sending it one of the messages * defined in the <code>ModifyListener</code> interface.<br/> Note that * this is NOT the correct way to listen for changes in the underlying model * for the combo. This should be provided by some other mechanism, such as a * {@link SelectionListener}. * * @param listener * the listener which should be notified * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been * disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * * @see ModifyListener * @see #removeModifyListener */ protected void addModifyListener(ModifyListener listener) { if(listener == null) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } if(checkText()) { text.getControl().addModifyListener(listener); } } private void addTextListener() { text.getControl().addListener(SWT.KeyDown, textListener); text.getControl().addListener(SWT.Modify, textListener); } /** * @return true if the {@link #button} field is in a fit state to be used */ protected boolean checkButton() { return (button != null && !button.isDisposed()); } /** * @return true if the {@link #content} field is in a fit state to be used */ protected boolean checkContent() { return (content != null && !content.isDisposed()); } /** * @return true if the {@link #contentShell} field is in a fit state to be * used */ protected boolean checkContentShell() { return (contentShell != null && !contentShell.isDisposed()); } /** * @return true if the {@link #text} field is in a fit state to be used */ protected boolean checkText() { return (text != null && !text.isDisposed()); } private void createButton(int style) { int mask = BUTTON_ONLY | SWT.TOGGLE | SWT.PUSH | SWT.ARROW | SWT.FLAT; int buttonStyle = style & mask; button = new VButton(panel, buttonStyle | SWT.NO_FOCUS); if((style & BUTTON_ONLY) == 0) { button.setMargins(0, 0); } button.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { setOpen(!isOpen()); } }); if(win32) { button.setPainter(new VButtonPainter() { @Override public void paintBackground(VControl control, Event e) { VButton button = (VButton) control; if(button.hasState(VControl.STATE_ACTIVE)) { Rectangle r = button.getBounds(); e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_GRAY)); e.gc.fillRoundRectangle(r.x, r.y, r.width-1, r.height-1, 2, 2); e.gc.drawRoundRectangle(r.x, r.y, r.width-1, r.height-1, 2, 2); } } }); } } private void createContentShell() { contentShell = new Shell(getShell(), SWT.NO_TRIM | SWT.ON_TOP); contentShell.addListener(SWT.Close, shellListener); contentShell.addListener(SWT.Deactivate, shellListener); } private void createText(int style) { textListener = new Listener() { public void handleEvent(Event event) { switch (event.type){ case SWT.KeyDown: if(event.stateMask == SWT.CTRL && event.keyCode == ' ') { event.doit = false; setOpen(true); } break; case SWT.Modify: Event e = new Event(); e.time = event.time; setModifyEventProperties(e); notifyListeners(SWT.Modify, e); break; } } }; int mask = SWT.TRAIL | SWT.LEAD; int textStyle = SWT.SINGLE | (style & mask); if(!win32 && (style & SWT.BORDER) != 0) { textStyle |= SWT.BORDER; } text = VNative.create(Text.class, panel, textStyle); addTextListener(); } /** * @param image */ protected final void doSetButtonImage(Image image) { if(checkButton()) { button.setImage(image); } } /** * @return the Control that was set as this popup shell's content with * setContent(Control) */ protected Control getContent() { return content; } /** * @return the content shell */ protected Shell getContentShell() { if(contentShell == null) { createContentShell(); } return contentShell; } /** * Returns the editable state. * * @return whether or not the receiver is editable * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been * disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public boolean getEditable() { return checkText() ? text.getControl().getEditable() : getEnabled(); } /** * Fixes bug 181442: [CDateTime] Incorrect getEnabled() */ public boolean getEnabled() { return checkText() ? text.getEnabled() : super.getEnabled(); } /** * @return the state of the holdOpen flag */ protected boolean getHoldOpen() { return holdOpen; } /** * returns the menu for this combo */ public Menu getMenu() { if(checkText()) { return text.getMenu(); } return super.getMenu(); } /** * @return the stretch control */ protected VControl getStretchControl() { return stretchControl; } public int getStyle() { return style; } /** * Returns the text of this combo * @return the combo's text */ public String getText() { return checkText() ? text.getText() : ""; //$NON-NLS-1$ } private void init(int style) { this.style = style; simple = (style & SWT.SIMPLE) != 0; dropDown = (style & (BUTTON_ONLY | SWT.DROP_DOWN)) != 0; if(simple) { panel.setLayout(new VSimpleLayout()); } else if(dropDown) { createButton(style); if((style & BUTTON_ONLY) == 0) { createText(style); if(win32) { setPositionControl(panel); } else { setPositionControl(text); } panel.setLayout(new DropComboLayout()); } else { setPositionControl(button); panel.setLayout(new VSimpleLayout()); } shellListener = new Listener() { public void handleEvent(Event event) { switch (event.type){ case SWT.Close: event.doit = false; setOpen(false); break; case SWT.Deactivate: if(!checkContent() || content.getMenu() == null || !content.getMenu().isVisible()) { setOpen(false); } break; } } }; addListener(SWT.Move, comboListener); addListener(SWT.Resize, comboListener); } else { panel.setLayout(new VSimpleLayout()); createText(style); } addListener(SWT.Dispose, disposeListener); } /** * @return true if style is CDT.DROP_DOWN */ protected boolean isDropDown() { return dropDown; } /** * @return the state of the popup shell's visibility */ public boolean isOpen() { return open; } /** * @return if style is CDT.SIMPLE */ protected boolean isSimple() { return simple; } /** * called just <i>after</i> the content shell is set not-visible and has * "closed" * <p> * override if you want to do something with the shell just after becoming * not visible * </p> * * @param popup * @see #preClose(Shell) */ protected void postClose(Shell popup) { // subclasses to implement if necessary } /** * called <i>after</i> the content shell is set visible and has "opened" * <p> * override if you want to do something with the shell just after becoming * visible * </p> * * @param popup * @see #preOpen(Shell) */ protected void postOpen(Shell popup) { // subclasses to implement if necessary } /** * called just <i>before</i> the content shell is set not-visible and * "closes" * <p> * override if you want to do something with the shell prior to it becoming * not visible * </p> * * @param popup * @see #postClose(Shell) */ protected void preClose(Shell popup) { // subclasses to implement if necessary } /** * called just <i>before</i> the content shell is set visible and "opens" * <p> * override if you want to do something with the shell prior to it becoming * visible * </p> * * @param popup * @see #postOpen(Shell) */ protected void preOpen(Shell popup) { // subclasses to implement if necessary } /** * Removes the listener from the collection of listeners who will be * notified when the receiver's text is modified. * * @param listener * the listener which should no longer be notified * * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been * disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * * @see ModifyListener * @see #addModifyListener */ protected void removeModifyListener(ModifyListener listener) { if(listener == null) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } if(checkText()) { text.getControl().removeModifyListener(listener); } } private void removeTextListener() { text.getControl().removeListener(SWT.KeyDown, textListener); text.getControl().removeListener(SWT.Modify, textListener); } /** * Set the alignment of the button in relation to the text box. * Only valid if style is DROP_DOWN. * @param alignment can be either SWT.LEFT or SWT.RIGHT. Other * values have no effect. */ protected void setButtonAlignment(int alignment) { if(SWT.LEFT == alignment) { leftAlign = true; } else if(SWT.RIGHT == alignment) { leftAlign = false; } layout(true); } /** * Set the custom image for the drop down button. Only valid if style is * DROP_DOWN. Passing null in will set the image to its default value. * @param image */ protected void setButtonImage(Image image) { doSetButtonImage(image); } /** * Set the text for the drop down button. Only valid if style is DROP_DOWN. * Passing null will clear the text. * @param text */ protected void setButtonText(String text) { if(checkButton()) { button.setText(text); } } /** * Set the visibility style of the drop button. * <p> * The style will be forced to NEVER if the contents are null * </p> * <dl> * <dt><b>Styles:</b></dt> * <dd>BUTTON_ALWAYS, BUTTON_AUTO, BUTTON_MANUAL, BUTTON_NEVER</dd> * </dl> * <dl> * <dt><b>Style BUTTON_ALWAYS:</b></dt> * <dd>Button will always be shown - standard SWT.DROP_DOWN behaviour. The * method setButtonVisible(boolean) has no affect with this style set</dd> * <dt><b>Style BUTTON_AUTO:</b></dt> * <dd>Button visibility will be handled automatically through focus * events, popup events, as well as programmatically</dd> * <dt><b>Style BUTTON_MANUAL:</b></dt> * <dd>Button visibility will only be handled programmatically</dd> * <dt><b>Style BUTTON_NEVER:</b></dt> * <dd>Button will never be shown - standard SWT.SIMPLE behaviour. The * method setButtonVisible(boolean) has no affect with this style set</dd> * </dl> * * @param visibility * the visibility style constant * @see #setButtonVisible(boolean) */ protected void setButtonVisibility(int visibility) { buttonVisibility = visibility; setButtonVisible(false); if(buttonVisibility == BUTTON_AUTO) { buttonVisibilityListener = new Listener() { public void handleEvent(Event event) { switch(event.type) { case SWT.FocusIn: setButtonVisible(true); break; case SWT.FocusOut: setButtonVisible(false); break; } } }; addListener(SWT.FocusIn, buttonVisibilityListener); addListener(SWT.FocusOut, buttonVisibilityListener); } else { if(buttonVisibilityListener != null) { removeListener(SWT.FocusIn, buttonVisibilityListener); removeListener(SWT.FocusOut, buttonVisibilityListener); buttonVisibilityListener = null; } } } /** * Set the visible state of the button * <p> * Note: This method is only useful when the button's visibility style is * either AUTO or MANUAL. * </p> * * @param visible * @see #setButtonVisibility(int) */ protected void setButtonVisible(boolean visible) { if(BUTTON_ALWAYS == buttonVisibility) { visible = true; } else if(BUTTON_NEVER == buttonVisibility) { visible = false; } if(checkButton()) { button.setVisible(visible); } layout(true); update(); } /** * set the content of the popup shell * <p> * Can be a single control, or a Composite consisting of many controls * </p> * * @param content */ protected void setContent(Control content) { this.content = content; if(this.content != null) { if(!simple) { this.content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); } } } /** * Called when the popup shell has been open, this method provides a * location for subclasses to set the focus to the content. * @return true if the focus was set, false otherwise */ protected abstract boolean setContentFocus(); /** * Sets the editable state. * * @param editable * the new editable state */ public void setEditable(boolean editable) { panel.setStyle(SWT.READ_ONLY, !editable); if(checkButton()) { button.setEnabled(editable); } if(checkText()) { if(editable != text.getControl().getEditable()) { text.getControl().setEditable(editable); if(editable) { addTextListener(); } else { removeTextListener(); } } } } public void setEnabled(boolean enabled) { super.setEnabled(enabled); panel.setEnabled(enabled); } public boolean setFocus() { return panel.setFocus(); } public void setFont(Font font) { super.setFont(font); if(checkButton()) { button.setFont(font); } if(checkText()) { text.setFont(font); } if(checkContent()) { content.setFont(font); } } /** * if holdOpen is true, the popup shell will not close regardless of events * and/or calls to popUp(false) until holdOpen is first set false * <p> * merely sets the holdOpen flag, does not change popup visibility state * </p> * * @param holdOpen */ protected void setHoldOpen(boolean holdOpen) { this.holdOpen = holdOpen; } /** * Sets the menu for the Text widget of this DropCombo * <p> * Note that setting the menu to null causes the native menu to be used * </p> * <p> * If the intent is to disable the menu, then set it to a blank menu * </p> */ public void setMenu(Menu menu) { if(checkText()) { text.getControl().setMenu(menu); } else { super.setMenu(menu); } } /** * Provides a chance for subclasses to set the properties of the * modify event called when the text is modified. Not valid if style * is SIMPLE. * <p> * For example, CDateTime overrides this method to set the data field * to the current time:<br/> * <code style="margin-left:25px">e.data = calendar.getTime();<code> * </p> * @param e */ protected void setModifyEventProperties(Event e) { // subclasses to implement } /** * If pop is true, then opens the popup shell (sets to visible)<br> * If pop is false, closes the popup shell (sets to not visible)<br> * If <code>content == null</code> this method simply returns.<br> * If <code>popup == null</code> then <code>popup</code> will be * created. * @param open true to open the popup shell, false to close it. * @see BaseCombo#setOpen(boolean, Runnable) */ protected void setOpen(boolean open) { setOpen(open, null); } /** * If pop is true, then opens the popup shell (sets to visible)<br> * If pop is false, closes the popup shell (sets to not visible)<br> * If <code>content == null</code> this method simply returns.<br> * If <code>popup == null</code> then <code>popup</code> will be * created. * @param open true to open the popup shell, false to close it. * @param callback a runnable to be run when the operation completes. * @see BaseCombo#setOpen(boolean) */ protected synchronized void setOpen(boolean open, final Runnable callback) { if(content == null || content.isDisposed()) { if(contentShell != null) { contentShell.dispose(); contentShell = null; } this.open = false; return; } if(contentShell == null || contentShell.isDisposed()) { createContentShell(); } if(getShell() != contentShell.getParent()) { content.setParent(this); contentShell.dispose(); contentShell = null; createContentShell(); } if(content.getParent() != contentShell) { content.setParent(contentShell); } if(!open) { if(!holdOpen) { this.open = false; preClose(contentShell); // Point location = positionControl.getComposite().toDisplay(positionControl.getLocation()); // Point contentLocation = contentShell.getLocation(); // if(location.y > contentLocation.y) { // aStyle |= Animator.UP; // } Point start = contentShell.getSize(); Point end = new Point(start.x, 0); Runnable runnable = new Runnable() { public void run() { postClose(contentShell); if(callback != null) { callback.run(); } } }; AnimationRunner runner = new AnimationRunner(); runner.runEffect(new Resize(contentShell, start, end, 200, new LinearInOut(), runnable, runnable)); if(checkText()) { text.setFocus(); } } } else { this.open = true; Point size = content.computeSize(-1, -1); content.setSize(size); Point location = positionControl.getComposite().toDisplay(positionControl.getLocation()); location.y += (positionControl.getSize().y + 2); int dHeight = getDisplay().getClientArea().height; if((location.y + size.y) > dHeight) { location.y -= (positionControl.getSize().y + size.y + 4); // aStyle |= Animator.UP; } if((stretchControl != null) && (size.x < stretchControl.getSize().x)) { size.x = stretchControl.getSize().x; // contentShell.setSize(size); } if(leftAlign) { location.x -= positionControl.getLocation().x; } else { location.x += (positionControl.getSize().x - size.x); if(location.x < 0) { location.x = 0; } } if(win32) { location.x += 2; } else if(carbon) { location.y += 8; } contentShell.setBounds(location.x, location.y, size.x, 0); // chance for subclasses to do something before the shell becomes visible preOpen(contentShell); Point start = new Point(size.x, 0); Point end = new Point(size.x, size.y); Runnable runnable = new Runnable() { public void run() { setContentFocus(); postOpen(contentShell); if(callback != null) { callback.run(); } } }; contentShell.setVisible(true); AnimationRunner runner = new AnimationRunner(); runner.runEffect(new Resize(contentShell, start, end, 200, new LinearInOut(), runnable, runnable)); contentShell.setRedraw(true); } if(BUTTON_AUTO == buttonVisibility) { setButtonVisible(!open); } } /** * sets the control to which the popup will align itself * <p> * the control does not necessarily need to be "this" or the button, but * will default to "this" if positionControl == null * </p> * * @param positionControl */ protected void setPositionControl(VControl positionControl) { if(positionControl == null) { this.positionControl = panel; } else { this.positionControl = positionControl; } } /** * If stretch is false, then the width of the popup will be set to its * preferred width (via computeSize(SWT.DEFAULT, SWT.DEFAULT)) * <p> * However, if stretchControl is true, the width of the popup will be * stretched to equal the width of this control (if, however, popup's * preferred width is greater than this control's width popup will not be * shrunk down) * </p> * * @param stretch */ protected void setStretch(boolean stretch) { this.stretchControl = stretch ? panel : null; } /** * Sets the tooltip on the text and button parts of this Composite widget. * * @param tooltip * the new tooltip text */ public void setToolTipText(String tooltip) { text.setToolTipText(tooltip); button.setToolTipText(tooltip); super.setToolTipText(tooltip); } }