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.

VWindow.java 40KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import java.util.ArrayList;
  6. import java.util.Iterator;
  7. import java.util.Set;
  8. import com.google.gwt.event.dom.client.DomEvent.Type;
  9. import com.google.gwt.event.shared.EventHandler;
  10. import com.google.gwt.event.shared.HandlerRegistration;
  11. import com.google.gwt.user.client.Command;
  12. import com.google.gwt.user.client.DOM;
  13. import com.google.gwt.user.client.DeferredCommand;
  14. import com.google.gwt.user.client.Element;
  15. import com.google.gwt.user.client.Event;
  16. import com.google.gwt.user.client.Window;
  17. import com.google.gwt.user.client.ui.Frame;
  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.ScrollListener;
  21. import com.google.gwt.user.client.ui.ScrollPanel;
  22. import com.google.gwt.user.client.ui.Widget;
  23. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  24. import com.vaadin.terminal.gwt.client.BrowserInfo;
  25. import com.vaadin.terminal.gwt.client.Console;
  26. import com.vaadin.terminal.gwt.client.Container;
  27. import com.vaadin.terminal.gwt.client.Paintable;
  28. import com.vaadin.terminal.gwt.client.RenderSpace;
  29. import com.vaadin.terminal.gwt.client.UIDL;
  30. import com.vaadin.terminal.gwt.client.Util;
  31. import com.vaadin.terminal.gwt.client.VDebugConsole;
  32. import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
  33. /**
  34. * "Sub window" component.
  35. *
  36. * TODO update position / scroll position / size to client
  37. *
  38. * @author IT Mill Ltd
  39. */
  40. public class VWindow extends VOverlay implements Container, ScrollListener,
  41. ShortcutActionHandlerOwner {
  42. /**
  43. * Minimum allowed height of a window. This refers to the content area, not
  44. * the outer borders.
  45. */
  46. private static final int MIN_CONTENT_AREA_HEIGHT = 100;
  47. /**
  48. * Minimum allowed width of a window. This refers to the content area, not
  49. * the outer borders.
  50. */
  51. private static final int MIN_CONTENT_AREA_WIDTH = 150;
  52. private static ArrayList<VWindow> windowOrder = new ArrayList<VWindow>();
  53. public static final String CLASSNAME = "v-window";
  54. /**
  55. * Difference between offsetWidth and inner width for the content area.
  56. */
  57. private int contentAreaBorderPadding = -1;
  58. /**
  59. * Pixels used by inner borders and paddings horizontally (calculated only
  60. * once). This is the difference between the width of the root element and
  61. * the content area, such that if root element width is set to "XYZpx" the
  62. * inner width (width-border-padding) of the content area is
  63. * X-contentAreaRootDifference.
  64. */
  65. private int contentAreaToRootDifference = -1;
  66. private static final int STACKING_OFFSET_PIXELS = 15;
  67. public static final int Z_INDEX = 10000;
  68. private Paintable layout;
  69. private Element contents;
  70. private Element header;
  71. private Element footer;
  72. private Element resizeBox;
  73. private final ScrollPanel contentPanel = new ScrollPanel();
  74. private boolean dragging;
  75. private int startX;
  76. private int startY;
  77. private int origX;
  78. private int origY;
  79. private boolean resizing;
  80. private int origW;
  81. private int origH;
  82. private Element closeBox;
  83. protected ApplicationConnection client;
  84. private String id;
  85. ShortcutActionHandler shortcutHandler;
  86. /** Last known positionx read from UIDL or updated to application connection */
  87. private int uidlPositionX = -1;
  88. /** Last known positiony read from UIDL or updated to application connection */
  89. private int uidlPositionY = -1;
  90. private boolean vaadinModality = false;
  91. private boolean resizable = true;
  92. private boolean draggable = true;
  93. private Element modalityCurtain;
  94. private Element draggingCurtain;
  95. private Element headerText;
  96. private boolean closable = true;
  97. boolean dynamicWidth = false;
  98. boolean dynamicHeight = false;
  99. boolean layoutRelativeWidth = false;
  100. boolean layoutRelativeHeight = false;
  101. // If centered (via UIDL), the window should stay in the centered -mode
  102. // until a position is received from the server, or the user moves or
  103. // resizes the window.
  104. boolean centered = false;
  105. private RenderSpace renderSpace = new RenderSpace(MIN_CONTENT_AREA_WIDTH,
  106. MIN_CONTENT_AREA_HEIGHT, true);
  107. private String width;
  108. private String height;
  109. private boolean immediate;
  110. private Element wrapper, wrapper2;
  111. private ClickEventHandler clickEventHandler = new ClickEventHandler(this,
  112. VPanel.CLICK_EVENT_IDENTIFIER) {
  113. @Override
  114. protected <H extends EventHandler> HandlerRegistration registerHandler(
  115. H handler, Type<H> type) {
  116. return addDomHandler(handler, type);
  117. }
  118. };
  119. private boolean visibilityChangesDisabled;
  120. public VWindow() {
  121. super(false, false, true); // no autohide, not modal, shadow
  122. // Different style of shadow for windows
  123. setShadowStyle("window");
  124. final int order = windowOrder.size();
  125. setWindowOrder(order);
  126. windowOrder.add(this);
  127. constructDOM();
  128. setPopupPosition(order * STACKING_OFFSET_PIXELS, order
  129. * STACKING_OFFSET_PIXELS);
  130. contentPanel.addScrollListener(this);
  131. // make it focusable, but last in focus chain
  132. DOM.setElementProperty(contentPanel.getElement(), "tabIndex", "0");
  133. }
  134. private void bringToFront() {
  135. int curIndex = windowOrder.indexOf(this);
  136. if (curIndex + 1 < windowOrder.size()) {
  137. windowOrder.remove(this);
  138. windowOrder.add(this);
  139. for (; curIndex < windowOrder.size(); curIndex++) {
  140. windowOrder.get(curIndex).setWindowOrder(curIndex);
  141. }
  142. }
  143. }
  144. /**
  145. * Returns true if window is the topmost window
  146. *
  147. * @return
  148. */
  149. private boolean isActive() {
  150. return windowOrder.get(windowOrder.size() - 1).equals(this);
  151. }
  152. public void setWindowOrder(int order) {
  153. setZIndex(order + Z_INDEX);
  154. }
  155. @Override
  156. protected void setZIndex(int zIndex) {
  157. super.setZIndex(zIndex);
  158. if (vaadinModality) {
  159. DOM.setStyleAttribute(modalityCurtain, "zIndex", "" + zIndex);
  160. }
  161. }
  162. protected void constructDOM() {
  163. setStyleName(CLASSNAME);
  164. header = DOM.createDiv();
  165. DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader");
  166. headerText = DOM.createDiv();
  167. DOM.setElementProperty(headerText, "className", CLASSNAME + "-header");
  168. contents = DOM.createDiv();
  169. DOM.setElementProperty(contents, "className", CLASSNAME + "-contents");
  170. footer = DOM.createDiv();
  171. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
  172. resizeBox = DOM.createDiv();
  173. DOM.setElementProperty(resizeBox, "className", CLASSNAME + "-resizebox");
  174. closeBox = DOM.createDiv();
  175. DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox");
  176. DOM.appendChild(footer, resizeBox);
  177. DOM.sinkEvents(getElement(), Event.ONLOSECAPTURE);
  178. DOM.sinkEvents(closeBox, Event.ONCLICK);
  179. DOM.sinkEvents(contents, Event.ONCLICK);
  180. wrapper = DOM.createDiv();
  181. DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap");
  182. wrapper2 = DOM.createDiv();
  183. DOM.setElementProperty(wrapper2, "className", CLASSNAME + "-wrap2");
  184. DOM.sinkEvents(wrapper, Event.ONKEYDOWN);
  185. DOM.appendChild(wrapper2, closeBox);
  186. DOM.appendChild(wrapper2, header);
  187. DOM.appendChild(header, headerText);
  188. DOM.appendChild(wrapper2, contents);
  189. DOM.appendChild(wrapper2, footer);
  190. DOM.appendChild(wrapper, wrapper2);
  191. DOM.appendChild(super.getContainerElement(), wrapper);
  192. sinkEvents(Event.MOUSEEVENTS);
  193. setWidget(contentPanel);
  194. }
  195. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  196. id = uidl.getId();
  197. this.client = client;
  198. // Workaround needed for Testing Tools (GWT generates window DOM
  199. // slightly different in different browsers).
  200. DOM.setElementProperty(closeBox, "id", id + "_window_close");
  201. if (uidl.hasAttribute("invisible")) {
  202. hide();
  203. return;
  204. }
  205. if (!uidl.hasAttribute("cached")) {
  206. if (uidl.getBooleanAttribute("modal") != vaadinModality) {
  207. setVaadinModality(!vaadinModality);
  208. }
  209. if (!isAttached()) {
  210. setVisible(false); // hide until possible centering
  211. show();
  212. }
  213. if (uidl.getBooleanAttribute("resizable") != resizable) {
  214. setResizable(!resizable);
  215. }
  216. setDraggable(!uidl.hasAttribute("fixedposition"));
  217. }
  218. visibilityChangesDisabled = true;
  219. if (client.updateComponent(this, uidl, false)) {
  220. return;
  221. }
  222. visibilityChangesDisabled = false;
  223. clickEventHandler.handleEventHandlerRegistration(client);
  224. immediate = uidl.hasAttribute("immediate");
  225. setClosable(!uidl.getBooleanAttribute("readonly"));
  226. // Initialize the position form UIDL
  227. int positionx = uidl.getIntVariable("positionx");
  228. int positiony = uidl.getIntVariable("positiony");
  229. if (positionx >= 0 || positiony >= 0) {
  230. if (positionx < 0) {
  231. positionx = 0;
  232. }
  233. if (positiony < 0) {
  234. positiony = 0;
  235. }
  236. setPopupPosition(positionx, positiony);
  237. }
  238. if (uidl.hasAttribute("caption")) {
  239. setCaption(uidl.getStringAttribute("caption"),
  240. uidl.getStringAttribute("icon"));
  241. }
  242. boolean showingUrl = false;
  243. int childIndex = 0;
  244. UIDL childUidl = uidl.getChildUIDL(childIndex++);
  245. while ("open".equals(childUidl.getTag())) {
  246. // TODO multiple opens with the same target will in practice just
  247. // open the last one - should we fix that somehow?
  248. final String parsedUri = client.translateVaadinUri(childUidl
  249. .getStringAttribute("src"));
  250. if (!childUidl.hasAttribute("name")) {
  251. final Frame frame = new Frame();
  252. DOM.setStyleAttribute(frame.getElement(), "width", "100%");
  253. DOM.setStyleAttribute(frame.getElement(), "height", "100%");
  254. DOM.setStyleAttribute(frame.getElement(), "border", "0px");
  255. frame.setUrl(parsedUri);
  256. contentPanel.setWidget(frame);
  257. showingUrl = true;
  258. } else {
  259. final String target = childUidl.getStringAttribute("name");
  260. Window.open(parsedUri, target, "");
  261. }
  262. childUidl = uidl.getChildUIDL(childIndex++);
  263. }
  264. final Paintable lo = client.getPaintable(childUidl);
  265. if (layout != null) {
  266. if (layout != lo) {
  267. // remove old
  268. client.unregisterPaintable(layout);
  269. contentPanel.remove((Widget) layout);
  270. // add new
  271. if (!showingUrl) {
  272. contentPanel.setWidget((Widget) lo);
  273. }
  274. layout = lo;
  275. }
  276. } else if (!showingUrl) {
  277. contentPanel.setWidget((Widget) lo);
  278. layout = lo;
  279. }
  280. dynamicWidth = !uidl.hasAttribute("width");
  281. dynamicHeight = !uidl.hasAttribute("height");
  282. layoutRelativeWidth = uidl.hasAttribute("layoutRelativeWidth");
  283. layoutRelativeHeight = uidl.hasAttribute("layoutRelativeHeight");
  284. if (dynamicWidth && layoutRelativeWidth) {
  285. /*
  286. * Relative layout width, fix window width before rendering (width
  287. * according to caption)
  288. */
  289. setNaturalWidth();
  290. }
  291. layout.updateFromUIDL(childUidl, client);
  292. if (!dynamicHeight && layoutRelativeWidth) {
  293. /*
  294. * Relative layout width, and fixed height. Must update the size to
  295. * be able to take scrollbars into account (layout gets narrower
  296. * space if it is higher than the window) -> only vertical scrollbar
  297. */
  298. client.runDescendentsLayout(this);
  299. }
  300. /*
  301. * No explicit width is set and the layout does not have relative width
  302. * so fix the size according to the layout.
  303. */
  304. if (dynamicWidth && !layoutRelativeWidth) {
  305. setNaturalWidth();
  306. }
  307. if (dynamicHeight && layoutRelativeHeight) {
  308. // Prevent resizing until height has been fixed
  309. resizable = false;
  310. }
  311. // we may have actions and notifications
  312. if (uidl.getChildCount() > 1) {
  313. final int cnt = uidl.getChildCount();
  314. for (int i = 1; i < cnt; i++) {
  315. childUidl = uidl.getChildUIDL(i);
  316. if (childUidl.getTag().equals("actions")) {
  317. if (shortcutHandler == null) {
  318. shortcutHandler = new ShortcutActionHandler(id, client);
  319. }
  320. shortcutHandler.updateActionMap(childUidl);
  321. } else if (childUidl.getTag().equals("notifications")) {
  322. // TODO needed? move ->
  323. for (final Iterator it = childUidl.getChildIterator(); it
  324. .hasNext();) {
  325. final UIDL notification = (UIDL) it.next();
  326. String html = "";
  327. if (notification.hasAttribute("icon")) {
  328. final String parsedUri = client
  329. .translateVaadinUri(notification
  330. .getStringAttribute("icon"));
  331. html += "<img src=\"" + parsedUri + "\" />";
  332. }
  333. if (notification.hasAttribute("caption")) {
  334. html += "<h1>"
  335. + notification
  336. .getStringAttribute("caption")
  337. + "</h1>";
  338. }
  339. if (notification.hasAttribute("message")) {
  340. html += "<p>"
  341. + notification
  342. .getStringAttribute("message")
  343. + "</p>";
  344. }
  345. final String style = notification.hasAttribute("style") ? notification
  346. .getStringAttribute("style") : null;
  347. final int position = notification
  348. .getIntAttribute("position");
  349. final int delay = notification.getIntAttribute("delay");
  350. new VNotification(delay).show(html, position, style);
  351. }
  352. }
  353. }
  354. }
  355. // setting scrollposition must happen after children is rendered
  356. contentPanel.setScrollPosition(uidl.getIntVariable("scrollTop"));
  357. contentPanel.setHorizontalScrollPosition(uidl
  358. .getIntVariable("scrollLeft"));
  359. // Center this window on screen if requested
  360. // This has to be here because we might not know the content size before
  361. // everything is painted into the window
  362. if (uidl.getBooleanAttribute("center")) {
  363. // mark as centered - this is unset on move/resize
  364. centered = true;
  365. center();
  366. } else {
  367. // don't try to center the window anymore
  368. centered = false;
  369. }
  370. updateShadowSizeAndPosition();
  371. setVisible(true);
  372. boolean sizeReduced = false;
  373. // ensure window is not larger than browser window
  374. if (getOffsetWidth() > Window.getClientWidth()) {
  375. setWidth(Window.getClientWidth() + "px");
  376. sizeReduced = true;
  377. }
  378. if (getOffsetHeight() > Window.getClientHeight()) {
  379. setHeight(Window.getClientHeight() + "px");
  380. sizeReduced = true;
  381. }
  382. if (dynamicHeight && layoutRelativeHeight) {
  383. /*
  384. * Window height is undefined, layout is 100% high so the layout
  385. * should define the initial window height but on resize the layout
  386. * should be as high as the window. We fix the height to deal with
  387. * this.
  388. */
  389. int h = contents.getOffsetHeight() + getExtraHeight();
  390. int w = getElement().getOffsetWidth();
  391. client.updateVariable(id, "height", h, false);
  392. client.updateVariable(id, "width", w, true);
  393. }
  394. if (sizeReduced) {
  395. // If we changed the size we need to update the size of the child
  396. // component if it is relative (#3407)
  397. client.runDescendentsLayout(this);
  398. }
  399. Util.runWebkitOverflowAutoFix(contentPanel.getElement());
  400. client.getView().scrollIntoView(uidl);
  401. }
  402. @Override
  403. public void setVisible(boolean visible) {
  404. /*
  405. * Visibility with VWindow works differently than with other Paintables
  406. * in Vaadin. Invisible VWindows are not attached to DOM at all. Flag is
  407. * used to avoid visibility call from
  408. * ApplicationConnection.updateComponent();
  409. */
  410. if (!visibilityChangesDisabled) {
  411. super.setVisible(visible);
  412. }
  413. }
  414. private void setDraggable(boolean draggable) {
  415. if (this.draggable == draggable) {
  416. return;
  417. }
  418. this.draggable = draggable;
  419. if (!this.draggable) {
  420. header.getStyle().setProperty("cursor", "default");
  421. } else {
  422. header.getStyle().setProperty("cursor", "");
  423. }
  424. }
  425. private void setNaturalWidth() {
  426. /*
  427. * Use max(layout width, window width) i.e layout content width or
  428. * caption width. We remove the previous set width so the width is
  429. * allowed to shrink. All widths are measured as outer sizes, i.e. the
  430. * borderWidth is added to the content.
  431. */
  432. DOM.setStyleAttribute(getElement(), "width", "");
  433. String oldHeaderWidth = ""; // Only for IE6
  434. if (BrowserInfo.get().isIE6()) {
  435. /*
  436. * For some reason IE6 has title DIV set to width 100% which
  437. * interferes with the header measuring. Also IE6 has width set to
  438. * the contentPanel.
  439. */
  440. oldHeaderWidth = headerText.getStyle().getProperty("width");
  441. DOM.setStyleAttribute(contentPanel.getElement(), "width", "auto");
  442. DOM.setStyleAttribute(contentPanel.getElement(), "zoom", "1");
  443. headerText.getStyle().setProperty("width", "auto");
  444. }
  445. // Content
  446. int contentWidth = contentPanel.getElement().getScrollWidth();
  447. contentWidth += getContentAreaToRootDifference();
  448. // Window width (caption)
  449. int windowCaptionWidth = getOffsetWidth();
  450. int naturalWidth = (contentWidth > windowCaptionWidth ? contentWidth
  451. : windowCaptionWidth);
  452. if (BrowserInfo.get().isIE6()) {
  453. headerText.getStyle().setProperty("width", oldHeaderWidth);
  454. }
  455. setWidth(naturalWidth + "px");
  456. }
  457. private int getContentAreaToRootDifference() {
  458. if (contentAreaToRootDifference < 0) {
  459. measure();
  460. }
  461. return contentAreaToRootDifference;
  462. }
  463. private int getContentAreaBorderPadding() {
  464. if (contentAreaBorderPadding < 0) {
  465. measure();
  466. }
  467. return contentAreaBorderPadding;
  468. }
  469. private void measure() {
  470. if (!isAttached()) {
  471. return;
  472. }
  473. contentAreaBorderPadding = Util.measureHorizontalPaddingAndBorder(
  474. contents, 4);
  475. int wrapperPaddingBorder = Util.measureHorizontalPaddingAndBorder(
  476. wrapper, 0)
  477. + Util.measureHorizontalPaddingAndBorder(wrapper2, 0);
  478. contentAreaToRootDifference = wrapperPaddingBorder
  479. + contentAreaBorderPadding;
  480. }
  481. /**
  482. * Sets the closable state of the window. Additionally hides/shows the close
  483. * button according to the new state.
  484. *
  485. * @param closable
  486. * true if the window can be closed by the user
  487. */
  488. protected void setClosable(boolean closable) {
  489. if (this.closable == closable) {
  490. return;
  491. }
  492. this.closable = closable;
  493. if (closable) {
  494. DOM.setStyleAttribute(closeBox, "display", "");
  495. } else {
  496. DOM.setStyleAttribute(closeBox, "display", "none");
  497. }
  498. }
  499. /**
  500. * Returns the closable state of the sub window. If the sub window is
  501. * closable a decoration (typically an X) is shown to the user. By clicking
  502. * on the X the user can close the window.
  503. *
  504. * @return true if the sub window is closable
  505. */
  506. protected boolean isClosable() {
  507. return closable;
  508. }
  509. @Override
  510. public void show() {
  511. if (vaadinModality) {
  512. showModalityCurtain();
  513. }
  514. super.show();
  515. setFF2CaretFixEnabled(true);
  516. fixFF3OverflowBug();
  517. }
  518. /** Disable overflow auto with FF3 to fix #1837. */
  519. private void fixFF3OverflowBug() {
  520. if (BrowserInfo.get().isFF3()) {
  521. DeferredCommand.addCommand(new Command() {
  522. public void execute() {
  523. DOM.setStyleAttribute(getElement(), "overflow", "");
  524. }
  525. });
  526. }
  527. }
  528. /**
  529. * Fix "missing cursor" browser bug workaround for FF2 in Windows and Linux.
  530. *
  531. * Calling this method has no effect on other browsers than the ones based
  532. * on Gecko 1.8
  533. *
  534. * @param enable
  535. */
  536. private void setFF2CaretFixEnabled(boolean enable) {
  537. if (BrowserInfo.get().isFF2()) {
  538. if (enable) {
  539. DeferredCommand.addCommand(new Command() {
  540. public void execute() {
  541. DOM.setStyleAttribute(getElement(), "overflow", "auto");
  542. }
  543. });
  544. } else {
  545. DOM.setStyleAttribute(getElement(), "overflow", "");
  546. }
  547. }
  548. }
  549. @Override
  550. public void hide() {
  551. if (vaadinModality) {
  552. hideModalityCurtain();
  553. }
  554. super.hide();
  555. }
  556. private void setVaadinModality(boolean modality) {
  557. vaadinModality = modality;
  558. if (vaadinModality) {
  559. modalityCurtain = DOM.createDiv();
  560. DOM.setElementProperty(modalityCurtain, "className", CLASSNAME
  561. + "-modalitycurtain");
  562. if (isAttached()) {
  563. showModalityCurtain();
  564. bringToFront();
  565. } else {
  566. DeferredCommand.addCommand(new Command() {
  567. public void execute() {
  568. // vaadinModality window must on top of others
  569. bringToFront();
  570. }
  571. });
  572. }
  573. } else {
  574. if (modalityCurtain != null) {
  575. if (isAttached()) {
  576. hideModalityCurtain();
  577. }
  578. modalityCurtain = null;
  579. }
  580. }
  581. }
  582. private void showModalityCurtain() {
  583. if (BrowserInfo.get().isFF2()) {
  584. DOM.setStyleAttribute(
  585. modalityCurtain,
  586. "height",
  587. DOM.getElementPropertyInt(RootPanel.getBodyElement(),
  588. "offsetHeight") + "px");
  589. DOM.setStyleAttribute(modalityCurtain, "position", "absolute");
  590. }
  591. DOM.setStyleAttribute(modalityCurtain, "zIndex",
  592. "" + (windowOrder.indexOf(this) + Z_INDEX));
  593. DOM.appendChild(RootPanel.getBodyElement(), modalityCurtain);
  594. }
  595. private void hideModalityCurtain() {
  596. DOM.removeChild(RootPanel.getBodyElement(), modalityCurtain);
  597. }
  598. /*
  599. * Shows (or hides) an empty div on top of all other content; used when
  600. * resizing or moving, so that iframes (etc) do not steal event.
  601. */
  602. private void showDraggingCurtain(boolean show) {
  603. if (show && draggingCurtain == null) {
  604. setFF2CaretFixEnabled(false); // makes FF2 slow
  605. draggingCurtain = DOM.createDiv();
  606. DOM.setStyleAttribute(draggingCurtain, "position", "absolute");
  607. DOM.setStyleAttribute(draggingCurtain, "top", "0px");
  608. DOM.setStyleAttribute(draggingCurtain, "left", "0px");
  609. DOM.setStyleAttribute(draggingCurtain, "width", "100%");
  610. DOM.setStyleAttribute(draggingCurtain, "height", "100%");
  611. DOM.setStyleAttribute(draggingCurtain, "zIndex", ""
  612. + VOverlay.Z_INDEX);
  613. DOM.appendChild(RootPanel.getBodyElement(), draggingCurtain);
  614. } else if (!show && draggingCurtain != null) {
  615. setFF2CaretFixEnabled(true); // makes FF2 slow
  616. DOM.removeChild(RootPanel.getBodyElement(), draggingCurtain);
  617. draggingCurtain = null;
  618. }
  619. }
  620. private void setResizable(boolean resizability) {
  621. resizable = resizability;
  622. if (resizability) {
  623. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
  624. DOM.setElementProperty(resizeBox, "className", CLASSNAME
  625. + "-resizebox");
  626. } else {
  627. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer "
  628. + CLASSNAME + "-footer-noresize");
  629. DOM.setElementProperty(resizeBox, "className", CLASSNAME
  630. + "-resizebox " + CLASSNAME + "-resizebox-disabled");
  631. }
  632. }
  633. @Override
  634. public void setPopupPosition(int left, int top) {
  635. if (top < 0) {
  636. // ensure window is not moved out of browser window from top of the
  637. // screen
  638. top = 0;
  639. }
  640. super.setPopupPosition(left, top);
  641. if (left != uidlPositionX && client != null) {
  642. client.updateVariable(id, "positionx", left, false);
  643. uidlPositionX = left;
  644. }
  645. if (top != uidlPositionY && client != null) {
  646. client.updateVariable(id, "positiony", top, false);
  647. uidlPositionY = top;
  648. }
  649. }
  650. public void setCaption(String c) {
  651. setCaption(c, null);
  652. }
  653. public void setCaption(String c, String icon) {
  654. String html = Util.escapeHTML(c);
  655. if (icon != null) {
  656. icon = client.translateVaadinUri(icon);
  657. html = "<img src=\"" + icon + "\" class=\"v-icon\" />" + html;
  658. }
  659. DOM.setInnerHTML(headerText, html);
  660. }
  661. @Override
  662. protected Element getContainerElement() {
  663. // in GWT 1.5 this method is used in PopupPanel constructor
  664. if (contents == null) {
  665. return super.getContainerElement();
  666. }
  667. return contents;
  668. }
  669. @Override
  670. public void onBrowserEvent(final Event event) {
  671. boolean bubble = true;
  672. if (event != null) {
  673. final int type = event.getTypeInt();
  674. if (type == Event.ONKEYDOWN && shortcutHandler != null) {
  675. shortcutHandler.handleKeyboardEvent(event);
  676. return;
  677. }
  678. final Element target = DOM.eventGetTarget(event);
  679. // Handle window caption tooltips
  680. if (client != null && DOM.isOrHasChild(header, target)) {
  681. client.handleTooltipEvent(event, this);
  682. }
  683. if (resizing || resizeBox == target) {
  684. onResizeEvent(event);
  685. bubble = false;
  686. } else if (target == closeBox) {
  687. if (type == Event.ONCLICK && isClosable()) {
  688. onCloseClick();
  689. bubble = false;
  690. }
  691. } else if (dragging || !DOM.isOrHasChild(contents, target)) {
  692. onDragEvent(event);
  693. bubble = false;
  694. } else if (type == Event.ONCLICK) {
  695. // clicked inside window, ensure to be on top
  696. if (!isActive()) {
  697. bringToFront();
  698. }
  699. }
  700. if (type == Event.ONMOUSEDOWN
  701. && !DOM.isOrHasChild(contentPanel.getElement(), target)) {
  702. Util.focus(contentPanel.getElement());
  703. }
  704. }
  705. if (!bubble) {
  706. event.cancelBubble(true);
  707. } else {
  708. // Super.onBrowserEvent takes care of Handlers added by the
  709. // ClickEventHandler
  710. super.onBrowserEvent(event);
  711. }
  712. }
  713. private void onCloseClick() {
  714. client.updateVariable(id, "close", true, true);
  715. }
  716. private void onResizeEvent(Event event) {
  717. if (resizable) {
  718. switch (event.getTypeInt()) {
  719. case Event.ONMOUSEDOWN:
  720. if (!isActive()) {
  721. bringToFront();
  722. }
  723. showDraggingCurtain(true);
  724. if (BrowserInfo.get().isIE()) {
  725. DOM.setStyleAttribute(resizeBox, "visibility", "hidden");
  726. }
  727. resizing = true;
  728. startX = event.getScreenX();
  729. startY = event.getScreenY();
  730. origW = getElement().getOffsetWidth();
  731. origH = getElement().getOffsetHeight();
  732. DOM.setCapture(getElement());
  733. event.preventDefault();
  734. break;
  735. case Event.ONMOUSEUP:
  736. showDraggingCurtain(false);
  737. if (BrowserInfo.get().isIE()) {
  738. DOM.setStyleAttribute(resizeBox, "visibility", "");
  739. }
  740. resizing = false;
  741. DOM.releaseCapture(getElement());
  742. setSize(event, true);
  743. break;
  744. case Event.ONLOSECAPTURE:
  745. showDraggingCurtain(false);
  746. if (BrowserInfo.get().isIE()) {
  747. DOM.setStyleAttribute(resizeBox, "visibility", "");
  748. }
  749. resizing = false;
  750. case Event.ONMOUSEMOVE:
  751. if (resizing) {
  752. centered = false;
  753. setSize(event, false);
  754. event.preventDefault();
  755. }
  756. break;
  757. default:
  758. event.preventDefault();
  759. break;
  760. }
  761. }
  762. }
  763. /**
  764. * Checks if the cursor was inside the browser content area when the event
  765. * happened.
  766. *
  767. * @param event
  768. * The event to be checked
  769. * @return true, if the cursor is inside the browser content area
  770. *
  771. * false, otherwise
  772. */
  773. private boolean cursorInsideBrowserContentArea(Event event) {
  774. if (event.getClientX() < 0 || event.getClientY() < 0) {
  775. // Outside to the left or above
  776. return false;
  777. }
  778. if (event.getClientX() > Window.getClientWidth()
  779. || event.getClientY() > Window.getClientHeight()) {
  780. // Outside to the right or below
  781. return false;
  782. }
  783. return true;
  784. }
  785. private void setSize(Event event, boolean updateVariables) {
  786. if (!cursorInsideBrowserContentArea(event)) {
  787. // Only drag while cursor is inside the browser client area
  788. return;
  789. }
  790. int w = event.getScreenX() - startX + origW;
  791. if (w < MIN_CONTENT_AREA_WIDTH + getContentAreaToRootDifference()) {
  792. w = MIN_CONTENT_AREA_WIDTH + getContentAreaToRootDifference();
  793. }
  794. int h = event.getScreenY() - startY + origH;
  795. if (h < MIN_CONTENT_AREA_HEIGHT + getExtraHeight()) {
  796. h = MIN_CONTENT_AREA_HEIGHT + getExtraHeight();
  797. }
  798. setWidth(w + "px");
  799. setHeight(h + "px");
  800. if (updateVariables) {
  801. // sending width back always as pixels, no need for unit
  802. client.updateVariable(id, "width", w, false);
  803. client.updateVariable(id, "height", h, immediate);
  804. }
  805. // Update child widget dimensions
  806. if (client != null) {
  807. client.handleComponentRelativeSize((Widget) layout);
  808. client.runDescendentsLayout((HasWidgets) layout);
  809. }
  810. Util.runWebkitOverflowAutoFix(contentPanel.getElement());
  811. }
  812. @Override
  813. /*
  814. * Width is set to the out-most element (v-window).
  815. *
  816. * This function should never be called with percentage values (it will
  817. * throw an exception)
  818. */
  819. public void setWidth(String width) {
  820. this.width = width;
  821. if (!isAttached()) {
  822. return;
  823. }
  824. if (width != null && !"".equals(width)) {
  825. int rootPixelWidth = -1;
  826. if (width.indexOf("px") < 0) {
  827. /*
  828. * Convert non-pixel values to pixels by setting the width and
  829. * then measuring it. Updates the "width" variable with the
  830. * pixel width.
  831. */
  832. DOM.setStyleAttribute(getElement(), "width", width);
  833. rootPixelWidth = getElement().getOffsetWidth();
  834. width = rootPixelWidth + "px";
  835. } else {
  836. rootPixelWidth = Integer.parseInt(width.substring(0,
  837. width.indexOf("px")));
  838. }
  839. // "width" now contains the new width in pixels
  840. if (BrowserInfo.get().isIE6()) {
  841. getElement().getStyle().setProperty("overflow", "hidden");
  842. }
  843. // Apply the new pixel width
  844. getElement().getStyle().setProperty("width", width);
  845. // Caculate the inner width of the content area
  846. int contentAreaInnerWidth = rootPixelWidth
  847. - getContentAreaToRootDifference();
  848. if (contentAreaInnerWidth < MIN_CONTENT_AREA_WIDTH) {
  849. contentAreaInnerWidth = MIN_CONTENT_AREA_WIDTH;
  850. int rootWidth = contentAreaInnerWidth
  851. + getContentAreaToRootDifference();
  852. DOM.setStyleAttribute(getElement(), "width", rootWidth + "px");
  853. }
  854. // IE6 needs the actual inner content width on the content element,
  855. // otherwise it won't wrap the content properly (no scrollbars
  856. // appear, content flows out of window)
  857. if (BrowserInfo.get().isIE6()) {
  858. DOM.setStyleAttribute(contentPanel.getElement(), "width",
  859. contentAreaInnerWidth + "px");
  860. }
  861. renderSpace.setWidth(contentAreaInnerWidth);
  862. updateShadowSizeAndPosition();
  863. }
  864. }
  865. @Override
  866. /*
  867. * Height is set to the out-most element (v-window).
  868. *
  869. * This function should never be called with percentage values (it will
  870. * throw an exception)
  871. */
  872. public void setHeight(String height) {
  873. this.height = height;
  874. if (!isAttached()) {
  875. return;
  876. }
  877. if (height != null && !"".equals(height)) {
  878. DOM.setStyleAttribute(getElement(), "height", height);
  879. int pixels = getElement().getOffsetHeight() - getExtraHeight();
  880. if (pixels < MIN_CONTENT_AREA_HEIGHT) {
  881. pixels = MIN_CONTENT_AREA_HEIGHT;
  882. int rootHeight = pixels + getExtraHeight();
  883. DOM.setStyleAttribute(getElement(), "height", (rootHeight)
  884. + "px");
  885. }
  886. renderSpace.setHeight(pixels);
  887. height = pixels + "px";
  888. contentPanel.getElement().getStyle().setProperty("height", height);
  889. updateShadowSizeAndPosition();
  890. }
  891. }
  892. private int extraH = 0;
  893. private int getExtraHeight() {
  894. extraH = header.getOffsetHeight() + footer.getOffsetHeight();
  895. return extraH;
  896. }
  897. private void onDragEvent(Event event) {
  898. switch (DOM.eventGetType(event)) {
  899. case Event.ONMOUSEDOWN:
  900. if (!isActive()) {
  901. bringToFront();
  902. }
  903. if (draggable) {
  904. showDraggingCurtain(true);
  905. dragging = true;
  906. startX = DOM.eventGetScreenX(event);
  907. startY = DOM.eventGetScreenY(event);
  908. origX = DOM.getAbsoluteLeft(getElement());
  909. origY = DOM.getAbsoluteTop(getElement());
  910. DOM.setCapture(getElement());
  911. DOM.eventPreventDefault(event);
  912. }
  913. break;
  914. case Event.ONMOUSEUP:
  915. dragging = false;
  916. showDraggingCurtain(false);
  917. DOM.releaseCapture(getElement());
  918. break;
  919. case Event.ONLOSECAPTURE:
  920. showDraggingCurtain(false);
  921. dragging = false;
  922. break;
  923. case Event.ONMOUSEMOVE:
  924. if (dragging) {
  925. centered = false;
  926. if (cursorInsideBrowserContentArea(event)) {
  927. // Only drag while cursor is inside the browser client area
  928. final int x = DOM.eventGetScreenX(event) - startX + origX;
  929. final int y = DOM.eventGetScreenY(event) - startY + origY;
  930. setPopupPosition(x, y);
  931. }
  932. DOM.eventPreventDefault(event);
  933. }
  934. break;
  935. default:
  936. break;
  937. }
  938. }
  939. @Override
  940. public boolean onEventPreview(Event event) {
  941. if (dragging) {
  942. onDragEvent(event);
  943. return false;
  944. } else if (resizing) {
  945. onResizeEvent(event);
  946. return false;
  947. } else if (vaadinModality) {
  948. // return false when modal and outside window
  949. final Element target = event.getTarget().cast();
  950. if (DOM.getCaptureElement() != null) {
  951. // Allow events when capture is set
  952. return true;
  953. }
  954. if (!DOM.isOrHasChild(getElement(), target)) {
  955. // not within the modal window, but let's see if it's in the
  956. // debug window
  957. Console console = ApplicationConnection.getConsole();
  958. if (console instanceof VDebugConsole
  959. && DOM.isOrHasChild(
  960. ((VDebugConsole) console).getElement(), target)) {
  961. return true; // allow debug-window clicks
  962. }
  963. return false;
  964. }
  965. }
  966. return true;
  967. }
  968. public void onScroll(Widget widget, int scrollLeft, int scrollTop) {
  969. client.updateVariable(id, "scrollTop", scrollTop, false);
  970. client.updateVariable(id, "scrollLeft", scrollLeft, false);
  971. }
  972. @Override
  973. public void addStyleDependentName(String styleSuffix) {
  974. // VWindow's getStyleElement() does not return the same element as
  975. // getElement(), so we need to override this.
  976. setStyleName(getElement(), getStylePrimaryName() + "-" + styleSuffix,
  977. true);
  978. }
  979. @Override
  980. protected void onAttach() {
  981. super.onAttach();
  982. setWidth(width);
  983. setHeight(height);
  984. }
  985. public RenderSpace getAllocatedSpace(Widget child) {
  986. if (child == layout) {
  987. return renderSpace;
  988. } else {
  989. // Exception ??
  990. return null;
  991. }
  992. }
  993. public boolean hasChildComponent(Widget component) {
  994. if (component == layout) {
  995. return true;
  996. } else {
  997. return false;
  998. }
  999. }
  1000. public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
  1001. contentPanel.setWidget(newComponent);
  1002. }
  1003. public boolean requestLayout(Set<Paintable> child) {
  1004. if (dynamicWidth && !layoutRelativeWidth) {
  1005. setNaturalWidth();
  1006. }
  1007. if (centered) {
  1008. center();
  1009. }
  1010. updateShadowSizeAndPosition();
  1011. // layout size change may affect its available space (scrollbars)
  1012. client.handleComponentRelativeSize((Widget) layout);
  1013. return true;
  1014. }
  1015. public void updateCaption(Paintable component, UIDL uidl) {
  1016. // NOP, window has own caption, layout captio not rendered
  1017. }
  1018. public ShortcutActionHandler getShortcutActionHandler() {
  1019. return shortcutHandler;
  1020. }
  1021. }