zul/src/main/java/org/zkoss/zul/Listitem.java

Summary

Maintainability
D
1 day
Test Coverage
/* Listitem.java

    Purpose:
        
    Description:
        
    History:
        Wed Jun 15 17:38:52     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.io.Serializable;
import java.util.HashMap;

import org.zkoss.lang.Objects;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.sys.BooleanPropertyAccess;
import org.zkoss.zk.ui.sys.IntegerPropertyAccess;
import org.zkoss.zk.ui.sys.PropertyAccess;
import org.zkoss.zul.impl.LoadStatus;
import org.zkoss.zul.impl.XulElement;

/**
 * A list item.
 *
 * <p>Default {@link #getZclass}: z-listitem (since 5.0.0)
 *
 * @author tomyeh
 */
public class Listitem extends XulElement {

    private transient Object _value;
    /** The index in the parent (only for implementation purpose). */
    private int _index = -1; //no parent at beginning
    private boolean _selected, _disabled, _selectable = true;
    /** whether the content of this item is loaded; used if
     * the listbox owning this item is using a list model.
     */
    private boolean _loaded;

    public Listitem() {
    }

    public Listitem(String label) {
        setLabel(label);
    }

    public <T> Listitem(String label, T value) {
        setLabel(label);
        setValue(value);
    }

    /** Returns the list box that it belongs to.
     * <p>It is the same as {@link #getParent}.
     */
    public Listbox getListbox() {
        return (Listbox) getParent();
    }

    /**
     * Returns the listgroup that this item belongs to, or null.
     * @since 3.5.0
     */
    public Listgroup getListgroup() {
        final Listbox lb = getListbox();
        if (lb != null)
            return lb.getListgroupAt(getIndex());
        return null;
    }

    public String getZclass() {
        return _zclass == null ? "z-listitem" : _zclass;
    }

    /**
     * Returns whether it is selectable.
     * <p>Default: true.</p>
     * @since 8.0.0
     */
    public boolean isSelectable() {
        return _selectable;
    }

    /** Sets whether it is selectable.
     *
     * <p>If the listbox is in a checkmark mode, the selectable state will affect
     * the checkable icon to display or not.</p>
     * <p>Default: true.</p>
     * @param selectable
     */
    public void setSelectable(boolean selectable) {
        if (_selectable != selectable) {
            _selectable = selectable;

            // non-checkable cannot be selected
            if (!_selectable)
                setSelected(false);
            smartUpdate("selectable", selectable);
        }
    }

    /** Returns the maximal length of each item's label.
     * It is a shortcut of getParent().getMaxlength();
     * Thus, it works only if the listbox's mold is "select".
     */
    public int getMaxlength() {
        final Listbox listbox = getListbox();
        return listbox != null ? listbox.getMaxlength() : 0;
    }

    /** Returns the value.
     * <p>Default: null.
     * <p>Note: the value is application dependent, you can place
     * whatever value you want.
     * <p>If you are using listitem with HTML Form (and with
     * the name attribute), it is better to specify a String-typed
     * value.
     */
    @SuppressWarnings("unchecked")
    public <T> T getValue() {
        return (T) _value;
    }

    /** Sets the value.
     * @param value the value.
     * <p>Note: the value is application dependent, you can place
     * whatever value you want.
     * <p>If you are using listitem with HTML Form (and with
     * the name attribute), it is better to specify a String-typed
     * value.
     */
    public <T> void setValue(T value) {
        if (!Objects.equals(_value, value)) {
            _value = value;

            final Listbox listbox = getListbox();
            if (listbox != null)
                if (listbox.inSelectMold())
                    smartUpdate("value", _value);
                else if (listbox.getName() != null)
                    smartUpdate("value", _value);
        }
    }

    /** Returns whether it is disabled.
     * <p>Default: false.
     */
    public boolean isDisabled() {
        return _disabled;
    }

