You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Util.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client;
  5. import java.util.HashMap;
  6. import java.util.HashSet;
  7. import java.util.Iterator;
  8. import java.util.Map;
  9. import java.util.Set;
  10. import com.google.gwt.dom.client.DivElement;
  11. import com.google.gwt.dom.client.Document;
  12. import com.google.gwt.user.client.Command;
  13. import com.google.gwt.user.client.DOM;
  14. import com.google.gwt.user.client.DeferredCommand;
  15. import com.google.gwt.user.client.Element;
  16. import com.google.gwt.user.client.Timer;
  17. import com.google.gwt.user.client.Window;
  18. import com.google.gwt.user.client.ui.HasWidgets;
  19. import com.google.gwt.user.client.ui.RootPanel;
  20. import com.google.gwt.user.client.ui.Widget;
  21. import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
  22. public class Util {
  23. /**
  24. * Helper method for debugging purposes.
  25. *
  26. * Stops execution on firefox browsers on a breakpoint.
  27. *
  28. */
  29. public static native void browserDebugger()
  30. /*-{
  31. if($wnd.console)
  32. debugger;
  33. }-*/;
  34. /**
  35. *
  36. * Returns the topmost element of from given coordinates.
  37. *
  38. * TODO fix crossplat issues clientX vs pageX. See quircksmode
  39. *
  40. * @param x
  41. * @param y
  42. * @return the element at given coordinates
  43. */
  44. public static native Element getElementFromPoint(int clientX, int clientY)
  45. /*-{
  46. var el = $wnd.document.elementFromPoint(clientX, clientY);
  47. if(el.nodeType == 3) {
  48. el = el.parentNode;
  49. }
  50. return el;
  51. }-*/;
  52. private static final int LAZY_SIZE_CHANGE_TIMEOUT = 400;
  53. private static Set<Paintable> latelyChangedWidgets = new HashSet<Paintable>();
  54. private static Timer lazySizeChangeTimer = new Timer() {
  55. private boolean lazySizeChangeTimerScheduled = false;
  56. @Override
  57. public void run() {
  58. componentSizeUpdated(latelyChangedWidgets);
  59. latelyChangedWidgets.clear();
  60. lazySizeChangeTimerScheduled = false;
  61. }
  62. @Override
  63. public void schedule(int delayMillis) {
  64. if (lazySizeChangeTimerScheduled) {
  65. cancel();
  66. } else {
  67. lazySizeChangeTimerScheduled = true;
  68. }
  69. super.schedule(delayMillis);
  70. }
  71. };
  72. /**
  73. * This helper method can be called if components size have been changed
  74. * outside rendering phase. It notifies components parent about the size
  75. * change so it can react.
  76. *
  77. * When using this method, developer should consider if size changes could
  78. * be notified lazily. If lazy flag is true, method will save widget and
  79. * wait for a moment until it notifies parents in chunks. This may vastly
  80. * optimize layout in various situation. Example: if component have a lot of
  81. * images their onload events may fire "layout phase" many times in a short
  82. * period.
  83. *
  84. * @param widget
  85. * @param lazy
  86. * run componentSizeUpdated lazyly
  87. */
  88. public static void notifyParentOfSizeChange(Paintable widget, boolean lazy) {
  89. if (lazy) {
  90. latelyChangedWidgets.add(widget);
  91. lazySizeChangeTimer.schedule(LAZY_SIZE_CHANGE_TIMEOUT);
  92. } else {
  93. Set<Paintable> widgets = new HashSet<Paintable>();
  94. widgets.add(widget);
  95. Util.componentSizeUpdated(widgets);
  96. }
  97. }
  98. /**
  99. * Called when the size of one or more widgets have changed during
  100. * rendering. Finds parent container and notifies them of the size change.
  101. *
  102. * @param widgets
  103. */
  104. public static void componentSizeUpdated(Set<Paintable> widgets) {
  105. if (widgets.isEmpty()) {
  106. return;
  107. }
  108. Map<Container, Set<Paintable>> childWidgets = new HashMap<Container, Set<Paintable>>();
  109. for (Paintable widget : widgets) {
  110. // ApplicationConnection.getConsole().log(
  111. // "Widget " + Util.getSimpleName(widget) + " size updated");
  112. Widget parent = ((Widget) widget).getParent();
  113. while (parent != null && !(parent instanceof Container)) {
  114. parent = parent.getParent();
  115. }
  116. if (parent != null) {
  117. Set<Paintable> set = childWidgets.get(parent);
  118. if (set == null) {
  119. set = new HashSet<Paintable>();
  120. childWidgets.put((Container) parent, set);
  121. }
  122. set.add(widget);
  123. }
  124. }
  125. Set<Paintable> parentChanges = new HashSet<Paintable>();
  126. for (Container parent : childWidgets.keySet()) {
  127. if (!parent.requestLayout(childWidgets.get(parent))) {
  128. parentChanges.add(parent);
  129. }
  130. }
  131. componentSizeUpdated(parentChanges);
  132. }
  133. public static float parseRelativeSize(String size) {
  134. if (size == null || !size.endsWith("%")) {
  135. return -1;
  136. }
  137. try {
  138. return Float.parseFloat(size.substring(0, size.length() - 1));
  139. } catch (Exception e) {
  140. ClientExceptionHandler.displayError(
  141. "Unable to parse relative size", e);
  142. }
  143. return -1;
  144. }
  145. /**
  146. * Returns closest parent Widget in hierarchy that implements Container
  147. * interface
  148. *
  149. * @param component
  150. * @return closest parent Container
  151. */
  152. public static Container getLayout(Widget component) {
  153. Widget parent = component.getParent();
  154. while (parent != null && !(parent instanceof Container)) {
  155. parent = parent.getParent();
  156. }
  157. if (parent != null) {
  158. assert ((Container) parent).hasChildComponent(component);
  159. return (Container) parent;
  160. }
  161. return null;
  162. }
  163. /**
  164. * Detects if current browser is IE.
  165. *
  166. * @deprecated use BrowserInfo class instead
  167. *
  168. * @return true if IE
  169. */
  170. @Deprecated
  171. public static boolean isIE() {
  172. return BrowserInfo.get().isIE();
  173. }
  174. /**
  175. * Detects if current browser is IE6.
  176. *
  177. * @deprecated use BrowserInfo class instead
  178. *
  179. * @return true if IE6
  180. */
  181. @Deprecated
  182. public static boolean isIE6() {
  183. return BrowserInfo.get().isIE6();
  184. }
  185. /**
  186. * @deprecated use BrowserInfo class instead
  187. * @return
  188. */
  189. @Deprecated
  190. public static boolean isIE7() {
  191. return BrowserInfo.get().isIE7();
  192. }
  193. /**
  194. * @deprecated use BrowserInfo class instead
  195. * @return
  196. */
  197. @Deprecated
  198. public static boolean isFF2() {
  199. return BrowserInfo.get().isFF2();
  200. }
  201. private static final Element escapeHtmlHelper = DOM.createDiv();
  202. /**
  203. * Converts html entities to text.
  204. *
  205. * @param html
  206. * @return escaped string presentation of given html
  207. */
  208. public static String escapeHTML(String html) {
  209. DOM.setInnerText(escapeHtmlHelper, html);
  210. return DOM.getInnerHTML(escapeHtmlHelper);
  211. }
  212. /**
  213. * Adds transparent PNG fix to image element; only use for IE6.
  214. *
  215. * @param el
  216. * IMG element
  217. * @param blankImageUrl
  218. * URL to transparent one-pixel gif
  219. */
  220. public native static void addPngFix(Element el, String blankImageUrl)
  221. /*-{
  222. el.attachEvent("onload", function() {
  223. var src = el.src;
  224. if (src.indexOf(".png") < 1) return;
  225. var w = el.width || 16;
  226. var h = el.height || 16;
  227. if(h==30 || w==28) {
  228. setTimeout(function(){
  229. el.style.height = el.height + "px";
  230. el.style.width = el.width + "px";
  231. el.src = blankImageUrl;
  232. },10);
  233. } else {
  234. el.src = blankImageUrl;
  235. el.style.height = h + "px";
  236. el.style.width = w + "px";
  237. }
  238. el.style.padding = "0";
  239. el.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+src+"', sizingMethod='crop')";
  240. },false);
  241. }-*/;
  242. /**
  243. * Clones given element as in JavaScript.
  244. *
  245. * Deprecate this if there appears similar method into GWT someday.
  246. *
  247. * @param element
  248. * @param deep
  249. * clone child tree also
  250. * @return
  251. */
  252. public static native Element cloneNode(Element element, boolean deep)
  253. /*-{
  254. return element.cloneNode(deep);
  255. }-*/;
  256. public static int measureHorizontalPaddingAndBorder(Element element,
  257. int paddingGuess) {
  258. String originalWidth = DOM.getStyleAttribute(element, "width");
  259. String originalOverflow = "";
  260. if (BrowserInfo.get().isIE6()) {
  261. originalOverflow = DOM.getStyleAttribute(element, "overflow");
  262. DOM.setStyleAttribute(element, "overflow", "hidden");
  263. }
  264. int originalOffsetWidth = element.getOffsetWidth();
  265. int widthGuess = (originalOffsetWidth - paddingGuess);
  266. if (widthGuess < 1) {
  267. widthGuess = 1;
  268. }
  269. DOM.setStyleAttribute(element, "width", widthGuess + "px");
  270. int padding = element.getOffsetWidth() - widthGuess;
  271. DOM.setStyleAttribute(element, "width", originalWidth);
  272. if (BrowserInfo.get().isIE6()) {
  273. DOM.setStyleAttribute(element, "overflow", originalOverflow);
  274. }
  275. return padding;
  276. }
  277. public static int measureVerticalPaddingAndBorder(Element element,
  278. int paddingGuess) {
  279. String originalHeight = DOM.getStyleAttribute(element, "height");
  280. int originalOffsetHeight = element.getOffsetHeight();
  281. int widthGuess = (originalOffsetHeight - paddingGuess);
  282. if (widthGuess < 1) {
  283. widthGuess = 1;
  284. }
  285. DOM.setStyleAttribute(element, "height", widthGuess + "px");
  286. int padding = element.getOffsetHeight() - widthGuess;
  287. DOM.setStyleAttribute(element, "height", originalHeight);
  288. return padding;
  289. }
  290. public static int measureHorizontalBorder(Element element) {
  291. int borders;
  292. int cw = element.getClientWidth();
  293. if (BrowserInfo.get().isIE()) {
  294. String width = element.getStyle().getProperty("width");
  295. String height = element.getStyle().getProperty("height");
  296. int offsetWidth = element.getOffsetWidth();
  297. int offsetHeight = element.getOffsetHeight();
  298. if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE8()) {
  299. if (offsetHeight < 1) {
  300. offsetHeight = 1;
  301. }
  302. if (offsetWidth < 1) {
  303. offsetWidth = 10;
  304. }
  305. element.getStyle().setPropertyPx("height", offsetHeight);
  306. }
  307. element.getStyle().setPropertyPx("width", offsetWidth);
  308. borders = element.getOffsetWidth() - element.getClientWidth();
  309. element.getStyle().setProperty("width", width);
  310. if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE8()) {
  311. element.getStyle().setProperty("height", height);
  312. }
  313. } else {
  314. borders = element.getOffsetWidth()
  315. - element.getPropertyInt("clientWidth");
  316. }
  317. assert borders >= 0;
  318. return borders;
  319. }
  320. public static int measureVerticalBorder(Element element) {
  321. int borders;
  322. if (BrowserInfo.get().isIE()) {
  323. String width = element.getStyle().getProperty("width");
  324. String height = element.getStyle().getProperty("height");
  325. int offsetWidth = element.getOffsetWidth();
  326. int offsetHeight = element.getOffsetHeight();
  327. // if (BrowserInfo.get().isIE6()) {
  328. if (offsetHeight < 1) {
  329. offsetHeight = 1;
  330. }
  331. if (offsetWidth < 1) {
  332. offsetWidth = 10;
  333. }
  334. element.getStyle().setPropertyPx("width", offsetWidth);
  335. // }
  336. element.getStyle().setPropertyPx("height", offsetHeight);
  337. borders = element.getOffsetHeight()
  338. - element.getPropertyInt("clientHeight");
  339. element.getStyle().setProperty("height", height);
  340. // if (BrowserInfo.get().isIE6()) {
  341. element.getStyle().setProperty("width", width);
  342. // }
  343. } else {
  344. borders = element.getOffsetHeight()
  345. - element.getPropertyInt("clientHeight");
  346. }
  347. assert borders >= 0;
  348. return borders;
  349. }
  350. public static int measureMarginLeft(Element element) {
  351. return element.getAbsoluteLeft()
  352. - element.getParentElement().getAbsoluteLeft();
  353. }
  354. public static int setHeightExcludingPaddingAndBorder(Widget widget,
  355. String height, int paddingBorderGuess) {
  356. if (height.equals("")) {
  357. setHeight(widget, "");
  358. return paddingBorderGuess;
  359. } else if (height.endsWith("px")) {
  360. int pixelHeight = Integer.parseInt(height.substring(0, height
  361. .length() - 2));
  362. return setHeightExcludingPaddingAndBorder(widget.getElement(),
  363. pixelHeight, paddingBorderGuess, false);
  364. } else {
  365. // Set the height in unknown units
  366. setHeight(widget, height);
  367. // Use the offsetWidth
  368. return setHeightExcludingPaddingAndBorder(widget.getElement(),
  369. widget.getOffsetHeight(), paddingBorderGuess, true);
  370. }
  371. }
  372. private static void setWidth(Widget widget, String width) {
  373. DOM.setStyleAttribute(widget.getElement(), "width", width);
  374. }
  375. private static void setHeight(Widget widget, String height) {
  376. DOM.setStyleAttribute(widget.getElement(), "height", height);
  377. }
  378. public static int setWidthExcludingPaddingAndBorder(Widget widget,
  379. String width, int paddingBorderGuess) {
  380. if (width.equals("")) {
  381. setWidth(widget, "");
  382. return paddingBorderGuess;
  383. } else if (width.endsWith("px")) {
  384. int pixelWidth = Integer.parseInt(width.substring(0,
  385. width.length() - 2));
  386. return setWidthExcludingPaddingAndBorder(widget.getElement(),
  387. pixelWidth, paddingBorderGuess, false);
  388. } else {
  389. setWidth(widget, width);
  390. return setWidthExcludingPaddingAndBorder(widget.getElement(),
  391. widget.getOffsetWidth(), paddingBorderGuess, true);
  392. }
  393. }
  394. public static int setWidthExcludingPaddingAndBorder(Element element,
  395. int requestedWidth, int horizontalPaddingBorderGuess,
  396. boolean requestedWidthIncludesPaddingBorder) {
  397. int widthGuess = requestedWidth - horizontalPaddingBorderGuess;
  398. if (widthGuess < 0) {
  399. widthGuess = 0;
  400. }
  401. DOM.setStyleAttribute(element, "width", widthGuess + "px");
  402. int captionOffsetWidth = DOM.getElementPropertyInt(element,
  403. "offsetWidth");
  404. int actualPadding = captionOffsetWidth - widthGuess;
  405. if (requestedWidthIncludesPaddingBorder) {
  406. actualPadding += actualPadding;
  407. }
  408. if (actualPadding != horizontalPaddingBorderGuess) {
  409. int w = requestedWidth - actualPadding;
  410. if (w < 0) {
  411. // Cannot set negative width even if we would want to
  412. w = 0;
  413. }
  414. DOM.setStyleAttribute(element, "width", w + "px");
  415. }
  416. return actualPadding;
  417. }
  418. public static int setHeightExcludingPaddingAndBorder(Element element,
  419. int requestedHeight, int verticalPaddingBorderGuess,
  420. boolean requestedHeightIncludesPaddingBorder) {
  421. int heightGuess = requestedHeight - verticalPaddingBorderGuess;
  422. if (heightGuess < 0) {
  423. heightGuess = 0;
  424. }
  425. DOM.setStyleAttribute(element, "height", heightGuess + "px");
  426. int captionOffsetHeight = DOM.getElementPropertyInt(element,
  427. "offsetHeight");
  428. int actualPadding = captionOffsetHeight - heightGuess;
  429. if (requestedHeightIncludesPaddingBorder) {
  430. actualPadding += actualPadding;
  431. }
  432. if (actualPadding != verticalPaddingBorderGuess) {
  433. int h = requestedHeight - actualPadding;
  434. if (h < 0) {
  435. // Cannot set negative height even if we would want to
  436. h = 0;
  437. }
  438. DOM.setStyleAttribute(element, "height", h + "px");
  439. }
  440. return actualPadding;
  441. }
  442. public static String getSimpleName(Object widget) {
  443. if (widget == null) {
  444. return "(null)";
  445. }
  446. String name = widget.getClass().getName();
  447. return name.substring(name.lastIndexOf('.') + 1);
  448. }
  449. public static void setFloat(Element element, String value) {
  450. if (BrowserInfo.get().isIE()) {
  451. DOM.setStyleAttribute(element, "styleFloat", value);
  452. } else {
  453. DOM.setStyleAttribute(element, "cssFloat", value);
  454. }
  455. }
  456. private static int detectedScrollbarSize = -1;
  457. public static int getNativeScrollbarSize() {
  458. if (detectedScrollbarSize < 0) {
  459. Element scroller = DOM.createDiv();
  460. scroller.getStyle().setProperty("width", "50px");
  461. scroller.getStyle().setProperty("height", "50px");
  462. scroller.getStyle().setProperty("overflow", "scroll");
  463. scroller.getStyle().setProperty("position", "absolute");
  464. scroller.getStyle().setProperty("marginLeft", "-5000px");
  465. RootPanel.getBodyElement().appendChild(scroller);
  466. detectedScrollbarSize = scroller.getOffsetWidth()
  467. - scroller.getPropertyInt("clientWidth");
  468. // Asserting the detected value causes a problem
  469. // at least in Hosted Mode Browser/Linux/GWT-1.5.3, so
  470. // use a default if detection fails.
  471. if (detectedScrollbarSize == 0) {
  472. detectedScrollbarSize = 20;
  473. }
  474. RootPanel.getBodyElement().removeChild(scroller);
  475. }
  476. return detectedScrollbarSize;
  477. }
  478. /**
  479. * Run workaround for webkits overflow auto issue.
  480. *
  481. * See: our bug #2138 and https://bugs.webkit.org/show_bug.cgi?id=21462
  482. *
  483. * @param elem
  484. * with overflow auto
  485. */
  486. public static void runWebkitOverflowAutoFix(final Element elem) {
  487. // Add max version if fix lands sometime to Webkit
  488. if (BrowserInfo.get().getWebkitVersion() > 0) {
  489. final String originalOverflow = elem.getStyle().getProperty(
  490. "overflow");
  491. if ("hidden".equals(originalOverflow)) {
  492. return;
  493. }
  494. // check the scrolltop value before hiding the element
  495. final int scrolltop = elem.getScrollTop();
  496. elem.getStyle().setProperty("overflow", "hidden");
  497. DeferredCommand.addCommand(new Command() {
  498. public void execute() {
  499. // Dough, Safari scroll auto means actually just a moped
  500. elem.getStyle().setProperty("overflow", originalOverflow);
  501. if (scrolltop > 0 || elem.getScrollTop() > 0) {
  502. int scrollvalue = scrolltop;
  503. if (scrolltop == 0) {
  504. // mysterious are the ways of webkits scrollbar
  505. // handling. In some cases webkit reports bad (0)
  506. // scrolltop before hiding the elment temporary,
  507. // sometimes after.
  508. scrollvalue = elem.getScrollTop();
  509. }
  510. // fix another bug where scrollbar remains in wrong
  511. // position
  512. elem.setScrollTop(scrollvalue - 1);
  513. elem.setScrollTop(scrollvalue);
  514. }
  515. }
  516. });
  517. }
  518. }
  519. /**
  520. * Parses the UIDL parameter and fetches the relative size of the component.
  521. * If a dimension is not specified as relative it will return -1. If the
  522. * UIDL does not contain width or height specifications this will return
  523. * null.
  524. *
  525. * @param uidl
  526. * @return
  527. */
  528. public static FloatSize parseRelativeSize(UIDL uidl) {
  529. boolean hasAttribute = false;
  530. String w = "";
  531. String h = "";
  532. if (uidl.hasAttribute("width")) {
  533. hasAttribute = true;
  534. w = uidl.getStringAttribute("width");
  535. }
  536. if (uidl.hasAttribute("height")) {
  537. hasAttribute = true;
  538. h = uidl.getStringAttribute("height");
  539. }
  540. if (!hasAttribute) {
  541. return null;
  542. }
  543. float relativeWidth = Util.parseRelativeSize(w);
  544. float relativeHeight = Util.parseRelativeSize(h);
  545. FloatSize relativeSize = new FloatSize(relativeWidth, relativeHeight);
  546. return relativeSize;
  547. }
  548. public static boolean isCached(UIDL uidl) {
  549. return uidl.getBooleanAttribute("cached");
  550. }
  551. public static void alert(String string) {
  552. if (true) {
  553. Window.alert(string);
  554. }
  555. }
  556. public static boolean equals(Object a, Object b) {
  557. if (a == null) {
  558. return b == null;
  559. }
  560. return a.equals(b);
  561. }
  562. public static void updateRelativeChildrenAndSendSizeUpdateEvent(
  563. ApplicationConnection client, HasWidgets container) {
  564. updateRelativeChildrenAndSendSizeUpdateEvent(client, container,
  565. (Paintable) container);
  566. }
  567. public static void updateRelativeChildrenAndSendSizeUpdateEvent(
  568. ApplicationConnection client, HasWidgets container, Paintable widget) {
  569. /*
  570. * Relative sized children must be updated first so the component has
  571. * the correct outer dimensions when signaling a size change to the
  572. * parent.
  573. */
  574. Iterator<Widget> childIterator = container.iterator();
  575. while (childIterator.hasNext()) {
  576. Widget w = childIterator.next();
  577. client.handleComponentRelativeSize(w);
  578. }
  579. HashSet<Paintable> widgets = new HashSet<Paintable>();
  580. widgets.add(widget);
  581. Util.componentSizeUpdated(widgets);
  582. }
  583. public static native int getRequiredWidth(
  584. com.google.gwt.dom.client.Element element)
  585. /*-{
  586. if (element.getBoundingClientRect) {
  587. var rect = element.getBoundingClientRect();
  588. return Math.ceil(rect.right - rect.left);
  589. } else {
  590. return element.offsetWidth;
  591. }
  592. }-*/;
  593. public static native int getRequiredHeight(
  594. com.google.gwt.dom.client.Element element)
  595. /*-{
  596. var height;
  597. if (element.getBoundingClientRect != null) {
  598. var rect = element.getBoundingClientRect();
  599. height = Math.ceil(rect.bottom - rect.top);
  600. } else {
  601. height = element.offsetHeight;
  602. }
  603. return height;
  604. }-*/;
  605. public static int getRequiredWidth(Widget widget) {
  606. return getRequiredWidth(widget.getElement());
  607. }
  608. public static int getRequiredHeight(Widget widget) {
  609. return getRequiredHeight(widget.getElement());
  610. }
  611. /**
  612. * Detects what is currently the overflow style attribute in given element.
  613. *
  614. * @param pe
  615. * the element to detect
  616. * @return true if auto or scroll
  617. */
  618. public static boolean mayHaveScrollBars(com.google.gwt.dom.client.Element pe) {
  619. String overflow = getComputedStyle(pe, "overflow");
  620. if (overflow != null) {
  621. if (overflow.equals("auto") || overflow.equals("scroll")) {
  622. return true;
  623. } else {
  624. return false;
  625. }
  626. } else {
  627. return false;
  628. }
  629. }
  630. /**
  631. * A simple helper method to detect "computed style" (aka style sheets +
  632. * element styles). Values returned differ a lot depending on browsers.
  633. * Always be very careful when using this.
  634. *
  635. * @param el
  636. * the element from which the style property is detected
  637. * @param p
  638. * the property to detect
  639. * @return String value of style property
  640. */
  641. private static native String getComputedStyle(
  642. com.google.gwt.dom.client.Element el, String p)
  643. /*-{
  644. try {
  645. if (el.currentStyle) {
  646. // IE
  647. return el.currentStyle[p];
  648. } else if (window.getComputedStyle) {
  649. // Sa, FF, Opera
  650. var view = el.ownerDocument.defaultView;
  651. return view.getComputedStyle(el,null).getPropertyValue(p);
  652. } else {
  653. // fall back for non IE, Sa, FF, Opera
  654. return "";
  655. }
  656. } catch (e) {
  657. return "";
  658. }
  659. }-*/;
  660. /**
  661. * IE7 sometimes "forgets" to render content. This function runs a hack to
  662. * workaround the bug if needed. This happens easily in framset. See #3295.
  663. */
  664. public static void runIE7ZeroSizedBodyFix() {
  665. if (BrowserInfo.get().isIE7()) {
  666. int offsetWidth = RootPanel.getBodyElement().getOffsetWidth();
  667. if (offsetWidth == 0) {
  668. shakeBodyElement();
  669. }
  670. }
  671. }
  672. /**
  673. * Does some very small adjustments to body element. We need this just to
  674. * overcome some IE bugs.
  675. */
  676. public static void shakeBodyElement() {
  677. final DivElement shaker = Document.get().createDivElement();
  678. RootPanel.getBodyElement().insertBefore(shaker,
  679. RootPanel.getBodyElement().getFirstChildElement());
  680. shaker.getStyle().setPropertyPx("height", 0);
  681. shaker.setInnerHTML("&nbsp;");
  682. RootPanel.getBodyElement().removeChild(shaker);
  683. }
  684. /**
  685. * Locates the child component of <literal>parent</literal> which contains
  686. * the element <literal>element</literal>. The child component is also
  687. * returned if "element" is part of its caption. If
  688. * <literal>element</literal> is not part of any child component, null is
  689. * returned.
  690. *
  691. * @param client
  692. * A reference to ApplicationConnection
  693. * @param parent
  694. * The widget that contains <literal>element</literal>.
  695. * @param element
  696. * An element that is a sub element of the parent
  697. * @return The Paintable which the element is a part of. Null if the element
  698. * does not belong to a child.
  699. */
  700. public static Paintable getChildPaintableForElement(
  701. ApplicationConnection client, Container parent, Element element) {
  702. Element rootElement = ((Widget) parent).getElement();
  703. while (element != null && element != rootElement) {
  704. Paintable paintable = client.getPaintable(element);
  705. if (paintable == null) {
  706. String ownerPid = VCaption.getCaptionOwnerPid(element);
  707. if (ownerPid != null) {
  708. paintable = client.getPaintable(ownerPid);
  709. }
  710. }
  711. if (paintable != null) {
  712. try {
  713. if (parent.hasChildComponent((Widget) paintable)) {
  714. return paintable;
  715. }
  716. } catch (ClassCastException e) {
  717. // We assume everything is a widget however there is no need
  718. // to crash everything if there is a paintable that is not.
  719. }
  720. }
  721. element = (Element) element.getParentElement();
  722. }
  723. return null;
  724. }
  725. /**
  726. * Will (attempt) to focus the given DOM Element.
  727. *
  728. * @param el
  729. * the element to focus
  730. */
  731. public static native void focus(Element el)
  732. /*-{
  733. try {
  734. el.focus();
  735. } catch (e) {
  736. }
  737. }-*/;
  738. }