com.extjs.gxt.ui.client.widget.ContentPanel.java Source code

Java tutorial

Introduction

Here is the source code for com.extjs.gxt.ui.client.widget.ContentPanel.java

Source

/*
 * Sencha GXT 2.3.1 - Sencha for GWT
 * Copyright(c) 2007-2013, Sencha, Inc.
 * licensing@sencha.com
 * 
 * http://www.sencha.com/products/gxt/license/
 */
package com.extjs.gxt.ui.client.widget;

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style.Direction;
import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.core.DomHelper;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.FxEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.fx.FxConfig;
import com.extjs.gxt.ui.client.util.Format;
import com.extjs.gxt.ui.client.util.IconHelper;
import com.extjs.gxt.ui.client.util.Markup;
import com.extjs.gxt.ui.client.util.Size;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.button.ButtonBar;
import com.extjs.gxt.ui.client.widget.button.ToolButton;
import com.extjs.gxt.ui.client.widget.layout.FlowLayout;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Accessibility;
import com.google.gwt.user.client.ui.Frame;

/**
 * ContentPanel is a component container that has specific functionality and
 * structural components that make it the perfect building block for
 * application-oriented user interfaces. The Panel contains bottom and top
 * toolbars, along with separate header, footer and body sections. It also
 * provides built-in expandable and collapsible behavior, along with a variety
 * of pre-built tool buttons that can be wired up to provide other customized
 * behavior.
 * 
 * Code snippet:
 * 
 * <pre>
 * ContentPanel cp = new ContentPanel();
 * cp.setHeading(&quot;Folder Contents&quot;);
 * cp.setSize(250, 140);
 * cp.setPosition(10, 10);
 * cp.setCollapsible(true);
 * cp.setFrame(true);
 * cp.setBodyStyle(&quot;backgroundColor: white;&quot;);
 * cp.getHeader().addTool(new ToolButton(&quot;x-tool-gear&quot;));
 * cp.getHeader().addTool(new ToolButton(&quot;x-tool-close&quot;));
 * cp.addText(getBogusText());
 * cp.addButton(new Button(&quot;Ok&quot;));
 * cp.setIconStyle(&quot;tree-folder-open&quot;);
 * RootPanel.get().add(cp);
 * cp.layout();
 * </pre>
 * 
 * <dl>
 * <dt><b>Events:</b></dt>
 * 
 * <dd><b>BeforeExpand</b> : ComponentEvent(component)<br>
 * <div>Fires before the panel is expanded. Listeners can cancel the action by
 * calling {@link BaseEvent#setCancelled(boolean)}.</div>
 * <ul>
 * <li>component : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>Expand</b> : ComponentEvent(component)<br>
 * <div>Fires after the panel is expanded</div>
 * <ul>
 * <li>component : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>BeforeCollapse</b> : ComponentEvent(component)<br>
 * <div>Fires before the panel is collapsed. Listeners can cancel the action by
 * calling {@link BaseEvent#setCancelled(boolean)}.</div>
 * <ul>
 * <li>component : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>Collapse</b> : ComponentEvent(component)<br>
 * <div>Fires after the panel is collapsed.</div>
 * <ul>
 * <li>component : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>BeforeClose</b> : ComponentEvent(component)<br>
 * <div>Fires before a content panel is closed. Listeners can cancel the action
 * by calling {@link BaseEvent#setCancelled(boolean)}.</div>
 * <ul>
 * <li>component : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>Close</b> : ComponentEvent(component)<br>
 * <div>Fires after a content panel is closed.</div>
 * <ul>
 * <li>component : this</li>
 * </ul>
 * </dd>
 * </dl>
 * 
 * <dl>
 * <dt>Inherited Events:</dt>
 * <dd>LayoutContainer AfterLayout</dt>
 * <dd>ScrollContainer Scroll</dd>
 * <dd>Container BeforeAdd</dd>
 * <dd>Container Add</dd>
 * <dd>Container BeforeRemove</dd>
 * <dd>Container Remove</dd>
 * <dd>BoxComponent Move</dd>
 * <dd>BoxComponent Resize</dd>
 * <dd>Component Enable</dd>
 * <dd>Component Disable</dd>
 * <dd>Component BeforeHide</dd>
 * <dd>Component Hide</dd>
 * <dd>Component BeforeShow</dd>
 * <dd>Component Show</dd>
 * <dd>Component Attach</dd>
 * <dd>Component Detach</dd>
 * <dd>Component BeforeRender</dd>
 * <dd>Component Render</dd>
 * <dd>Component BrowserEvent</dd>
 * <dd>Component BeforeStateRestore</dd>
 * <dd>Component StateRestore</dd>
 * <dd>Component BeforeStateSave</dd>
 * <dd>Component SaveState</dd>
 * </dl>
 */
