cc.alcina.framework.gwt.client.util.WidgetUtils.java Source code

Java tutorial

Introduction

Here is the source code for cc.alcina.framework.gwt.client.util.WidgetUtils.java

Source

/*
 * 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 cc.alcina.framework.gwt.client.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.BodyElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Text;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.DockLayoutPanel.Direction;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLTable;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.SplitLayoutPanel;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;

import cc.alcina.framework.common.client.collections.CollectionFilter;
import cc.alcina.framework.common.client.logic.reflection.registry.Registry;
import cc.alcina.framework.common.client.util.CommonUtils;
import cc.alcina.framework.common.client.util.LooseContext;
import cc.alcina.framework.common.client.util.Rect;
import cc.alcina.framework.gwt.client.ClientNotifications;
import cc.alcina.framework.gwt.client.browsermod.BrowserMod;
import cc.alcina.framework.gwt.client.widget.HasComplexPanel;
import cc.alcina.framework.gwt.client.widget.dialog.RelativePopupPanel;
import cc.alcina.framework.gwt.client.widget.handlers.HasChildHandlers;
import cc.alcina.framework.gwt.client.widget.layout.HasLayoutInfo;
import cc.alcina.framework.gwt.client.widget.layout.HasLayoutInfo.LayoutInfo;

/**
 *
 * @author Nick Reddel
 */
public class WidgetUtils {
    public static final String CONTEXT_REALLY_ABSOLUTE_TOP = WidgetUtils.class.getName()
            + ".CONTEXT_REALLY_ABSOLUTE_TOP";

    private static boolean debug = false;

    private static List<Widget> hiddenWidgets;

    private static List<SplitLayoutPanel> morphedWidgets;

    private static List<ElementLayout> elementLayouts;

    private static Text tempPositioningText;

    private static final String SPLIT_PANEL_RESTORE_PROP = "_split_panel_restore";

    private static long lastSquelch = 0;

    public static boolean debugScroll;

    public static void addOrRemoveStyleName(Widget w, String styleName, boolean add) {
        if (!add) {
            w.removeStyleName(styleName);
        } else {
            w.addStyleName(styleName);
        }
    }

    public static List<Widget> allChildren(HasWidgets p) {
        List<Widget> widgets = new ArrayList<Widget>();
        if (p instanceof Widget) {
            widgets.add((Widget) p);
        }
        Iterator<Widget> iterator = p.iterator();
        while (iterator.hasNext()) {
            Widget w = iterator.next();
            if (w instanceof HasWidgets) {
                widgets.addAll(allChildren((HasWidgets) w));
            } else {
                widgets.add(w);
            }
        }
        return widgets;
    }

    // TODO - check all calls here either the cp implements haschildhandlers, or
    // explain why t'hell not...(doesn't add handlers to the child widgets would
    // be a good reason)
    public static void clearChildren(ComplexPanel cp) {
        for (int i = cp.getWidgetCount() - 1; i >= 0; i--) {
            Widget widget = cp.getWidget(i);
            cp.remove(i);
        }
        if (cp instanceof HasChildHandlers) {
            HasChildHandlers hch = (HasChildHandlers) cp;
            hch.detachHandlers();
        }
    }

    public static void clearChildren(TabPanel tp) {
        for (int i = tp.getWidgetCount() - 1; i >= 0; i--) {
            tp.remove(i);
        }
    }

    public static native void clearFocussedDocumentElement()/*-{
                                                            if ($doc.activeElement) {
                                                            var tagName = $doc.activeElement.tagName.toLowerCase();
                                                            if (tagName != "body" && tagName != "html") {
                                                            $doc.activeElement.blur();
                                                            }
                                                            }
                                                            }-*/;

    public static final native void click(Element elt) /*-{
                                                       var elem_remote = elt.@com.google.gwt.dom.client.Element::ensureRemote()();
                                                       elem_remote.click();
                                                       try {
                                                       elem_remote.focus();
                                                       } catch (e) {
                                                           
                                                       }
                                                       }-*/;

    public static Element clickGetAnchorAncestor(ClickEvent clickEvent) {
        Event event = Event.as(clickEvent.getNativeEvent());
        // handle localisation spans
        Element target = null;
        if (!Element.is(event.getEventTarget())) {
            return null;
        }
        target = Element.as(event.getEventTarget());
        Element anchor = DomUtils.getSelfOrAncestorWithTagName(target, "A");
        return anchor;
    }

    public static ComplexPanel complexChildOrSelf(Widget w) {
        if (w instanceof ComplexPanel) {
            return (ComplexPanel) w;
        }
        if (w instanceof HasComplexPanel) {
            return ((HasComplexPanel) w).getComplexPanel();
        }
        if (w instanceof SimplePanel) {
            return complexChildOrSelf(((SimplePanel) w).getWidget());
        }
        return RootPanel.get();
    }

    public static void copySize(Widget from, Widget to) {
        to.setSize(from.getOffsetWidth() + "px", from.getOffsetHeight() + "px");
    }

