/* Menuitem.java Purpose: Description: History: Thu Sep 22 10:58:23 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 org.zkoss.lang.Objects; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.UiException; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zk.ui.event.CheckEvent; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.UploadEvent; import org.zkoss.zul.impl.LabelImageElement; /** * A single choice in a {@link Menupopup} element. * It acts much like a button but it is rendered on a menu. * * <p>Default {@link #getZclass}: z-menuitem. (since 3.5.0) * @author tomyeh */ public class Menuitem extends LabelImageElement implements org.zkoss.zk.ui.ext.Disable { private AuxInfo _auxinf; static { addClientEvent(Menuitem.class, Events.ON_CHECK, CE_IMPORTANT); } public Menuitem() { } public Menuitem(String label) { super(label); } public Menuitem(String label, String src) { super(label, src); } /** Returns whether the check mark shall be displayed in front * of each item. * <p>Default: false. * @since 3.5.0 */ public boolean isCheckmark() { return _auxinf != null && _auxinf.checkmark; } /** Sets whether the check mark shall be displayed in front * of each item. * <p>Note the checkbox can be checked only if {@link #isAutocheck()} is true * @since 3.5.0 */ public void setCheckmark(boolean checkmark) { if ((_auxinf != null && _auxinf.checkmark) != checkmark) { initAuxInfo().checkmark = checkmark; smartUpdate("checkmark", isCheckmark()); } } public String getZclass() { return _zclass == null ? "z-menuitem" : _zclass; } /** * Sets whether it is disabled. * @since 3.0.1 */ public void setDisabled(boolean disabled) { if ((_auxinf != null && _auxinf.disabled) != disabled) { initAuxInfo().disabled = disabled; smartUpdate("disabled", isDisabled()); } } /** Returns whether it is disabled. * <p>Default: false. * @since 3.0.1 */ public boolean isDisabled() { return _auxinf != null && _auxinf.disabled; } /** Returns a list of component IDs that shall be disabled when the user * clicks this menuitem. * @since 5.0.7 */ public String getAutodisable() { return _auxinf != null ? _auxinf.autodisable : null; } /** Sets a list of component IDs that shall be disabled when the user * clicks this menuitem. * * <p>To represent the menuitem itself, the developer can specify <code>self</code>. * For example, <code><menuitem id="ok" autodisable="self,cancel"/></code> * is the same as <code><menuitem id="ok" autodisable="ok,cancel"/></code> * that will disable * both the ok and cancel menuitem when an user clicks it. * * <p>The menuitem 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 menuitem is disabled * with this method. * * <p>However, if you prefer to enable them later manually, you can * prefix with '+'. For example, * <code><menuitem 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.7 */ public void setAutodisable(String autodisable) { if (!Objects.equals(_auxinf != null ? _auxinf.autodisable : null, autodisable)) { initAuxInfo().autodisable = autodisable; smartUpdate("autodisable", getAutodisable()); } } /** Returns the value. * <p>Default: "". */ public String getValue() { return _auxinf != null ? _auxinf.value : ""; } /** Sets the value. */ public void setValue(String value) { if (value == null) value = ""; if (!Objects.equals(_auxinf != null ? _auxinf.value : "", value)) { initAuxInfo().value = value; smartUpdate("value", getValue()); } } /** Returns whether it is checked. * <p>Default: false. */ public boolean isChecked() { return _auxinf != null && _auxinf.checked; } /** Sets whether it is checked. * <p> This only applies when {@link #isCheckmark()} = true. (since 3.5.0) */ public void setChecked(boolean checked) { if ((_auxinf != null && _auxinf.checked) != checked) { initAuxInfo().checked = checked; if (_auxinf.checked) _auxinf.checkmark = true; smartUpdate("checked", isChecked()); } } /** Returns whether the menuitem check mark will update each time * the menu item is selected. * <p>Default: false. */ public boolean isAutocheck() { return _auxinf != null && _auxinf.autocheck; } /** Sets whether the menuitem check mark will update each time * the menu item is selected. * <p> This only applies when {@link #isCheckmark()} = true. (since 3.5.0) */ public void setAutocheck(boolean autocheck) { if ((_auxinf != null && _auxinf.autocheck) != autocheck) { initAuxInfo().autocheck = autocheck; smartUpdate("autocheck", isAutocheck()); } } /** Returns the href. * <p>Default: null. If null, the button has no function unless you * specify the onClick handler. */ public String getHref() { return _auxinf != null ? _auxinf.href : null; } /** Sets the href. */ public void setHref(String href) throws WrongValueException { 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 whether this is an top-level menu, i.e., not owning * by another {@link Menupopup}. */ public boolean isTopmost() { return !(getParent() instanceof Menupopup); } /** 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 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,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: Sets whether to treat the uploaded file(s) as binary, i.e., * not to convert it to image, audio or text files.</li> * </ul> * * @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)) { initAuxInfo().upload = upload; smartUpdate("upload", getUpload()); } } //Bug #2871082 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 } //-- Component --// public void beforeParentChanged(Component parent) { if (parent != null && !(parent instanceof Menupopup) && !(parent instanceof Menubar)) throw new UiException("Unsupported parent for menuitem: " + parent); super.beforeParentChanged(parent); } //Cloneable// public Object clone() { final Menuitem clone = (Menuitem) super.clone(); if (_auxinf != null) clone._auxinf = (AuxInfo) _auxinf.clone(); return clone; } /** Not childable. */ protected boolean isChildable() { return false; } // super protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer) throws java.io.IOException { super.renderProperties(renderer); render(renderer, "checkmark", isCheckmark()); render(renderer, "disabled", isDisabled()); render(renderer, "checked", isChecked()); render(renderer, "autocheck", isAutocheck()); render(renderer, "autodisable", getAutodisable()); final String href; render(renderer, "href", href = getEncodedHref()); //Bug #2871082 render(renderer, "target", getTarget()); render(renderer, "upload", getUpload()); render(renderer, "value", getValue()); org.zkoss.zul.impl.Utils.renderCrawlableA(href, getLabel()); } protected void renderCrawlable(String label) throws java.io.IOException { //does nothing since generated in renderProperties } //-- ComponentCtrl --// /** Processes an AU request. * * <p>Default: in addition to what are handled by {@link LabelImageElement#service}, * it also handles onCheck. * @since 5.0.0 */ public void service(org.zkoss.zk.au.AuRequest request, boolean everError) { final String cmd = request.getCommand(); if (cmd.equals(Events.ON_CHECK)) { CheckEvent evt = CheckEvent.getCheckEvent(request); initAuxInfo().checked = evt.isChecked(); if (_auxinf.checked) _auxinf.checkmark = true; Events.postEvent(evt); } else super.service(request, everError); } //Bug #2871082 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 class AuxInfo implements java.io.Serializable, Cloneable { private String value = ""; private String href, target; private String autodisable; protected String upload; private boolean disabled; private boolean autocheck, checked; private boolean checkmark; public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); } } } }