/**
* Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
*
* Project home : http://code.daikit.com/daikit4gxt
*
* 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 com.daikit.daikit4gxt.client.ui.cell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.sencha.gxt.cell.core.client.DisableCell;
import com.sencha.gxt.cell.core.client.FocusableCell;
import com.sencha.gxt.cell.core.client.ResizeCell;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.util.KeyNav;
import com.sencha.gxt.widget.core.client.HasIcon;
import com.sencha.gxt.widget.core.client.event.BeforeSelectEvent;
import com.sencha.gxt.widget.core.client.event.BeforeSelectEvent.BeforeSelectHandler;
import com.sencha.gxt.widget.core.client.event.BeforeSelectEvent.HasBeforeSelectHandlers;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent.HasSelectHandlers;
import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;
/**
* @author tcaselli
* @version $Revision$ Last modifier: $Author$ Last commit: $Date$
*/
public abstract class IconButtonCell extends ResizeCell<String> implements HasBeforeSelectHandlers, HasSelectHandlers, HasIcon,
FocusableCell, DisableCell
{
/**
* Icon alignment enum.
*/
public enum IconAlign
{
/**
* Icons are aligned to the <b>right</b>.
*/
RIGHT,
/**
* Icons are aligned to the <b>bottom</b>.
*/
BOTTOM,
/**
* Icons are aligned to the <b>top</b>.
*/
TOP,
/**
* Icons are aligned to the <b>left</b>.
*/
LEFT;
}
protected ImageResource icon;
protected SafeHtml text = SafeHtmlUtils.EMPTY_SAFE_HTML;
protected final IconButtonCellAppearance appearance;
private IconAlign iconAlign = IconAlign.LEFT;
private boolean handleMouseEvents = true;
private int minWidth = -1;
private String tooltip;
/**
* Constructor with null tooltip (so not displayed)
*
* @param icon
* the icon to be displayed
*/
public IconButtonCell(final ImageResource icon)
{
this(icon, null, null);
}
/**
* Constructor with null tooltip (so not displayed)
*
* @param icon
* the icon to be displayed
* @param selectHandler
* the select handler
*/
public IconButtonCell(final ImageResource icon, final SelectHandler selectHandler)
{
this(icon, null, selectHandler);
}
/**
* Constructor
*
* @param icon
* the icon to be displayed
* @param tooltip
* not displayed if null, set to null by default
* @param selectHandler
* the {@link SelectHandler}
*/
public IconButtonCell(final ImageResource icon, final String tooltip, final SelectHandler selectHandler)
{
this(new IconButtonCellDefaultAppearance());
this.setIcon(icon);
this.setTooltip(tooltip);
if (selectHandler != null)
{
this.addSelectHandler(selectHandler);
}
}
/**
* Constructor
*
* @param appearance
*/
public IconButtonCell(final IconButtonCellAppearance appearance)
{
super("click", "keydown", "mousedown", "mouseup", "mouseover", "mouseout", "focus", "blur");
this.appearance = appearance;
}
/**
* Default implementation of this interface is
*
* @author tcaselli
* @version $Revision$ Last modifier: $Author$ Last commit: $Date$
*/
@SuppressWarnings("javadoc")
public interface IconButtonCellAppearance
{
XElement getImageElement(XElement parent);
XElement getFocusElement(XElement parent);
void onFocus(XElement parent, boolean focused, NativeEvent event);
void onOver(XElement parent, boolean over, NativeEvent event);
void onPress(XElement parent, boolean click, NativeEvent event);
void render(IconButtonCell cell, Context context, String value, SafeHtmlBuilder sb);
void setCellIconVisibilityHandler(CellIconVisiblityHandler cellIconVisiblityHandler);
CellIconVisiblityHandler getCellIconVisibilityHandler();
}
private class UnpushHandler implements NativePreviewHandler
{
private final XElement parent;
private final HandlerRegistration reg;
public UnpushHandler(final XElement parent)
{
this.parent = parent;
this.reg = Event.addNativePreviewHandler(this);
}
@Override
public void onPreviewNativeEvent(final NativePreviewEvent event)
{
if ("mouseup".equals(event.getNativeEvent().getType()))
{
// Unregister self.
reg.removeHandler();
// Unpush the element.
appearance.onOver(parent, false, event.getNativeEvent());
appearance.onPress(parent, false, event.getNativeEvent());
}
}
}
@Override
public void disable(final com.google.gwt.cell.client.Cell.Context context, final Element parent)
{
appearance.onOver(parent.<XElement> cast(), false, null);
appearance.onFocus(parent.<XElement> cast(), false, null);
}
@Override
public void enable(final com.google.gwt.cell.client.Cell.Context context, final Element parent)
{
appearance.onOver(parent.<XElement> cast(), false, null);
}
@Override
public XElement getFocusElement(final XElement parent)
{
return appearance.getFocusElement(parent);
}
/**
* Get the icon
*/
@Override
public ImageResource getIcon()
{
return icon;
}
/**
* Get the icon
*
* @param context
* the context
* @return {@link #getIcon()} or use the context
*/
protected abstract ImageResource getIcon(final Context context);
@Override
public void setIcon(final ImageResource icon)
{
this.icon = icon;
}
@Override
public HandlerRegistration addSelectHandler(final SelectHandler handler)
{
return addHandler(handler, SelectEvent.getType());
}
@Override
public HandlerRegistration addBeforeSelectHandler(final BeforeSelectHandler handler)
{
return addHandler(handler, BeforeSelectEvent.getType());
}
@Override
public void render(final com.google.gwt.cell.client.Cell.Context context, final String value, final SafeHtmlBuilder sb)
{
appearance.render(this, context, value, sb);
}
@Override
public boolean redrawOnResize()
{
return true;
}
@Override
public void onBrowserEvent(final Cell.Context context, final Element parent, final String value, final NativeEvent event,
final ValueUpdater<String> valueUpdater)
{
// ignore the parent element
if (isDisableEvents() || !getAppearance().getCellIconVisibilityHandler().isCellIconVisible(context))
{
return;
}
final Element target = event.getEventTarget().cast();
if (!parent.getFirstChildElement().isOrHasChild(target))
{
return;
}
final XElement p = parent.cast();
final String eventType = event.getType();
if ("click".equals(eventType))
{
onClick(context, p, value, event, valueUpdater);
}
else if ("mouseover".equals(eventType))
{
onMouseOver(p, event);
}
else if ("mouseout".equals(eventType))
{
onMouseOut(p, event);
}
else if ("mousedown".equals(eventType))
{
onMouseDown(p, event);
}
else if ("mouseup".equals(eventType))
{
onMouseUp(p, event);
}
else if ("focus".equals(eventType))
{
onFocus(p, event);
}
else if ("blur".equals(eventType))
{
onBlur(p, event);
}
else if ("keydown".equals(eventType))
{
if (KeyNav.getKeyEvent() == Event.ONKEYDOWN)
{
onNavigationKey(context, parent, value, event, valueUpdater);
}
}
else if ("keypress".equals(eventType))
{
if (KeyNav.getKeyEvent() == Event.ONKEYPRESS)
{
onNavigationKey(context, parent, value, event, valueUpdater);
}
}
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// GETTERS SETTERS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* @return the appearance
*/
public IconButtonCellAppearance getAppearance()
{
return appearance;
}
/**
* Returns the icon align.
*
* @return the iconAlign
*/
public IconAlign getIconAlign()
{
return iconAlign;
}
/**
* Returns the icon align.
*
* @return {@link #getIcon()} or use the context
*/
protected abstract IconAlign getIconAlign(final Context context);
/**
* Returns the icon's minimum width.
*
* @return the minWidth the minimum width
*/
public int getMinWidth()
{
return minWidth;
}
/**
* Returns false if mouse over effect is disabled.
*
* @return false if mouse effects disabled
*/
public boolean getMouseEvents()
{
return handleMouseEvents;
}
/**
* Sets he minimum width for this button (used to give a set of buttons a common width)
*
* @param minWidth
* the minimum width
*/
public void setMinWidth(final int minWidth)
{
this.minWidth = minWidth;
}
/**
* Get the tooltip. Cannot be changed at runtime.
*
* @return the tooltip
*/
public String getTooltip()
{
return tooltip;
}
/**
* Get the tooltip.
*
* @param context
* the context
* @return {@link #getTooltip()} or use the context
*/
public abstract String getTooltip(final Context context);
/**
* Set the tooltip. Cannot be changed at runtime.
*
* @param tooltip
* the tooltip
*/
public void setTooltip(final String tooltip)
{
this.tooltip = tooltip;
}
/**
* Sets the icon alignment (defaults to LEFT).
*
* @param iconAlign
* the icon alignment
*/
public void setIconAlign(final IconAlign iconAlign)
{
this.iconAlign = iconAlign;
}
/**
* False to disable visual cues on mouseover, mouseout and mousedown (defaults to true).
*
* @param handleMouseEvents
* false to disable mouse over changes
*/
public void setMouseEvents(final boolean handleMouseEvents)
{
this.handleMouseEvents = handleMouseEvents;
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// UI CALLBACKS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
protected void onBlur(final XElement p, final NativeEvent event)
{
appearance.onFocus(p, false, event);
}
protected void onClick(final Context context, final XElement p, final String value, final NativeEvent event,
final ValueUpdater<String> valueUpdater)
{
if (fireCancellableEvent(context, new BeforeSelectEvent(context)))
{
appearance.onOver(p, false, null);
fireEvent(context, new SelectEvent(context));
}
}
protected void onFocus(final XElement p, final NativeEvent event)
{
appearance.onFocus(p, true, event);
}
protected void onMouseDown(final XElement parent, final NativeEvent event)
{
if (handleMouseEvents)
{
final Element target = event.getEventTarget().cast();
// stop images from being dragged in firefox
if ("IMG".equals(target.getTagName()))
{
event.preventDefault();
}
appearance.onPress(parent, true, event);
new UnpushHandler(parent);
}
}
protected void onMouseOut(final XElement p, final NativeEvent event)
{
appearance.onOver(p, false, event);
}
protected void onMouseOver(final XElement p, final NativeEvent event)
{
appearance.onOver(p, true, event);
}
protected void onMouseUp(final XElement p, final NativeEvent event)
{
appearance.onPress(p, false, event);
}
protected void onNavigationKey(final com.google.gwt.cell.client.Cell.Context context, final Element parent,
final String value, final NativeEvent event, final ValueUpdater<String> valueUpdater)
{
final int key = event.getKeyCode();
if (!isDisableEvents() && (key == KeyCodes.KEY_ENTER || key == 32))
{
onClick(context, parent.<XElement> cast(), value, event, valueUpdater);
}
}
}