Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

Util.java 29KB

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