/* * Copyright (C) 2012 Jan Pokorsky * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package cz.cas.lib.proarc.webapp.client.action; import com.google.gwt.event.logical.shared.HasValueChangeHandlers; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.GwtEvent; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.event.shared.HandlerRegistration; import com.smartgwt.client.util.Page; import com.smartgwt.client.widgets.IconButton; import com.smartgwt.client.widgets.events.ClickEvent; import com.smartgwt.client.widgets.events.ClickHandler; import com.smartgwt.client.widgets.events.ShowContextMenuEvent; import com.smartgwt.client.widgets.events.ShowContextMenuHandler; import com.smartgwt.client.widgets.events.VisibilityChangedEvent; import com.smartgwt.client.widgets.events.VisibilityChangedHandler; import com.smartgwt.client.widgets.grid.ListGrid; import com.smartgwt.client.widgets.grid.ListGridRecord; import com.smartgwt.client.widgets.menu.IconMenuButton; import com.smartgwt.client.widgets.menu.Menu; import com.smartgwt.client.widgets.menu.MenuItem; import com.smartgwt.client.widgets.menu.events.MenuIconClickEvent; import com.smartgwt.client.widgets.menu.events.MenuIconClickHandler; import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent; import com.smartgwt.client.widgets.toolbar.ToolStrip; import com.smartgwt.client.widgets.toolbar.ToolStripButton; import java.util.ArrayList; import java.util.Arrays; import java.util.logging.Logger; /** * Helpers for {@link Action}'s instances. * * @author Jan Pokorsky */ public final class Actions { private static final Logger LOG = Logger.getLogger(Actions.class.getName()); /** * No-op action useful for menu buttons. */ public static Action emptyAction(String title, String icon, String tooltip) { return new AbstractAction(title, icon, tooltip) { @Override public void performAction(ActionEvent event) { throw new UnsupportedOperationException("Not supported"); } }; } public static ToolStripButton asToolStripButton(final Action action, final Object source) { ToolStripButton tsb = new ToolStripButton(); String title = action.getTitle(); if (title != null) { tsb.setTitle(title); } String icon = action.getIcon(); if (icon != null) { tsb.setIcon(icon); } String tooltip = action.getTooltip(); if (tooltip != null) { tsb.setTooltip(tooltip); } tsb.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { ActionEvent aEvent = new ActionEvent(source); action.performAction(aEvent); } }); return tsb; } /** Gets action view listening to action source changes. */ public static IconButton asIconButton(Action action, ActionSource source) { return asIconButton(action, source.getSource(), source); } /** Gets action view. */ public static IconButton asIconButton(Action action, Object source) { return asIconButton(action, source, null); } private static <T> IconButton asIconButton( final Action action, final Object source, ActionSource actionSource) { final IconButton btn = new IconButton(); btn.setHoverWidth(200); String title = action.getTitle(); if (title != null) { btn.setTitle(title); } else { btn.setTitle(""); } String icon = action.getIcon(); if (icon != null) { btn.setIcon(icon); } String tooltip = action.getTooltip(); if (tooltip != null) { btn.setTooltip(tooltip); } btn.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { ActionEvent aEvent = new ActionEvent(source); action.performAction(aEvent); } }); if (actionSource != null) { actionSource.addValueChangeHandler(new ValueChangeHandler<ActionEvent>() { @Override public void onValueChange(ValueChangeEvent<ActionEvent> event) { boolean accept = action.accept(event.getValue()); if (accept) { String atooltip = action.getTooltip(); String btooltip = btn.getTooltip(); if (atooltip == null ? btooltip != null : !atooltip.equals(btooltip)) { btn.setTooltip(atooltip); } String atitle = action.getTitle(); String btitle = btn.getTitle(); if (atitle == null ? btitle != null : !atitle.equals(btitle)) { btn.setTitle(atitle); } } btn.setVisible(accept); } }); } return btn; } /** Gets action view. */ public static IconMenuButton asIconMenuButton(final Action action, final Object source) { return asIconMenuButton(action, source, null); } /** Gets action view listening to action source changes. */ public static IconMenuButton asIconMenuButton(Action action, ActionSource source) { return asIconMenuButton(action, source.getSource(), source); } private static IconMenuButton asIconMenuButton(final Action action, final Object source, ActionSource actionSource) { final IconMenuButton btn = new IconMenuButton(); String title = action.getTitle(); if (title != null) { btn.setTitle(title); } String icon = action.getIcon(); if (icon != null) { btn.setIcon(icon); } String tooltip = action.getTooltip(); if (tooltip != null) { btn.setTooltip(tooltip); } IconMenuButtonHandler handler = new IconMenuButtonHandler(btn, source, action); btn.addClickHandler(handler); btn.addMenuIconClickHandler(handler); if (actionSource != null) { actionSource.addValueChangeHandler(new ValueChangeHandler<ActionEvent>() { @Override public void onValueChange(ValueChangeEvent<ActionEvent> event) { boolean accept = action.accept(event.getValue()); btn.setVisible(accept); } }); } return btn; } /** Gets action view. */ public static MenuItem asMenuItem(final Action action, final Object source) { return asMenuItem(action, source, false); } /** Gets action view. */ public static MenuItem asMenuItem(final Action action, final Object source, boolean willAsk) { return asMenuItem(action, source, null, willAsk); } /** Gets action view listening to action source changes. */ public static MenuItem asMenuItem(final Action action, final ActionSource source, boolean willAsk) { return asMenuItem(action, source.getSource(), source, willAsk); } private static MenuItem asMenuItem(final Action action, final Object source, ActionSource actionSource, boolean willAsk) { final MenuItem mi = new MenuItem(); mi.setEnabled(Boolean.TRUE); String title = action.getTitle(); if (title != null) { title = willAsk ? title + "..." : title; mi.setTitle(title); } String icon = action.getIcon(); if (icon != null) { // workaround: Menu uses own skin img dir default icon = icon.replace("[SKIN]", Page.getSkinImgDir()); mi.setIcon(icon); } mi.addClickHandler(new com.smartgwt.client.widgets.menu.events.ClickHandler() { @Override public void onClick(MenuItemClickEvent event) { ActionEvent aEvent = new ActionEvent(source); action.performAction(aEvent); } }); if (actionSource != null) { actionSource.addValueChangeHandler(new ValueChangeHandler<ActionEvent>() { @Override public void onValueChange(ValueChangeEvent<ActionEvent> event) { boolean accept = action.accept(event.getValue()); mi.setEnabled(accept); } }); } return mi; } /** * Creates default menu. */ public static Menu createMenu() { final LiveMenu menu = new LiveMenu(); menu.setShowShadow(Boolean.TRUE); menu.addVisibilityChangedHandler(new VisibilityChangedHandler() { @Override public void onVisibilityChanged(VisibilityChangedEvent event) { if (event.getIsVisible()) { menu.includeEnabled(); } } }); return menu; } /** * Creates default toolbar. */ public static ToolStrip createToolStrip() { ToolStrip t = new ToolStrip(); t.setMembersMargin(2); t.setLayoutMargin(2); t.setWidth100(); return t; } /** * Gets selected objects from the action event. * @param <T> type of selected objects * @param event event holding {@link Selectable} * @return the selection or {@code null} */ public static <T> T[] getSelection(ActionEvent event) { Object source = event.getSource(); if (source instanceof Selectable) { Selectable<T> selectable = (Selectable<T>) source; return selectable.getSelection(); } return null; } /** * Fixes {@link ListGrid} context menu * to update grid selection on right click properly. * <p>Bug: right click selects row without firing selection event. */ public static void fixListGridContextMenu(final ListGrid grid) { grid.addShowContextMenuHandler(new ShowContextMenuHandler() { @Override public void onShowContextMenu(ShowContextMenuEvent event) { int eventRow = grid.getEventRow(); if (eventRow < 0) { return ; } ListGridRecord[] selectedRecords = grid.getSelectedRecords(); if (selectedRecords.length <= 1) { // ListGrid does not fire selection updated event if right click // no select if multi-selection exists grid.selectSingleRecord(eventRow); } Menu contextMenu = grid.getContextMenu(); contextMenu.showContextMenu(); event.cancel(); } }); } /** * Helper class to fire action source changes that can result to show/hide * action view that listen to this instance. */ public static final class ActionSource implements HasValueChangeHandlers<ActionEvent> { private HandlerManager handlerManager = new HandlerManager(this); private final Object source; public ActionSource(Object source) { this.source = source; } @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler<ActionEvent> handler) { return handlerManager.addHandler(ValueChangeEvent.getType(), handler); } public Object getSource() { return source; } @Override public void fireEvent(GwtEvent<?> event) { if (event == null) { fireEvent(); return ; } handlerManager.fireEvent(event); } public void fireEvent() { ValueChangeEvent.fire(this, new ActionEvent(source)); } } /** * Hides disabled items and superfluous separators. */ private static final class LiveMenu extends Menu { private final ArrayList<MenuItem> menuItems = new ArrayList<MenuItem>(); /** * Recomputes menu. */ public void includeEnabled() { ArrayList<MenuItem> enabled = new ArrayList<MenuItem>(menuItems.size()); // skip separators of hidden actions boolean lastWasSeparator = true; for (MenuItem item : menuItems) { if (item.getEnabled() || (item.getIsSeparator() && !lastWasSeparator)) { enabled.add(item); lastWasSeparator = item.getIsSeparator(); } } MenuItem[] toArray = enabled.toArray(new MenuItem[enabled.size()]); super.setItems(toArray); } public void includeAll() { if (menuItems.size() != getItems().length) { MenuItem[] toArray = menuItems.toArray(new MenuItem[menuItems.size()]); super.setItems(toArray); } } @Override public void addItem(MenuItem item) { menuItems.add(item); super.addItem(item); } @Override public void addItem(MenuItem item, int index) { includeAll(); menuItems.add(index, item); super.addItem(item, index); } @Override public void setItems(MenuItem... items) { menuItems.clear(); menuItems.addAll(Arrays.asList(items)); super.setItems(items); } @Override public void removeItem(MenuItem item) { menuItems.remove(item); super.removeItem(item); } } private static class IconMenuButtonHandler implements ClickHandler, MenuIconClickHandler { private final IconMenuButton btn; private final Object source; private final Action action; public IconMenuButtonHandler(IconMenuButton btn, Object source, Action action) { this.btn = btn; this.source = source; this.action = action; } @Override public void onClick(ClickEvent event) { onClick(); } @Override public void onMenuIconClick(MenuIconClickEvent event) { onClick(); } private void onClick() { Menu menu = btn.getMenu(); boolean showMenu = menu != null && !(menu.isDrawn() && menu.isVisible()); if (showMenu) { // here could be default action btn.showMenu(); return; } if (menu == null) { ActionEvent aEvent = new ActionEvent(source); action.performAction(aEvent); } } } }