    /** Sets whether it is disabled.
     */
    public void setDisabled(boolean disabled) {
        if (_disabled != disabled) {
            _disabled = disabled;
            smartUpdate("disabled", _disabled);
        }
    }

    /** Returns whether it is selected.
     * <p>Default: false.
     */
    public boolean isSelected() {
        return _selected;
    }

    /** Sets whether it is selected.
     */
    public void setSelected(boolean selected) {
        if (_selected != selected) {
            final Listbox listbox = (Listbox) getParent();
            if (listbox != null) {
                //Note: we don't update it here but let its parent does the job
                listbox.toggleItemSelection(this);
            } else {
                _selected = selected;
            }
        }
    }

    /** Returns the label of the {@link Listcell} it contains, or null
     * if no such cell.
     */
    public String getLabel() {
        final Listcell cell = (Listcell) getFirstChild();
        return cell != null ? cell.getLabel() : null;
    }

    /** Sets the label of the {@link Listcell} it contains.
     *
     * <p>If it is not created, we automatically create it.
     */
    public void setLabel(String label) {
        autoFirstCell().setLabel(label);
    }

    private Listcell autoFirstCell() {
        Listcell cell = (Listcell) getFirstChild();
        if (cell == null) {
            cell = new Listcell();
            cell.applyProperties();
            cell.setParent(this);
        }
        return cell;
    }

    /** Returns the image of the {@link Listcell} it contains.
     */
    public String getImage() {
        final Listcell cell = (Listcell) getFirstChild();
        return cell != null ? cell.getImage() : null;
    }

    /** Sets the image of the {@link Listcell} it contains.
     *
     * <p>If it is not created, we automatically create it.
     */
    public void setImage(String image) {
        autoFirstCell().setImage(image);
    }

    /** Returns the index of this item (a.k.a., the order in the listbox).
     */
    public int getIndex() {
        return _index;
    }

    /** Sets whether the content of this item is loaded; used if
     * the listbox owning this item is using a list model.
     */
    /*package*/ void setLoaded(boolean loaded) {
        if (loaded != _loaded) {
            _loaded = loaded;

            final Listbox listbox = getListbox();
            if (listbox != null && listbox.getModel() != null)
                smartUpdate("_loaded", _loaded);
        }
    }

    /** Returns whether the content of this item is loaded.
     * It is meaningful only if {@link #getListbox} is live data,
     * i.e., {@link Listbox#getModel} is not null.
     *
     * @since 2.4.0
     */
    public boolean isLoaded() {
        return _loaded;
    }

    //-- Utilities for implementation only (called by Listbox) */
    /*package*/ void setIndexDirectly(int index) {
        setIndex(index);
    }

    protected void setIndex(int index) {
        _index = index;
    }

    /*package*/ void setSelectedDirectly(boolean selected) {
        _selected = selected;
    }

    public boolean setVisible(boolean visible) {
        if (isVisible() == visible)
            return visible;

        final boolean result = super.setVisible(visible);
        final Listbox listbox = (Listbox) getParent();
        if (listbox != null) {
            if (listbox.inSelectMold())
                listbox.invalidate();
            final Listgroup g = listbox.getListgroupAt(getIndex());
            if (g == null || g.isOpen())
                listbox.addVisibleItemCount(visible ? 1 : -1);
        }

        // Bug ZK-485, send after invoking listbox.addVisibleItemCount()
        smartUpdate("visible", visible);
        return result;
    }

    protected void smartUpdate(String name, Object value) { //make it accessible in this package
        Listbox box = getListbox();
        if (isVisible() || box == null || !box.inSelectMold())
            super.smartUpdate(name, value);
    }

    protected void smartUpdate(String name, boolean value) { //make it accessible in this package
        Listbox box = getListbox();
        if (isVisible() || box == null || !box.inSelectMold())
            super.smartUpdate(name, value);
    }

    protected void smartUpdate(String name, int value) {
        Listbox box = getListbox();
        if (isVisible() || box == null || !box.inSelectMold())
            super.smartUpdate(name, value);
    }

