/** * Copyright 2010 Google Inc. * * 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 org.waveprotocol.wave.client.widget.common; import com.google.common.base.Preconditions; import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.DoubleClickEvent; import com.google.gwt.event.dom.client.DoubleClickHandler; import com.google.gwt.event.dom.client.HasClickHandlers; import com.google.gwt.event.dom.client.HasDoubleClickHandlers; import com.google.gwt.event.dom.client.HasMouseDownHandlers; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.WidgetCollection; import org.waveprotocol.wave.client.common.util.LogicalPanel; /** * A generic panel for use by UiBinder templates, for widgets that require more * powerful control over their DOM and/or child lifecycle. * * Like {@link HTMLPanel}, its state can be defined in UiBinder using regular * HTML, allowing child widgets to be attached statically at arbitrary * locations. Unlike HTMLPanel, however, it also exposes the ability to attach * child widgets <em>dynamically</em> at arbitrary locations. It also exposes * the ability to adopt widgets already in the DOM (and, symmetrically, to * orphan them). * */ public class ImplPanel extends HTMLPanel implements LogicalPanel, HasClickHandlers, HasDoubleClickHandlers, HasMouseDownHandlers { /** * Creates an panel with some initial inner-HTML. * * @param html initial inner contents */ public ImplPanel(String html) { super(html); } /** * Adds a widget to this panel. */ @Override public void add(Widget child) { add(child, getElement()); } // Expose proteced method. @Override public void add(Widget child, com.google.gwt.user.client.Element container) { super.add(child, container); } /** * Adds a widget, inserting it as the first child in the DOM structure. * * @param child widget to add */ public void insertFirst(Widget child) { insertBefore(child, getElement(), getElement().getFirstChildElement()); } @Override protected void insert(Widget child, com.google.gwt.user.client.Element container, int beforeIndex, boolean domInsert) { // This method is intentionally suppressed, because ComplexPanel's insert() // method operates under the assumption that the only HTML contents of this // panel's element are the elements of this panel's child widgets (i.e., no // HTML decorations or chrome elements). This is not the desired behaviour // for ImplPanel. throw new UnsupportedOperationException("Index-based insertion is not supported by ImplPanel"); } /** * Inserts a widget into this panel, attaching its HTML to a specified * location within this panel's HTML. * <p> * Note that in order for this panel to have arbitrary HTML decorations, * rather than none at all, this panel must not care about the logical order * of its child widgets (an inherited restriction from ComplexPanel, which * assumes logical child index == physical DOM index). * <p> * Assumes (but does not check) that {@code container} is a descendant of this * widget's element, and that {@code reference} is a direct child of {@code * container}. * * @param child * @param container * @param reference */ public void insertBefore(Widget child, Element container, Element reference) { // The implementation below is identical to add(), except the physical DOM // insertion is positional. // Detach new child. child.removeFromParent(); // Logical attach. getChildren().add(child); // Physical attach. container.insertBefore(child.getElement(), reference); // Adopt. adopt(child); } /** * Inserts a widget into this panel, attaching its HTML to a specified * location within this panel's HTML. * <p> * Note that in order for this panel to have arbitrary HTML decorations, * rather than none at all, this panel must not care about the logical order * of its child widgets (an inherited restriction from ComplexPanel, which * assumes logical child index == physical DOM index). * <p> * Assumes (but does not check) that {@code container} is a descendant of this * widget's element, and that {@code reference} is a direct child of {@code * container}. * * @param child * @param container * @param reference */ public void insertAfter(Widget child, Element container, Element reference) { // The implementation below is identical to add(), except the physical DOM // insertion is positional. // Detach new child. child.removeFromParent(); // Logical attach. getChildren().add(child); // Physical attach. if (reference == null) { container.insertFirst(child.getElement()); } else { container.insertAfter(child.getElement(), reference); } // Adopt. adopt(child); } @Override public void doAdopt(Widget child) { Preconditions.checkArgument(child != null && child.getParent() == null, "Not an orphan"); getChildren().add(child); adopt(child); } @Override public void doOrphan(Widget child) { Preconditions.checkArgument(child != null && child.getParent() == this, "Not a child"); orphan(child); getChildren().remove(child); } /** * Narrows an object to a widget, if it is a child of this panel. Otherwise, * throws an exception. * * @param o object to narrow * @return {@code o} as a widget * @throws IllegalArgumentException if {@code o} is not a child of this panel. */ public Widget narrowChild(Object o) { // Note: it is very important that this method can be implemented WITHOUT // casting. This implementation uses casting as an optimization. However, // a non-casting version is provided in comments. Anyone touching this // method must ensure that a non-casting possibility exists. // // for (Widget child : getChildren()) { // if (child == o) { // return child; // } // } // return null; // Widget w = o instanceof Widget ? (Widget) o : null; if (w != null && w.getParent() == this) { return w; } else { throw new IllegalArgumentException("Not a child"); } } @Override public HandlerRegistration addClickHandler(ClickHandler handler) { return addDomHandler(handler, ClickEvent.getType()); } @Override public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) { return addDomHandler(handler, DoubleClickEvent.getType()); } @Override public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) { return addDomHandler(handler, MouseDownEvent.getType()); } @Override public WidgetCollection getChildren() { return super.getChildren(); } }