public class ContentPanel extends LayoutContainer implements IconSupport {

    protected String bodStyle;
    protected El body, bwrap;
    protected String bwrapStyle;
    protected String collapseStyle;

    protected ButtonBar fbar;
    protected boolean frame;
    protected Header head;
    protected String headerStyle, footerStyle;
    protected String headerTextStyle;
    protected String tbarStyle, bbarStyle;
    private boolean animating;
    private boolean animCollapse = true;
    private boolean bodyBorder = true;
    private String bodyStyle, bodyStyleName;
    private Component bottomComponent;
    private HorizontalAlignment buttonAlign = HorizontalAlignment.RIGHT;
    private ToolButton collapseBtn;
    private boolean collapsed, hideCollapseTool;
    private boolean collapsible;
    private El foot, tbar, bbar;
    private boolean footer, titleCollapse;
    private boolean headerVisible = true;
    private int minButtonWidth = 75;

    private Component topComponent;

    /**
     * Creates a new panel instance.
     */
    public ContentPanel() {
        baseStyle = "x-panel";
        fbar = new ButtonBar();
        fbar.setParent(this);
        fbar.setMinButtonWidth(minButtonWidth);
        fbar.setAlignment(buttonAlign);
        head = new Header();
        head.setParent(this);
        disabledStyle = null;
        setDeferHeight(true);
        getFocusSupport().setIgnore(false);
    }

    /**
     * Creates a new content panel.
     * 
     * @param layout the panel's layout
     */
    public ContentPanel(Layout layout) {
        this();
        setLayout(layout);
    }

    /**
     * Adds a button the the panel.
     * 
     * @param button the button to add
     */
    public void addButton(Button button) {
        fbar.add(button);
    }

    /**
     * Collapses the panel body so that it becomes hidden. Fires the
     * <i>BeforeCollapse</i> before collapsing, then the <i>Collapse</i> event
     * after collapsing.
     */
    public void collapse() {
        if (rendered) {
            if (!collapsed && !animating && fireEvent(Events.BeforeCollapse)) {
                hideShadow();
                onCollapse();
            }
        } else {
            collapsed = true;
        }
    }

    /**
     * Expands the panel body so that it becomes visible. Fires the
     * <i>BeforeExpand</i> before expanding, then the <i>Expand</i> event after
     * expanding.
     */
    public void expand() {
        if (rendered) {
            if (collapsed && !animating && fireEvent(Events.BeforeExpand)) {
                hideShadow();
                onExpand();
            }
        } else {
            collapsed = false;
        }
    }

    /**
     * Returns true if animation is enabled for expand / collapse.
     * 
     * @return the animCollapse true for animations
     */
    public boolean getAnimCollapse() {
        return animCollapse;
    }

    /**
     * Returns the panel's body element.
     * 
     * @return the body
     */
    public El getBody() {
        return body;
    }

    /**
     * Returns true if the body border is enabled.
     * 
     * @return the body border state
     */
    public boolean getBodyBorder() {
        return bodyBorder;
    }

    /**
     * Returns the body style.
     * 
     * @return the body style
     */
    public String getBodyStyle() {
        return bodyStyle;
    }

    /**
     * Returns the panel's bottom component.
     * 
     * @return the bottom component
     */
    public Component getBottomComponent() {
        return bottomComponent;
    }

    /**
     * Returns the panel's button alignment.
     * 
     * @return the button alignment
     */
    public HorizontalAlignment getButtonAlign() {
        return buttonAlign;
    }

