/* Groupbox.java

	Purpose:
		
	Description:
		
	History:
		Fri Jul 29 16:55:24     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.UiException;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.OpenEvent;
import org.zkoss.zul.impl.Utils;
import org.zkoss.zul.impl.XulElement;

/**
 * Groups a set of child elements to have a visual effect.
 * <p>Default {@link #getZclass}: "z-groupbox". If {@link #getMold()} is 3d,
 * "z-groupbox-3d" is assumed.(since 3.5.0)
 *
 * <p>Events: onOpen.
 *
 * @author tomyeh
 */
public class Groupbox extends XulElement {
	private transient Caption _caption;
	/** The style used for the content block. */
	private String _cntStyle;
	/** The style class used for the content block. */
	private String _cntSclass;
	private String _title = "";
	private boolean _open = true, _closable = true;

	static {
		addClientEvent(Groupbox.class, Events.ON_OPEN, CE_IMPORTANT);
	}

	/** Returns the caption of this groupbox.
	 */
	public Caption getCaption() {
		return _caption;
	}

	/** Returns whether this groupbox is open.
	 *
	 * <p>Default: true.
	 */
	public boolean isOpen() {
		return _open;
	}

	/** Opens or closes this groupbox.
	 */
	public void setOpen(boolean open) {
		if (_open != open) {
			_open = open;
			smartUpdate("open", _open);
		}
	}

	/** Returns whether user can open or close the group box.
	 * In other words, if false, users are no longer allowed to
	 * change the open status (by clicking on the title).
	 *
	 * <p>Default: true.
	 */
	public boolean isClosable() {
		return _closable;
	}

	/** Sets whether user can open or close the group box.
	 */
	public void setClosable(boolean closable) {
		if (_closable != closable) {
			_closable = closable;
			smartUpdate("closable", closable);
		}
	}

	//ZK-3678: Provide a switch to enable/disable iscroll
	/*package*/ boolean isNativeScrollbar() {
		return Utils.testAttribute(this, "org.zkoss.zul.nativebar", true, true);
	}

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

		render(renderer, "contentStyle", _cntStyle);
		render(renderer, "contentSclass", _cntSclass);
		render(renderer, "title", _title);
		if (!_open)
			renderer.render("open", false);
		if (!_closable)
			renderer.render("closable", false);
		//ZK-3678: Provide a switch to enable/disable iscroll
		if (!isNativeScrollbar())
			renderer.render("_nativebar", false);
	}

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

	/** Returns the CSS style for the content block of the groupbox.
	 * Used only if {@link #getMold} is not default.
	 */
	public String getContentStyle() {
		return _cntStyle;
	}

	/** Sets the CSS style for the content block of the groupbox.
	 * Used only if {@link #getMold} is not default.
	 *
	 * <p>Default: null.
	 */
	public void setContentStyle(String style) {
		if (!Objects.equals(_cntStyle, style)) {
			_cntStyle = style;
			smartUpdate("contentStyle", _cntStyle);
		}
	}

	/** Returns the style class used for the content block of the groupbox.
	 * Used only if {@link #getMold} is not default.
	 */
	public String getContentSclass() {
		return _cntSclass;
	}

	/** Sets the style class used for the content block.
	 *
	 * @see #getContentSclass
	 * @since 3.0.0
	 */
	public void setContentSclass(String scls) {
		if (!Objects.equals(_cntSclass, scls)) {
			_cntSclass = scls;
			smartUpdate("contentSclass", _cntSclass);
		}
	}

	/** Returns the title.
	 * Besides this attribute, you could use {@link Caption} to define
	 * a more sophisticated caption (a.k.a., title).
	 * <p> It will be displayed before caption.
	 * <p>Default: empty.
	 * @since 6.0.0
	 */
	public String getTitle() {
		return _title;
	}

	/** Sets the title.
	 * @since 6.0.0
	 */
	public void setTitle(String title) {
		if (title == null)
			title = "";
		if (!Objects.equals(_title, title)) {
			_title = title;
			smartUpdate("title", title);
		}
	}

	//-- Component --//
	public void beforeChildAdded(Component child, Component refChild) {
		if (child instanceof Caption) {
			if (_caption != null && _caption != child)
				throw new UiException("Only one caption is allowed: " + this);
		} else if (refChild instanceof Caption) {
			throw new UiException("caption must be the first child");
		}
		super.beforeChildAdded(child, refChild);
	}

	public boolean insertBefore(Component child, Component refChild) {
		if (child instanceof Caption) {
			refChild = getFirstChild();
			//always makes caption as the first child
			if (super.insertBefore(child, refChild)) {
				_caption = (Caption) child;
				return true;
			}
		} else {
			return super.insertBefore(child, refChild);
		}
		return false;
	}

	public void onChildRemoved(Component child) {
		if (child instanceof Caption)
			_caption = null;
		super.onChildRemoved(child);
	}

	//-- ComponentCtrl --//
	/** Processes an AU request.
	 *
	 * <p>Default: in addition to what are handled by {@link XulElement#service},
	 * it also handles onOpen.
	 * @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_OPEN)) {
			OpenEvent evt = OpenEvent.getOpenEvent(request);
			_open = evt.isOpen();
			Events.postEvent(evt);
		} else
			super.service(request, everError);
	}

	//Cloneable//
	public Object clone() {
		final Groupbox clone = (Groupbox) super.clone();

		if (_caption != null)
			clone.afterUnmarshal();
		return clone;
	}

	/** @param cnt # of children that need special handling (used for optimization).
	 * -1 means process all of them
	 */
	private void afterUnmarshal() {
		for (Component child : getChildren())
			if (child instanceof Caption) {
				_caption = (Caption) child;
				break; //done
			}
	}

	//Serializable//
	private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
		s.defaultReadObject();
		afterUnmarshal();
	}
}
