/* LayoutRegion.java Purpose: Description: History: Feb 10, 2009 4:17:16 PM , Created by jumperchen Copyright (C) 2008 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.Iterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.zk.ui.event.SlideEvent; import org.zkoss.zul.impl.Utils; import org.zkoss.zul.impl.XulElement; /** * A layout region in a border layout. * <p> * Events:<br/> onOpen, onSize.<br/> * * <h3>Support Caption component</h3> * [ZK EE] * [Since 6.5.0] * * @author jumperchen * @since 5.0.0 */ public abstract class LayoutRegion extends XulElement { private static final Logger log = LoggerFactory.getLogger(LayoutRegion.class); private String _border = "normal"; private int[] _margins = new int[] { 0, 0, 0, 0 }; private boolean _flex; private boolean _autoscroll; private String _title = null; private int _maxsize = 2000; private int _minsize = 0; private int[] _cmargins; private boolean _splittable; private boolean _collapsible; private boolean _open = true; private boolean _slide = false; private transient Caption _caption; static { addClientEvent(LayoutRegion.class, Events.ON_OPEN, CE_IMPORTANT); addClientEvent(LayoutRegion.class, Events.ON_SLIDE, CE_IMPORTANT); addClientEvent(LayoutRegion.class, Events.ON_SIZE, CE_IMPORTANT | CE_DUPLICATE_IGNORE); } public LayoutRegion() { _cmargins = getDefaultCmargins(); } /** Returns the caption of this region. * @since 6.5.0 */ public Caption getCaption() { return _caption; } /** * Returns the border. * <p> * The border actually controls what CSS class to use: If border is null, it * implies "none". * * <p> * If you also specify the CSS class ({@link #setClass}), it overwrites * whatever border you specify here. * * <p> * Default: "normal". */ public String getBorder() { return _border; } /** * Sets the border (either none or normal). * * @param border * the border. If null or "0", "none" is assumed. */ public void setBorder(String border) { if (border == null || "0".equals(border)) border = "none"; if (!_border.equals(border)) { _border = border; smartUpdate("border", _border); } } /** * @deprecated As of release 6.0.2, use {@link #getHflex()} and {@link #getVflex()} on child component instead * <p> * Default: false. */ public boolean isFlex() { return _flex; } /** * @deprecated As of release 6.0.2, use {@link #setHflex(String)} and {@link #setVflex(String)} on child component instead * */ public void setFlex(boolean flex) { log.warn("The flex attribute is deprecated, use setHflex and setVflex on child component instead."); if (_flex != flex) { _flex = flex; smartUpdate("flex", _flex); } } /** * Returns the margins, which is a list of numbers separated by comma. * * <p> * Default: "0,0,0,0". */ public String getMargins() { return Utils.intsToString(_margins); } /** * Sets margins for the element "0,1,2,3" that direction is * "top,left,right,bottom" */ public void setMargins(String margins) { final int[] imargins = Utils.stringToInts(margins, 0); if (!Objects.equals(imargins, _margins)) { _margins = imargins; smartUpdate("margins", getMargins()); } } /** * Returns whether enable overflow scrolling. * <p> * Default: false. */ public boolean isAutoscroll() { return _autoscroll; } /** * Sets whether enable overflow scrolling. */ public void setAutoscroll(boolean autoscroll) { if (_autoscroll != autoscroll) { _autoscroll = autoscroll; smartUpdate("autoscroll", _autoscroll); } } /** * Returns this regions position (north/south/east/west/center). * * @see Borderlayout#NORTH * @see Borderlayout#SOUTH * @see Borderlayout#EAST * @see Borderlayout#WEST * @see Borderlayout#CENTER */ public abstract String getPosition(); /** * Sets the size of this region. This method is shortcut for * {@link #setHeight(String)} and {@link #setWidth(String)}. If this region * is {@link North} or {@link South}, this method will invoke * {@link #setHeight(String)}. If this region is {@link West} or * {@link East}, this method will invoke {@link #setWidth(String)}. * Otherwise it will throw a {@link UnsupportedOperationException}. */ public abstract void setSize(String size); /** * Returns the size of this region. This method is shortcut for * {@link #getHeight()} and {@link #getWidth()}. If this region is * {@link North} or {@link South}, this method will invoke * {@link #getHeight()}. If this region is {@link West} or {@link East}, * this method will invoke {@link #getWidth()}. Otherwise it will throw a * {@link UnsupportedOperationException}. */ public abstract String getSize(); /** * Returns the title. * <p>Default: null. */ public String getTitle() { return _title; } /** * Sets the title. */ public void setTitle(String title) { if (!Objects.equals(_title, title)) { _title = title; smartUpdate("title", _title); } } /** * Returns whether enable the split functionality. * <p> * Default: false. */ public boolean isSplittable() { return _splittable; } /** * Sets whether enable the split functionality. */ public void setSplittable(boolean splittable) { if (_splittable != splittable) { _splittable = splittable; smartUpdate("splittable", _splittable); } } /** * Sets the maximum size of the resizing element. */ public void setMaxsize(int maxsize) { if (_maxsize != maxsize) { _maxsize = maxsize; smartUpdate("maxsize", _maxsize); } } /** * Returns the maximum size of the resizing element. * <p> * Default: 2000. */ public int getMaxsize() { return _maxsize; } /** * Sets the minimum size of the resizing element. */ public void setMinsize(int minsize) { if (_minsize != minsize) { _minsize = minsize; smartUpdate("minsize", minsize); } } /** * Returns the minimum size of the resizing element. * <p> * Default: 0. */ public int getMinsize() { return _minsize; } /** * Returns the collapsed margins, which is a list of numbers separated by comma. * @see #setCmargins * @see #getDefaultCmargins */ public String getCmargins() { return Utils.intsToString(_cmargins); } /** * Sets the collapsed margins for the element "0,1,2,3" that direction is * "top,left,right,bottom" * * <p> * Default: "3,3,3,3" for center, "0,3,3,0" for east and west, * and "3,0,0,3" for north and south. * @see #getCmargins * @see #getDefaultCmargins */ public void setCmargins(String cmargins) { final int[] imargins = Utils.stringToInts(cmargins, 0); if (!Objects.equals(imargins, _cmargins)) { _cmargins = imargins; smartUpdate("cmargins", getCmargins()); } } /** Returns the default collapsed margin. * @since 5.0.5 */ protected abstract int[] getDefaultCmargins(); /** * Returns whether set the initial display to collapse. * <p>It only applied when {@link #getTitle()} is not null. (since 3.5.0) * <p> * Default: false. */ public boolean isCollapsible() { return _collapsible; } /** * Sets whether set the initial display to collapse. * * <p>It only applied when {@link #getTitle()} is not null. (since 3.5.0) */ public void setCollapsible(boolean collapsible) { if (collapsible != _collapsible) { _collapsible = collapsible; smartUpdate("collapsible", _collapsible); } } /** * Returns whether it is open (i.e., not collapsed. Meaningful only if * {@link #isCollapsible} is not false. * <p> * Default: true. */ public boolean isOpen() { return _open; } /** * Opens or collapses the splitter. Meaningful only if * {@link #isCollapsible} is not false. */ public void setOpen(boolean open) { if (_open != open) { _open = open; smartUpdate("open", open); } } public boolean isSlide() { return _slide; } public void setSlide(boolean slide) { if (_slide != slide) { _slide = slide; smartUpdate("slide", slide); } } /*package*/ boolean isNativeScrollbar() { return Utils.testAttribute(this, "org.zkoss.zul.nativebar", true, true); } public String getZclass() { return _zclass == null ? "z-" + getPosition() : _zclass; } 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 { int size = getChildren().size(); if ((size > 0 && _caption == null) || size > 1) throw new UiException("Only one child and one caption is allowed: " + this); } super.beforeChildAdded(child, refChild); } public void beforeParentChanged(Component parent) { if (parent != null && !(parent instanceof Borderlayout)) throw new UiException("Wrong parent: " + parent); super.beforeParentChanged(parent); } 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; invalidate(); return true; } return false; } return super.insertBefore(child, refChild); } public void onChildRemoved(Component child) { if (child instanceof Caption) { _caption = null; invalidate(); } super.onChildRemoved(child); } protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer) throws java.io.IOException { super.renderProperties(renderer); if (!"normal".equals(_border)) render(renderer, "border", _border); render(renderer, "flex", _flex); render(renderer, "autoscroll", _autoscroll); if (_margins[0] != 0 || _margins[1] != 0 || _margins[2] != 0 || _margins[3] != 0) render(renderer, "margins", getMargins()); render(renderer, "title", _title); if (_maxsize != 2000) renderer.render("maxsize", _maxsize); if (_minsize != 0) renderer.render("minsize", _minsize); render(renderer, "splittable", _splittable); render(renderer, "collapsible", _collapsible); int[] cms = getDefaultCmargins(); for (int j = cms.length; --j >= 0;) if (cms[j] != _cmargins[j]) { render(renderer, "cmargins", getCmargins()); break; } //always generate since different region might have different default if (!_open) renderer.render("open", _open); if (isNativeScrollbar()) renderer.render("_nativebar", true); } /** Processes an AU request. * * <p>Default: in addition to what are handled by {@link org.zkoss.zul.LayoutRegion#service}, * it also handles onOpen. */ 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 if (cmd.equals(Events.ON_SLIDE)) { SlideEvent evt = SlideEvent.getSlideEvent(request); _slide = evt.isSlide(); Events.postEvent(evt); } else super.service(request, everError); } //Cloneable// public Object clone() { final LayoutRegion clone = (LayoutRegion) super.clone(); if (clone._caption != null) clone.afterUnmarshal(); return clone; } private void afterUnmarshal() { for (Iterator it = getChildren().iterator(); it.hasNext();) { final Object child = it.next(); if (child instanceof Caption) { _caption = (Caption) child; break; } } } //Serializable// private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); afterUnmarshal(); } }