rocket.widget.client.Panel.java Source code

Java tutorial

Introduction

Here is the source code for rocket.widget.client.Panel.java

Source

/*
 * Copyright Miroslav Pokorny
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package rocket.widget.client;

import java.util.Iterator;

import rocket.collection.client.CollectionsHelper;
import rocket.event.client.Event;
import rocket.event.client.EventListener;
import rocket.util.client.Checker;
import rocket.util.client.Utilities;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.WidgetCollection;

/**
 * Convenient base class for any Panel implementation. It includes support for
 * hijacking an element sourced from the dom.
 * 
 * Creating a new panel including a new element.
 * <ul>
 * <li>{@link #beforeCreatePanelElement()}</li>
 * <li>{@link #createPanelElement()}</li>
 * <li>{@link #afterCreatePanelElement()}</li>
 * <li>{@link #applyStyleName()} Override this to do nothing if this panel has
 * no initial style.</li>
 * </li>
 * 
 * Create a new panel with an element hijacked from the dom.
 * <ul>
 * <li>{@link #checkElement(Element)} Check that the element is of the correct
 * type</li>
 * <li>{@link #beforeCreatePanelElement()}</li>
 * <li>{@link #setElement( Element from constructor )}</li>
 * <li>{@link #afterCreatePanelElement()}</li>
 * <li>{@link #applyStyleName()} Override this to do nothing if this panel has
 * no initial style.</li>
 * </li>
 * 
 * The initial style of the root element from this panel is taken from
 * {@link #getInitialStyleName()}
 * 
 * @author Miroslav Pokorny (mP)
 */
abstract public class Panel extends com.google.gwt.user.client.ui.Panel implements HasWidgets, EventListener {

    /**
     * This constructor should be used when creating a new panel from scratch
     */
    protected Panel() {
        prepare();
    }

    protected void prepare() {
        this.setWidgetCollection(this.createWidgetCollection());

        this.beforeCreatePanelElement();
        this.setElement(this.createPanelElement());
        this.afterCreatePanelElement();
        this.applyStyleName();
    }

    /**
     * This constructor should be used when creating a new widget but using an
     * element taken from the dom.
     * 
     * @param element
     */
    protected Panel(final Element element) {
        prepare(element);
    }

    protected void prepare(final Element element) {
        this.checkElement(element);

        this.setWidgetCollection(this.createWidgetCollection());

        final Hijacker hijacker = new Hijacker(element);
        hijacker.save();

        this.beforeCreatePanelElement();
        this.setElement(element);
        this.afterCreatePanelElement();

        RootPanel.get().add(this);
        hijacker.restore();

        this.applyStyleName();
    }

    /**
     * This special constructor should only be used by {@link CompositePanel}.
     * 
     * @param ignored
     */
    Panel(final boolean ignored) {
        super();
    }

    /**
     * This method is called when the lets wrap an existing DOM element
     * constructor is used. Sub classes should check that the element is the
     * appropriate type.
     * 
     * @param element
     */
    abstract protected void checkElement(Element element);

    protected void beforeCreatePanelElement() {
        final int eventBitsSunk = this.getSunkEventsBitMask();

        final EventListenerDispatcher dispatcher = this.createEventListenerDispatcher();
        this.setEventListenerDispatcher(dispatcher);
        dispatcher.prepareListenerCollections(eventBitsSunk);
    }

    /**
     * Sub classes must create the root panel element.
     * 
     * @return The new element
     */
    abstract protected Element createPanelElement();

    /**
     * This method provides an opportunity for sub classes to do stuff after the
     * element has been created.
     */
    protected void afterCreatePanelElement() {
    }

    protected void applyStyleName() {
        this.setStyleName(this.getInitialStyleName());
    }

    /**
     * Sub classes must override this method to return the stylename applied to
     * the primary element.
     * 
     * @return
     */
    abstract protected String getInitialStyleName();

    @Override
    protected void onAttach() {
        super.onAttach();

        this.doSinkEvents();
    }

    /**
     * Sets event sinking and listener.
     */
    protected void doSinkEvents() {
        final Element original = this.getElement();
        final Element target = this.getSunkEventsTarget();

        if (original != target) {
            DOM.sinkEvents(original, 0);
        }
        final int mask = this.getSunkEventsBitMask();
        if (0 != mask) {
            DOM.sinkEvents(target, mask);
            DOM.setEventListener(target, this);
        }
    }

    /**
     * Sub classes must return a bit mask that indicates which events are being
     * sunk.
     * 
     * @return
     */
    abstract protected int getSunkEventsBitMask();

    protected Element getSunkEventsTarget() {
        return this.getElement();
    }

    /**
     * The complement of onAttach. This method removes the event listener for
     * the sunk event target.
     */
    protected void onDetach() {
        if (0 != this.getSunkEventsBitMask()) {
            DOM.setEventListener(this.getSunkEventsTarget(), this);
        }
        super.onDetach();
    }