    public String getMold() {
        return getParent() != null ? "select".equals(getParent().getMold()) ? "select" : super.getMold()
                : super.getMold();
    }

    /**
     * @deprecated as of release 6.0.0. To control the size of Listbox related 
     * components, please refer to {@link Listbox} and {@link Listheader} instead.
     */
    public void setWidth(String width) {
        // Don't need to remove this method, it's used to override super.setWidth();
    }

    /**
     * @deprecated as of release 6.0.0. To control the size of Listbox related 
     * components, please refer to {@link Listbox} and {@link Listheader} instead.
     */
    public void setHflex(String flex) {
        // Don't need to remove this method, it's used to override super.setHflex();
    }

    protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer) throws java.io.IOException {
        super.renderProperties(renderer);

        render(renderer, "selected", isSelected());
        render(renderer, "disabled", isDisabled());
        render(renderer, "_loaded", _loaded ? _loaded : getListbox().getModel() == null);
        renderer.render("_index", _index);

        if (_value instanceof String) {
            render(renderer, "value", _value);
        }

        if (!isSelectable())
            renderer.render("checkable", false);
    }

    protected void addMoved(Component oldparent, Page oldpg, Page newpg) {
        if (oldparent == null || !((Listbox) oldparent).isLoadingModel()) {
            super.addMoved(oldparent, oldpg, newpg);
        }
    }

    private static HashMap<String, PropertyAccess> _properties = new HashMap<String, PropertyAccess>(2);

    static {
        _properties.put("_loaded", new BooleanPropertyAccess() {
            public void setValue(Component cmp, Boolean loaded) {
                ((Listitem) cmp)._loaded = loaded;
            }

            public Boolean getValue(Component cmp) {
                return ((Listitem) cmp)._loaded;
            }
        });
        _properties.put("_index", new IntegerPropertyAccess() {
            public void setValue(Component cmp, Integer index) {
                ((Listitem) cmp)._index = index;
            }

            public Integer getValue(Component cmp) {
                return ((Listitem) cmp)._index;
            }
        });
    }

    public PropertyAccess getPropertyAccess(String prop) {
        PropertyAccess pa = _properties.get(prop);
        if (pa != null)
            return pa;
        return super.getPropertyAccess(prop);
    }

    //-- Component --//
    public void beforeParentChanged(Component parent) {
        if (parent != null && !(parent instanceof Listbox))
            throw new UiException("Listitem's parent must be Listbox, not " + parent);
        super.beforeParentChanged(parent);
    }

    public void beforeChildAdded(Component child, Component refChild) {
        if (!(child instanceof Listcell))
            throw new UiException("Unsupported child for listitem: " + child);
        super.beforeChildAdded(child, refChild);
    }

    // -- Serializable --//
    private synchronized void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
        s.defaultWriteObject();

        if (_value instanceof Serializable) {
            s.writeBoolean(true);
            s.writeObject(_value);
        } else {
            s.writeBoolean(false);
        }
    }

    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        if (s.readBoolean())
            _value = s.readObject();
    }

    //Clone//
    public Object clone() {
        final Listitem clone = (Listitem) super.clone();
        clone._index = -1;
        //note: we have to reset, since listbox.insertBefore assumes
        //that a parent-less listitem's index is -1
        return clone;
    }

    //-- ComponentCtrl --//
    public Object getExtraCtrl() {
        return new ExtraCtrl();
    }

    /** A utility class to implement {@link #getExtraCtrl}.
     * It is used only by component developers.
     */
    protected class ExtraCtrl extends XulElement.ExtraCtrl implements LoadStatus {
        //-- LoadStatus --//
        public boolean isLoaded() {
            return Listitem.this.isLoaded();
        }

        public void setLoaded(boolean loaded) {
            Listitem.this.setLoaded(loaded);
        }

        public void setIndex(int index) {
            Listitem.this.setIndex(index);
        }
    }

}