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.

ICustomLayout.java 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. package com.itmill.toolkit.terminal.gwt.client.ui;
  2. import java.util.HashMap;
  3. import java.util.Iterator;
  4. import com.google.gwt.user.client.DOM;
  5. import com.google.gwt.user.client.Element;
  6. import com.google.gwt.user.client.ui.ComplexPanel;
  7. import com.google.gwt.user.client.ui.Widget;
  8. import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
  9. import com.itmill.toolkit.terminal.gwt.client.Caption;
  10. import com.itmill.toolkit.terminal.gwt.client.CaptionWrapper;
  11. import com.itmill.toolkit.terminal.gwt.client.Layout;
  12. import com.itmill.toolkit.terminal.gwt.client.Paintable;
  13. import com.itmill.toolkit.terminal.gwt.client.UIDL;
  14. /**
  15. * Custom Layout implements complext layouting defined with HTML template.
  16. *
  17. * @author IT Mill
  18. *
  19. */
  20. public class ICustomLayout extends ComplexPanel implements Paintable, Layout {
  21. /** Location-name to containing element in DOM map */
  22. private HashMap locationToElement = new HashMap();
  23. /** Location-name to contained widget map */
  24. private HashMap locationToWidget = new HashMap();
  25. /** Widget to captionwrapper map */
  26. private HashMap widgetToCaptionWrapper = new HashMap();
  27. /** Currently rendered style */
  28. String currentStyle;
  29. /** Unexecuted scripts loaded from the template */
  30. private String scripts = "";
  31. /** Paintable ID of this paintable */
  32. private String pid;
  33. private ApplicationConnection client;
  34. public ICustomLayout() {
  35. setElement(DOM.createDiv());
  36. }
  37. /**
  38. * Sets widget to given location.
  39. *
  40. * If location already contains a widget it will be removed.
  41. *
  42. * @param widget Widget to be set into location.
  43. * @param location location name where widget will be added
  44. *
  45. * @throws IllegalArgumentException
  46. * if no such location is found in the layout.
  47. */
  48. public void setWidget(Widget widget, String location) {
  49. if (widget == null)
  50. return;
  51. // If no given location is found in the layout, and exception is throws
  52. Element elem = (Element) locationToElement.get(location);
  53. if (elem == null) {
  54. throw new IllegalArgumentException("No location " + location
  55. + " found");
  56. }
  57. // Get previous widget
  58. Widget previous = (Widget) locationToWidget.get(location);
  59. // NOP if given widget already exists in this location
  60. if (previous == widget)
  61. return;
  62. remove(previous);
  63. // Add widget to location
  64. super.add(widget, elem);
  65. locationToWidget.put(location, widget);
  66. }
  67. /** Update the layout from UIDL */
  68. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  69. this.client = client;
  70. // Client manages general cases
  71. if (client.updateComponent(this, uidl, false))
  72. return;
  73. // Update PID
  74. pid = uidl.getId();
  75. // Update HTML template if needed
  76. updateHTML(uidl, client);
  77. // For all contained widgets
  78. for (Iterator i = uidl.getChildIterator(); i.hasNext();) {
  79. UIDL uidlForChild = (UIDL) i.next();
  80. if (uidlForChild.getTag().equals("location")) {
  81. String location = uidlForChild.getStringAttribute("name");
  82. Widget child = client.getWidget(uidlForChild.getChildUIDL(0));
  83. try {
  84. setWidget(child, location);
  85. ((Paintable) child).updateFromUIDL(uidlForChild
  86. .getChildUIDL(0), client);
  87. } catch (IllegalArgumentException e) {
  88. // If no location is found, this component is not visible
  89. }
  90. }
  91. }
  92. }
  93. /** Update implementing HTML-layout if needed. */
  94. private void updateHTML(UIDL uidl, ApplicationConnection client) {
  95. // Update only if style has changed
  96. String newStyle = uidl.getStringAttribute("style");
  97. if (currentStyle != null && currentStyle.equals(newStyle))
  98. return;
  99. // Get the HTML-template from client
  100. String template = client.getResource("layout/" + newStyle + ".html");
  101. if (template == null) {
  102. template = "Layout file layout/" + newStyle + ".html is missing.";
  103. } else {
  104. currentStyle = newStyle;
  105. }
  106. // Connect body of the template to DOM
  107. template = extractBodyAndScriptsFromTemplate(template);
  108. DOM.setInnerHTML(getElement(), template);
  109. // Remap locations to elements
  110. locationToElement.clear();
  111. scanForLocations(getElement());
  112. // Remap image srcs in layout
  113. Widget parent = getParent();
  114. while (parent != null && !(parent instanceof IWindow))
  115. parent = parent.getParent();
  116. if (parent != null && ((IWindow) parent).getTheme() != null)
  117. ;
  118. prefixImgSrcs(getElement(), "../theme/" + ((IWindow) parent).getTheme()
  119. + "/layout/");
  120. }
  121. /** Collect locations from template */
  122. private void scanForLocations(Element elem) {
  123. String location = getLocation(elem);
  124. if (location != null) {
  125. locationToElement.put(location, elem);
  126. DOM.setInnerHTML(elem, "");
  127. } else {
  128. int len = DOM.getChildCount(elem);
  129. for (int i = 0; i < len; i++) {
  130. scanForLocations(DOM.getChild(elem, i));
  131. }
  132. }
  133. }
  134. /** Get the location attribute for given element */
  135. private static native String getLocation(Element elem) /*-{
  136. return elem.getAttribute("location");
  137. }-*/;
  138. /** Scripts are evaluated when the document has been rendered */
  139. protected void onLoad() {
  140. super.onLoad();
  141. // Evaluate scripts only once
  142. if (scripts != null) {
  143. eval(scripts);
  144. scripts = null;
  145. }
  146. }
  147. /** Evaluate given script in browser document */
  148. private static native void eval(String script) /*-{
  149. try {
  150. eval("{ var document = $doc; var window = $wnd; "+ script + "}");
  151. } catch (e) {
  152. }
  153. }-*/;
  154. /** Prefix all img tag srcs with given prefix. */
  155. private static native void prefixImgSrcs(Element e, String srcPrefix) /*-{
  156. try {
  157. var divs = e.getElementsByTagName("img");
  158. var base = "" + $doc.location;
  159. var l = base.length-1;
  160. while (l >= 0 && base.charAt(l) != "/") l--;
  161. base = base.substring(0,l+1);
  162. for (var i = 0; i < divs.length; i++) {
  163. var div = divs[i];
  164. var src = div.getAttribute("src");
  165. if (src.indexOf(base) == 0) div.setAttribute("src",base + srcPrefix + src.substring(base.length));
  166. else if (src.indexOf("http") != 0) div.setAttribute("src",srcPrefix + src);
  167. }
  168. } catch (e) { alert(e + " " + srcPrefix);}
  169. }-*/;
  170. /**
  171. * Exctract body part and script tags from raw html-template.
  172. *
  173. * Saves contents of all script-tags to private property: scripts. Returns
  174. * contents of the body part for the html without script-tags. Also replaces
  175. * all _UID_ tags with an unique id-string.
  176. *
  177. * @param html
  178. * Original HTML-template received from server
  179. * @return html that is used to create the HTMLPanel.
  180. */
  181. private String extractBodyAndScriptsFromTemplate(String html) {
  182. // Replace UID:s
  183. html = html.replaceAll("_UID_", pid + "__");
  184. // Exctract script-tags
  185. scripts = "";
  186. int endOfPrevScript = 0;
  187. int nextPosToCheck = 0;
  188. String lc = html.toLowerCase();
  189. String res = "";
  190. int scriptStart = lc.indexOf("<script", nextPosToCheck);
  191. while (scriptStart > 0) {
  192. res += html.substring(endOfPrevScript, scriptStart);
  193. scriptStart = lc.indexOf(">", scriptStart);
  194. int j = lc.indexOf("</script>", scriptStart);
  195. scripts += html.substring(scriptStart + 1, j) + ";";
  196. nextPosToCheck = endOfPrevScript = j + "</script>".length();
  197. scriptStart = lc.indexOf("<script", nextPosToCheck);
  198. }
  199. res += html.substring(endOfPrevScript);
  200. // Extract body
  201. html = res;
  202. lc = html.toLowerCase();
  203. int startOfBody = lc.indexOf("<body");
  204. if (startOfBody < 0) {
  205. res = html;
  206. } else {
  207. res = "";
  208. startOfBody = lc.indexOf(">", startOfBody) + 1;
  209. int endOfBody = lc.indexOf("</body>", startOfBody);
  210. if (endOfBody > startOfBody)
  211. res = html.substring(startOfBody, endOfBody);
  212. else
  213. res = html.substring(startOfBody);
  214. }
  215. return res;
  216. }
  217. /** Replace child components */
  218. public void replaceChildComponent(Widget from, Widget to) {
  219. String location = getLocation(from);
  220. if (location == null)
  221. throw new IllegalArgumentException();
  222. setWidget(to, location);
  223. }
  224. /** Does this layout contain given child*/
  225. public boolean hasChildComponent(Widget component) {
  226. return locationToWidget.containsValue(component);
  227. }
  228. /** Update caption for given widget */
  229. public void updateCaption(Widget component, UIDL uidl) {
  230. CaptionWrapper wrapper = (CaptionWrapper) widgetToCaptionWrapper.get(component);
  231. if (Caption.isNeeded(uidl)) {
  232. if (wrapper == null) {
  233. String loc = getLocation(component);
  234. super.remove(component);
  235. wrapper = new CaptionWrapper(component);
  236. super.add(wrapper, (Element) locationToElement.get(loc));
  237. widgetToCaptionWrapper.put(component, wrapper);
  238. }
  239. wrapper.updateCaption(uidl);
  240. } else {
  241. if (wrapper != null) {
  242. String loc = getLocation(component);
  243. super.remove(wrapper);
  244. super.add(wrapper.getWidget(), (Element) locationToElement.get(loc));
  245. widgetToCaptionWrapper.remove(component);
  246. }
  247. }
  248. }
  249. /** Get the location of an widget */
  250. public String getLocation(Widget w) {
  251. for (Iterator i = locationToWidget.keySet().iterator(); i.hasNext();) {
  252. String location = (String) i.next();
  253. if (locationToWidget.get(location) == w)
  254. return location;
  255. }
  256. return null;
  257. }
  258. /** Removes given widget from the layout */
  259. public boolean remove(Widget w) {
  260. client.unregisterPaintable((Paintable) w);
  261. String location = getLocation(w);
  262. if (location != null)
  263. locationToWidget.remove(location);
  264. CaptionWrapper cw = (CaptionWrapper) widgetToCaptionWrapper.get(w);
  265. if (cw != null) {
  266. widgetToCaptionWrapper.remove(w);
  267. return super.remove(cw);
  268. } else
  269. return super.remove(w);
  270. }
  271. /** Adding widget without specifying location is not supported */
  272. public void add(Widget w) {
  273. throw new UnsupportedOperationException();
  274. }
  275. /** Clear all widgets from the layout */
  276. public void clear() {
  277. super.clear();
  278. locationToWidget.clear();
  279. widgetToCaptionWrapper.clear();
  280. }
  281. }