    public ButtonBar getButtonBar() {
        return fbar;
    }

    /**
     * Returns the panel's collapse button.
     * 
     * @return the collapse button
     */
    public ToolButton getCollapseBtn() {
        return collapseBtn;
    }

    /**
     * Returns true if the panel is collapsible.
     * 
     * @return the collapsible state
     */
    public boolean getCollapsible() {
        return collapsible;
    }

    /**
     * Provides access to internal elements.
     * <p>
     * Valid values are:
     * <ul>
     * <li>"body"</li>
     * <li>"header"</li>
     * <li>"bwrap"</li>
     * </ul>
     * </p>
     * 
     * @param name the element name
     * @return the element
     */
    public Element getElement(String name) {
        if (name.equals("header")) {
            return head.getElement();
        } else if (name.equals("bwrap")) {
            return bwrap.dom;
        } else if (name.equals("body")) {
            return body.dom;
        }
        return null;
    }

    /**
     * Returns true if framing is enabled.
     * 
     * @return the frame state
     */
    public boolean getFrame() {
        return frame;
    }

    /**
     * Returns the height in pixels of the framing elements of this panel
     * (including any top and bottom bars and header and footer elements, but not
     * including the body height). To retrieve the body height see
     * {@link #getInnerHeight}.
     * 
     * @return the frame height
     */
    public int getFrameHeight() {
        int h = el().getFrameWidth("tb") + bwrap.getFrameWidth("tb");
        if (frame) {
            Element hd = el().dom.getFirstChildElement().cast();
            Element ft = bwrap.dom.getLastChild().cast();
            h += (fly(hd).getHeight() + fly(ft).getHeight());
            Element mc = bwrap.subChild(3).dom;
            h += fly(mc).getFrameWidth("tb");
        } else {
            if (head != null) {
                h += head.getOffsetHeight();
            }
            if (foot != null) {
                h += foot.getHeight();
            }
        }
        h += (tbar != null ? tbar.getHeight() : 0) + (bbar != null ? bbar.getHeight() : 0);
        return h;
    }

    /**
     * Returns the width in pixels of the framing elements of this panel (not
     * including the body width). To retrieve the body width see
     * {@link #getInnerWidth}.
     * 
     * @return The frame width
     */
    public int getFrameWidth() {
        int w = el().getFrameWidth("lr") + bwrap.getFrameWidth("lr");
        if (frame) {
            Element l = bwrap.dom.getFirstChildElement().cast();
            w += (fly(l).getFrameWidth("l") + fly(l).firstChild().getFrameWidth("r"));
            Element mc = bwrap.subChild(3).dom;
            w += fly(mc).getFrameWidth("lr");
        }
        return w;
    }

    /**
     * Returns the panel's header.
     * 
     * @return the header
     */
    public Header getHeader() {
        return head;
    }

    /**
     * Returns the panel's heading.
     * 
     * @return the heading
     */
    public SafeHtml getHeadingHtml() {
        return head.getHtml();
    }

    /**
     * Returns the panel's icon style.
     * 
     * @return the icon style
     */
    public AbstractImagePrototype getIcon() {
        return head.getIcon();
    }

    /**
     * Returns the height in pixels of the body element (not including the height
     * of any framing elements). For the frame height see {@link #getFrameHeight}.
     * 
     * @return the inner height
     */
    public int getInnerHeight() {
        return getHeight() - getFrameHeight();
    }

    /**
     * Returns the width in pixels of the body element (not including the width of
     * any framing elements). For the frame width see {@link #getFrameWidth}.
     * 
     * @return the body width
     */
    public int getInnerWidth() {
        return getWidth() - getFrameWidth();
    }

    @Override
    public El getLayoutTarget() {
        return body;
    }

    public int getMinButtonWidth() {
        return minButtonWidth;
    }

    /**
     * @return the titleCollapse
     */
    public boolean getTitleCollapse() {
        return titleCollapse;
    }

    /**
     * Returns the panels top component.
     * 
     * @return the top component
     */
    public Component getTopComponent() {
        return topComponent;
    }