    /**
     * Returns the number of widgets within this panel.
     * 
     * @return
     */
    public int getWidgetCount() {
        return this.getWidgetCollection().size();
    }

    public int indexOf(final Widget widget) {
        return this.getWidgetCollection().indexOf(widget);
    }

    /**
     * Retrieves a widget within this panel.
     * 
     * @param index
     * @return
     */
    public Widget get(final int index) {
        return this.getWidgetCollection().get(index);
    }

    /**
     * Adds a new widget to the end of this panel.
     */
    public void add(final Widget widget) {
        this.insert(widget, this.getWidgetCount());
    }

    /**
     * Sub-classes need to insert the given widget into the DOM
     * 
     * @param widget
     * @param indexBefore
     */
    public void insert(final Widget widget, int indexBefore) {
        if (widget.getParent() != null) {
            throw new IllegalArgumentException(
                    "The parameter:widget already has a parent, remove from that first and then add/insert again to this "
                            + this.getClass().getName());
        }

        this.insert0(widget, indexBefore);
        this.adopt(widget);
        this.getWidgetCollection().insert(widget, indexBefore);
    }

    protected void insert0(final Widget widget, int indexBefore) {
        Checker.notNull("parameter:widget", widget);
        this.insert0(widget.getElement(), indexBefore);
    }

    /**
     * Sub-classes need to create/find the element which will become the parent
     * of the Widget's element
     * 
     * @param element
     * @param indexBefore
     */
    protected abstract void insert0(Element element, int indexBefore);

    /**
     * Attempts to remove an existing widget from this panel if it is a child.
     * 
     * @return true if the widget was a child and was successfully removed,
     *         otehrwise returns false.
     */
    public boolean remove(final Widget widget) {
        Checker.notNull("parameter:widget", widget);

        boolean removed = false;
        final WidgetCollection widgets = this.getWidgetCollection();
        final int index = widgets.indexOf(widget);
        if (-1 != index) {
            this.remove(index);
            removed = true;
        }

        return removed;
    }

    /**
     * Removes the widget at the given slot.
     * 
     * @param index
     */
    public boolean remove(final int index) {
        final WidgetCollection widgets = this.getWidgetCollection();

        final Widget widget = widgets.get(index);
        this.remove0(widget.getElement(), index);// cleanup opportunity
        this.orphan(widget);
        widgets.remove(index);

        return true;
    }

    protected void remove0(final Widget widget, final int index) {
        Checker.notNull("parameter:widget", widget);
        this.remove0(widget.getElement(), index);
    }

    /**
     * Cleanup opportunity for sub-classes to remove other outstanding elements
     * from the dom.
     * 
     * @param element
     * @param index
     */
    protected abstract void remove0(Element element, int index);

    /**
     * Clears or removes all widgets from this panel.
     */
    public void clear() {
        CollectionsHelper.removeAll(this.iterator());
    }

    /**
     * Returns an iterator which may be used to visit all the widgets belonging
     * to this panel.
     */
    public Iterator<Widget> iterator() {
        return this.getWidgetCollection().iterator();
    }

    /**
     * This collection includes all the widgest that belong to each of the
     * individual cells.
     */
    private WidgetCollection widgetCollection;

    protected WidgetCollection getWidgetCollection() {
        Checker.notNull("field:widgetCollection", widgetCollection);
        return widgetCollection;
    }

    protected void setWidgetCollection(final WidgetCollection widgetCollection) {
        Checker.notNull("parameter:widgetCollection", widgetCollection);
        this.widgetCollection = widgetCollection;
    }

    protected WidgetCollection createWidgetCollection() {
        return new WidgetCollection(this);
    }

    /**
     * Dispatches the and fires the appropriate listeners based on the event
     * type
     */
    public void onBrowserEvent(final com.google.gwt.user.client.Event rawEvent) {
        Event event = null;
        try {
            event = Event.getEvent(rawEvent);
            event.setWidget(this);
            this.onBrowserEvent(event);
        } finally {
            Utilities.destroyIfNecessary(event);
        }
    }

    public void onBrowserEvent(final Event event) {
        this.getEventListenerDispatcher().onBrowserEvent(event);
    }

    /**
     * The dispatcher that fires the appropriate listener event for any
     * registered event listeners.
     */
    private EventListenerDispatcher eventListenerDispatcher;

    protected EventListenerDispatcher getEventListenerDispatcher() {
        Checker.notNull("field:eventListenerDispatcher", this.eventListenerDispatcher);
        return this.eventListenerDispatcher;
    }

    protected void setEventListenerDispatcher(final EventListenerDispatcher eventListenerDispatcher) {
        Checker.notNull("parameter:eventListenerDispatcher", eventListenerDispatcher);
        this.eventListenerDispatcher = eventListenerDispatcher;
    }

    protected EventListenerDispatcher createEventListenerDispatcher() {
        return new EventListenerDispatcher();
    }
}