    public static void copyTextToClipboard(String text) {
        FlowPanel fp = new FlowPanel();
        TextArea ta = new TextArea();
        ta.setSize("600px", "300px");
        ta.setText(text);
        fp.add(ta);
        PopupPanel pp = new PopupPanel();
        pp.add(fp);
        pp.setAnimationEnabled(false);
        pp.show();
        ta.setSelectionRange(0, text.length());
        try {
            execCopy();
        } catch (JavaScriptException e) {
            pp.hide();
            if (e.getMessage().contains("NS_ERROR_XPC_NOT_ENOUGH_ARGS")) {
                Registry.impl(ClientNotifications.class)
                        .showMessage(new HTML("<div class='info'>Sorry, clipboard operations"
                                + " are disabled by Mozilla/Firefox" + " security settings. <br><br> Please see "
                                + "<a href='http://www.mozilla.org/editor/midasdemo/securityprefs.html'>"
                                + "http://www.mozilla.org/editor/midasdemo/securityprefs.html</a></div> "));
            } else {
                throw e;
            }
        }
        pp.hide();
    }

    public static NativeEvent createZeroClick() {
        return Document.get().createClickEvent(0, 0, 0, 0, 0, false, false, false, false);
    }

    public static void disableTextBoxHelpers(Widget textBox) {
        Element elt = textBox.getElement();
        elt.setAttribute("autocapitalize", "off");
        elt.setAttribute("autocorrect", "off");
        elt.setAttribute("autocomplete", "off");
        elt.setAttribute("spellcheck", "false");
    }

    public static native boolean docHasFocus() /*-{
                                               if (typeof $wnd.document.hasFocus !== "undefined") {
                                               return $wnd.document.hasFocus();
                                               } else {
                                               return true;
                                               }
                                               }-*/;

    public static native boolean docIsVisible() /*-{
                                                if (typeof $wnd.document.hidden !== "undefined") {
                                                return !$wnd.document.hidden;
                                                } else {
                                                return true;
                                                }
                                                }-*/;

    public static native boolean execCopy() /*-{
                                            return $wnd.document.execCommand("copy");
                                            }-*/;