    /**
     * Returns true if the panel is collapsed.
     * 
     * @return the collapsed state
     */
    public boolean isCollapsed() {
        return collapsed;
    }

    /**
     * Returns <code>true</code> if the panel is expanded.
     * 
     * @return the expand state
     */
    public boolean isExpanded() {
        return !isCollapsed();
    }

    /**
     * Returns true if the footer is enabled.
     * 
     * @return the show footer state
     */
    public boolean isFooter() {
        return footer;
    }

    /**
     * Returns true if the header is visible.
     * 
     * @return the header visible state
     */
    public boolean isHeaderVisible() {
        return headerVisible;
    }

    /**
     * Returns true if the collapse tool is hidden.
     * 
     * @return the hide collapse tool state
     */
    public boolean isHideCollapseTool() {
        return hideCollapseTool;
    }

    @Override
    public void onComponentEvent(ComponentEvent ce) {
        super.onComponentEvent(ce);
        if (ce.getEventTypeInt() == Event.ONCLICK) {
            onClick(ce);
        }
    }

    /**
     * Sets whether expand and collapse is animating (defaults to true,
     * pre-render).
     * 
     * @param animCollapse true to enable animations
     */
    public void setAnimCollapse(boolean animCollapse) {
        assertPreRender();
        this.animCollapse = animCollapse;
    }

    /**
     * True to display the borders of the panel's body element, false to hide them
     * (defaults to true, pre-render). Only applies to non-framed panels.
     * 
     * @param bodyBorder true for a body border
     */
    public void setBodyBorder(boolean bodyBorder) {
        assertPreRender();
        this.bodyBorder = bodyBorder;
    }

    /**
     * Custom CSS styles to be applied to the body element in the format expected
     * by {@link El#applyStyles} (pre-render).
     * 
     * @param bodyStyle the body style
     */
    public void setBodyStyle(String bodyStyle) {
        assertPreRender();
        this.bodyStyle = bodyStyle;
    }

    /**
     * A style name that is added to the panel's body element (pre-render).
     * 
     * @param style the style name
     */
    public void setBodyStyleName(String style) {
        assertPreRender();
        this.bodyStyleName = style;
    }

    /**
     * Sets the panel's bottom component (pre-render). The component's natural
     * height will be used and will not be changed by the panel.
     * 
     * @param bottomComponent the bottom component
     */
    public void setBottomComponent(Component bottomComponent) {
        assertPreRender();
        if (this.bottomComponent != null) {
            Component c = this.bottomComponent;
            this.bottomComponent = null;
            c.removeFromParent();
        }
        if (bottomComponent != null) {
            this.bottomComponent = bottomComponent;
            this.bottomComponent.setParent(this);
        }
    }

    /**
     * Sets the button alignment of any buttons added to this panel (defaults to
     * RIGHT, pre-render).
     * 
     * @param buttonAlign the button alignment
     */
    public void setButtonAlign(HorizontalAlignment buttonAlign) {
        assertPreRender();
        this.buttonAlign = buttonAlign;
        fbar.setAlignment(buttonAlign);
    }

    /**
     * True to make the panel collapsible and have the expand/collapse toggle
     * button automatically rendered into the header tool button area, false to
     * keep the panel statically sized with no button (defaults to false,
     * pre-render).
     * 
     * @param collapsible the collapsible to set
     */
    public void setCollapsible(boolean collapsible) {
        assertPreRender();
        this.collapsible = collapsible;
    }

    /**
     * Sets the panel's expand state.
     * 
     * @param expanded <code>true<code> true to expand
     */
    public void setExpanded(boolean expanded) {
        if (expanded) {
            expand();
        } else {
            collapse();
        }
    }

    /**
     * True to create the footer element explicitly, false to skip creating it
     * (pre-render). By default, when footer is not specified, if one or more
     * buttons have been added to the panel the footer will be created
     * automatically, otherwise it will not.
     * 
     * @param footer the footer state
     */
    public void setFooter(boolean footer) {
        assertPreRender();
        this.footer = footer;
    }

    /**
     * True to render the panel with custom rounded borders, false to render with
     * plain 1px square borders (defaults to false, pre-render).
     * 
     * @param frame true to use the frame style
     */
    public void setFrame(boolean frame) {
        assertPreRender();
        this.frame = frame;
    }

