package org.ovirt.engine.ui.webadmin.widget.action;
import java.util.ArrayList;
import java.util.List;
import org.ovirt.engine.core.compat.EventArgs;
import org.ovirt.engine.core.compat.IEventListener;
import org.ovirt.engine.ui.webadmin.gin.ClientGinjectorProvider;
import org.ovirt.engine.ui.webadmin.idhandler.HasElementId;
import org.ovirt.engine.ui.webadmin.uicommon.model.CommonModelChangeEvent;
import org.ovirt.engine.ui.webadmin.uicommon.model.CommonModelChangeEvent.CommonModelChangeHandler;
import org.ovirt.engine.ui.webadmin.uicommon.model.SearchableModelProvider;
import org.ovirt.engine.ui.webadmin.utils.ElementIdUtils;
import org.ovirt.engine.ui.webadmin.widget.FeatureNotImplementedYetPopup;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.ContextMenuHandler;
import com.google.gwt.event.logical.shared.InitializeEvent;
import com.google.gwt.event.logical.shared.InitializeHandler;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* Base class used to implement action panel widgets.
* <p>
* Subclasses are free to style the UI, given that they declare:
* <ul>
* <li>{@link #actionPanel} widget into which action button widgets will be rendered
* </ul>
*
* @param <T>
* Action panel item type.
*/
public abstract class AbstractActionPanel<T> extends Composite implements HasElementId {
@UiField
public FlowPanel actionPanel;
// List of action buttons managed by this action panel
private final List<ActionButtonDefinition<T>> actionButtonList = new ArrayList<ActionButtonDefinition<T>>();
private final SearchableModelProvider<T, ?> dataProvider;
private final PopupPanel contextPopupPanel;
private final MenuBar contextMenuBar;
private String elementId = DOM.createUniqueId();
public AbstractActionPanel(SearchableModelProvider<T, ?> dataProvider) {
this.dataProvider = dataProvider;
this.contextPopupPanel = new PopupPanel(true);
this.contextMenuBar = new MenuBar(true);
}
protected SearchableModelProvider<T, ?> getDataProvider() {
return dataProvider;
}
@Override
protected void initWidget(Widget widget) {
super.initWidget(widget);
contextPopupPanel.setWidget(contextMenuBar);
}
@Override
public void setElementId(String elementId) {
this.elementId = elementId;
}
protected String getElementId() {
return elementId;
}
/**
* Adds a new button to the action panel.
*/
public void addActionButton(final ActionButtonDefinition<T> buttonDef) {
final ActionButton newActionButton = createNewActionButton(buttonDef);
// Configure the button according to its definition
newActionButton.setEnabledHtml(buttonDef.getEnabledHtml());
newActionButton.setDisabledHtml(buttonDef.getDisabledHtml());
newActionButton.setTitle(buttonDef.getTitle());
// Set button element ID for better accessibility
newActionButton.asWidget().getElement().setId(
ElementIdUtils.createElementId(elementId, buttonDef.getTitle()));
// Add the button to the action panel
if (!buttonDef.isAvailableOnlyFromContext()) {
actionPanel.add(newActionButton.asWidget());
}
actionButtonList.add(buttonDef);
// Add button widget click handler
newActionButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (buttonDef.isImplemented()) {
buttonDef.onClick(getSelectedItems());
}
else {
FeatureNotImplementedYetPopup fniyp = new FeatureNotImplementedYetPopup(
(Widget) event.getSource(), buttonDef.isImplInUserPortal());
fniyp.show();
}
}
});
registerSelectionChangeHandler(buttonDef);
// Update button whenever its definition gets re-initialized
buttonDef.addInitializeHandler(new InitializeHandler() {
@Override
public void onInitialize(InitializeEvent event) {
updateActionButton(newActionButton, buttonDef);
}
});
updateActionButton(newActionButton, buttonDef);
}
private void registerSelectionChangeHandler(final ActionButtonDefinition<T> buttonDef) {
// Update button definition whenever list model item selection changes
final IEventListener itemSelectionChangeHandler = new IEventListener() {
@Override
public void eventRaised(org.ovirt.engine.core.compat.Event ev, Object sender, EventArgs args) {
// Update action button on item selection change
buttonDef.update();
}
};
addSelectionChangeListener(itemSelectionChangeHandler);
ClientGinjectorProvider.instance().getEventBus()
.addHandler(CommonModelChangeEvent.getType(), new CommonModelChangeHandler() {
@Override
public void onCommonModelChange(CommonModelChangeEvent event) {
addSelectionChangeListener(itemSelectionChangeHandler);
}
});
}
private void addSelectionChangeListener(final IEventListener itemSelectionChangeHandler) {
dataProvider.getModel().getSelectedItemChangedEvent().addListener(itemSelectionChangeHandler);
dataProvider.getModel().getSelectedItemsChangedEvent().addListener(itemSelectionChangeHandler);
}
/**
* Adds a context menu handler to the given widget.
*/
public void addContextMenuHandler(Widget widget) {
widget.addDomHandler(new ContextMenuHandler() {
@Override
public void onContextMenu(ContextMenuEvent event) {
event.preventDefault();
event.stopPropagation();
// Show context menu only when not empty
if (hasActionButtons()) {
int eventX = event.getNativeEvent().getClientX();
int eventY = event.getNativeEvent().getClientY();
updateContextMenu();
contextPopupPanel.setPopupPosition(eventX, eventY);
contextPopupPanel.show();
}
}
}, ContextMenuEvent.getType());
}
/**
* Rebuilds context menu items to match the action button list.
*/
void updateContextMenu() {
contextMenuBar.clearItems();
for (final ActionButtonDefinition<T> buttonDef : actionButtonList) {
MenuItem item = new MenuItem(buttonDef.getTitle(), new Command() {
@Override
public void execute() {
contextPopupPanel.hide();
buttonDef.onClick(getSelectedItems());
}
});
updateMenuItem(item, buttonDef);
contextMenuBar.addItem(item);
}
}
/**
* Ensures that the specified action button is visible or hidden and enabled or disabled as it should.
*/
void updateActionButton(ActionButton button, ActionButtonDefinition<T> buttonDef) {
button.asWidget().setVisible(buttonDef.isAccessible());
button.setEnabled(buttonDef.isEnabled(getSelectedItems()));
}
/**
* Ensures that the specified menu item is visible or hidden and enabled or disabled as it should.
*/
void updateMenuItem(MenuItem item, ActionButtonDefinition<T> buttonDef) {
item.setVisible(buttonDef.isAccessible());
item.setEnabled(buttonDef.isEnabled(getSelectedItems()));
}
/**
* Returns {@code true} if this action panel has at least one action button, {@code false} otherwise.
*/
boolean hasActionButtons() {
return !actionButtonList.isEmpty();
}
/**
* Returns items currently selected in the action panel.
*/
protected abstract List<T> getSelectedItems();
/**
* Returns a new action button widget based on the given definition.
*/
protected abstract ActionButton createNewActionButton(ActionButtonDefinition<T> buttonDef);
}