/* * This is part of Geomajas, a GIS framework, http://www.geomajas.org/. * * Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium. * * The program is available in open source according to the GNU Affero * General Public License. All contributions in this program are covered * by the Geomajas Contributors License Agreement. For full licensing * details, see LICENSE.txt in the project root. */ package org.geomajas.widget.utility.gwt.client.ribbon.dropdown; import java.util.ArrayList; import java.util.List; import org.geomajas.gwt.client.action.ToolbarBaseAction; import org.geomajas.gwt.client.action.toolbar.ButtonGroup; import org.geomajas.gwt.client.action.toolbar.DropDownButtonAction; import org.geomajas.gwt.client.action.toolbar.parameter.ButtonLayoutStyle; import org.geomajas.widget.utility.common.client.ribbon.RibbonColumn; import org.geomajas.widget.utility.common.client.ribbon.RibbonColumn.TitleAlignment; import org.geomajas.widget.utility.gwt.client.action.ButtonAction; import org.geomajas.widget.utility.gwt.client.action.ToolbarButtonAction; import org.geomajas.widget.utility.gwt.client.action.ToolbarButtonCanvas; import org.geomajas.widget.utility.gwt.client.ribbon.RibbonButton; import org.geomajas.widget.utility.gwt.client.ribbon.RibbonColumnCanvas; import org.geomajas.widget.utility.gwt.client.util.GuwLayout; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; import com.smartgwt.client.types.AnimationEffect; import com.smartgwt.client.types.Overflow; import com.smartgwt.client.types.Positioning; import com.smartgwt.client.widgets.Label; import com.smartgwt.client.widgets.StatefulCanvas; import com.smartgwt.client.widgets.events.ClickHandler; import com.smartgwt.client.widgets.layout.VStack; /** * Ribbon-only class that aligns itself with its button in the animateShow override. * Furthermore a {@link NativePreviewHandler} is added to {@link Event} in the override as well, * that will close this panel when clicked outside this panel and button. * * @author Emiel Ackermann */ public class DropDownPanel extends VStack { private final StatefulCanvas button; private HandlerRegistration registration; private List<VStack> groups = new ArrayList<VStack>(); private List<Label> groupTitles = new ArrayList<Label>(); private List<VStack> bodies = new ArrayList<VStack>(); private List<RibbonColumn> buttons = new ArrayList<RibbonColumn>(); public DropDownPanel(final DropDownRibbonButton button) { super(); this.button = button; ButtonAction buttonAction = button.getButtonAction(); ToolbarBaseAction toolbarAction = ((ToolbarButtonAction) buttonAction).getToolbarAction(); setWidth(((DropDownButtonAction) toolbarAction).getPanelWidth()); setPosition(Positioning.ABSOLUTE); setOverflow(Overflow.VISIBLE); setAutoHeight(); } public void addGroup(ButtonGroup title, List<ButtonAction> actions) { VStack group = new VStack(); group.setOverflow(Overflow.VISIBLE); group.setAutoHeight(); group.setWidth100(); ButtonLayoutStyle buttonButtonLayoutStyle = ButtonLayoutStyle.ICON_AND_TITLE; if (null != title) { Label groupTitle = new Label(title.getTitle()); groupTitle.setOverflow(Overflow.VISIBLE); groupTitle.setAutoHeight(); groupTitle.setWidth100(); group.addMember(groupTitle); groupTitles.add(groupTitle); buttonButtonLayoutStyle = (ButtonLayoutStyle) (null == title.getButtonLayoutStyle() ? ButtonLayoutStyle.ICON_AND_TITLE : title.getButtonLayoutStyle()); } VStack body = new VStack(); body.setOverflow(Overflow.VISIBLE); body.setAutoHeight(); body.setWidth100(); for (ButtonAction action : actions) { RibbonColumn button = getButton(action, buttonButtonLayoutStyle); body.addMember(button.asWidget()); buttons.add(button); } group.addMember(body); bodies.add(body); addMember(group); groups.add(group); } /** * Converts the given action into a {@link RibbonColumn}. * * @param action ButtonAction * @param buttonButtonLayoutStyle the layout of the group. Is used if the action does not contain one itself. * @return column RibbonColumn containing the button. */ private RibbonColumn getButton(ButtonAction action, ButtonLayoutStyle buttonButtonLayoutStyle) { RibbonColumn column; if (action instanceof ToolbarButtonCanvas) { column = new RibbonColumnCanvas((ToolbarButtonCanvas) action); } else { // if no layout was given, use the one given by the group if (null == action.getButtonLayoutStyle()) { action.setButtonLayoutStyle(buttonButtonLayoutStyle); } RibbonButton button = new RibbonButton(action, 16, TitleAlignment.RIGHT); if (ButtonLayoutStyle.ICON_AND_TITLE.equals(buttonButtonLayoutStyle)) { button = new RibbonButton(action, GuwLayout.DropDown.ribbonBarDropDownButtonIconSize, TitleAlignment.RIGHT); button.setHeight(GuwLayout.DropDown.ribbonBarDropDownButtonIconSize + 4); } else if (ButtonLayoutStyle.ICON_TITLE_AND_DESCRIPTION.equals(buttonButtonLayoutStyle)) { button = new RibbonButton(action, GuwLayout.DropDown.ribbonBarDropDownButtonDescriptionIconSize, TitleAlignment.RIGHT); button.setHeight(GuwLayout.DropDown.ribbonBarDropDownButtonDescriptionIconSize + 4); } button.setOverflow(Overflow.VISIBLE); button.setWidth100(); button.setMargin(2); button.addClickHandler(new ClickHandler() { public void onClick( com.smartgwt.client.widgets.events.ClickEvent event) { hide(); } }); column = button; } return column; } @Override public void setStyleName(String styleName) { super.setStyleName(styleName); for (VStack group : groups) { group.setStyleName(styleName + "Group"); } for (Label groupTitle : groupTitles) { groupTitle.setStyleName(styleName + "GroupTitle"); } for (VStack body : bodies) { body.setStyleName(styleName + "GroupBody"); } for (RibbonColumn button : buttons) { if (button instanceof RibbonButton) { button.setButtonBaseStyle(styleName + "Button"); } } } @Override public void animateShow(final AnimationEffect effect) { this.moveTo(button.getPageLeft(), button.getPageBottom() - 2); button.setSelected(true); registration = previewMouseUpHandler(); super.animateShow(effect); } /** * Through a {@link NativePreviewHandler} and its {@link NativePreviewEvent} * all mouse events are caught here before they are processed. * If the event is of type {@link Event#ONMOUSEUP} and the click was outside * the button or this drop-down panel, the panel is closed. * * @return handler registration */ private HandlerRegistration previewMouseUpHandler() { return Event.addNativePreviewHandler(new NativePreviewHandler() { public void onPreviewNativeEvent(NativePreviewEvent preview) { int typeInt = preview.getTypeInt(); if (typeInt == Event.ONMOUSEUP) { Event event = Event.as(preview.getNativeEvent()); // Can not retrieve the clicked widget from the Event, so here goes... int clientX = event.getClientX(); int clientY = event.getClientY(); // Was the button clicked? int left = button.getPageLeft(); int right = button.getPageRight(); int top = button.getPageTop(); int bottom = button.getPageBottom(); boolean mouseIsOutside = true; if (clientX > left && clientX < right && clientY > top && clientY < bottom) { mouseIsOutside = false; } if (mouseIsOutside) { // Was this panel clicked? right = getPageRight(); top = getPageTop(); bottom = getPageBottom(); if (clientX > left && clientX < right && clientY > top && clientY < bottom) { mouseIsOutside = false; } } if (mouseIsOutside) { hide(); } } } }); } /** * hide() instead of animateHide(), to make sure * the panel hides immediately (when navigating away from the ribbon). */ @Override public void hide() { button.setSelected(false); if (null != registration) { registration.removeHandler(); } super.hide(); } }