org.zkoss.zul.impl.XulElement Maven / Gradle / Ivy
/* XulElement.java
Purpose:
Description:
History:
Mon Jun 20 16:01:40 2005, Created by tomyeh
Copyright (C) 2005 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zul.impl;
import java.io.Serializable;
import java.util.HashMap;
import org.zkoss.lang.Objects;
import org.zkoss.zk.au.DeferredValue;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.sys.ObjectPropertyAccess;
import org.zkoss.zk.ui.sys.PropertyAccess;
import org.zkoss.zk.ui.sys.StringPropertyAccess;
import org.zkoss.zul.Popup;
/**
* The fundamental class for XUL elements.
*
* @author tomyeh
*/
public abstract class XulElement extends HtmlBasedComponent {
/** AuxInfo: use a class (rather than multiple member) to save footprint */
private AuxInfo _auxinf;
/** Returns what keystrokes to intercept.
* Default: null.
* @since 3.0.6
*/
public String getCtrlKeys() {
return _auxinf != null ? _auxinf.ctrlKeys : null;
}
/** Sets what keystrokes to intercept.
*
*
The string could be a combination of the following:
*
* - ^k
* - A control key, i.e., Ctrl+k, where k could be a~z, 0~9, #n
* - @k
* - A alt key, i.e., Alt+k, where k could be a~z, 0~9, #n
* - $n
* - A shift key, i.e., Shift+n, where n could be #n.
* Note: $a ~ $z are not supported.
* - #home
* - Home
* - #end
* - End
* - #ins
* - Insert
* - #del
* - Delete
* - #bak
* - Backspace
* - #left
* - Left arrow
* - #right
* - Right arrow
* - #up
* - Up arrow
* - #down
* - Down arrow
* - #pgup
* - PageUp
* - #pgdn
* - PageDn
* - #f1 #f2 ... #f12
* - Function keys representing F1, F2, ... F12
*
*
* For example,
*
* - ^a^d@c#f10#left#right
* - It means you want to intercept Ctrl+A, Ctrl+D, Alt+C, F10,
* Left and Right.
* - ^#left
* - It means Ctrl+Left.
* - ^#f1
* - It means Ctrl+F1.
* - @#f3
* - It means Alt+F3.
*
*
* Note: it doesn't support Ctrl+Alt, Shift+Ctrl, Shift+Alt or Shift+Ctrl+Alt.
* @since 3.0.6
*/
public void setCtrlKeys(String ctrlKeys) throws UiException {
if (ctrlKeys != null && ctrlKeys.length() == 0)
ctrlKeys = null;
if (!Objects.equals(_auxinf != null ? _auxinf.ctrlKeys : null, ctrlKeys)) {
initAuxInfo().ctrlKeys = ctrlKeys;
smartUpdate("ctrlKeys", getCtrlKeys());
}
}
/** Returns the ID of the popup ({@link Popup}) that should appear
* when the user right-clicks on the element (a.k.a., context menu).
*
*
Default: null (no context menu).
*/
public String getContext() {
return _auxinf != null && _auxinf.context != null ? (String) _auxinf.context.getValue() : null;
}
/** Sets the ID of the popup ({@link Popup}) that should appear
* when the user right-clicks on the element (a.k.a., context menu).
*
*
An onOpen event is sent to the context menu if it is going to
* appear. Therefore, developers can manipulate it dynamically
* (perhaps based on OpenEvent.getReference) by listening to the onOpen
* event.
*
*
Note: To simplify the use, it not only searches its ID space,
* but also all ID spaces in the desktop.
* It first searches its own ID space, and then the other Id spaces
* in the same browser window (might have one or multiple desktops).
*
*
(since 3.0.2) If there are two components with the same ID (of course, in
* different ID spaces), you can specify the UUID with the following
* format:
* uuid(comp_uuid)
*
*
Example:
*
* <label context="some">
* <label context="uuid(${some.uuid})"/>
*
* Both reference a component whose ID is "some".
* But, if there are several components with the same ID,
* the first one can reference to any of them.
* And, the second one reference to the component in the same ID space
* (of the label component).
*
*
* (since 3.6.3) the context menu can be shown by a position from {@link Popup#open(org.zkoss.zk.ui.Component, String)}
* or the location of x
and y
, you can specify the following format:
*
* id, position
* id, position=before_start
* id, x=15, y=20
* uuid(comp_uuid), position
* uuid(comp_uuid), x=15, y=20
*
* For example,
*
* <button label="show" context="id, start_before"/>
*
* (since 6.5.2) the context menu can also be shown on customized location of x
and y
by adding parentheses"()", for example,
*
* <button label="show" context="id, x=(zk.currentPointer[0] + 10), y=(zk.currentPointer[1] - 10)"/>
*
* @see #setContext(Popup)
*/
public void setContext(String context) {
if (!Objects.equals(_auxinf != null ? _auxinf.context : null, context)) {
initAuxInfo().context = new DeferedUuid(context);
smartUpdate("context", _auxinf.context);
}
}
/** Sets the UUID of the popup that should appear
* when the user right-clicks on the element (a.k.a., context menu).
*
* Note: it actually invokes
* setContext("uuid(" + popup.getUuid() + ")")
* @since 3.0.2
* @see #setContext(String)
* @see Popup#open(org.zkoss.zk.ui.Component, String)
*/
public void setContext(Popup popup) {
if (!Objects.equals(_auxinf != null ? _auxinf.context : null, popup)) {
initAuxInfo().context = new DeferedUuid(popup);
smartUpdate("context", _auxinf.context);
}
}
/**
* Sets the Attributes for the Context Popup
*
* Note that position will be ignored if coordinates are set.
*
* @param popup the Context popup component, can be null
* @param position e.g. "after_start", can be null
* @param x e.g. "50" or "(zk.currentPointer[0] + 10)", can be null
* @param y e.g. "50" or "(zk.currentPointer[0] + 10)", can be null
* @param type e.g. "toggle", can be null
*
* @since 8.5.0
*/
public void setContextAttributes(Popup popup, String position, String x, String y, String type) {
setContext(popup);
if (_auxinf != null && _auxinf.context != null) {
DeferedUuid pp = (DeferedUuid) _auxinf.context;
pp.setPosition(position);
pp.setCoordinates(x, y);
pp.setType(type);
smartUpdate("context", _auxinf.context);
}
}
/** Returns the ID of the popup ({@link Popup}) that should appear
* when the user clicks on the element.
*
*
Default: null (no popup).
*/
public String getPopup() {
return _auxinf != null && _auxinf.popup != null ? (String) _auxinf.popup.getValue() : null;
}
/** Sets the ID of the popup ({@link Popup}) that should appear
* when the user clicks on the element.
*
*
An onOpen event is sent to the popup menu if it is going to
* appear. Therefore, developers can manipulate it dynamically
* (perhaps based on OpenEvent.getReference) by listening to the onOpen
* event.
*
*
Note: To simplify the use, it not only searches its ID space,
* but also all ID spaces in the desktop.
* It first searches its own ID space, and then the other Id spaces
* in the same browser window (might have one or multiple desktops).
*
*
(since 3.0.2) If there are two components with the same ID (of course, in
* different ID spaces), you can specify the UUID with the following
* format:
* uuid(comp_uuid)
*
*
(since 3.6.3) the popup can be shown by a position from {@link Popup#open(org.zkoss.zk.ui.Component, String)}
* or the location of x
and y
, you can specify the following format:
*
* id, position
* id, position=before_start
* id, x=15, y=20
* uuid(comp_uuid), position
* uuid(comp_uuid), x=15, y=20
*
* For example,
*
* <button label="show" popup="id, start_before"/>
*
* (since 6.5.2) the popup can also be shown on customized location of x
and y
by adding parentheses"()", for example,
*
* <button label="show" context="id, x=(zk.currentPointer[0] + 10), y=(zk.currentPointer[1] - 10)"/>
*
* @see #setPopup(Popup)
* @see Popup#open(org.zkoss.zk.ui.Component, String)
*/
public void setPopup(String popup) {
if (!Objects.equals(_auxinf != null ? _auxinf.popup : null, popup)) {
initAuxInfo().popup = new DeferedUuid(popup);
smartUpdate("popup", _auxinf.popup);
}
}
/** Sets the UUID of the popup that should appear
* when the user clicks on the element.
*
* Note: it actually invokes
* setPopup("uuid(" + popup.getUuid() + ")")
* @since 3.0.2
* @see #setPopup(String)
*/
public void setPopup(Popup popup) {
if (!Objects.equals(_auxinf != null ? _auxinf.popup : null, popup)) {
initAuxInfo().popup = new DeferedUuid(popup);
smartUpdate("popup", _auxinf.popup);
}
}
/**
* Sets the Attributes for the Popup
*
* Note that position will be ignored if coordinates are set.
*
* @param popup the popup component, can be null
* @param position e.g. "after_start", can be null
* @param x e.g. "50" or "(zk.currentPointer[0] + 10)", can be null
* @param y e.g. "50" or "(zk.currentPointer[0] + 10)", can be null
* @param type e.g. "toggle", can be null
*
* @since 8.5.0
*/
public void setPopupAttributes(Popup popup, String position, String x, String y, String type) {
setPopup(popup);
if (_auxinf != null && _auxinf.popup != null) {
DeferedUuid pp = (DeferedUuid) _auxinf.popup;
pp.setPosition(position);
pp.setCoordinates(x, y);
pp.setType(type);
smartUpdate("popup", _auxinf.popup);
}
}
/** Returns the ID of the popup ({@link Popup}) that should be used
* as a tooltip window when the mouse hovers over the element for a moment.
* The tooltip will automatically disappear when the mouse is moved away.
*
*
Default: null (no tooltip).
*/
public String getTooltip() {
return _auxinf != null && _auxinf.tooltip != null ? (String) _auxinf.tooltip.getValue() : null;
}
/** Sets the ID of the popup ({@link Popup}) that should be used
* as a tooltip window when the mouse hovers over the element for a moment.
*
*
An onOpen event is sent to the tooltip if it is going to
* appear. Therefore, developers can manipulate it dynamically
* (perhaps based on OpenEvent.getReference) by listening to the onOpen
* event.
*
*
Note: To simplify the use, it not only searches its ID space,
* but also all ID spaces in the desktop.
* It first searches its own ID space, and then the other Id spaces
* in the same browser window (might have one or multiple desktops).
*
*
(since 3.0.2) If there are two components with the same ID (of course, in
* different ID spaces), you can specify the UUID with the following
* format:
* uuid(comp_uuid)
*
*
(since 3.6.3) the tooltip can be shown by a position from
* {@link Popup#open(org.zkoss.zk.ui.Component, String)}
* or the location of x
and y
, and can be specified
* with a delay time (in millisecond), you can specify the following format:
*
*
* id, position
* id, position=before_start, delay=500
* id, x=15, y=20
* uuid(comp_uuid2), position
* uuid(comp_uuid), x=15, y=20
*
* For example,
*
* <button label="show" tooltip="id, start_before"/>
*
* (since 6.5.2) the tooltip can also be shown on customized location of x
and y
by adding parentheses"()", for example,
*
* <button label="show" context="id, x=(zk.currentPointer[0] + 10), y=(zk.currentPointer[1] - 10)"/>
*
*
* @see #setTooltip(Popup)
* @see Popup#open(org.zkoss.zk.ui.Component, String)
*/
public void setTooltip(String tooltip) {
// ZK-816
if (!Objects.equals(_auxinf != null ? _auxinf.tooltip : null, tooltip)) {
initAuxInfo().tooltip = new DeferedUuid(tooltip);
smartUpdate("tooltip", _auxinf.tooltip);
}
}
/** Sets the UUID of the popup that should be used
* as a tooltip window when the mouse hovers over the element for a moment.
*
* Note: it actually invokes
* setTooltip("uuid(" + popup.getUuid() + ")")
* @since 3.0.2
* @see #setTooltip(String)
*/
public void setTooltip(Popup popup) {
// ZK-816, component keep wrong tooltip reference if set tooltip before tooltip attached
if (!Objects.equals(_auxinf != null ? _auxinf.tooltip : null, popup)) {
initAuxInfo().tooltip = new DeferedUuid(popup);
smartUpdate("tooltip", _auxinf.tooltip);
}
}
/**
* Sets the Attributes for the Tooltip Popup
*
* Note that position will be ignored if coordinates are set.
*
* @param popup the tooltip popup component, can be null
* @param position e.g. "after_start", can be null
* @param x e.g. "50" or "(zk.currentPointer[0] + 10)", can be null
* @param y e.g. "50" or "(zk.currentPointer[0] + 10)", can be null
* @param delay in milliseconds, can be null
*
* @since 8.5.0
*/
public void setTooltipAttributes(Popup popup, String position, String x, String y, Integer delay) {
setTooltip(popup);
if (_auxinf != null && _auxinf.tooltip != null) {
DeferedUuid pp = (DeferedUuid) _auxinf.tooltip;
pp.setPosition(position);
pp.setCoordinates(x, y);
pp.setDelay(delay);
smartUpdate("tooltip", _auxinf.tooltip);
}
}
//super//
protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer) throws java.io.IOException {
super.renderProperties(renderer);
render(renderer, "popup", getPopup());
render(renderer, "context", getContext());
// ZK-816
render(renderer, "tooltip", getTooltip());
render(renderer, "ctrlKeys", getCtrlKeys());
}
//--ComponentCtrl--//
private static HashMap _properties = new HashMap(5);
static {
_properties.put("context", new ObjectPropertyAccess() {
public void setValue(Component cmp, Object context) {
if (context instanceof Popup)
((XulElement) cmp).setContext((Popup) context);
else if (context != null)
((XulElement) cmp).setContext(String.valueOf(context));
}
public String getValue(Component cmp) {
return ((XulElement) cmp).getContext();
}
});
_properties.put("popup", new ObjectPropertyAccess() {
public void setValue(Component cmp, Object popup) {
if (popup instanceof Popup)
((XulElement) cmp).setPopup((Popup) popup);
else if (popup != null)
((XulElement) cmp).setPopup(String.valueOf(popup));
}
public String getValue(Component cmp) {
return ((XulElement) cmp).getPopup();
}
});
_properties.put("tooltip", new ObjectPropertyAccess() {
public void setValue(Component cmp, Object tooltip) {
if (tooltip instanceof Popup)
((XulElement) cmp).setTooltip((Popup) tooltip);
else if (tooltip != null)
((XulElement) cmp).setTooltip(String.valueOf(tooltip));
}
public String getValue(Component cmp) {
return ((XulElement) cmp).getTooltip();
}
});
_properties.put("ctrlKeys", new StringPropertyAccess() {
public void setValue(Component cmp, String ctrlKeys) {
((XulElement) cmp).setCtrlKeys(ctrlKeys);
}
public String getValue(Component cmp) {
return ((XulElement) cmp).getCtrlKeys();
}
});
}
public PropertyAccess getPropertyAccess(String prop) {
PropertyAccess pa = _properties.get(prop);
if (pa != null)
return pa;
return super.getPropertyAccess(prop);
}
//Cloneable//
public Object clone() {
final XulElement clone = (XulElement) super.clone();
if (_auxinf != null)
clone._auxinf = (AuxInfo) _auxinf.clone();
return clone;
}
private final AuxInfo initAuxInfo() {
if (_auxinf == null)
_auxinf = new AuxInfo();
return _auxinf;
}
/** Merge multiple members into an single object (and create on demand)
* to minimize the footprint
* @since 5.0.4
*/
private static class AuxInfo implements java.io.Serializable, Cloneable {
/** The popup ID that will be shown when click. */
private DeferredValue popup;
/** The context ID that will be shown when right-click. */
private DeferredValue context;
/** The tooltip ID that will be shown when mouse-over. */
private DeferredValue tooltip;
/** What control and function keys to intercepts. */
private String ctrlKeys;
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
}
private static class DeferedUuid implements DeferredValue, Serializable {
private static final long serialVersionUID = -122378869909137783L;
private Popup popup;
private String popupString;
private String position;
private String[] coordinates;
private Integer delay;
private String type;
public DeferedUuid(String popupString) {
super();
this.popupString = popupString;
}
public DeferedUuid(Popup tooltip) {
super();
this.popup = tooltip;
}
public void setPosition(String position) {
this.position = position;
}
public void setCoordinates(String x, String y) {
this.coordinates = new String[]{x, y};
}
public void setDelay(Integer delay) {
this.delay = delay;
}
public void setType(String type) {
this.type = type;
}
public Object getValue() {
if (popupString != null) {
return popupString;
} else if (popup != null) {
String uuidString = "uuid(" + popup.getUuid() + ")";
if (position != null) {
uuidString += ", " + position;
} else if (coordinates != null) {
if (coordinates[0] != null)
uuidString += ", x=" + coordinates[0];
if (coordinates[1] != null)
uuidString += ", y=" + coordinates[1];
}
if (delay != null) {
uuidString += ", delay=" + delay;
}
if (type != null) {
uuidString += ", type=" + type;
}
return uuidString;
} else {
return null;
}
}
public int hashCode() {
if (popupString != null) {
return popupString.hashCode();
} else if (popup != null) {
return popup.hashCode();
} else {
return super.hashCode();
}
}
public boolean equals(Object obj) {
if (obj instanceof String) {
return Objects.equals(popupString, obj);
} else if (obj instanceof Popup) {
return Objects.equals(popup, obj);
} else if (obj == null) {
return popup == null && popupString == null;
} else {
return false;
}
}
}
}