/*
* Ext GWT - Ext for GWT
* Copyright(c) 2007-2009, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
package com.extjs.gxt.ui.client.widget;
import java.util.Stack;
import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.core.XDOM;
import com.extjs.gxt.ui.client.util.Markup;
import com.extjs.gxt.ui.client.util.Rectangle;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Accessibility;
/**
* An extended {@link El} object that supports a shadow and shim, constrain to
* viewport and automatic maintaining of shadow/shim positions.
*/
public class Layer extends El {
public enum ShadowPosition {
DROP, SIDES, FRAME;
}
private static Stack<El> shims = new Stack<El>();
private static Stack<El> shadows = new Stack<El>();
private El shadow;
private boolean shadowEnabled;
private El shim;
private boolean shimEnabled;
private Rectangle shadowAdjusts;
private ShadowPosition shadowPosition;
private int shadowOffset = 4;
/**
* Creates a new layer instance.
*/
public Layer() {
super(DOM.createDiv());
setShadowPosition(ShadowPosition.SIDES);
}
/**
* Creates a new layer instance wrapping the specified element.
*
* @param element the element
*/
public Layer(Element element) {
super(element);
makePositionable();
setShadowPosition(ShadowPosition.SIDES);
}
@Override
public El alignTo(Element align, String pos, int[] offsets) {
super.alignTo(align, pos, offsets);
sync(true);
return this;
}
private El createShadow() {
El el;
if (GXT.isIE) {
el = new El(DOM.createDiv());;
el.setStyleName("x-ie-shadow");
if (GXT.isIE) {
el.setStyleAttribute("filter", "progid:DXImageTransform.Microsoft.alpha("
+ "opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius=" + shadowOffset + ")");
}
} else {
el = new El(Markup.SHADOW);
}
return el;
}
/**
* Creates an iframe shim for this element to keep selects and other windowed
* objects from showing through.
*
* @return the new shim element
*/
private El createShim() {
El el = new El(DOM.createIFrame());
el.dom.setPropertyString("frameborder", "no");
el.dom.setPropertyString("frameBorder", "no");
el.dom.setClassName("ext-shim");
el.setTabIndex(-1);
el.setVisibility(true);
el.setVisible(false);
if (GXT.isIE && GXT.isSecure) {
el.dom.setPropertyString("src", GXT.SSL_SECURE_URL);
}
Accessibility.setRole(el.dom, "presentation");
return el;
}
/**
* Disables the shadow.
*/
public void disableShadow() {
shadowEnabled = false;
hideShadow();
}
/**
* Disables the shim.
*/
public void disableShim() {
shimEnabled = false;
hideShim();
}
/**
* Disables the shim and the shadow.
*/
public void disableUnders() {
disableShadow();
disableShim();
}
/**
* Enables the shadow.
*/
public void enableShadow() {
shadowEnabled = true;
}
/**
* Enables the shim.
*/
public void enableShim() {
shimEnabled = true;
}
/**
* Returns the layer's shadow.
*
* @return the shadow or null
*/
public El getShadow() {
if (shadow != null) {
return shadow;
}
shadow = shadows.size() > 0 ? shadows.pop() : null;
if (shadow == null) {
shadow = createShadow();
shadow.setVisible(false);
}
El pn = getParent();
if (pn == null) return null;
El p = shadow.getParent();
if (p != null && p.dom == XDOM.getBody()) {
pn.insertChild(shadow.dom, 0);
} else if (p == null || pn.dom != p.dom) {
try {
pn.insertBefore(shadow.dom, dom);
} catch (Exception e) {
e.printStackTrace();
}
}
shadow.setZIndex(getZIndex() - 1);
return shadow;
}
/**
* Returns the shadow position.
*
* @return the shadow position
*/
public ShadowPosition getShadowPosition() {
return shadowPosition;
}
/**
* Returns the shadow offset.
*
* @return the shadow offset
*/
public int getShadowOffset() {
return shadowOffset;
}
/**
* Returns the layer's shim.
*
* @return the shim
*/
public El getShim() {
if (shim != null) {
return shim;
}
shim = shims.size() > 0 ? shims.pop() : null;
if (shim == null) {
shim = createShim();
shim.setVisible(false);
}
El pn = getParent();
El p = shim.getParent();
if (p != null && p.dom == XDOM.getBody()) {
pn.insertChild(shim.dom, 0);
} else if (p == null || pn.dom != p.dom) {
pn.insertBefore(shim.dom, dom);
}
shim.setZIndex(getZIndex() - 2);
return shim;
}
/**
* Hides the layer's shadow.
*/
public void hideShadow() {
if (shadow != null) {
shadow.hide();
shadow.removeFromParent();
shadows.push(shadow);
shadow = null;
}
}
/**
* Hides the shim.
*/
public void hideShim() {
if (shim != null) {
shim.hide();
shim.removeFromParent();
shims.push(shim);
shim = null;
}
}
/**
* Hides the shim and the shadow.
*/
public void hideUnders() {
hideShadow();
hideShim();
}
@Override
public El remove() {
super.remove();
hideUnders();
return this;
}
@Override
public El setHeight(int height, boolean adjust) {
super.setHeight(height, adjust);
sync(true);
return this;
}
@Override
public El setHeight(String height) {
super.setHeight(height);
sync(true);
return this;
}
@Override
public El setLeft(int left) {
super.setLeft(left);
sync(true);
return this;
}
/**
* Sets the shadow position (defaults to SIDES).
*
* @param shadowPosition the position
*/
public void setShadowPosition(ShadowPosition shadowPosition) {
this.shadowPosition = shadowPosition;
int radius = shadowOffset / 2;
shadowAdjusts = new Rectangle();
switch (shadowPosition) {
case SIDES:
shadowAdjusts.width = shadowOffset * 2;
shadowAdjusts.x = -shadowOffset;
shadowAdjusts.y = shadowOffset - 1;
if (GXT.isIE) {
shadowAdjusts.x -= (shadowOffset - radius);
shadowAdjusts.y -= (shadowOffset + radius);
shadowAdjusts.x += 1;
shadowAdjusts.width -= (shadowOffset - radius) * 2;
shadowAdjusts.width -= radius + 1;
shadowAdjusts.height -= 1;
}
break;
case FRAME:
shadowAdjusts.width = shadowAdjusts.height = (shadowOffset * 2);
shadowAdjusts.x = shadowAdjusts.y = -shadowOffset;
shadowAdjusts.y += 1;
shadowAdjusts.height -= 2;
if (GXT.isIE) {
shadowAdjusts.x -= (shadowOffset - radius);
shadowAdjusts.y -= (shadowOffset - radius);
shadowAdjusts.width -= (shadowOffset + radius);
shadowAdjusts.width += 1;
shadowAdjusts.height -= (shadowOffset + radius);
shadowAdjusts.height += 3;
}
break;
default:
shadowAdjusts.width = 0;
shadowAdjusts.x = shadowAdjusts.y = shadowOffset;
shadowAdjusts.y -= 1;
if (GXT.isIE) {
shadowAdjusts.x -= shadowOffset + radius;
shadowAdjusts.y -= shadowOffset + radius;
shadowAdjusts.width -= radius;
shadowAdjusts.height -= radius;
shadowAdjusts.y += 1;
}
break;
}
}
/**
* Sets the shadow offset (defaults to 4).
*
* @param shadowOffset the offset
*/
public void setShadowOffset(int shadowOffset) {
this.shadowOffset = shadowOffset;
setShadowPosition(shadowPosition);
}
@Override
public El setTop(int top) {
super.setTop(top);
sync(true);
return this;
}
@Override
public El setVisibility(boolean visible) {
super.setVisibility(visible);
if (!visible) {
hideUnders();
} else {
sync(true);
}
return this;
}
@Override
public El setVisible(boolean visible) {
super.setVisible(visible);
if (!visible) {
hideUnders();
} else {
sync(true);
}
return this;
}
@Override
public El setWidth(int width, boolean adjust) {
super.setWidth(width, adjust);
sync(true);
return this;
}
@Override
public El setWidth(String width) {
super.setWidth(width);
sync(true);
return this;
}
@Override
public El setXY(int x, int y) {
super.setXY(x, y);
sync(true);
return this;
}
@Override
public El setZIndex(int zIndex) {
super.setZIndex(zIndex);
if (shadow != null) {
shadow.setZIndex(zIndex - 1);
}
if (shim != null) {
shim.setZIndex(zIndex - 2);
}
return this;
}
/**
* Syncs the shadow and shim.
*
* @param show true to show
*/
@Override
public El sync(boolean show) {
super.sync(show);
if (isVisible() && (shadowEnabled || shimEnabled)) {
int w = getWidth();
int h = getHeight();
int l = getLeft();
int t = getTop();
if (shadowEnabled && getParent() != null) {
if (shadow == null) {
shadow = getShadow();
}
if (shadow != null && show) {
shadow.show();
}
shadow.setLeft(l + shadowAdjusts.x);
shadow.setTop(t + shadowAdjusts.y);
int sw = w + shadowAdjusts.width;
int sh = h + shadowAdjusts.height;
if (shadow.getWidth() != sw || shadow.getHeight() != sh) {
shadow.setSize(sw, sh);
if (!GXT.isIE) {
int width = Math.max(0, sw - 12);
fly((Element) shadow.dom.getChildNodes().getItem(0).getChildNodes().getItem(1)).setWidth(width);
fly((Element) shadow.dom.getChildNodes().getItem(1).getChildNodes().getItem(1)).setWidth(width);
fly((Element) shadow.dom.getChildNodes().getItem(2).getChildNodes().getItem(1)).setWidth(width);
int height = Math.max(0, sh - 12);
fly((Element) shadow.dom.getChildNodes().getItem(1)).setHeight(height);
}
}
}
if (shimEnabled) {
if (shim == null) {
shim = getShim();
}
if (show) {
shim.setVisible(true);
}
Rectangle a = shadow == null ? new Rectangle(0, 0, 0, 0) : shadowAdjusts;
if (GXT.isIE && shadow != null && shadow.isVisible()) {
w += 8;
h += 8;
}
try {
shim.setLeft(Math.min(l, l + a.x));
shim.setTop(Math.min(t, t + a.y));
shim.setWidth(Math.max(1, w + a.width));
shim.setHeight(Math.max(1, h + a.height));
} catch (Exception e) {
GWT.log("shim error", e);
}
}
}
return this;
}
}