    /**
     * True to create the header element explicitly, false to skip creating it
     * (defaults to true, pre-render). By default, when header is not specified,
     * if a {@link #setHeadingHtml(String)} is set the header will be created
     * automatically, otherwise it will not. If a title is set but header is
     * explicitly set to false, the header will not be rendered.
     * 
     * @param headerVisible true to show the header
     */
    public void setHeaderVisible(boolean headerVisible) {
        assertPreRender();
        this.headerVisible = headerVisible;
    }

    /**
     * Sets the title text for the panel as HTML.
     * 
     * @param html the title text as HTML
     */
    public void setHeadingHtml(SafeHtml html) {
        head.setHtml(html);
    }

    /**
     * Sets the title text for the panel as HTML.
     * 
     * @param text the title text
     */
    public void setHeadingText(String text) {
        head.setText(text);
    }

    /**
     * Sets whether the collapse tool should be displayed when the panel is
     * collapsible.
     * 
     * @param hideCollapseTool true if the tool is hidden
     */
    public void setHideCollapseTool(boolean hideCollapseTool) {
        this.hideCollapseTool = hideCollapseTool;
        if (rendered) {
            collapseBtn.setVisible(!hideCollapseTool);
        }
    }

    /**
     * Sets the item's icon style. The style name should match a CSS style that
     * specifies a background image using the following format:
     * 
     * <pre>
     * &lt;code&gt; .my-icon { background: url(images/icons/my-icon.png) no-repeat
     * center left !important; } &lt;/code&gt;
     * </pre>
     * 
     * @param icon the icon
     */
    public void setIcon(AbstractImagePrototype icon) {
        head.setIcon(icon);
    }

    public void setIconStyle(String icon) {
        setIcon(IconHelper.create(icon));
    }

    public void setMinButtonWidth(int minButtonWidth) {
        this.minButtonWidth = minButtonWidth;
        fbar.setMinButtonWidth(minButtonWidth);
    }

    /**
     * True to allow expanding and collapsing the panel (when {@link #collapsible}
     * = true) by clicking anywhere in the header bar, false to allow it only by
     * clicking to tool button (defaults to false, pre-render).
     * 
     * @param titleCollapse the titleCollapse to set
     */
    public void setTitleCollapse(boolean titleCollapse) {
        assertPreRender();
        this.titleCollapse = titleCollapse;
    }

    /**
     * Sets the panel's top component (pre-render).
     * 
     * @param topComponent the component
     */
    public void setTopComponent(Component topComponent) {
        assertPreRender();
        if (this.topComponent != null) {
            Component c = this.topComponent;
            this.topComponent = null;
            c.removeFromParent();
        }
        if (topComponent != null) {
            this.topComponent = topComponent;
            this.topComponent.setParent(this);
        }
    }

    /**
     * Adds the content from the given url.
     * 
     * @param url the url
     * @return the new frame instance
     */
    public Frame setUrl(String url) {
        Frame f = new Frame(url);
        f.getElement().setPropertyInt("frameBorder", 0);
        f.setSize("100%", "100%");
        setLayout(new FlowLayout());
        removeAll();
        add(f);
        layout();
        return f;
    }

    protected Size adjustBodySize() {
        return new Size(0, 0);
    }

    protected void afterCollapse() {
        addStyleName(collapseStyle);
        removeStyleName(baseStyle + "-animated");
        collapsed = true;
        animating = false;

        for (Component c : getItems()) {
            if (!c.hidden && c.isRendered()) {
                c.notifyHide();
            }
        }

        if (topComponent != null && topComponent.isRendered()) {
            topComponent.notifyHide();
        }
        if (bottomComponent != null && bottomComponent.isRendered()) {
            bottomComponent.notifyHide();
        }
        if (fbar != null && fbar.isRendered()) {
            fbar.notifyHide();
        }

        sync(true);

        if (GXT.isAriaEnabled()) {
            Accessibility.setState(getElement(), "aria-expanded", "false");
            collapseBtn.setTitle(GXT.MESSAGES.panel_expandPanel());
        }

        ComponentEvent ce = new ComponentEvent(this);
        fireEvent(Events.Collapse, ce);
    }

