package org.ovirt.engine.ui.webadmin.section.main.view.popup.host.panels;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.gwtbootstrap3.client.ui.constants.Placement;
import org.ovirt.engine.core.common.businessentities.comparators.LexoNumericComparator;
import org.ovirt.engine.ui.common.widget.MenuBar;
import org.ovirt.engine.ui.common.widget.PopupPanel;
import org.ovirt.engine.ui.common.widget.tooltip.TooltipWidth;
import org.ovirt.engine.ui.common.widget.tooltip.WidgetTooltip;
import org.ovirt.engine.ui.uicommonweb.models.hosts.network.NetworkCommand;
import org.ovirt.engine.ui.uicommonweb.models.hosts.network.NetworkItemModel;
import org.ovirt.engine.ui.uicommonweb.models.hosts.network.NetworkOperation;
import org.ovirt.engine.ui.webadmin.ApplicationResources;
import org.ovirt.engine.ui.webadmin.gin.AssetProvider;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.DragDropEventBase;
import com.google.gwt.event.dom.client.DragStartEvent;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.Widget;
public abstract class NetworkItemPanel<T extends NetworkItemModel<?>> extends FocusPanel {
public static final String SETUP_NETWORKS_DATA = "SetupNetworksData"; //$NON-NLS-1$
public static final String SETUP_NETWORKS_TYPE = "SetupNetworksType"; //$NON-NLS-1$
private static final ApplicationResources resources = AssetProvider.getResources();
final Image dragImage = new Image(resources.itemDraggable());
final PushButton actionButton;
final T item;
private final boolean draggable;
protected final NetworkPanelsStyle style;
protected NetworkItemPanel<?> parentPanel;
private MenuBar menu;
private WidgetTooltip tooltip;
// statics
private static final PopupPanel menuPopup = new PopupPanel(true);
private static final ItemInfoPopup infoPopup = new ItemInfoPopup();
private static String lastDragData = ""; //$NON-NLS-1$
public NetworkItemPanel(T item, NetworkPanelsStyle style, boolean draggable) {
this.draggable = draggable;
getElement().setDraggable(draggable ? Element.DRAGGABLE_TRUE : Element.DRAGGABLE_FALSE);
dragImage.setVisible(false);
Image editImage = new Image(resources.editHover());
actionButton = new PushButton(editImage, (ClickEvent event) -> {
dragImage.setVisible(false);
NetworkItemPanel.this.onAction();
});
actionButton.getDownFace().setImage(new Image(resources.editMouseDown()));
actionButton.setPixelSize(editImage.getWidth(), editImage.getHeight());
this.item = item;
this.style = style;
init();
addMouseOverHandler(event -> NetworkItemPanel.this.onMouseOver());
addMouseOutHandler(event -> NetworkItemPanel.this.onMouseOut());
//
// add mousedown handler for hiding the InfoItemPopup tooltip.
// This is important because we have drag-and-drop targets in this dialog,
// and on mousedown to initiate a drag, tooltips must be hidden
// so they're not in the way of drop targets.
addMouseDownHandler(event -> NetworkItemPanel.this.tooltip.hide());
}
protected abstract Widget getContents();
protected abstract void onAction();
protected void onMouseOut() {
dragImage.setVisible(false);
// handle nested panels (for example bonded nics) so nic.mouseOut() should cause parent.mouseIn()
if (parentPanel != null) {
parentPanel.onMouseOver();
}
}
protected void onMouseOver() {
dragImage.setVisible(draggable);
}
private void executeCommand(NetworkOperation operation, NetworkCommand command) {
menuPopup.hide();
NetworkItemPanel.this.item.getSetupModel().onOperation(operation, command);
}
private void init() {
menu = menuFor(item);
getElement().addClassName(style.itemPanel());
initTooltip();
setWidget(tooltip);
addDomHandler(event -> {
NetworkItemPanel<?> sourcePanel = (NetworkItemPanel<?>) event.getSource();
NativeEvent nativeEvent = event.getNativeEvent();
showContextMenu(sourcePanel, nativeEvent.getClientX(), nativeEvent.getClientY());
event.stopPropagation();
event.preventDefault();
}, ContextMenuEvent.getType());
// drag start
if (draggable) {
addBitlessDomHandler(event -> {
NetworkItemPanel<?> sourcePanel = (NetworkItemPanel<?>) event.getSource();
// Required: set data for the event.
lastDragData = sourcePanel.item.getType() + " " + sourcePanel.item.getName(); //$NON-NLS-1$
event.setData("Text", lastDragData); //$NON-NLS-1$
// show a ghost of the widget under cursor.
NativeEvent nativeEvent = event.getNativeEvent();
int x = nativeEvent.getClientX() - sourcePanel.getAbsoluteLeft();
int y = nativeEvent.getClientY() - sourcePanel.getAbsoluteTop();
event.getDataTransfer().setDragImage(sourcePanel.getElement(), x, y);
}, DragStartEvent.getType());
}
}
protected void initTooltip() {
tooltip = new WidgetTooltip(getContents());
tooltip.setPlacement(Placement.BOTTOM);
SafeHtml tooltipContent = infoPopup.getTooltipContent(item);
if (tooltipContent != null) {
tooltip.setHtml(tooltipContent);
}
}
/**
* Generate a Menu for the provided Network Item.
*/
private MenuBar menuFor(NetworkItemModel<?> item) {
MenuBar menu = rootMenu(item);
Map<NetworkOperation, List<NetworkCommand>> operationMap = item.getSetupModel().commandsFor(item);
for (final Entry<NetworkOperation, List<NetworkCommand>> entry : operationMap.entrySet()) {
final List<NetworkCommand> commands = entry.getValue();
if (entry.getKey().isUnary()) {
assert commands.size() == 1 : "Got a NetworkCommand List with more than one Unary Operation"; //$NON-NLS-1$
menu.addItem(entry.getKey().getVerb(item), () -> executeCommand(entry.getKey(), commands.get(0)));
} else {
Collections.sort(commands, Comparator.comparing(NetworkCommand::getName, new LexoNumericComparator()));
MenuBar subMenu = subMenu();
for (final NetworkCommand command : commands) {
subMenu.addItem(new MenuItem(command.getName(), () -> executeCommand(entry.getKey(), command)));
}
menu.addItem(entry.getKey().getVerb(item), subMenu);
}
}
return menu;
}
private MenuBar rootMenu(NetworkItemModel<?> item) {
MenuBar menuBar = new MenuBar(true);
return menuBar;
}
private void showContextMenu(NetworkItemPanel<?> panel, int clientX, int clientY) {
if (!menu.isEmpty()){
menuPopup.setWidget(menu);
menuPopup.setPopupPosition(clientX, clientY);
menuPopup.show();
}
}
private MenuBar subMenu() {
MenuBar menuBar = new MenuBar(true);
return menuBar;
}
public T getItem() {
return item;
}
public static String getData(String dragDropEventData) {
if (dragDropEventData == null) {
return ""; //$NON-NLS-1$
}
int split = dragDropEventData.indexOf(" "); //$NON-NLS-1$
if (split == -1 || dragDropEventData.length() == split + 1) {
return ""; //$NON-NLS-1$
}
return dragDropEventData.substring(split + 1);
}
public static String getType(String dragDropEventData) {
if (dragDropEventData == null) {
return ""; //$NON-NLS-1$
}
int split = dragDropEventData.indexOf(" "); //$NON-NLS-1$
if (split == -1 || split == 0) {
return ""; //$NON-NLS-1$
}
return dragDropEventData.substring(0, split);
}
public static String getDragDropEventData(DragDropEventBase<?> event, boolean isDrop) {
if (isDrop) {
return event.getData("Text"); //$NON-NLS-1$
} else {
// On most of the browsers drag, dragenter, dragleave, dragover and dragend don't have access to event's
// data
return lastDragData;
}
}
public void setToolTipMaxWidth(TooltipWidth width) {
tooltip.setMaxWidth(width);
}
}