/* * Copyright 2014-2015 CyberVision, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kaaproject.avro.ui.gwt.client.widget.grid.cell; import static com.google.gwt.dom.client.BrowserEvents.CLICK; import static com.google.gwt.dom.client.BrowserEvents.KEYDOWN; import static com.google.gwt.dom.client.BrowserEvents.KEYUP; import java.util.HashMap; import java.util.Map; import org.kaaproject.avro.ui.gwt.client.util.Utils; import com.google.gwt.cell.client.AbstractCell; import com.google.gwt.cell.client.ValueUpdater; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.Style.VerticalAlign; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.safecss.shared.SafeStyles; import com.google.gwt.safecss.shared.SafeStylesBuilder; import com.google.gwt.safecss.shared.SafeStylesUtils; import com.google.gwt.safehtml.client.SafeHtmlTemplates; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.safehtml.shared.SafeUri; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.MenuBar; import com.google.gwt.user.client.ui.MenuItem; import com.google.gwt.user.client.ui.PopupPanel; public class ActionsButtonCell<T> extends AbstractCell<T> { interface Template extends SafeHtmlTemplates { @Template("<div class=\"gwt-ToggleButton\" tabindex=\"-1\" role=\"button\" style='{0}'>" + "<span style='{1}'></span>" + "<span class='{2}'><span>{3}</span></span>" + "</div>") SafeHtml actionsButtonUp(SafeStyles buttonStyle, SafeStyles imageStyle, String caretClassName, String text); @Template("<div class=\"gwt-ToggleButton gwt-ToggleButton-down\" tabindex=\"-1\" role=\"button\" style='{0}'>" + "<span style='{1}'></span>" + "<span class='{2}'><span>{3}</span></span>" + "</div>") SafeHtml actionsButtonDown(SafeStyles buttonStyle, SafeStyles imageStyle, String caretClassName, String text); @Template("<div tabindex=\"0\" style=\"vertical-align:middle;\">" + "<span style='{0}'></span>" + "<span>{1}</span>" + "</div>") SafeHtml menuImageItemContent(SafeStyles style, String text); @Template("<div tabindex=\"0\" style=\"vertical-align:middle;\">" + "<span>{0}</span>" + "</div>") SafeHtml menuItemContent(String text); } private static Template template; private SafeHtml actionsButtonHtml; private SafeHtml actionsButtonHtmlDown; private PopupPanel actionsPopup; private HandlerRegistration popupCloseHandlerRegistration; private T currentValue; private final Map<Object, Boolean> viewDataMap = new HashMap<Object, Boolean>(); private MenuBar menu = new MenuBar(true); public void clearViewData(Object key) { if (key != null) { viewDataMap.remove(key); } } public void clearViewData() { viewDataMap.clear(); } public Boolean getViewData(Object key) { return (key == null) ? null : viewDataMap.get(key); } public void setViewData(Object key, Boolean viewData) { if (key == null) { return; } if (viewData == null) { clearViewData(key); } else { viewDataMap.put(key, viewData); } } public ActionsButtonCell(ImageResource imageResource, String text) { super(CLICK, KEYDOWN); if (template == null) { template = GWT.create(Template.class); } SafeStylesBuilder buttonStyleBuilder = new SafeStylesBuilder(); SafeStylesBuilder imageStyleBuilder = new SafeStylesBuilder(); buttonStyleBuilder.verticalAlign(VerticalAlign.MIDDLE).paddingRight(20, Unit.PX); if (imageResource != null) { buttonStyleBuilder.paddingLeft(20, Unit.PX); SafeUri uri = imageResource.getSafeUri(); int width = imageResource.getWidth(); int height = imageResource.getHeight(); int paddingLeft = width; String background = "url(\"" + uri.asString() + "\") no-repeat scroll right center"; imageStyleBuilder.trustedNameAndValue("background", background) .width(width, Unit.PX).height(height, Unit.PX) .paddingLeft(paddingLeft, Unit.PX).marginRight(10, Unit.PX); } else { imageStyleBuilder.display(Display.NONE); } SafeStyles buttonStyle = SafeStylesUtils .fromTrustedString(buttonStyleBuilder.toSafeStyles().asString()); SafeStyles imageStyle = SafeStylesUtils .fromTrustedString(imageStyleBuilder.toSafeStyles().asString()); this.actionsButtonHtml = template.actionsButtonUp(buttonStyle, imageStyle, Utils.avroUiStyle.buttonCaret(), text); this.actionsButtonHtmlDown = template.actionsButtonDown(buttonStyle, imageStyle, Utils.avroUiStyle.buttonCaret(), text); actionsPopup = new PopupPanel(true, false); actionsPopup.addStyleName(Utils.avroUiStyle.actionPopup()); actionsPopup.setWidget(menu); } public void addMenuItem(String text, final ActionMenuItemListener<T> listener) { addMenuItem(null, text, listener); } public void addMenuItem(ImageResource image, String text, final ActionMenuItemListener<T> listener) { SafeHtml html = null; if (image != null) { SafeUri uri = image.getSafeUri(); int left = image.getLeft(); int top = image.getTop(); int width = image.getWidth(); int height = image.getHeight(); int paddingRight = width + 8; String background = "url(\"" + uri.asString() + "\") no-repeat " + (-left + "px ") + (-top + "px"); SafeStylesBuilder builder = new SafeStylesBuilder(); builder.trustedNameAndValue("background", background) .width(width, Unit.PX).height(height, Unit.PX) .paddingRight(paddingRight, Unit.PX); SafeStyles style = SafeStylesUtils.fromTrustedString(builder .toSafeStyles().asString()); html = template.menuImageItemContent(style, text); } else { html = template.menuItemContent(text); } final MenuItem item = new MenuItem(html, new Command() { @Override public void execute() { if (actionsPopup != null && actionsPopup.isVisible()) actionsPopup.hide(); listener.onMenuItemSelected(currentValue); } }); menu.addItem(item); } @Override public boolean isEditing(Context context, Element parent, T value) { Boolean viewData = getViewData(context.getKey()); return viewData == null ? false : viewData.booleanValue(); } @Override public void onBrowserEvent(Context context, Element parent, T value, NativeEvent event, ValueUpdater<T> valueUpdater) { int x = event.getClientX(); int y = event.getClientY(); Element child = parent.getFirstChildElement(); if (x >= child.getAbsoluteLeft() && x <= child.getAbsoluteRight() && y >= child.getAbsoluteTop() && y <= child.getAbsoluteBottom()) { Object key = context.getKey(); Boolean viewData = getViewData(key); if (viewData != null && viewData.booleanValue()) { // Handle the edit event. setViewData(key, false); setValue(context, parent, value); } else { String type = event.getType(); int keyCode = event.getKeyCode(); boolean enterPressed = KEYUP.equals(type) && keyCode == KeyCodes.KEY_ENTER; if (CLICK.equals(type) || enterPressed) { // Go into edit mode. clearViewData(); setViewData(key, true); setValue(context, parent, value); showPopup(context, parent, value, valueUpdater); } } } } private void showPopup(final Context context, final Element parent, final T value, final ValueUpdater<T> valueUpdater) { currentValue = value; actionsPopup.getElement().getStyle().setZIndex(2000); actionsPopup.setPopupPositionAndShow(new PopupPanel.PositionCallback() { public void setPosition(int offsetWidth, int offsetHeight) { int left = parent.getFirstChildElement().getAbsoluteLeft(); int top = parent.getFirstChildElement().getAbsoluteTop() + parent.getFirstChildElement().getOffsetHeight() + 2; if (left + actionsPopup.getOffsetWidth() > Window .getClientWidth()) { left = parent.getFirstChildElement().getAbsoluteRight() - actionsPopup.getOffsetWidth(); } actionsPopup.setPopupPosition(left, top); } }); if (popupCloseHandlerRegistration != null) { popupCloseHandlerRegistration.removeHandler(); } popupCloseHandlerRegistration = actionsPopup .addCloseHandler(new CloseHandler<PopupPanel>() { @Override public void onClose(CloseEvent<PopupPanel> event) { setViewData(context.getKey(), false); setValue(context, parent, value); } }); } @Override public void render(Context context, T value, SafeHtmlBuilder sb) { Object key = context.getKey(); Boolean viewData = getViewData(key); if (viewData != null && !viewData.booleanValue()) { clearViewData(key); viewData = null; } boolean down = viewData != null && viewData.booleanValue(); if (down) sb.append(actionsButtonHtmlDown); else sb.append(actionsButtonHtml); } public static interface ActionMenuItemListener<T> { void onMenuItemSelected(T value); } }