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.

IView.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.terminal.gwt.client.ui;
  5. import java.util.HashSet;
  6. import java.util.Iterator;
  7. import java.util.Set;
  8. import com.google.gwt.user.client.Command;
  9. import com.google.gwt.user.client.DOM;
  10. import com.google.gwt.user.client.DeferredCommand;
  11. import com.google.gwt.user.client.Element;
  12. import com.google.gwt.user.client.Event;
  13. import com.google.gwt.user.client.Timer;
  14. import com.google.gwt.user.client.Window;
  15. import com.google.gwt.user.client.WindowCloseListener;
  16. import com.google.gwt.user.client.WindowResizeListener;
  17. import com.google.gwt.user.client.ui.HasFocus;
  18. import com.google.gwt.user.client.ui.RootPanel;
  19. import com.google.gwt.user.client.ui.SimplePanel;
  20. import com.google.gwt.user.client.ui.Widget;
  21. import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
  22. import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
  23. import com.itmill.toolkit.terminal.gwt.client.Container;
  24. import com.itmill.toolkit.terminal.gwt.client.Focusable;
  25. import com.itmill.toolkit.terminal.gwt.client.Paintable;
  26. import com.itmill.toolkit.terminal.gwt.client.RenderSpace;
  27. import com.itmill.toolkit.terminal.gwt.client.UIDL;
  28. import com.itmill.toolkit.terminal.gwt.client.Util;
  29. /**
  30. *
  31. */
  32. public class IView extends SimplePanel implements Container,
  33. WindowResizeListener, WindowCloseListener {
  34. private static final String CLASSNAME = "i-view";
  35. private String theme;
  36. private Paintable layout;
  37. private final HashSet subWindows = new HashSet();
  38. private String id;
  39. private ShortcutActionHandler actionHandler;
  40. /** stored width for IE resize optimization */
  41. private int width;
  42. /** stored height for IE resize optimization */
  43. private int height;
  44. private ApplicationConnection connection;
  45. /**
  46. * We are postponing resize process with IE. IE bugs with scrollbars in some
  47. * situations, that causes false onWindowResized calls. With Timer we will
  48. * give IE some time to decide if it really wants to keep current size
  49. * (scrollbars).
  50. */
  51. private Timer resizeTimer;
  52. public IView(String elementId) {
  53. super();
  54. setStyleName(CLASSNAME);
  55. DOM.sinkEvents(getElement(), Event.ONKEYDOWN);
  56. // iview is focused when created so element needs tabIndex
  57. // 1 due 0 is at the end of natural tabbing order
  58. DOM.setElementProperty(getElement(), "tabIndex", "1");
  59. RootPanel.get(elementId).add(this);
  60. RootPanel.get(elementId).removeStyleName("i-app-loading");
  61. // set focus to iview element by default to listen possible keyboard
  62. // shortcuts
  63. if (BrowserInfo.get().isOpera() || BrowserInfo.get().isSafari()
  64. && BrowserInfo.get().getWebkitVersion() < 526) {
  65. // old webkits don't support focusing div elements
  66. Element fElem = DOM.createInputCheck();
  67. DOM.setStyleAttribute(fElem, "margin", "0");
  68. DOM.setStyleAttribute(fElem, "padding", "0");
  69. DOM.setStyleAttribute(fElem, "border", "0");
  70. DOM.setStyleAttribute(fElem, "outline", "0");
  71. DOM.setStyleAttribute(fElem, "width", "1px");
  72. DOM.setStyleAttribute(fElem, "height", "1px");
  73. DOM.setStyleAttribute(fElem, "position", "absolute");
  74. DOM.setStyleAttribute(fElem, "opacity", "0.1");
  75. DOM.appendChild(getElement(), fElem);
  76. focus(fElem);
  77. } else {
  78. focus(getElement());
  79. }
  80. }
  81. private static native void focus(Element el)
  82. /*-{
  83. try {
  84. el.focus();
  85. } catch (e) {
  86. }
  87. }-*/;
  88. public String getTheme() {
  89. return theme;
  90. }
  91. /**
  92. * Used to reload host page on theme changes.
  93. */
  94. private static native void reloadHostPage()
  95. /*-{
  96. $wnd.location.reload();
  97. }-*/;
  98. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  99. id = uidl.getId();
  100. boolean firstPaint = connection == null;
  101. connection = client;
  102. String newTheme = uidl.getStringAttribute("theme");
  103. if (theme != null && !newTheme.equals(theme)) {
  104. // Complete page refresh is needed due css can affect layout
  105. // calculations etc
  106. reloadHostPage();
  107. } else {
  108. theme = newTheme;
  109. }
  110. if (uidl.hasAttribute("style")) {
  111. addStyleName(uidl.getStringAttribute("style"));
  112. }
  113. if (uidl.hasAttribute("name")) {
  114. client.setWindowName(uidl.getStringAttribute("name"));
  115. }
  116. com.google.gwt.user.client.Window.setTitle(uidl
  117. .getStringAttribute("caption"));
  118. // Process children
  119. int childIndex = 0;
  120. // Open URL:s
  121. while (childIndex < uidl.getChildCount()
  122. && "open".equals(uidl.getChildUIDL(childIndex).getTag())) {
  123. final UIDL open = uidl.getChildUIDL(childIndex);
  124. final String url = open.getStringAttribute("src");
  125. final String target = open.getStringAttribute("name");
  126. if (target == null) {
  127. // This window is closing. Send close event before
  128. // going to the new url
  129. onWindowClosed();
  130. goTo(url);
  131. } else {
  132. // TODO width & height
  133. Window.open(url, target != null ? target : null, "");
  134. }
  135. childIndex++;
  136. }
  137. // Draw this application level window
  138. UIDL childUidl = uidl.getChildUIDL(childIndex);
  139. final Paintable lo = client.getPaintable(childUidl);
  140. if (layout != null) {
  141. if (layout != lo) {
  142. // remove old
  143. client.unregisterPaintable(layout);
  144. // add new
  145. setWidget((Widget) lo);
  146. layout = lo;
  147. }
  148. } else {
  149. setWidget((Widget) lo);
  150. layout = lo;
  151. }
  152. layout.updateFromUIDL(childUidl, client);
  153. // Update subwindows
  154. final HashSet removedSubWindows = new HashSet(subWindows);
  155. // Open new windows
  156. while ((childUidl = uidl.getChildUIDL(childIndex++)) != null) {
  157. if ("window".equals(childUidl.getTag())) {
  158. final Paintable w = client.getPaintable(childUidl);
  159. if (subWindows.contains(w)) {
  160. removedSubWindows.remove(w);
  161. } else {
  162. subWindows.add(w);
  163. }
  164. w.updateFromUIDL(childUidl, client);
  165. } else if ("actions".equals(childUidl.getTag())) {
  166. if (actionHandler == null) {
  167. actionHandler = new ShortcutActionHandler(id, client);
  168. }
  169. actionHandler.updateActionMap(childUidl);
  170. } else if (childUidl.getTag().equals("notifications")) {
  171. for (final Iterator it = childUidl.getChildIterator(); it
  172. .hasNext();) {
  173. final UIDL notification = (UIDL) it.next();
  174. String html = "";
  175. if (notification.hasAttribute("icon")) {
  176. final String parsedUri = client
  177. .translateToolkitUri(notification
  178. .getStringAttribute("icon"));
  179. html += "<IMG src=\"" + parsedUri + "\" />";
  180. }
  181. if (notification.hasAttribute("caption")) {
  182. html += "<H1>"
  183. + notification.getStringAttribute("caption")
  184. + "</H1>";
  185. }
  186. if (notification.hasAttribute("message")) {
  187. html += "<p>"
  188. + notification.getStringAttribute("message")
  189. + "</p>";
  190. }
  191. final String style = notification.hasAttribute("style") ? notification
  192. .getStringAttribute("style")
  193. : null;
  194. final int position = notification
  195. .getIntAttribute("position");
  196. final int delay = notification.getIntAttribute("delay");
  197. new INotification(delay).show(html, position, style);
  198. }
  199. }
  200. }
  201. // Close old windows
  202. for (final Iterator rem = removedSubWindows.iterator(); rem.hasNext();) {
  203. final IWindow w = (IWindow) rem.next();
  204. client.unregisterPaintable(w);
  205. subWindows.remove(w);
  206. w.hide();
  207. }
  208. if (uidl.hasAttribute("focused")) {
  209. final String focusPid = uidl.getStringAttribute("focused");
  210. // set focused component when render phase is finished
  211. DeferredCommand.addCommand(new Command() {
  212. public void execute() {
  213. final Paintable toBeFocused = connection
  214. .getPaintable(focusPid);
  215. /*
  216. * Two types of Widgets can be focused, either implementing
  217. * GWT HasFocus of a thinner Toolkit specific Focusable
  218. * interface.
  219. */
  220. if (toBeFocused instanceof HasFocus) {
  221. final HasFocus toBeFocusedWidget = (HasFocus) toBeFocused;
  222. toBeFocusedWidget.setFocus(true);
  223. } else if (toBeFocused instanceof Focusable) {
  224. ((Focusable) toBeFocused).focus();
  225. } else {
  226. ApplicationConnection.getConsole().log(
  227. "Could not focus component");
  228. }
  229. }
  230. });
  231. }
  232. // Add window listeners on first paint, to prevent premature
  233. // variablechanges
  234. if (firstPaint) {
  235. Window.addWindowCloseListener(this);
  236. Window.addWindowResizeListener(this);
  237. }
  238. onWindowResized(Window.getClientWidth(), Window.getClientHeight());
  239. // IE somehow fails some layout on first run, force layout
  240. // functions
  241. // client.runDescendentsLayout(this);
  242. }
  243. @Override
  244. public void onBrowserEvent(Event event) {
  245. super.onBrowserEvent(event);
  246. if (DOM.eventGetType(event) == Event.ONKEYDOWN && actionHandler != null) {
  247. actionHandler.handleKeyboardEvent(event);
  248. return;
  249. }
  250. }
  251. public void onWindowResized(int width, int height) {
  252. if (Util.isIE()) {
  253. /*
  254. * IE will give us some false resized events due bugs with
  255. * scrollbars. Postponing layout phase to see if size was really
  256. * changed.
  257. */
  258. if (resizeTimer == null) {
  259. resizeTimer = new Timer() {
  260. @Override
  261. public void run() {
  262. boolean changed = false;
  263. if (IView.this.width != getOffsetWidth()) {
  264. IView.this.width = getOffsetWidth();
  265. changed = true;
  266. ApplicationConnection.getConsole().log(
  267. "window w" + IView.this.width);
  268. }
  269. if (IView.this.height != getOffsetHeight()) {
  270. IView.this.height = getOffsetHeight();
  271. changed = true;
  272. ApplicationConnection.getConsole().log(
  273. "window h" + IView.this.height);
  274. }
  275. if (changed) {
  276. ApplicationConnection
  277. .getConsole()
  278. .log(
  279. "Running layout functions due window resize");
  280. connection.runDescendentsLayout(IView.this);
  281. }
  282. }
  283. };
  284. } else {
  285. resizeTimer.cancel();
  286. }
  287. resizeTimer.schedule(200);
  288. } else {
  289. if (width == IView.this.width && height == IView.this.height) {
  290. // No point in doing resize operations if window size has not
  291. // changed
  292. return;
  293. }
  294. IView.this.width = Window.getClientWidth();
  295. IView.this.height = Window.getClientHeight();
  296. // temporary set overflow hidden, not to let scrollbars disturb
  297. // layout functions
  298. final String overflow = DOM.getStyleAttribute(getElement(),
  299. "overflow");
  300. DOM.setStyleAttribute(getElement(), "overflow", "hidden");
  301. ApplicationConnection.getConsole().log(
  302. "Running layout functions due window resize");
  303. connection.runDescendentsLayout(this);
  304. DOM.setStyleAttribute(getElement(), "overflow", overflow);
  305. }
  306. }
  307. public native static void goTo(String url)
  308. /*-{
  309. $wnd.location = url;
  310. }-*/;
  311. public void onWindowClosed() {
  312. // Change focus on this window in order to ensure that all state is
  313. // collected from textfields
  314. ITextField.flushChangesFromFocusedTextField();
  315. // Send the closing state to server
  316. connection.updateVariable(id, "close", true, false);
  317. connection.sendPendingVariableChangesSync();
  318. }
  319. public String onWindowClosing() {
  320. return null;
  321. }
  322. private final RenderSpace myRenderSpace = new RenderSpace() {
  323. private int excessHeight = -1;
  324. private int excessWidth = -1;
  325. @Override
  326. public int getHeight() {
  327. return getElement().getOffsetHeight() - getExcessHeight();
  328. }
  329. private int getExcessHeight() {
  330. if (excessHeight < 0) {
  331. detetExessSize();
  332. }
  333. return excessHeight;
  334. }
  335. private void detetExessSize() {
  336. getElement().getStyle().setProperty("overflow", "hidden");
  337. excessHeight = getElement().getOffsetHeight()
  338. - getElement().getPropertyInt("clientHeight");
  339. excessWidth = getElement().getOffsetWidth()
  340. - getElement().getPropertyInt("clientWidth");
  341. }
  342. @Override
  343. public int getWidth() {
  344. return getElement().getOffsetWidth() - getExcessWidth();
  345. }
  346. private int getExcessWidth() {
  347. if (excessWidth < 0) {
  348. detetExessSize();
  349. }
  350. return excessWidth;
  351. }
  352. @Override
  353. public int getScrollbarSize() {
  354. return Util.getNativeScrollbarSize();
  355. }
  356. };
  357. public RenderSpace getAllocatedSpace(Widget child) {
  358. return myRenderSpace;
  359. }
  360. public boolean hasChildComponent(Widget component) {
  361. return (component != null && component == layout);
  362. }
  363. public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
  364. // TODO Auto-generated method stub
  365. }
  366. public boolean requestLayout(Set<Paintable> child) {
  367. /*
  368. * Can never propagate further and we do not want need to re-layout the
  369. * layout which has caused this request.
  370. */
  371. return true;
  372. }
  373. public void updateCaption(Paintable component, UIDL uidl) {
  374. // TODO Auto-generated method stub
  375. }
  376. }