    protected void afterExpand() {
        removeStyleName(collapseStyle);
        removeStyleName(baseStyle + "-animated");
        collapsed = false;
        animating = false;

        for (Component c : getItems()) {
            if (!c.hidden && c.isRendered()) {
                c.notifyShow();
            }
        }

        if (topComponent != null && topComponent.isRendered()) {
            topComponent.notifyShow();
        }
        if (bottomComponent != null && bottomComponent.isRendered()) {
            bottomComponent.notifyShow();
        }
        if (fbar != null && fbar.isRendered()) {
            fbar.notifyShow();
        }

        sync(true);

        if (GXT.isAriaEnabled()) {
            Accessibility.setState(getElement(), "aria-expanded", "true");
            collapseBtn.setTitle(GXT.MESSAGES.panel_collapsePanel());
        }

        ComponentEvent ce = new ComponentEvent(this);
        fireEvent(Events.Expand, ce);
    }

    protected void createStyles(String baseStyle) {
        headerStyle = baseStyle + "-header";
        headerTextStyle = baseStyle + "-header-text";
        bwrapStyle = baseStyle + "-bwrap";
        tbarStyle = baseStyle + "-tbar";
        bodStyle = baseStyle + "-body";
        bbarStyle = baseStyle + "-bbar";
        footerStyle = baseStyle + "-footer";
        collapseStyle = baseStyle + "-collapsed";
    }

    @Override
    protected void doAttachChildren() {
        super.doAttachChildren();
        if (head.isRendered()) {
            ComponentHelper.doAttach(head);
        }
        if (fbar.isRendered()) {
            ComponentHelper.doAttach(fbar);
        }
        ComponentHelper.doAttach(topComponent);
        ComponentHelper.doAttach(bottomComponent);
    }

    @Override
    protected void doDetachChildren() {
        super.doDetachChildren();
        if (head.isRendered()) {
            ComponentHelper.doDetach(head);
        }
        ComponentHelper.doDetach(fbar);
        ComponentHelper.doDetach(topComponent);
        ComponentHelper.doDetach(bottomComponent);
    }

    // internal, used for performance reasons
    protected Size getFrameSize() {
        Size elFrameSize = el().getFrameSize();
        Size bwrapFrameSize = bwrap.getFrameSize();

        Size mcFrameSize = null;
        if (frame) {
            Element mc = bwrap.subChild(3).dom;
            mcFrameSize = fly(mc).getFrameSize();
        }

        int w = elFrameSize.width + bwrapFrameSize.width;
        if (frame) {
            Element l = bwrap.dom.getFirstChildElement().cast();
            w += (fly(l).getFrameWidth("l") + fly(l).firstChild().getFrameWidth("r"));
            w += mcFrameSize.width;
        }

        int h = elFrameSize.height + bwrapFrameSize.height;
        if (frame) {
            Element hd = el().dom.getFirstChildElement().cast();
            Element ft = bwrap.dom.getLastChild().cast();
            h += (fly(hd).getHeight() + fly(ft).getHeight());
            h += mcFrameSize.height;
        } else {
            if (head != null) {
                h += head.getOffsetHeight();
            }
            if (foot != null) {
                h += foot.getHeight();
            }
        }
        h += (tbar != null ? tbar.getHeight() : 0) + (bbar != null ? bbar.getHeight() : 0);

        return new Size(w, h);
    }

    protected void initTools() {
        if (collapsible && !hideCollapseTool) {
            collapseBtn = new ToolButton("x-tool-toggle");
            if (GXT.isAriaEnabled()) {
                collapseBtn.setTitle(GXT.MESSAGES.panel_collapsePanel());
            }
            collapseBtn.addListener(Events.Select, new Listener<ComponentEvent>() {
                public void handleEvent(ComponentEvent ce) {
                    ce.stopEvent();
                    setExpanded(!isExpanded());
                }
            });
            head.addTool(collapseBtn);
        }
    }

