/* Button.java
Purpose:
Description:
History:
Wed Jun 8 10:31:02 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;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.zkoss.lang.Objects;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.impl.LabelImageElement;
/**
* A button.
* <p>Default {@link #getZclass}: z-button.(since 3.5.0)
* @author tomyeh
*/
public class Button extends LabelImageElement implements org.zkoss.zk.ui.ext.Disable {
private AuxInfo _auxinf;
static {
addClientEvent(Button.class, Events.ON_FOCUS, CE_DUPLICATE_IGNORE);
addClientEvent(Button.class, Events.ON_BLUR, CE_DUPLICATE_IGNORE);
}
public Button() {
}
public Button(String label) {
super(label);
}
public Button(String label, String image) {
super(label, image);
}
/** Returns whether it is disabled.
* <p>Default: false.
*/
public boolean isDisabled() {
return _auxinf != null && _auxinf.disabled;
}
/** Sets whether it is disabled.
* @see #setAutodisable
*/
public void setDisabled(boolean disabled) {
if ((_auxinf != null && _auxinf.disabled) != disabled) {
initAuxInfo().disabled = disabled;
smartUpdate("disabled", isDisabled());
}
}
/** Returns a list of component IDs that shall be disabled when the user
* clicks this button.
* @since 5.0.0
*/
public String getAutodisable() {
return _auxinf != null ? _auxinf.autodisable : null;
}
/** Sets a list of component IDs that shall be disabled when the user
* clicks this button.
*
* <p>To represent the button itself, the developer can specify <code>self</code>.
* For example, <code><button id="ok" autodisable="self,cancel"/></code>
* is the same as <code><button id="ok" autodisable="ok,cancel"/></code>
* that will disable
* both the ok and cancel buttons when an user clicks it.
*
* <p>The button being disabled will be enabled automatically
* once the client receives a response from the server.
* In other words, the server doesn't notice if a button is disabled
* with this method.
*
* <p>However, if you prefer to enable them later manually, you can
* prefix with '+'. For example,
* <code><button id="ok" autodisable="+self,+cancel"/></code>
*
* <p>Then, you have to enable them manually such as
* <pre><code>if (something_happened){
* ok.setDisabled(false);
* cancel.setDisabled(false);
*</code></pre>
*
* <p>Default: null.
* @since 5.0.0
*/
public void setAutodisable(String autodisable) {
if (!Objects.equals(_auxinf != null ? _auxinf.autodisable : null, autodisable)) {
initAuxInfo().autodisable = autodisable;
smartUpdate("autodisable", getAutodisable());
}
}
/** Returns the direction.
* <p>Default: "normal".
*/
public String getDir() {
return _auxinf != null ? _auxinf.dir : NORMAL;
}
/** Sets the direction to layout image.
* @param dir either "normal" or "reverse".
*/
public void setDir(String dir) throws WrongValueException {
if (!NORMAL.equals(dir) && !"reverse".equals(dir))
throw new WrongValueException(dir);
if (!Objects.equals(_auxinf != null ? _auxinf.dir : NORMAL, dir)) {
initAuxInfo().dir = dir;
smartUpdate("dir", getDir());
}
}
/** Returns the orient.
* <p>Default: "horizontal".
*/
public String getOrient() {
return _auxinf != null ? _auxinf.orient : HORIZONTAL;
}
/** Sets the orient to layout image.
* @param orient either "horizontal" or "vertical".
*/
public void setOrient(String orient) throws WrongValueException {
if (!HORIZONTAL.equals(orient) && !"vertical".equals(orient))
throw new WrongValueException(orient);
if (!Objects.equals(_auxinf != null ? _auxinf.orient : HORIZONTAL, orient)) {
initAuxInfo().orient = orient;
smartUpdate("orient", getOrient());
}
}
/** Returns the button type.
* <p>Default: "button".
* @since 5.0.4
*/
public String getType() {
return _auxinf != null ? _auxinf.type : BUTTON;
}
/** Sets the button type.
* <p>Default: "button".
* It is meaningful only if it is used with a HTML form.
* Refer to <a href="http://www.htmlcodetutorial.com/forms/_BUTTON_TYPE.html">HTML Button Type</a>
* for details.
* @param type either "button", "submit" or "reset".
* @since 5.0.4
*/
public void setType(String type) throws WrongValueException {
if (!BUTTON.equals(type) && !"submit".equals(type) && !"reset".equals(type))
throw new WrongValueException(type);
if (!Objects.equals(_auxinf != null ? _auxinf.type : BUTTON, type)) {
initAuxInfo().type = type;
smartUpdate("type", getType());
}
}
/** Returns the href that the browser shall jump to, if an user clicks
* this button.
* <p>Default: null. If null, the button has no function unless you
* specify the onClick event listener.
* <p>If it is not null, the onClick event won't be sent.
*/
public String getHref() {
return _auxinf != null ? _auxinf.href : null;
}
/** Sets the href.
*/
public void setHref(String href) {
if (href != null && href.length() == 0)
href = null;
if (!Objects.equals(_auxinf != null ? _auxinf.href : null, href)) {
initAuxInfo().href = href;
smartUpdate("href", new EncodedHref()); //Bug 1850895
}
}
/** Returns the target frame or window.
*
* <p>Note: it is useful only if href ({@link #setHref}) is specified
* (i.e., use the onClick listener).
*
* <p>Default: null.
*/
public String getTarget() {
return _auxinf != null ? _auxinf.target : null;
}
/** Sets the target frame or window.
* @param target the name of the frame or window to hyperlink.
*/
public void setTarget(String target) {
if (target != null && target.length() == 0)
target = null;
if (!Objects.equals(_auxinf != null ? _auxinf.target : null, target)) {
initAuxInfo().target = target;
smartUpdate("target", getTarget());
}
}
/** Returns non-null if this button is used for file upload, or null otherwise.
* Refer to {@link #setUpload} for more details.
* @since 5.0.0
*/
public String getUpload() {
return _auxinf != null ? _auxinf.upload : null;
}
/** Sets the JavaScript class at the client to handle the upload if this
* button is used for file upload.
* <p>Default: null.
*
* <p>For example, the following example declares a button for file upload:
* <pre><code><button label="Upload" upload="true"
* onUpload="handle(event.media)"/></code></pre>
*
* <p>As shown above, after the file is uploaded, an instance of
* {@link org.zkoss.zk.ui.event.UploadEvent} is sent this component.
*
* <p>If you want to customize the handling of the file upload at
* the client, you can specify a JavaScript class when calling
* this method:
* <code><button upload="foo.Upload"/></code>
*
* <p> Another options for the upload can be specified as follows:
* <pre><code><button label="Upload" upload="true,maxsize=-1,multiple=true,accept=audio/*|video/*|image/*,native"</code></pre>
* <ul>
* <li>maxsize: the maximal allowed upload size of the component, in kilobytes, or
* a negative value if no limit.</li>
* <li>native: treating the uploaded file(s) as binary, i.e., not to convert it to
* image, audio or text files.</li>
* <li>multiple: treating the file chooser allows multiple files to upload,
* the setting only works with HTML5 supported browsers (since ZK 6.0.0).</li>
* <li>accept: specifies the types of files that the server accepts,
* the setting only works with HTML5 supported browsers (since ZK 7.0.0).</li>
* </ul>
*
* <p> Note: if the options of the <code>false</code> or the customized handler
* (like <code>foo.Upload</code>) are not specified, the option of <code>true</code>
* is implicit by default.
*
* @param upload a JavaScript class to handle the file upload
* at the client, or "true" if the default class is used,
* or null or "false" to disable the file download (and then
* this button behaves like a normal button).
* @since 5.0.0
*/
public void setUpload(String upload) {
if (upload != null && (upload.length() == 0 || "false".equals(upload)))
upload = null;
if (!Objects.equals(upload, _auxinf != null ? _auxinf.upload : null)) {
onUploadChanged(upload);
initAuxInfo().upload = upload;
if (upload != null) { //for AuUploader
Matcher matcher = Pattern.compile("maxsize=([^,]+)").matcher(upload);
if (matcher.find()) {
try {
Integer maxsz = Integer.parseInt(matcher.group(1));
setAttribute(org.zkoss.zk.ui.impl.Attributes.UPLOAD_MAX_SIZE, maxsz);
} catch (NumberFormatException e) {
throw new UiException("The upload max size should be a positive integer.");
}
}
}
smartUpdate("upload", getUpload());
}
}
/** Called when the upload attribute is modified. */
private void onUploadChanged(String upload) {
if (upload != null && !"trendy".equals(getMold()) && getDefinition().getMoldNames().contains("trendy")) //Toolbarbutton doesn't support trendy
setMold("trendy");
}
private String getEncodedHref() {
final Desktop dt = getDesktop();
return _auxinf != null && _auxinf.href != null && dt != null ? dt.getExecution().encodeURL(_auxinf.href) : null;
//if desktop is null, it doesn't belong to any execution
}
//-- super --//
protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer) throws java.io.IOException {
super.renderProperties(renderer);
String s;
if (!NORMAL.equals(s = getDir()))
render(renderer, "dir", s);
if (!HORIZONTAL.equals(s = getOrient()))
render(renderer, "orient", s);
if (!BUTTON.equals(s = getType()))
render(renderer, "type", s);
render(renderer, "disabled", isDisabled());
render(renderer, "autodisable", getAutodisable());
final String href;
render(renderer, "href", href = getEncodedHref());
render(renderer, "target", getTarget());
render(renderer, "upload", getUpload());
org.zkoss.zul.impl.Utils.renderCrawlableA(href, getLabel());
}
protected void renderCrawlable(String label) throws java.io.IOException {
//does nothing since generated in renderProperties
}
public String getZclass() {
return _zclass != null ? _zclass : "z-button";
}
//Cloneable//
public Object clone() {
final Button clone = (Button) super.clone();
if (_auxinf != null)
clone._auxinf = (AuxInfo) _auxinf.clone();
return clone;
}
//Component//
/** No child is allowed.
*/
protected boolean isChildable() {
return false;
}
private class EncodedHref implements org.zkoss.zk.au.DeferredValue {
public Object getValue() {
return getEncodedHref();
}
}
protected void updateByClient(String name, Object value) {
if ("disabled".equals(name))
setDisabled(value instanceof Boolean ? ((Boolean) value).booleanValue()
: "true".equals(Objects.toString(value)));
else
super.updateByClient(name, value);
}
private AuxInfo initAuxInfo() {
if (_auxinf == null)
_auxinf = new AuxInfo();
return _auxinf;
}
private static final String HORIZONTAL = "horizontal", NORMAL = "normal", BUTTON = "button";
private static class AuxInfo implements java.io.Serializable, Cloneable {
private String orient = HORIZONTAL;
private String dir = NORMAL;
private String type = BUTTON;
private String href, target;
private String autodisable;
protected String upload;
private boolean disabled;
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
}
}