123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716 |
- /*
- @ITMillApache2LicenseForJavaFiles@
- */
-
- package com.vaadin.terminal.gwt.client;
-
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Set;
-
- import com.google.gwt.user.client.Command;
- import com.google.gwt.user.client.DOM;
- import com.google.gwt.user.client.DeferredCommand;
- import com.google.gwt.user.client.Element;
- import com.google.gwt.user.client.Timer;
- import com.google.gwt.user.client.Window;
- 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.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
-
- public class Util {
-
- /**
- * Helper method for debugging purposes.
- *
- * Stops execution on firefox browsers on a breakpoint.
- *
- */
- public static native void browserDebugger()
- /*-{
- if($wnd.console)
- debugger;
- }-*/;
-
- private static final int LAZY_SIZE_CHANGE_TIMEOUT = 400;
- private static Set<Paintable> latelyChangedWidgets = new HashSet<Paintable>();
-
- private static Timer lazySizeChangeTimer = new Timer() {
- private boolean lazySizeChangeTimerScheduled = false;
-
- @Override
- public void run() {
- componentSizeUpdated(latelyChangedWidgets);
- latelyChangedWidgets.clear();
- lazySizeChangeTimerScheduled = false;
- }
-
- @Override
- public void schedule(int delayMillis) {
- if (lazySizeChangeTimerScheduled) {
- cancel();
- } else {
- lazySizeChangeTimerScheduled = true;
- }
- super.schedule(delayMillis);
- }
- };
-
- /**
- * This helper method can be called if components size have been changed
- * outside rendering phase. It notifies components parent about the size
- * change so it can react.
- *
- * When using this method, developer should consider if size changes could
- * be notified lazily. If lazy flag is true, method will save widget and
- * wait for a moment until it notifies parents in chunks. This may vastly
- * optimize layout in various situation. Example: if component have a lot of
- * images their onload events may fire "layout phase" many times in a short
- * period.
- *
- * @param widget
- * @param lazy
- * run componentSizeUpdated lazyly
- */
- public static void notifyParentOfSizeChange(Paintable widget, boolean lazy) {
- if (lazy) {
- latelyChangedWidgets.add(widget);
- lazySizeChangeTimer.schedule(LAZY_SIZE_CHANGE_TIMEOUT);
- } else {
- Set<Paintable> widgets = new HashSet<Paintable>();
- widgets.add(widget);
- Util.componentSizeUpdated(widgets);
- }
- }
-
- /**
- * Called when the size of one or more widgets have changed during
- * rendering. Finds parent container and notifies them of the size change.
- *
- * @param widgets
- */
- public static void componentSizeUpdated(Set<Paintable> widgets) {
- if (widgets.isEmpty()) {
- return;
- }
-
- Map<Container, Set<Paintable>> childWidgets = new HashMap<Container, Set<Paintable>>();
-
- for (Paintable widget : widgets) {
- // ApplicationConnection.getConsole().log(
- // "Widget " + Util.getSimpleName(widget) + " size updated");
- Widget parent = ((Widget) widget).getParent();
- while (parent != null && !(parent instanceof Container)) {
- parent = parent.getParent();
- }
- if (parent != null) {
- Set<Paintable> set = childWidgets.get(parent);
- if (set == null) {
- set = new HashSet<Paintable>();
- childWidgets.put((Container) parent, set);
- }
- set.add(widget);
- }
- }
-
- Set<Paintable> parentChanges = new HashSet<Paintable>();
- for (Container parent : childWidgets.keySet()) {
- if (!parent.requestLayout(childWidgets.get(parent))) {
- parentChanges.add(parent);
- }
- }
-
- componentSizeUpdated(parentChanges);
- }
-
- public static float parseRelativeSize(String size) {
- if (size == null || !size.endsWith("%")) {
- return -1;
- }
-
- try {
- return Float.parseFloat(size.substring(0, size.length() - 1));
- } catch (Exception e) {
- ClientExceptionHandler.displayError(
- "Unable to parse relative size", e);
- }
-
- return -1;
- }
-
- /**
- * Returns closest parent Widget in hierarchy that implements Container
- * interface
- *
- * @param component
- * @return closest parent Container
- */
- public static Container getLayout(Widget component) {
- Widget parent = component.getParent();
- while (parent != null && !(parent instanceof Container)) {
- parent = parent.getParent();
- }
- if (parent != null) {
- assert ((Container) parent).hasChildComponent(component);
-
- return (Container) parent;
- }
- return null;
- }
-
- /**
- * Detects if current browser is IE.
- *
- * @deprecated use BrowserInfo class instead
- *
- * @return true if IE
- */
- @Deprecated
- public static boolean isIE() {
- return BrowserInfo.get().isIE();
- }
-
- /**
- * Detects if current browser is IE6.
- *
- * @deprecated use BrowserInfo class instead
- *
- * @return true if IE6
- */
- @Deprecated
- public static boolean isIE6() {
- return BrowserInfo.get().isIE6();
- }
-
- /**
- * @deprecated use BrowserInfo class instead
- * @return
- */
- @Deprecated
- public static boolean isIE7() {
- return BrowserInfo.get().isIE7();
- }
-
- /**
- * @deprecated use BrowserInfo class instead
- * @return
- */
- @Deprecated
- public static boolean isFF2() {
- return BrowserInfo.get().isFF2();
- }
-
- private static final Element escapeHtmlHelper = DOM.createDiv();
-
- /**
- * Converts html entities to text.
- *
- * @param html
- * @return escaped string presentation of given html
- */
- public static String escapeHTML(String html) {
- DOM.setInnerText(escapeHtmlHelper, html);
- return DOM.getInnerHTML(escapeHtmlHelper);
- }
-
- /**
- * Adds transparent PNG fix to image element; only use for IE6.
- *
- * @param el
- * IMG element
- * @param blankImageUrl
- * URL to transparent one-pixel gif
- */
- public native static void addPngFix(Element el, String blankImageUrl)
- /*-{
- el.attachEvent("onload", function() {
- var src = el.src;
- if (src.indexOf(".png")<1) return;
- var w = el.width||16;
- var h = el.height||16;
- el.src =blankImageUrl;
- el.style.height = h+"px";
- el.style.width = w+"px";
- el.style.padding = "0px";
- el.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+src+"', sizingMethod='scale')";
- },false);
- }-*/;
-
- /**
- * Clones given element as in JavaScript.
- *
- * Deprecate this if there appears similar method into GWT someday.
- *
- * @param element
- * @param deep
- * clone child tree also
- * @return
- */
- public static native Element cloneNode(Element element, boolean deep)
- /*-{
- return element.cloneNode(deep);
- }-*/;
-
- public static int measureHorizontalPaddingAndBorder(Element element,
- int paddingGuess) {
- String originalWidth = DOM.getStyleAttribute(element, "width");
- String originalOverflow = "";
- if (BrowserInfo.get().isIE6()) {
- originalOverflow = DOM.getStyleAttribute(element, "overflow");
- DOM.setStyleAttribute(element, "overflow", "hidden");
- }
- int originalOffsetWidth = element.getOffsetWidth();
- int widthGuess = (originalOffsetWidth - paddingGuess);
- if (widthGuess < 1) {
- widthGuess = 1;
- }
- DOM.setStyleAttribute(element, "width", widthGuess + "px");
- int padding = element.getOffsetWidth() - widthGuess;
-
- DOM.setStyleAttribute(element, "width", originalWidth);
- if (BrowserInfo.get().isIE6()) {
- DOM.setStyleAttribute(element, "overflow", originalOverflow);
- }
- return padding;
- }
-
- public static int measureVerticalPaddingAndBorder(Element element,
- int paddingGuess) {
- String originalHeight = DOM.getStyleAttribute(element, "height");
- int originalOffsetHeight = element.getOffsetHeight();
- int widthGuess = (originalOffsetHeight - paddingGuess);
- if (widthGuess < 1) {
- widthGuess = 1;
- }
- DOM.setStyleAttribute(element, "height", widthGuess + "px");
- int padding = element.getOffsetHeight() - widthGuess;
-
- DOM.setStyleAttribute(element, "height", originalHeight);
- return padding;
- }
-
- public static int measureHorizontalBorder(Element element) {
- int borders;
- int cw = element.getClientWidth();
- if (BrowserInfo.get().isIE()) {
- String width = element.getStyle().getProperty("width");
- String height = element.getStyle().getProperty("height");
-
- int offsetWidth = element.getOffsetWidth();
- int offsetHeight = element.getOffsetHeight();
- if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE8()) {
- if (offsetHeight < 1) {
- offsetHeight = 1;
- }
- if (offsetWidth < 1) {
- offsetWidth = 10;
- }
- element.getStyle().setPropertyPx("height", offsetHeight);
- }
- element.getStyle().setPropertyPx("width", offsetWidth);
-
- borders = element.getOffsetWidth() - element.getClientWidth();
-
- element.getStyle().setProperty("width", width);
- if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE8()) {
- element.getStyle().setProperty("height", height);
- }
- } else {
- borders = element.getOffsetWidth()
- - element.getPropertyInt("clientWidth");
- }
- assert borders >= 0;
-
- return borders;
- }
-
- public static int measureVerticalBorder(Element element) {
- int borders;
- if (BrowserInfo.get().isIE()) {
- String width = element.getStyle().getProperty("width");
- String height = element.getStyle().getProperty("height");
-
- int offsetWidth = element.getOffsetWidth();
- int offsetHeight = element.getOffsetHeight();
- // if (BrowserInfo.get().isIE6()) {
- if (offsetHeight < 1) {
- offsetHeight = 1;
- }
- if (offsetWidth < 1) {
- offsetWidth = 10;
- }
- element.getStyle().setPropertyPx("width", offsetWidth);
- // }
-
- element.getStyle().setPropertyPx("height", offsetHeight);
-
- borders = element.getOffsetHeight()
- - element.getPropertyInt("clientHeight");
-
- element.getStyle().setProperty("height", height);
- // if (BrowserInfo.get().isIE6()) {
- element.getStyle().setProperty("width", width);
- // }
- } else {
- borders = element.getOffsetHeight()
- - element.getPropertyInt("clientHeight");
- }
- assert borders >= 0;
-
- return borders;
- }
-
- public static int measureMarginLeft(Element element) {
- return element.getAbsoluteLeft()
- - element.getParentElement().getAbsoluteLeft();
- }
-
- public static int setHeightExcludingPaddingAndBorder(Widget widget,
- String height, int paddingBorderGuess) {
- if (height.equals("")) {
- setHeight(widget, "");
- return paddingBorderGuess;
- } else if (height.endsWith("px")) {
- int pixelHeight = Integer.parseInt(height.substring(0, height
- .length() - 2));
- return setHeightExcludingPaddingAndBorder(widget.getElement(),
- pixelHeight, paddingBorderGuess, false);
- } else {
- // Set the height in unknown units
- setHeight(widget, height);
- // Use the offsetWidth
- return setHeightExcludingPaddingAndBorder(widget.getElement(),
- widget.getOffsetHeight(), paddingBorderGuess, true);
- }
- }
-
- private static void setWidth(Widget widget, String width) {
- DOM.setStyleAttribute(widget.getElement(), "width", width);
- }
-
- private static void setHeight(Widget widget, String height) {
- DOM.setStyleAttribute(widget.getElement(), "height", height);
- }
-
- public static int setWidthExcludingPaddingAndBorder(Widget widget,
- String width, int paddingBorderGuess) {
- if (width.equals("")) {
- setWidth(widget, "");
- return paddingBorderGuess;
- } else if (width.endsWith("px")) {
- int pixelWidth = Integer.parseInt(width.substring(0,
- width.length() - 2));
- return setWidthExcludingPaddingAndBorder(widget.getElement(),
- pixelWidth, paddingBorderGuess, false);
- } else {
- setWidth(widget, width);
- return setWidthExcludingPaddingAndBorder(widget.getElement(),
- widget.getOffsetWidth(), paddingBorderGuess, true);
- }
- }
-
- public static int setWidthExcludingPaddingAndBorder(Element element,
- int requestedWidth, int horizontalPaddingBorderGuess,
- boolean requestedWidthIncludesPaddingBorder) {
-
- int widthGuess = requestedWidth - horizontalPaddingBorderGuess;
- if (widthGuess < 0) {
- widthGuess = 0;
- }
-
- DOM.setStyleAttribute(element, "width", widthGuess + "px");
- int captionOffsetWidth = DOM.getElementPropertyInt(element,
- "offsetWidth");
-
- int actualPadding = captionOffsetWidth - widthGuess;
-
- if (requestedWidthIncludesPaddingBorder) {
- actualPadding += actualPadding;
- }
-
- if (actualPadding != horizontalPaddingBorderGuess) {
- int w = requestedWidth - actualPadding;
- if (w < 0) {
- // Cannot set negative width even if we would want to
- w = 0;
- }
- DOM.setStyleAttribute(element, "width", w + "px");
-
- }
-
- return actualPadding;
-
- }
-
- public static int setHeightExcludingPaddingAndBorder(Element element,
- int requestedHeight, int verticalPaddingBorderGuess,
- boolean requestedHeightIncludesPaddingBorder) {
-
- int heightGuess = requestedHeight - verticalPaddingBorderGuess;
- if (heightGuess < 0) {
- heightGuess = 0;
- }
-
- DOM.setStyleAttribute(element, "height", heightGuess + "px");
- int captionOffsetHeight = DOM.getElementPropertyInt(element,
- "offsetHeight");
-
- int actualPadding = captionOffsetHeight - heightGuess;
-
- if (requestedHeightIncludesPaddingBorder) {
- actualPadding += actualPadding;
- }
-
- if (actualPadding != verticalPaddingBorderGuess) {
- int h = requestedHeight - actualPadding;
- if (h < 0) {
- // Cannot set negative height even if we would want to
- h = 0;
- }
- DOM.setStyleAttribute(element, "height", h + "px");
-
- }
-
- return actualPadding;
-
- }
-
- public static String getSimpleName(Object widget) {
- if (widget == null) {
- return "(null)";
- }
-
- String name = widget.getClass().getName();
- return name.substring(name.lastIndexOf('.') + 1);
- }
-
- public static void setFloat(Element element, String value) {
- if (BrowserInfo.get().isIE()) {
- DOM.setStyleAttribute(element, "styleFloat", value);
- } else {
- DOM.setStyleAttribute(element, "cssFloat", value);
- }
- }
-
- private static int detectedScrollbarSize = -1;
-
- public static int getNativeScrollbarSize() {
- if (detectedScrollbarSize < 0) {
- Element scroller = DOM.createDiv();
- scroller.getStyle().setProperty("width", "50px");
- scroller.getStyle().setProperty("height", "50px");
- scroller.getStyle().setProperty("overflow", "scroll");
- scroller.getStyle().setProperty("position", "absolute");
- scroller.getStyle().setProperty("marginLeft", "-5000px");
- RootPanel.getBodyElement().appendChild(scroller);
- detectedScrollbarSize = scroller.getOffsetWidth()
- - scroller.getPropertyInt("clientWidth");
-
- // Asserting the detected value causes a problem
- // at least in Hosted Mode Browser/Linux/GWT-1.5.3, so
- // use a default if detection fails.
- if (detectedScrollbarSize == 0) {
- detectedScrollbarSize = 20;
- }
-
- RootPanel.getBodyElement().removeChild(scroller);
-
- }
- return detectedScrollbarSize;
- }
-
- /**
- * Run workaround for webkits overflow auto issue.
- *
- * See: our bug #2138 and https://bugs.webkit.org/show_bug.cgi?id=21462
- *
- * @param elem
- * with overflow auto
- */
- public static void runWebkitOverflowAutoFix(final Element elem) {
- // add max version if fix landes sometime to webkit
- if (BrowserInfo.get().getWebkitVersion() > 0) {
- elem.getStyle().setProperty("overflow", "hidden");
-
- DeferredCommand.addCommand(new Command() {
- public void execute() {
- // Dough, safari scoll auto means actually just a moped
- elem.getStyle().setProperty("overflow", "auto");
- if (elem.getScrollTop() > 0) {
- // fix another bug where scrollbar remains in wrong
- // position
- int scrolltop = elem.getScrollTop();
- elem.setScrollTop(scrolltop - 1);
- elem.setScrollTop(scrolltop);
- }
- }
- });
- }
-
- }
-
- /**
- * Parses the UIDL parameter and fetches the relative size of the component.
- * If a dimension is not specified as relative it will return -1. If the
- * UIDL does not contain width or height specifications this will return
- * null.
- *
- * @param uidl
- * @return
- */
- public static FloatSize parseRelativeSize(UIDL uidl) {
- boolean hasAttribute = false;
- String w = "";
- String h = "";
- if (uidl.hasAttribute("width")) {
- hasAttribute = true;
- w = uidl.getStringAttribute("width");
- }
- if (uidl.hasAttribute("height")) {
- hasAttribute = true;
- h = uidl.getStringAttribute("height");
- }
-
- if (!hasAttribute) {
- return null;
- }
-
- float relativeWidth = Util.parseRelativeSize(w);
- float relativeHeight = Util.parseRelativeSize(h);
-
- FloatSize relativeSize = new FloatSize(relativeWidth, relativeHeight);
- return relativeSize;
-
- }
-
- public static boolean isCached(UIDL uidl) {
- return uidl.getBooleanAttribute("cached");
- }
-
- public static void alert(String string) {
- if (true) {
- Window.alert(string);
- }
- }
-
- public static boolean equals(Object a, Object b) {
- if (a == null) {
- return b == null;
- }
-
- return a.equals(b);
- }
-
- public static void updateRelativeChildrenAndSendSizeUpdateEvent(
- ApplicationConnection client, HasWidgets container) {
- updateRelativeChildrenAndSendSizeUpdateEvent(client, container,
- (Paintable) container);
- }
-
- public static void updateRelativeChildrenAndSendSizeUpdateEvent(
- ApplicationConnection client, HasWidgets container, Paintable widget) {
- /*
- * Relative sized children must be updated first so the component has
- * the correct outer dimensions when signaling a size change to the
- * parent.
- */
- Iterator<Widget> childIterator = container.iterator();
- while (childIterator.hasNext()) {
- Widget w = childIterator.next();
- client.handleComponentRelativeSize(w);
- }
-
- HashSet<Paintable> widgets = new HashSet<Paintable>();
- widgets.add(widget);
- Util.componentSizeUpdated(widgets);
- }
-
- public static native int getRequiredWidth(
- com.google.gwt.dom.client.Element element)
- /*-{
- if (element.getBoundingClientRect) {
- var rect = element.getBoundingClientRect();
- return Math.ceil(rect.right - rect.left);
- } else {
- return element.offsetWidth;
- }
- }-*/;
-
- public static native int getRequiredHeight(
- com.google.gwt.dom.client.Element element)
- /*-{
- var height;
- if (element.getBoundingClientRect != null) {
- var rect = element.getBoundingClientRect();
- height = Math.ceil(rect.bottom - rect.top);
- } else {
- height = element.offsetHeight;
- }
- return height;
- }-*/;
-
- public static int getRequiredWidth(Widget widget) {
- return getRequiredWidth(widget.getElement());
- }
-
- public static int getRequiredHeight(Widget widget) {
- return getRequiredHeight(widget.getElement());
- }
-
- /**
- * Detects what is currently the overflow style attribute in given element.
- *
- * @param pe
- * the element to detect
- * @return true if auto or scroll
- */
- public static boolean mayHaveScrollBars(com.google.gwt.dom.client.Element pe) {
- String overflow = getComputedStyle(pe, "overflow");
- if (overflow != null) {
- if (overflow.equals("auto") || overflow.equals("scroll")) {
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
- /**
- * A simple helper method to detect "computed style" (aka style sheets +
- * element styles). Values returned differ a lot depending on browsers.
- * Always be very careful when using this.
- *
- * @param el
- * the element from which the style property is detected
- * @param p
- * the property to detect
- * @return String value of style property
- */
- private static native String getComputedStyle(
- com.google.gwt.dom.client.Element el, String p)
- /*-{
- try {
-
- if (el.currentStyle) {
- // IE
- return el.currentStyle[p];
- } else if (window.getComputedStyle) {
- // Sa, FF, Opera
- var view = el.ownerDocument.defaultView;
- return view.getComputedStyle(el,null).getPropertyValue(p);
- } else {
- // fall back for non IE, Sa, FF, Opera
- return "";
- }
- } catch (e) {
- return "";
- }
-
- }-*/;
-
- }
|