    protected void layoutBars() {
        if (footer && !fbar.layoutExecuted) {
            fbar.layout(false);
        }
        if (topComponent instanceof Container<?> && !((Container<?>) topComponent).layoutExecuted) {
            ((Container<?>) topComponent).layout(false);
        }
        if (bottomComponent instanceof Container<?> && !((Container<?>) bottomComponent).layoutExecuted) {
            ((Container<?>) bottomComponent).layout(false);
        }
    }

    @Override
    protected void notifyHide() {
        if (!collapsed) {
            super.notifyHide();

            if (topComponent != null && topComponent.isRendered()) {
                topComponent.notifyHide();
            }
            if (bottomComponent != null && bottomComponent.isRendered()) {
                bottomComponent.notifyHide();
            }
            if (fbar != null && fbar.isRendered()) {
                fbar.notifyHide();
            }
        }
        if (headerVisible && head != null) {
            head.notifyHide();
        }
    }

    @Override
    protected void notifyShow() {
        if (!collapsed) {
            super.notifyShow();

            if (topComponent != null && topComponent.isRendered()) {
                topComponent.notifyShow();
            }
            if (bottomComponent != null && bottomComponent.isRendered()) {
                bottomComponent.notifyShow();
            }
            if (fbar != null && fbar.isRendered()) {
                fbar.notifyShow();
            }
        }
        if (headerVisible && head != null) {
            head.notifyShow();
        }
    }

    protected void onClick(ComponentEvent ce) {
        if (collapsible && titleCollapse && head != null && ce.within(head.getElement())) {
            setExpanded(!isExpanded());
        }
    }

    protected void onCollapse() {
        if (animCollapse) {
            animating = true;
            addStyleName(baseStyle + "-animated");
            bwrap.slideOut(Direction.UP, new FxConfig(300, new Listener<FxEvent>() {
                public void handleEvent(FxEvent fe) {
                    afterCollapse();
                }
            }));
        } else {
            bwrap.hide();
            afterCollapse();
        }
    }

    protected void onDisable() {
        mask();
        super.onDisable();
    }

    protected void onEnable() {
        unmask();
        super.onEnable();
    }

    protected void onExpand() {
        if (animCollapse) {
            animating = true;
            addStyleName(baseStyle + "-animated");
            bwrap.slideIn(Direction.DOWN, new FxConfig(300, new Listener<FxEvent>() {
                public void handleEvent(FxEvent fe) {
                    afterExpand();
                }
            }));
        } else {
            bwrap.show();
            afterExpand();
        }
    }

