package com.extjs.gxt.ui.client.widget.button; import com.extjs.gxt.ui.client.GXT; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.Style.ButtonArrowAlign; import com.extjs.gxt.ui.client.Style.ButtonScale; import com.extjs.gxt.ui.client.Style.IconAlign; import com.extjs.gxt.ui.client.core.El; import com.extjs.gxt.ui.client.core.Template; import com.extjs.gxt.ui.client.event.ButtonEvent; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.PreviewEvent; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.util.BaseEventPreview; import com.extjs.gxt.ui.client.util.IconHelper; import com.extjs.gxt.ui.client.util.TextMetrics; import com.extjs.gxt.ui.client.widget.BoxComponent; import com.extjs.gxt.ui.client.widget.IconSupport; import com.extjs.gxt.ui.client.widget.menu.Menu; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.AbstractImagePrototype; import com.google.gwt.user.client.ui.Accessibility; /** * A button component. * * <dl> * <dt><b>Events:</b></dt> * * <dd><b>BeforeSelect</b> : ButtonEvent(button, event)<br> * <div>Fires before this button is selected.</div> * <ul> * <li>button : this</li> * <li>event : the dom event</li> * </ul> * </dd> * * <dd><b>Select</b> : ButtonEvent(button, event)<br> * <div>Fires when this button is selected.</div> * <ul> * <li>button : this</li> * <li>event : the dom event</li> * </ul> * </dd> * * <dd><b>MenuShow</b> : ButtonEvent(button, item)<br> * <div>If this button has a menu, this event fires when it is shown.</div> * <ul> * <li>button : this</li> * <li>menu : the menu</li> * </ul> * </dd> * * <dd><b>MenuHide</b> : ButtonEvent(button, item)<br> * <div>If this button has a menu, this event fires when it is hidden.</div> * <ul> * <li>button : this</li> * <li>menu : the menu</li> * </ul> * </dd> * * </dt> */ public class Button extends BoxComponent implements IconSupport { private static Template buttonTemplate; protected El buttonEl; protected String buttonSelector = "button"; protected Menu menu; protected ButtonScale scale = ButtonScale.SMALL; protected Template template; protected String text; private ButtonArrowAlign arrowAlign = ButtonArrowAlign.RIGHT; private boolean handleMouseEvents = true; private IconAlign iconAlign = IconAlign.LEFT; private AbstractImagePrototype icon; private String menuAlign = "tl-bl?"; private int minWidth = Style.DEFAULT; private int tabIndex = 0; private String type = "button"; private BaseEventPreview preview; /** * Creates a new button. */ public Button() { baseStyle = "x-btn"; preview = new BaseEventPreview() { protected boolean onAutoHide(PreviewEvent ce) { Button.this.onMouseOut(null); return true; } }; } /** * Creates a new button with the given text. * * @param text the button text */ public Button(String text) { this(); setText(text); } /** * Creates a new button with the given text and specified selection listener. * * @param text the button's text * @param listener the selection listener */ public Button(String text, SelectionListener<ButtonEvent> listener) { this(text); addSelectionListener(listener); } /** * Creates a new button with the given text and iconStyle. * * @param text the button text * @param icon the icon */ public Button(String text, AbstractImagePrototype icon) { this(text); setIcon(icon); } /** * Creates a new button with the given text, iconStyle and specified selection * listener. * * @param text the button text * @param icon the icon * @param listener the selection listener */ public Button(String text, AbstractImagePrototype icon, SelectionListener<ButtonEvent> listener) { this(text, icon); addSelectionListener(listener); } /** * Adds a selection listener. * * @param listener the listener to add */ public void addSelectionListener(SelectionListener<ButtonEvent> listener) { addListener(Events.Select, listener); } /** * Returns the button's arrow alignment. * * @return the arrow alignment */ public ButtonArrowAlign getArrowAlign() { return arrowAlign; } /** * Returns the button's icon alignment. * * @return the icon alignment */ public IconAlign getIconAlign() { return iconAlign; } /** * Returns the button's icon style. * * @return the icon style */ public AbstractImagePrototype getIcon() { return icon; } /** * Returns the button's menu (if it has one). * * @return the menu */ public Menu getMenu() { return menu; } /** * Returns the button's menu alignment. * * @return the menu alignment */ public String getMenuAlign() { return menuAlign; } /** * Returns the button's minimum width. * * @return the minWidth the minimum width */ public int getMinWidth() { return minWidth; } /** * Returns true if mouse over effect is disabled. * * @return the handleMouseEvents the handle mouse event state */ public boolean getMouseEvents() { return handleMouseEvents; } /** * Returns the buttons scale. * * @return the scale */ public ButtonScale getScale() { return scale; } /** * Returns the button's text. * * @return the button text */ public String getText() { return text; } /** * @return the type */ public String getType() { return type; } /** * Hide this button's menu (if it has one). */ public void hideMenu() { if (menu != null) { menu.hide(); ButtonEvent be = new ButtonEvent(this); be.setMenu(menu); fireEvent(Events.MenuHide, be); } } @Override public void onComponentEvent(ComponentEvent ce) { super.onComponentEvent(ce); ButtonEvent be = (ButtonEvent) ce; switch (ce.getEventTypeInt()) { case Event.ONMOUSEOVER: onMouseOver(ce); break; case Event.ONMOUSEOUT: onMouseOut(ce); break; case Event.ONMOUSEDOWN: ce.stopEvent(); onMouseDown(ce); break; case Event.ONMOUSEUP: ce.stopEvent(); onMouseUp(ce); break; case Event.ONCLICK: ce.stopEvent(); onClick(ce); break; case Event.ONFOCUS: onFocus(ce); break; case Event.ONBLUR: onBlur(be); } } /** * Removes a previously added listener. * * @param listener the listener to be removed */ public void removeSelectionListener(SelectionListener<ButtonEvent> listener) { removeListener(Events.Select, listener); } /** * Sets the arrow alignment (defaults to RIGHT). * * @param arrowAlign the arrow alignment */ public void setArrowAlign(ButtonArrowAlign arrowAlign) { this.arrowAlign = arrowAlign; } /** * Sets the icon alignment (defaults to LEFT). * * @param iconAlign the icon alignment */ public void setIconAlign(IconAlign iconAlign) { this.iconAlign = iconAlign; } /** * Sets the button's icon style. The style name should match a CSS style that * specifies a background image using the following format: * * <pre> * * <code> .my-icon { background: url(images/icons/my-icon.png) no-repeat * center left !important; } </code> * * </pre> * * @param icon the icon */ public void setIcon(AbstractImagePrototype icon) { if (rendered) { if (buttonEl.selectNode("img") != null) { buttonEl.selectNode("img").remove(); el().removeStyleName("x-btn-text-icon", "x-btn-icon", "x-btn-noicon"); } el().addStyleName( (icon != null ? ((text != null && text.length() > 0) ? " x-btn-text-icon" : " x-btn-icon") : " x-btn-noicon")); if (icon != null) { Element e = (Element) icon.createElement().cast(); buttonEl.insertFirst(e); El.fly(e).makePositionable(true); String align = "b-b"; if (iconAlign == IconAlign.BOTTOM) { align = "b-b"; } else if (iconAlign == IconAlign.TOP) { align = "t-t"; } else if (iconAlign == IconAlign.LEFT) { align = "tl-tl"; } else if (iconAlign == IconAlign.RIGHT) { align = "tr-tr"; } El.fly(e).alignTo(buttonEl.dom, align, null); } } this.icon = icon; } public void setIconStyle(String icon) { setIcon(IconHelper.create(icon)); } /** * Sets the button's menu. * * @param menu the menu */ public void setMenu(Menu menu) { this.menu = menu; menu.addListener(Events.Hide, new Listener<ComponentEvent>() { public void handleEvent(ComponentEvent be) { focus(); } }); } /** * Sets the position to align the menu to, see {@link El#alignTo} for more * details (defaults to 'tl-bl?', pre-render). * * @param menuAlign the menu alignment */ public void setMenuAlign(String menuAlign) { this.menuAlign = menuAlign; } /** * Sets he minimum width for this button (used to give a set of buttons a * common width) * * @param minWidth the minimum width */ public void setMinWidth(int minWidth) { this.minWidth = minWidth; } /** * False to disable visual cues on mouseover, mouseout and mousedown (defaults * to true). * * @param handleMouseEvents false to disable mouse over cahnges */ public void setMouseEvents(boolean handleMouseEvents) { this.handleMouseEvents = handleMouseEvents; } /** * Sets the button scale. * * @param scale the scale to set */ public void setScale(ButtonScale scale) { this.scale = scale; } /** * Sets the button's tab index. * * @param index the tab index */ public void setTabIndex(int index) { this.tabIndex = index; if (rendered && buttonEl != null) { buttonEl.dom.setPropertyInt("tabIndex", index); } } /** * Sets the button's text. * * @param text the new text */ public void setText(String text) { this.text = text; if (rendered) { if (text != null && text.equals("")) { text = " "; } buttonEl.update(text); autoWidth(); } } /** * Submit, reset or button (defaults to 'button'). * * @param type the new type */ public void setType(String type) { this.type = type; } /** * Show this button's menu (if it has one). */ public void showMenu() { if (menu != null) { menu.show(getElement(), menuAlign); ButtonEvent be = new ButtonEvent(this); be.setMenu(menu); fireEvent(Events.MenuShow, be); } } @Override protected void afterRender() { super.afterRender(); setTabIndex(tabIndex); setIcon(icon); autoWidth(); } protected void autoWidth() { if (rendered && width == null) { setWidth("auto"); if (GXT.isIE7 && GXT.isStrict) { if (buttonEl != null && buttonEl.getWidth() > 20) { buttonEl.clip(); TextMetrics.get().bind(buttonEl.dom); buttonEl.setWidth(TextMetrics.get().getWidth(text) + buttonEl.getFrameWidth("lr"), true); } } if (minWidth != Style.DEFAULT) { if (getWidth() < minWidth) { setWidth(minWidth); } } } } @Override protected ComponentEvent createComponentEvent(Event event) { return new ButtonEvent(this); } @Override protected El getFocusEl() { return buttonEl; } protected String getMenuClass() { if (menu != null) { if (arrowAlign == ButtonArrowAlign.BOTTOM) { return "x-btn-arrow-bottom"; } else { return "x-btn-arrow"; } } else { return ""; } } protected void onBlur(ButtonEvent e) { removeStyleName(baseStyle + "-focus"); } protected void onClick(ComponentEvent ce) { ce.preventDefault(); focus(); hideToolTip(); if (!disabled) { ButtonEvent be = new ButtonEvent(this); if (!fireEvent(Events.BeforeSelect, be)) { return; } if (menu != null && !menu.isVisible()) { showMenu(); } fireEvent(Events.Select, be); } } @Override protected void onDetach() { super.onDetach(); preview.remove(); } @Override protected void onDisable() { if (!GXT.isIE6 || text == null) { addStyleName(disabledStyle); } removeStyleName(baseStyle + "-over"); el().disable(); } @Override protected void onEnable() { super.onEnable(); el().enable(); } protected void onFocus(ComponentEvent ce) { if (!disabled) { addStyleName(baseStyle + "-focus"); } } protected void onMenuHide(ComponentEvent ce) { removeStyleName(baseStyle + "-menu-active"); } protected void onMenuShow(ComponentEvent ce) { addStyleName(baseStyle + "-menu-active"); } protected void onMouseDown(ComponentEvent ce) { addStyleName(baseStyle + "-click"); } protected void onMouseOut(ComponentEvent ce) { removeStyleName(baseStyle + "-click"); removeStyleName(baseStyle + "-over"); } protected void onMouseOver(ComponentEvent ce) { if (!disabled && handleMouseEvents) { addStyleName(baseStyle + "-over"); } preview.add(); } protected void onMouseUp(ComponentEvent ce) { removeStyleName(baseStyle + "-click"); } protected void onRender(Element target, int index) { if (template == null) { if (buttonTemplate == null) { StringBuffer sb = new StringBuffer(); sb.append("<table cellspacing=\"0\" class=\"x-btn\" role=\"presentation\"><tbody class=\"{2}\" >"); sb.append("<tr><td class=\"x-btn-tl\"><i> </i></td><td class=\"x-btn-tc\"></td><td class=\"x-btn-tr\"><i> </i></td></tr>"); sb.append("<tr><td class=\"x-btn-ml\"><i> </i></td><td class=\"x-btn-mc\"><em class=\"{3}\" unselectable=\"on\"><button class=\"x-btn-text\" type=\"{1}\">{0}</button></em></td><td class=\"x-btn-mr\"><i> </i></td></tr>"); sb.append("<tr><td class=\"x-btn-bl\"><i> </i></td><td class=\"x-btn-bc\"></td><td class=\"x-btn-br\"><i> </i></td></tr>"); sb.append("</tbody></table>"); buttonTemplate = new Template(sb.toString()); } template = buttonTemplate; } setElement(template.create((text != null && text.length() > 0) ? text : " ", getType(), "x-btn-" + scale.name().toLowerCase() + " x-btn-icon-" + scale.name().toLowerCase() + "-" + iconAlign.name().toLowerCase(), getMenuClass()), target, index); super.onRender(target, index); buttonEl = el().selectNode(buttonSelector); buttonEl.makePositionable(); if (getFocusEl() != null) { getFocusEl().addEventsSunk(Event.FOCUSEVENTS); } preview.getIgnoreList().add(getElement()); buttonEl.setTabIndex(0); if (GXT.isAriaEnabled()) { Accessibility.setRole(buttonEl.dom, Accessibility.ROLE_BUTTON); if (menu != null) { Accessibility.setState(buttonEl.dom, "aria-haspopup", "true"); } } sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.FOCUSEVENTS); } }