/*
* 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);
}
}
}
}