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