    // TODO - actually Widget.fireEvent() would be much better here - duh...
    // nah - this is the right way
    public static void fireClickOnHandler(final HasClickHandlers source, final ClickHandler handler) {
        final HandlerRegistration handlerRegistration = source.addClickHandler(handler);
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
            @Override
            public void execute() {
                NativeEvent event = createZeroClick();
                DomEvent.fireNativeEvent(event, source);
                handlerRegistration.removeHandler();
            }
        });
    }

    public static native void focus(Element elem) /*-{
                                                  elem.focus();
                                                  }-*/;

    public static int getBestOffsetHeight(Element e) {
        return getBestOffsetHeight(e, false);
    }

    public static int getBestOffsetWidth(Element e) {
        return getBestOffsetWidth(e, false);
    }

    public static native String getBoundingClientRect(Element elem) /*-{
                                                                    if (!String.prototype.format) {
                                                                    String.prototype.format = String.prototype.f = function() {
                                                                    var s = this, i = arguments.length;
                                                                        
                                                                    while (i--) {
                                                                    s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
                                                                    }
                                                                    return s;
                                                                    };
                                                                    }
                                                                    var implAccess = elem.@com.google.gwt.dom.client.Element::implAccess()();
                                                                    var remote = implAccess.@com.google.gwt.dom.client.Element.ElementImplAccess::ensureRemote()();
                                                                    var rect = remote.getBoundingClientRect();
                                                                    return "x:{0}, y:{1},w:{2},h:{3}".format(rect.x, rect.y, rect.width,
                                                                    rect.height);
                                                                    }-*/;

    public static native String getComputedStyle(Element eltMulti, String attributeName)/*-{
                                                                                        var elt = eltMulti.@com.google.gwt.dom.client.Element::typedRemote()();
                                                                                        if (elt.currentStyle) {
                                                                                        return elt.currentStyle[attributeName];
                                                                                        }
                                                                                        if ($wnd.getComputedStyle) {
                                                                                        return $wnd.getComputedStyle(elt, null)[attributeName];
                                                                                        }
                                                                                        }-*/;

    public static native String getComputedStyleProperty(Element eltMulti, String strCssRule) /*-{
                                                                                              var elem = eltMulti.@com.google.gwt.dom.client.Element::typedRemote()();
                                                                                              if ($doc.defaultView && $doc.defaultView.getComputedStyle) {
                                                                                              strValue = $doc.defaultView.getComputedStyle(elem, "").getPropertyValue(
                                                                                              strCssRule);
                                                                                              } else if (oElm.currentStyle) {
                                                                                              strCssRule = strCssRule.replace(/\-(\w)/g, function(strMatch, p1) {
                                                                                              return p1.toUpperCase();
                                                                                              });
                                                                                              strValue = oElm.currentStyle[strCssRule];
                                                                                              }
                                                                                              return strValue;
                                                                                              }-*/;

    public static List<Element> getElementAncestors(Element elem) {
        List<Element> elements = new ArrayList<>();
        while (elem != null) {
            elements.add(elem);
            elem = elem.getParentElement();
        }
        return elements;
    }

    public static native Element getElementByNameOrId(Document doc, String name) /*-{
                                                                                 var e = doc.getElementById(name);
                                                                                 if (!e) {
                                                                                 e = doc.getElementsByName(name)
                                                                                 && doc.getElementsByName(name).length == 1 ? doc
                                                                                 .getElementsByName(name)[0] : null;
                                                                                 }
                                                                                 return e;
                                                                                 }-*/;

    public static Element getElementForAroundPositioning(Element from) {
        boolean hidden = isZeroOffsetDims(from);
        if (!isZeroOffsetDims(from)) {
            return from;
        }
        ClientNodeIterator itr = new ClientNodeIterator(from, ClientNodeIterator.SHOW_ELEMENT);
        Element elt = null;
        while ((elt = (Element) itr.nextNode()) != null) {
            if (!isZeroOffsetDims(elt)) {
                return elt;
            }
        }
        while (elt != null) {
            if (!isZeroOffsetDims(elt)) {
                return elt;
            }
            elt = elt.getParentElement();
        }
        return null;
    }

    public static Element getElementForPositioning0(Element from) {
        assert tempPositioningText == null;
        if (!isVisibleAncestorChain(from)) {
            return null;
        }
        boolean hidden = isZeroOffsetDims(from);
        int kidCount = from.getChildCount();
        if (kidCount != 0 && !hidden) {
            return from;
        }
        Node parent = from.getParentNode();
        if (parent != null && parent.getFirstChild() == from && parent.getNodeType() == Node.ELEMENT_NODE
                && !isZeroOffsetDims((Element) parent)) {
            return (Element) parent;
        }
        ClientNodeIterator itr = new ClientNodeIterator(from, ClientNodeIterator.SHOW_ALL);
        Element fromContainingBlock = DomUtils.getContainingBlock(from);
        Node node = from;
        int insertTextIfOffsetMoreThanXChars = 100;
        while ((node = node.getPreviousSibling()) != null) {
            if (node.getNodeType() == Node.TEXT_NODE) {
                insertTextIfOffsetMoreThanXChars -= TextUtils.normalizeWhitespaceAndTrim(node.getNodeValue())
                        .length();
                if (insertTextIfOffsetMoreThanXChars < 0) {
                    // this causes a relayout - so we try and avoid. most of the
                    // time, positioning elements will contain text (or be from
                    // a friendly browser), or be at the start of a block elt)
                    tempPositioningText = Document.get().createTextNode("---");
                    from.appendChild(tempPositioningText);
                    return from;
                }
            }
        }
        // give up after 50 node iterations (big tables maybe)
        int max = 50;
        while ((node = itr.nextNode()) != null && max-- > 0) {
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                if (!isZeroOffsetDims(node.getParentElement()) && node.getNodeName().equalsIgnoreCase("img")) {
                    return (Element) node;
                }
                if (!UIObject.isVisible((Element) node)) {
                    itr.skipChildren();
                }
            } else {
                // text
                if (!isZeroOffsetDims(node.getParentElement())
                        // we don't want the combined ancestor of everyone...
                        && (!node.getParentElement().isOrHasChild(from) ||
                        // but we do want <p><a><b>*some-text*</b></p>
                                DomUtils.getContainingBlock(node) == fromContainingBlock)) {
                    return node.getParentElement();
                }
            }
        }
        return from.getParentElement();
    }

    public static native Element getElementForSelector(Element elto, String selector) /*-{
                                                                                      if (!($doc.querySelector)) {
                                                                                      return null;
                                                                                      }
                                                                                      @cc.alcina.framework.gwt.client.util.WidgetUtils::ensureRemote(Lcom/google/gwt/dom/client/Element;)(elto);
                                                                                      var elt = elto.@com.google.gwt.dom.client.Element::typedRemote()();
                                                                                      var from = (elt) ? elt : $doc;
                                                                                      var splits = selector.split("::");
                                                                                      for (var idx = 0; idx < splits.length; idx += 2) {
                                                                                      var selectorPart = splits[idx];
                                                                                      var textRegex = idx == splits.length - 1 ? null : splits[idx + 1];
                                                                                      if (textRegex == null) {
                                                                                      from = from.querySelector(selectorPart);
                                                                                      break;
                                                                                      }
                                                                                      var nl = from.querySelectorAll(splits[idx]);
                                                                                      var found = false;
                                                                                      for (var i = 0; i < nl.length; i++) {
                                                                                      var item = nl[i];
                                                                                      if (item.innerHTML.indexOf(textRegex) != -1
                                                                                      || item.innerHTML.match(new RegExp(textRegex))) {
                                                                                      from = item;
                                                                                      found = true;
                                                                                      break;
                                                                                      }
                                                                                      }
                                                                                      if (!found) {
                                                                                      return null;
                                                                                      }
                                                                                      }
                                                                                      var eltout = @com.google.gwt.dom.client.LocalDom::nodeFor(Lcom/google/gwt/core/client/JavaScriptObject;)(from);
                                                                                      return eltout;
                                                                                      }-*/;

    public static native NodeList getElementsForSelector(Element elto, String selector) /*-{
                                                                                        if (!($doc.querySelector)) {
                                                                                        return null;
                                                                                        }
                                                                                        var elt = elto.@com.google.gwt.dom.client.Element::typedRemote()();
                                                                                        var from = (elt) ? elt : $doc;
                                                                                        var nodeList = from.querySelectorAll(selector);
                                                                                        return @com.google.gwt.dom.client.NodeList::new(Lcom/google/gwt/dom/client/DomNodeList;)(nodeList);
                                                                                        }-*/;

    public static native Element getFocussedDocumentElement()/*-{
                                                             if ($doc.activeElement) {
                                                             var tagName = $doc.activeElement.tagName.toLowerCase();
                                                             return tagName != "body" && tagName != "html" ? @com.google.gwt.dom.client.LocalDom::nodeFor(Lcom/google/gwt/core/client/JavaScriptObject;)($doc.activeElement)
                                                             : null;
                                                             }
                                                             return null;
                                                             }-*/;

    public static native int getOffsetHeightWithMargins(Element elem) /*-{
                                                                      var implAccess = elem.@com.google.gwt.dom.client.Element::implAccess()();
                                                                      var remote = implAccess.@com.google.gwt.dom.client.Element.ElementImplAccess::ensureRemote()();
                                                                      if (remote.style.display == 'none') {
                                                                      return 0;
                                                                      }
                                                                      var h = remote.offsetHeight;
                                                                      var marginTop = @cc.alcina.framework.gwt.client.util.WidgetUtils::getComputedStyle(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;)(elem,"margin");
                                                                      var marginBottom = @cc.alcina.framework.gwt.client.util.WidgetUtils::getComputedStyle(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;)(elem,"margin");
                                                                      if (marginTop.indexOf("px") != -1) {
                                                                      h += parseInt(marginTop.substring(0, marginTop.length - 2));
                                                                      }
                                                                      if (marginBottom.indexOf("px") != -1) {
                                                                      h += parseInt(marginBottom.substring(0, marginBottom.length - 2));
                                                                      }
                                                                      return h;
                                                                      }-*/;

    @SuppressWarnings("unchecked")
    public static <W extends Widget> W getParentWidget(Widget w, Class<W> widgetClass) {
        while (w != null) {
            if (w.getClass() == widgetClass) {
                return (W) w;
            }
            w = w.getParent();
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static <W extends Widget> W getParentWidget(Widget w, String widgetClassName) {
        while (w != null) {
            if (CommonUtils.simpleClassName(w.getClass()).equals(widgetClassName)) {
                return (W) w;
            }
            w = w.getParent();
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static Widget getParentWidgetSatisfyingCallback(Widget w, CollectionFilter<Object> callback) {
        while (w != null) {
            if (callback.allow(w)) {
                return w;
            }
            w = w.getParent();
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static <T extends Widget> T getParentWidgetSatisfyingTypedCallback(Widget w,
            CollectionFilter<Widget> callback) {
        while (w != null) {
            if (callback.allow(w)) {
                return (T) w;
            }
            w = w.getParent();
        }
        return null;
    }

    public static Widget getPositioningParent(Widget widget) {
        while (widget.getParent() != null) {
            String pos = getComputedStyle(widget.getElement(), "position");
            if (pos != null && (pos.equals("relative") || pos.equals("absolute"))) {
                return widget;
            }
            widget = widget.getParent();
        }
        return widget;// root panel
    }

    public native static int getRelativeTopTo(Element elem, Element end) /*-{
                                                                         var top = 0;
                                                                         while (elem != end) {
                                                                         top += elem.offsetTop;
                                                                         elem = elem.offsetParent;
                                                                         }
                                                                         return top;
                                                                         }-*/;

    public static native int getScrollLeft(Element elem) /*-{
                                                         var left = 0;
                                                         var curr = elem;
                                                         // This intentionally excludes body which has a null offsetParent.
                                                         while (curr.offsetParent) {
                                                         left -= curr.scrollLeft;
                                                         curr = curr.parentNode;
                                                         }
                                                             
                                                         return left;
                                                         }-*/;

    public static native int getScrollTop(Element elem) /*-{
                                                        var top = 0;
                                                        var curr = elem;
                                                        // This intentionally excludes body which has a null offsetParent.
                                                        while (curr.offsetParent) {
                                                        top -= curr.scrollTop;
                                                        curr = curr.parentNode;
                                                        }
                                                        return top;
                                                        }-*/;

    public static void hardCancelEvent(NativePreviewEvent event) {
        event.cancel();
        cancelPossibleIEShortcut();
    }

    public static boolean hasTempPositioningText() {
        return tempPositioningText != null;
    }

    public static native boolean isBrowserSupportsCopy() /*-{
                                                         return $wnd.document.queryCommandSupported("copy");
                                                         }-*/;

    public static boolean isLessThanXpixelsFrom(Element e, int hDistance, int vDistance) {
        Event currentEvent = Event.getCurrentEvent();
        return isLessThanXpixelsFrom0(e, hDistance, vDistance, currentEvent.getClientX(),
                currentEvent.getClientY());
    }

    public static boolean isNewTabModifier() {
        Event event = Event.getCurrentEvent();
        return isNewTabModifier(event);
    }

    public static boolean isNewTabModifier(NativeEvent event) {
        return BrowserMod.getOperatingSystem().equals("Macintosh") ? event.getMetaKey() : event.getCtrlKey();
    }

    public static boolean isVisibleAncestorChain(Element e) {
        if (e == null || e.getOwnerDocument() == null) {
            return false;
        }
        Element documentElement = e.getOwnerDocument().getDocumentElement();
        while (e != documentElement) {
            if (!UIObject.isVisible(e)) {
                return false;
            }
            if ("hidden".equals(e.getStyle().getVisibility())) {
                return false;
            }
            Element old = e;
            e = e.getParentElement();
            // detached
            if (e == null) {
                return false;
            }
        }
        return true;
    }

    public static boolean isVisibleAncestorChain(Widget w) {
        return isVisibleAncestorChain(w.getElement());
    }

    public static boolean isVisibleWithOffsetParent(Widget w) {
        return isVisibleWithOffsetParent(w.getElement());
    }

    public static boolean isZeroOffsetDims(Element e) {
        return e.getOffsetHeight() == 0 || e.getOffsetWidth() == 0;
    }

    public static void maximiseWidget(Widget widget) {
        restoreFromMaximise();
        hiddenWidgets = new ArrayList<Widget>();
        morphedWidgets = new ArrayList<SplitLayoutPanel>();
        elementLayouts = new ArrayList<ElementLayout>();
        Element e = widget.getElement();
        while (widget.getParent() != null) {
            Widget parent = widget.getParent();
            if (parent instanceof SplitLayoutPanel) {
                morphSplitPanel((SplitLayoutPanel) parent, widget, false);
            } else if (parent instanceof HasWidgets && !(parent instanceof TabPanel)) {
                HasWidgets hw = (HasWidgets) parent;
                for (Iterator<Widget> itr = hw.iterator(); itr.hasNext();) {
                    Widget w = itr.next();
                    if (w != widget && w.isVisible()) {
                        hiddenWidgets.add(w);
                        w.setVisible(false);
                    }
                }
            }
            widget = widget.getParent();
        }
        while (e.getParentElement() != RootPanel.get().getElement()) {
            ElementLayout layout = new ElementLayout(e);
            elementLayouts.add(layout);
            layout.maximise();
            e = e.getParentElement();
        }
    }

    public static void maybeClosePopupParent(ClickEvent clickEvent) {
        Widget w = (Widget) clickEvent.getSource();
        CollectionFilter<Object> callback = new CollectionFilter<Object>() {
            @Override
            public boolean allow(Object o) {
                return o instanceof RelativePopupPanel;
            }
        };
        RelativePopupPanel pp = (RelativePopupPanel) getParentWidgetSatisfyingCallback(w, callback);
        if (pp != null) {
            pp.hide();
        }
    }

    public static void populateListFromEnum(ListBox box, Object[] objs) {
        for (Object obj : objs) {
            String k = obj.toString();
            String friendly = k.toLowerCase().replace('_', ' ');
            box.addItem(friendly, k);
        }
    }

    public static int propertyPx(String propertyString) {
        if (propertyString.indexOf("px") == -1) {
            return 0;
        }
        return (int) Float.parseFloat(propertyString.replace("px", ""));
    }

    public static boolean recentSquelch() {
        return System.currentTimeMillis() - lastSquelch < 100;
    }

    public static void releaseTempPositioningText() {
        if (tempPositioningText != null) {
            tempPositioningText.removeFromParent();
            tempPositioningText = null;
        }
    }

    public static void replace(Widget current, Widget newWidget) {
        replace(current, newWidget, null);
    }

    public static void replace(Widget current, Widget newWidget, Panel parent) {
        if (parent == null) {
            parent = (Panel) current.getParent();
        }
        if (current == null || current.getParent() != parent) {
            parent.add(newWidget);
            return;
        }
        if (parent instanceof SimplePanel) {
            ((SimplePanel) parent).setWidget(newWidget);
            return;
        }
        ComplexPanel cp = (ComplexPanel) parent;
        int index = cp.getWidgetIndex(current);
        cp.remove(index);
        if (cp instanceof FlowPanel) {
            FlowPanel fp = (FlowPanel) cp;
            fp.insert(newWidget, index);
        }
    }

    public static void resizeUsingInfo(int containerHeight, int containerWidth, Iterator<Widget> widgets,
            int parentAdjustHeight, int parentAdjustWidth) {
        while (widgets.hasNext()) {
            Widget widget = widgets.next();
            if (widget == null || !widget.isVisible()) {
                continue;
            }
            int availableHeight = containerHeight;
            int availableWidth = containerWidth;
            if (widget instanceof HasLayoutInfo) {
                String name = widget.getClass().getName();
                if (debug) {
                    GWT.log(CommonUtils.formatJ("%s: ", CommonUtils.simpleClassName(widget.getClass())), null);
                }
                LayoutInfo info = ((HasLayoutInfo) widget).getLayoutInfo();
                info.beforeLayout();
                if (info.to100percentOfAvailableHeight() || info.to100percentOfAvailableWidth()) {
                    int usedHeight = 0;
                    int usedWidth = 0;
                    Widget parent = widget.getParent();
                    Iterator<Widget> childIterator = null;
                    availableHeight = info.useBestOffsetForParentHeight()
                            ? getBestOffsetHeight(parent.getElement(), true)
                            : containerHeight;
                    availableHeight = Math.min(availableHeight, containerHeight);
                    availableWidth = info.useBestOffsetForParentWidth()
                            ? getBestOffsetWidth(parent.getElement(), true)
                            : containerWidth;
                    availableWidth = Math.min(availableWidth, containerWidth);
                    if (parent instanceof HasLayoutInfo) {
                        childIterator = ((HasLayoutInfo) parent).getLayoutInfo().getLayoutWidgets();
                    } else if (parent instanceof HasWidgets) {
                        childIterator = ((HasWidgets) parent).iterator();
                    }
                    boolean ignoreChildrenForHeight = info.to100percentOfAvailableHeight()
                            && (isDirectionalLayoutPanel(parent, true) || info.ignoreSiblingsForHeight());
                    boolean ignoreChildrenForWidth = info.to100percentOfAvailableWidth()
                            && (isDirectionalLayoutPanel(parent, false) || info.ignoreSiblingsForWidth());
                    if (childIterator != null) {
                        while (childIterator.hasNext()) {
                            Widget cw = childIterator.next();
                            if (cw != widget && WidgetUtils.isVisibleWithOffsetParent(cw.getElement())
                                    && cw.isAttached()) {
                                if (!ignoreChildrenForHeight) {
                                    usedHeight += getBestOffsetHeight(cw.getElement(), true, false);
                                }
                                if (!ignoreChildrenForWidth) {
                                    usedWidth += getBestOffsetWidth(cw.getElement());
                                }
                            }
                        }
                    }
                    if (info.to100percentOfAvailableHeight()) {
                        availableHeight = availableHeight - usedHeight - parentAdjustHeight
                                - info.getAdjustHeight();
                        if (debug) {
                            GWT.log(CommonUtils.formatJ("%s: %s - comp %s",
                                    CommonUtils.simpleClassName(widget.getClass()), availableHeight,
                                    containerHeight), null);
                        }
                        if (availableHeight >= 0) {
                            widget.setHeight((availableHeight) + "px");
                        }
                    }
                    if (info.to100percentOfAvailableWidth()) {
                        availableWidth = availableWidth - usedWidth - parentAdjustWidth - info.getAdjustWidth();
                        if (availableWidth >= 0) {
                            widget.setWidth((availableWidth) + "px");
                        }
                    }
                }
                Iterator<Widget> toResize = info.getWidgetsToResize();
                while (toResize.hasNext()) {
                    toResize.next().setHeight(containerHeight + "px");
                }
                resizeUsingInfo(availableHeight, availableWidth, info.getLayoutWidgets(),
                        info.getClientAdjustHeight(), info.getClientAdjustWidth());
                info.afterLayout();
            } // haslayoutinfo
            else if (widget instanceof HasWidgets) {
                resizeUsingInfo(availableHeight, availableWidth, ((HasWidgets) widget).iterator(), 0, 0);
            }
        } // while
    }

    public static void resizeUsingInfo(int availableHeight, int availableWidth, Widget w) {
        resizeUsingInfo(availableHeight, availableWidth, Arrays.asList(new Widget[] { w }).iterator(), 0, 0);
    }

    public static void resizeUsingInfo(Widget w) {
        resizeUsingInfo(getBestOffsetHeight(w.getElement()), getBestOffsetWidth(w.getElement()), w);
    }

    public static void restoreFromMaximise() {
        if (hiddenWidgets == null) {
            return;
        }
        // reverse order for webkit/gecko and (??)
        for (int i = elementLayouts.size() - 1; i >= 0; i--) {
            ElementLayout layout = elementLayouts.get(i);
            layout.restore();
        }
        for (SplitLayoutPanel w : morphedWidgets) {
            morphSplitPanel(w, null, true);
        }
        for (Widget w : hiddenWidgets) {
            w.setVisible(true);
        }
        hiddenWidgets = null;
    }

    public static void scrollBodyTo(int y) {
        BodyElement body = Document.get().getBody();
        body.setPropertyInt("scrollTop", y);
        Element documentElement = Document.get().getDocumentElement();
        documentElement.setPropertyInt("scrollTop", y);
        scrollTo(0, y);
    }

    public static void scrollIntoView(Element e) {
        scrollIntoView(e, 0);
    }

    public static void scrollIntoView(Element e, int fromTop) {
        scrollIntoView(e, fromTop, false);
    }

    public static void scrollIntoView(Element elem, int fromTop, boolean forceFromTop) {
        int y1 = Document.get().getBodyOffsetTop() + Window.getScrollTop();
        int y2 = y1 + Window.getClientHeight();
        Element parent = elem.getParentElement();
        int absoluteTop = elem.getAbsoluteTop();
        int offsetHeight = elem.getOffsetHeight();
        int absoluteBottom = absoluteTop + offsetHeight;
        boolean recalcAbsoluteTopAfterScroll = true;
        if (absoluteTop == 0) {
            Text tptCopy = tempPositioningText;
            tempPositioningText = null;
            Element positioning = WidgetUtils.getElementForPositioning0(elem);
            if (positioning != null) {
                absoluteTop = positioning.getAbsoluteTop();
                recalcAbsoluteTopAfterScroll = false;
            }
            releaseTempPositioningText();
            tempPositioningText = tptCopy;
        }
        if (!forceFromTop && (absoluteTop >= y1 && absoluteTop < y2)) {
            return;
        }
        if (forceFromTop && LooseContext.is(CONTEXT_REALLY_ABSOLUTE_TOP)) {
            int to = absoluteTop - fromTop;
            scrollBodyTo(to);
            return;
        }
        scrollElementIntoView(elem);
        y1 = Document.get().getBodyOffsetTop() + Window.getScrollTop();
        y2 = y1 + Window.getClientHeight();
        // not sure why...but I feel there's a reason
        if (recalcAbsoluteTopAfterScroll) {
            absoluteTop = elem.getAbsoluteTop();
        }
        if (absoluteTop < y1 || absoluteTop > y2 || fromTop != 0) {
            scrollBodyTo((Math.max(0, absoluteTop - fromTop)));
        }
    }

    public static void scrollIntoViewWhileKeepingRect(Rect bounds, Widget widget, int pad) {
        // assume widget is below bounds
        int scrollTop = Window.getScrollTop();
        int clientHeight = Window.getClientHeight();
        int widgetTop = widget.getAbsoluteTop();
        int widgetHeight = widget.getOffsetHeight();
        if (widgetTop + widgetHeight + pad > scrollTop + clientHeight) {
            int bestDeltaDown = widgetTop + widgetHeight + pad - (scrollTop + clientHeight);
            int delta = Math.min(bounds.y1 - scrollTop, bestDeltaDown);
            delta = Math.max(0, delta);
            smoothScrollTo(scrollTop + delta, widget);
        }
    }

    public static void scrollTo(int x, int y) {
        debugScroll("" + x + ":" + y);
        Window.scrollTo(x, y);
        debugScroll("" + x + ":" + y);
    }

    public static native void selectElement(Element elem)/*-{
                                                         var sel, range;
                                                         var implAccess = elem.@com.google.gwt.dom.client.Element::implAccess()();
                                                         var remote = implAccess.@com.google.gwt.dom.client.Element.ElementImplAccess::ensureRemote()();
                                                         if ($wnd.getSelection && $doc.createRange) {
                                                         sel = $wnd.getSelection();
                                                         range = $doc.createRange();
                                                         range.selectNodeContents(remote);
                                                         sel.removeAllRanges();
                                                         sel.addRange(range);
                                                         } else if ($doc.body.createTextRange) {
                                                         range = $doc.body.createTextRange();
                                                         range.moveToElementText(remote);
                                                         range.select();
                                                         }
                                                         }-*/;

    public static void setColumnVisibility(HTMLTable table, int column, boolean visible) {
        int rows = table.getRowCount();
        for (int row = 0; row < rows; row++) {
            UIObject.setVisible(table.getCellFormatter().getElement(row, column), visible);
        }
    }

    public static void setCssVisibility(Widget widget, boolean visible) {
        widget.getElement().getStyle().setProperty("visibility", visible ? "visible" : "hidden");
    }

    public static void setOpacity(Widget w, int opacityPercent) {
        Element e = w.getElement();
        String opacity = opacityPercent == 100 ? "1.0" : "0." + CommonUtils.padTwo(opacityPercent);
        e.getStyle().setProperty("opacity", opacity);
    }

    public static void setOrRemoveStyleName(Widget widget, String styleName, boolean set) {
        if (set) {
            widget.addStyleName(styleName);
        } else {
            widget.removeStyleName(styleName);
        }
    }

    public static void showInNewTabOrThisWindow(String url) {
        Event currentEvent = Event.getCurrentEvent();
        currentEvent.preventDefault();
        if (WidgetUtils.isNewTabModifier(currentEvent)) {
            Window.open(url, "_blank", "");
        } else {
            Window.Location.assign(url);
        }
    }

    public static void smoothScrollTo(int scrollTo, Widget widget) {
        new SmoothScroller(scrollTo, widget);
    }

    public static void squelchCurrentEvent() {
        Event currentEvent = Event.getCurrentEvent();
        lastSquelch = System.currentTimeMillis();
        if (currentEvent != null) {
            currentEvent.stopPropagation();
            currentEvent.preventDefault();
        }
    }

    public static void squelchIfNotNewTab() {
        if (!isNewTabModifier()) {
            squelchCurrentEvent();
        }
    }

    public static void toggleStyleName(Widget w, String styleName) {
        String current = w.getStyleName();
        if (current.contains(styleName)) {
            w.removeStyleName(styleName);
        } else {
            w.addStyleName(styleName);
        }
    }

    public static FlowPanel wrapInDiv(Widget widget) {
        FlowPanel fp = new FlowPanel();
        fp.add(widget);
        return fp;
    }

    private static native void cancelPossibleIEShortcut() /*-{
                                                          try {
                                                          $wnd.event.keyCode = 0; // this is a hack to capture ctrl+f ctrl+p etc
                                                          } catch (e) {
                                                              
                                                          }
                                                          }-*/;

    private static void debugScroll(String message) {
        if (debugScroll) {
            ClientNotifications.get()
                    .log(CommonUtils.formatJ("scroll from: %s,%s", Window.getScrollLeft(), Window.getScrollTop()));
            ClientNotifications.get().log(message);
        }
    }

    private static void ensureRemote(Element element) {
        element.implAccess().ensureRemote();
    }

    private static int getBestOffsetHeight(Element e, boolean parentPass) {
        return getBestOffsetHeight(e, parentPass, true);
    }

    private static int getBestOffsetHeight(Element e, boolean parentPass, boolean allowParentPass) {
        int h = e.getPropertyInt("offsetHeight");
        if (h != 0 || e.getParentElement() == null) {
            return h;
        }
        if (e.getFirstChildElement() == null && !parentPass) {
            return getBestOffsetHeight(e, true);
        }
        if (!allowParentPass) {
            return 0;
        }
        return getBestOffsetHeight(parentPass ? e.getParentElement() : e.getFirstChildElement(), parentPass);
    }

    private static int getBestOffsetWidth(Element e, boolean parentPass) {
        int h = e.getPropertyInt("offsetWidth");
        if (h != 0 || e.getParentElement() == null) {
            return h;
        }
        if (e.getFirstChildElement() == null && !parentPass) {
            return getBestOffsetWidth(e, true);
        }
        return getBestOffsetWidth(parentPass ? e.getParentElement() : e.getFirstChildElement(), parentPass);
    }

    private static boolean isDirectionalLayoutPanel(Widget panel, boolean horizontal) {
        if (panel instanceof DockLayoutPanel) {
            DockLayoutPanel dlp = (DockLayoutPanel) panel;
            Iterator<Widget> itr = dlp.iterator();
            for (Widget widget : dlp) {
                Direction dir = dlp.getWidgetDirection(widget);
                if (horizontal && (dir == Direction.NORTH || dir == Direction.SOUTH)) {
                    return false;
                }
                if (!horizontal && (dir == Direction.WEST || dir == Direction.EAST)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    private static native boolean isLessThanXpixelsFrom0(Element e, int hDistance, int vDistance, int x,
            int y) /*-{
                   try {
                   var rects = e.getClientRects();
                   for (var idx = 0; idx < rects.length; idx++) {
                   var rect = rects[idx];
                   var hOk = rect.left - x < hDistance && x - rect.right < hDistance;
                   var vOk = rect.top - y < vDistance && y - rect.bottom < vDistance;
                   if (hOk && vOk) {
                   return true;
                   }
                   }
                   return false;
                   } catch (e2) {
                   return false;
                   }
                   }-*/;

    private native static boolean isVisibleWithOffsetParent(Element elem)/*-{
                                                                         var implAccess = elem.@com.google.gwt.dom.client.Element::implAccess()();
                                                                         var remote = implAccess.@com.google.gwt.dom.client.Element.ElementImplAccess::ensureRemote()();
                                                                         return (remote.style.display != 'none' && remote.offsetParent != null);
                                                                         }-*/;

    private static void morphSplitPanel(SplitLayoutPanel splitPanel, Widget keepChild, boolean restore) {
        final String zeroSize = "0px";
        boolean hsp = isDirectionalLayoutPanel(splitPanel, true);
        for (int index = 0; index < splitPanel.getWidgetCount(); index++) {
            Widget w = splitPanel.getWidget(index);
            if (CommonUtils.simpleClassName(w.getClass()).contains("Splitter")) {
                w.setVisible(restore);
            } else {
                Element container = splitPanel.getWidgetContainerElement(w);
                container.getStyle().setDisplay(restore || keepChild == w ? Display.BLOCK : Display.NONE);
            }
        }
        if (!restore) {
            morphedWidgets.add(splitPanel);
        }
    }

    private static void scrollElementIntoView(Element e) {
        debugScroll(CommonUtils.formatJ("elt:%s", e));
        e.scrollIntoView();
    }

    // those values might be needed for non-webkit
    @SuppressWarnings("unused")
    private static class ElementLayout {
        private String position;

        private String height;

        private String width;

        // ff3.5, at least
        private String overflow;

        private final Element element;

        public ElementLayout(Element element) {
            this.element = element;
        }

        void maximise() {
            position = element.getStyle().getProperty("position");
            overflow = element.getStyle().getProperty("overflow");
            element.getStyle().setProperty("position", "");
            element.getStyle().setProperty("overflow", "");
        }

        void restore() {
            element.getStyle().setProperty("position", position);
            element.getStyle().setProperty("overflow", overflow);
        }
    }

    static class SmoothScroller implements Handler {
        private HandlerRegistration attachHandlerRegistration;

        private Timer timer;

        private double lastWindowPos;

        int ticks = 0;

        public SmoothScroller(final int scrollTo, final Widget widget) {
            lastWindowPos = Window.getScrollTop();
            final int tickCount = 30;
            final double delta = (scrollTo - lastWindowPos) / tickCount;
            timer = new Timer() {
                @Override
                public void run() {
                    int windowPos = Window.getScrollTop();
                    if (Math.abs(windowPos - lastWindowPos) > 1) {
                        cancel();
                    } else {
                        if (ticks++ > tickCount) {
                            Window.scrollTo(0, scrollTo);
                            cancel();
                        } else {
                            lastWindowPos += delta;
                            Window.scrollTo(0, (int) lastWindowPos);
                        }
                    }
                }
            };
            timer.scheduleRepeating(16);
            this.attachHandlerRegistration = widget.addAttachHandler(this);
        }

        @Override
        public void onAttachOrDetach(AttachEvent event) {
            timer.cancel();
            attachHandlerRegistration.removeHandler();
        }
    }
}