| /* |
| * Copyright 2008 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 com.google.gwt.user.client.ui; |
| |
| import com.google.gwt.dom.builder.shared.HtmlBuilderFactory; |
| import com.google.gwt.dom.builder.shared.HtmlSpanBuilder; |
| import com.google.gwt.dom.client.Element; |
| import com.google.gwt.event.logical.shared.AttachEvent; |
| import com.google.gwt.safehtml.shared.SafeHtml; |
| import com.google.gwt.safehtml.shared.SafeHtmlBuilder; |
| import com.google.gwt.user.client.DOM; |
| import com.google.gwt.user.client.Event; |
| |
| /** |
| * A type of widget that can wrap another widget, hiding the wrapped widget's |
| * methods. When added to a panel, a composite behaves exactly as if the widget |
| * it wraps had been added. |
| * |
| * <p> |
| * The composite is useful for creating a single widget out of an aggregate of |
| * multiple other widgets contained in a single panel. |
| * </p> |
| * |
| * <p> |
| * <h3>Example</h3> |
| * {@example com.google.gwt.examples.CompositeExample} |
| * </p> |
| */ |
| public abstract class Composite extends Widget implements IsRenderable { |
| |
| private Widget widget; |
| |
| private IsRenderable renderable; |
| |
| private Element elementToWrap; |
| |
| @Override |
| public void claimElement(Element element) { |
| if (renderable != null) { |
| renderable.claimElement(element); |
| setElement(widget.getElement()); |
| } else { |
| this.elementToWrap = element; |
| } |
| } |
| |
| @Override |
| public void initializeClaimedElement() { |
| if (renderable != null) { |
| renderable.initializeClaimedElement(); |
| } else { |
| elementToWrap.getParentNode().replaceChild(widget.getElement(), elementToWrap); |
| } |
| } |
| |
| @Override |
| public boolean isAttached() { |
| if (widget != null) { |
| return widget.isAttached(); |
| } |
| return false; |
| } |
| |
| @Override |
| public void onBrowserEvent(Event event) { |
| // Fire any handler added to the composite itself. |
| super.onBrowserEvent(event); |
| |
| // Delegate events to the widget. |
| widget.onBrowserEvent(event); |
| } |
| |
| @Override |
| public SafeHtml render(RenderableStamper stamper) { |
| if (renderable != null) { |
| return renderable.render(stamper); |
| } else { |
| checkInit(); |
| |
| HtmlSpanBuilder spanBuilder = HtmlBuilderFactory.get() |
| .createSpanBuilder(); |
| stamper.stamp(spanBuilder).end(); |
| return spanBuilder.asSafeHtml(); |
| } |
| } |
| |
| @Override |
| public void render(RenderableStamper stamper, SafeHtmlBuilder builder) { |
| if (renderable != null) { |
| renderable.render(stamper, builder); |
| } else { |
| builder.append(render(stamper)); |
| } |
| } |
| |
| /** |
| * Provides subclasses access to the topmost widget that defines this |
| * composite. |
| * |
| * @return the widget |
| */ |
| protected Widget getWidget() { |
| return widget; |
| } |
| |
| /** |
| * Check if the composite is initialized. |
| */ |
| private void checkInit() { |
| if (widget == null) { |
| throw new IllegalStateException("initWidget() is not called yet"); |
| } |
| } |
| |
| /** |
| * Sets the widget to be wrapped by the composite. The wrapped widget must be |
| * set before calling any {@link Widget} methods on this object, or adding it |
| * to a panel. This method may only be called once for a given composite. |
| * |
| * @param widget the widget to be wrapped |
| */ |
| protected void initWidget(Widget widget) { |
| // Validate. Make sure the widget is not being set twice. |
| if (this.widget != null) { |
| throw new IllegalStateException("Composite.initWidget() may only be " |
| + "called once."); |
| } |
| |
| if (widget == null) { |
| throw new NullPointerException("widget cannot be null"); |
| } |
| |
| if (widget instanceof IsRenderable) { |
| // In case the Widget being wrapped is an IsRenderable, we save that fact. |
| this.renderable = (IsRenderable) widget; |
| } |
| |
| // Detach the new child. |
| widget.removeFromParent(); |
| |
| // Use the contained widget's element as the composite's element, |
| // effectively merging them within the DOM. |
| Element elem = widget.getElement(); |
| setElement(elem); |
| |
| if (PotentialElement.isPotential(elem)) { |
| PotentialElement.as(elem).setResolver(this); |
| } |
| |
| // Logical attach. |
| this.widget = widget; |
| |
| // Adopt. |
| widget.setParent(this); |
| } |
| |
| @Override |
| protected void onAttach() { |
| checkInit(); |
| |
| if (!isOrWasAttached()) { |
| widget.sinkEvents(eventsToSink); |
| eventsToSink = -1; |
| } |
| |
| widget.onAttach(); |
| |
| // Clobber the widget's call to setEventListener(), causing all events to |
| // be routed to this composite, which will delegate back to the widget by |
| // default (note: it's not necessary to clear this in onDetach(), because |
| // the widget's onDetach will do so). |
| DOM.setEventListener(getElement(), this); |
| |
| // Call doAttachChildren() and then onLoad() directly, because we're not |
| // calling super.onAttach(). |
| doAttachChildren(); |
| onLoad(); |
| AttachEvent.fire(this, true); |
| } |
| |
| @Override |
| protected void onDetach() { |
| try { |
| onUnload(); |
| doDetachChildren(); |
| AttachEvent.fire(this, false); |
| } finally { |
| // We don't want an exception in user code to keep us from calling the |
| // super implementation (or event listeners won't get cleaned up and |
| // the attached flag will be wrong). |
| widget.onDetach(); |
| } |
| } |
| |
| @Override |
| protected Element resolvePotentialElement() { |
| setElement(widget.resolvePotentialElement()); |
| return getElement(); |
| } |
| |
| /** |
| * This method was for initializing the Widget to be wrapped by this |
| * Composite, but has been deprecated in favor of {@link #initWidget(Widget)}. |
| * |
| * @deprecated Use {@link #initWidget(Widget)} instead |
| */ |
| @Deprecated |
| protected void setWidget(Widget widget) { |
| initWidget(widget); |
| } |
| } |