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