    @Override
    protected void onRender(Element parent, int pos) {
        super.onRender(parent, pos);

        if (fbar.getItemCount() > 0) {
            footer = true;
        }

        if (frame) {
            String s = Format.substitute(Markup.BBOX, baseStyle);
            DomHelper.insertHtml("afterBegin", el().dom, s);

            head.baseStyle = headerStyle;
            head.setTextStyle(headerTextStyle);
            initTools();
            head.render(el().dom);
            el().subChild(3).dom.appendChild(head.getElement());
            bwrap = el().createChild("<div role=presentation class='" + bwrapStyle + "'></div>");

            Element bw = bwrap.dom;
            Element ml = DOM.getChild(el().dom, 1);
            Element bl = DOM.getChild(el().dom, 2);
            DOM.appendChild(bw, ml);
            DOM.appendChild(bw, bl);

            Element mc = fly(bw).getSubChild(3);

            if (topComponent != null) {
                tbar = fly(mc).createChild("<div role=presentation class=" + tbarStyle + "></div>");
            }
            body = fly(mc).createChild("<div role=presentation class=" + bodStyle + "></div>");
            if (bottomComponent != null) {
                bbar = fly(mc).createChild("<div role=presentation class=" + bbarStyle + "></div>");
            }

            El e = fly(bw).lastChild().firstChild().firstChild();
            foot = e.createChild("<div role=presentation class=" + footerStyle + "></div>");

        } else {
            head.baseStyle = headerStyle;
            head.setTextStyle(headerTextStyle);
            initTools();
            head.render(el().dom);
            bwrap = el().createChild("<div role=presentation class=" + bwrapStyle + "></div>");

            Element bw = bwrap.dom;
            if (topComponent != null) {
                tbar = fly(bw).createChild("<div role=presentation class=" + tbarStyle + "></div>");
            }
            body = fly(bw).createChild("<div role=presentation class=" + bodStyle + "></div>");
            if (bottomComponent != null) {
                bbar = fly(bw).createChild("<div role=presentation class=" + bbarStyle + "></div>");
            }
            foot = fly(bw).createChild("<div role=presentation class=" + footerStyle + "></div>");
        }

        if (!headerVisible) {
            head.hide();
            body.addStyleName(bodStyle + "-noheader");
            if (tbar != null) {
                tbar.addStyleName(tbarStyle + "-noheader");
            }
        }

        if (footer && fbar.getItemCount() > 0) {
            Element div = DOM.createDiv();
            fly(div).addStyleName("x-panel-btns");
            foot.appendChild(div);
            fbar.render(div);
            Element clearDiv = DOM.createDiv();
            clearDiv.setClassName("x-clear");
            div.appendChild(clearDiv);
        } else if (!footer) {
            bwrap.lastChild().addStyleName(baseStyle + "-nofooter");
        }

        if (!bodyBorder) {
            el().addStyleName(baseStyle + "-noborder");
            body.addStyleName(bodStyle + "-noborder");
            if (tbar != null) {
                tbar.addStyleName(tbarStyle + "-noborder");
            }
            if (bbar != null) {
                bbar.addStyleName(bbarStyle + "-noborder");
            }
        }

        if (bodyStyle != null) {
            body.applyStyles(bodyStyle);
        }

        if (bodyStyleName != null) {
            body.addStyleName(bodyStyleName);
        }

        if (headerVisible) {
            head.disableTextSelection(true);
        }

        if (topComponent != null) {
            topComponent.render(tbar.dom);
        }

        if (bottomComponent != null) {
            bottomComponent.render(bbar.dom);
        }

        if (titleCollapse) {
            head.setStyleAttribute("cursor", "pointer");
            sinkEvents(Event.ONCLICK);
        }

        if (collapsible && GXT.isAriaEnabled()) {
            Accessibility.setState(getElement(), "aria-expanded", "true");
        }

        if (GXT.isAriaEnabled()) {
            Accessibility.setRole(getElement(), "region");
            if (head != null) {
                getAriaSupport().setLabelledBy(head.getId() + "-label");
            }
        }

        // early render
        layoutBars();

        if (collapsed) {
            boolean anim = animCollapse;
            collapsed = false;
            setAnimCollapse(false);
            collapse();
            setAnimCollapse(anim);
        }
    }

    @Override
    protected void onResize(int width, int height) {
        super.onResize(width, height);
        Size frameSize = getFrameSize();
        Size adjustBodySize = adjustBodySize();
        if (isAutoWidth()) {
            getLayoutTarget().setWidth("auto");
        } else if (width != -1) {
            width -= frameSize.width;
            if (tbar != null) {
                tbar.setWidth(width, true);
                if (topComponent instanceof BoxComponent) {
                    ((BoxComponent) topComponent).setWidth(width);
                }
            }
            if (bbar != null) {
                bbar.setWidth(width, true);
                if (bottomComponent instanceof BoxComponent) {
                    ((BoxComponent) bottomComponent).setWidth(width);
                }
            }
            if (fbar.isRendered()) {
                fbar.setWidth(width - fbar.el().getParent().getFrameWidth("lr"));
            }
            getLayoutTarget().setWidth(width - adjustBodySize.width, true);
        }
        if (isAutoHeight()) {
            getLayoutTarget().setHeight("auto");
        } else if (height != -1) {
            height -= frameSize.height;
            getLayoutTarget().setHeight(height - adjustBodySize.height, true);
        }
        if (mask) {
            mask(maskMessage, maskMessageStyleName);
        }
    }

    @Override
    protected boolean remove(Component item) {
        if (item == topComponent) {
            setTopComponent(null);
            return true;
        } else if (item == bottomComponent) {
            setBottomComponent(null);
            return true;
        }
        return super.remove(item);
    }

}