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 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038
  1. /*
  2. * Copyright 2000-2013 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.ui;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Comparator;
  20. import com.google.gwt.core.client.Scheduler;
  21. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  22. import com.google.gwt.dom.client.Style;
  23. import com.google.gwt.dom.client.Style.Position;
  24. import com.google.gwt.dom.client.Style.Unit;
  25. import com.google.gwt.event.dom.client.BlurEvent;
  26. import com.google.gwt.event.dom.client.BlurHandler;
  27. import com.google.gwt.event.dom.client.FocusEvent;
  28. import com.google.gwt.event.dom.client.FocusHandler;
  29. import com.google.gwt.event.dom.client.KeyDownEvent;
  30. import com.google.gwt.event.dom.client.KeyDownHandler;
  31. import com.google.gwt.event.dom.client.ScrollEvent;
  32. import com.google.gwt.event.dom.client.ScrollHandler;
  33. import com.google.gwt.user.client.Command;
  34. import com.google.gwt.user.client.DOM;
  35. import com.google.gwt.user.client.Element;
  36. import com.google.gwt.user.client.Event;
  37. import com.google.gwt.user.client.Window;
  38. import com.google.gwt.user.client.ui.Widget;
  39. import com.vaadin.client.ApplicationConnection;
  40. import com.vaadin.client.BrowserInfo;
  41. import com.vaadin.client.ConnectorMap;
  42. import com.vaadin.client.Focusable;
  43. import com.vaadin.client.LayoutManager;
  44. import com.vaadin.client.Util;
  45. import com.vaadin.client.debug.internal.VDebugWindow;
  46. import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
  47. import com.vaadin.shared.EventId;
  48. import com.vaadin.shared.ui.window.WindowMode;
  49. /**
  50. * "Sub window" component.
  51. *
  52. * @author Vaadin Ltd
  53. */
  54. public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
  55. ScrollHandler, KeyDownHandler, FocusHandler, BlurHandler, Focusable {
  56. private static ArrayList<VWindow> windowOrder = new ArrayList<VWindow>();
  57. private static boolean orderingDefered;
  58. public static final String CLASSNAME = "v-window";
  59. private static final int STACKING_OFFSET_PIXELS = 15;
  60. public static final int Z_INDEX = 10000;
  61. /** For internal use only. May be removed or replaced in the future. */
  62. public Element contents;
  63. /** For internal use only. May be removed or replaced in the future. */
  64. public Element header;
  65. /** For internal use only. May be removed or replaced in the future. */
  66. public Element footer;
  67. private Element resizeBox;
  68. /** For internal use only. May be removed or replaced in the future. */
  69. public final FocusableScrollPanel contentPanel = new FocusableScrollPanel();
  70. private boolean dragging;
  71. private int startX;
  72. private int startY;
  73. private int origX;
  74. private int origY;
  75. private boolean resizing;
  76. private int origW;
  77. private int origH;
  78. /** For internal use only. May be removed or replaced in the future. */
  79. public Element closeBox;
  80. /** For internal use only. May be removed or replaced in the future. */
  81. public Element maximizeRestoreBox;
  82. /** For internal use only. May be removed or replaced in the future. */
  83. public ApplicationConnection client;
  84. /** For internal use only. May be removed or replaced in the future. */
  85. public String id;
  86. /** For internal use only. May be removed or replaced in the future. */
  87. public ShortcutActionHandler shortcutHandler;
  88. /** Last known positionx read from UIDL or updated to application connection */
  89. private int uidlPositionX = -1;
  90. /** Last known positiony read from UIDL or updated to application connection */
  91. private int uidlPositionY = -1;
  92. /** For internal use only. May be removed or replaced in the future. */
  93. public boolean vaadinModality = false;
  94. /** For internal use only. May be removed or replaced in the future. */
  95. public boolean resizable = true;
  96. private boolean draggable = true;
  97. /** For internal use only. May be removed or replaced in the future. */
  98. public boolean resizeLazy = false;
  99. private Element modalityCurtain;
  100. private Element draggingCurtain;
  101. private Element resizingCurtain;
  102. private Element headerText;
  103. private boolean closable = true;
  104. /**
  105. * If centered (via UIDL), the window should stay in the centered -mode
  106. * until a position is received from the server, or the user moves or
  107. * resizes the window.
  108. * <p>
  109. * For internal use only. May be removed or replaced in the future.
  110. */
  111. public boolean centered = false;
  112. /** For internal use only. May be removed or replaced in the future. */
  113. public boolean immediate;
  114. private Element wrapper;
  115. /** For internal use only. May be removed or replaced in the future. */
  116. public boolean visibilityChangesDisabled;
  117. /** For internal use only. May be removed or replaced in the future. */
  118. public int bringToFrontSequence = -1;
  119. private VLazyExecutor delayedContentsSizeUpdater = new VLazyExecutor(200,
  120. new ScheduledCommand() {
  121. @Override
  122. public void execute() {
  123. updateContentsSize();
  124. }
  125. });
  126. public VWindow() {
  127. super(false, false, true); // no autohide, not modal, shadow
  128. // Different style of shadow for windows
  129. setShadowStyle("window");
  130. constructDOM();
  131. contentPanel.addScrollHandler(this);
  132. contentPanel.addKeyDownHandler(this);
  133. contentPanel.addFocusHandler(this);
  134. contentPanel.addBlurHandler(this);
  135. }
  136. public void bringToFront() {
  137. int curIndex = windowOrder.indexOf(this);
  138. if (curIndex + 1 < windowOrder.size()) {
  139. windowOrder.remove(this);
  140. windowOrder.add(this);
  141. for (; curIndex < windowOrder.size(); curIndex++) {
  142. windowOrder.get(curIndex).setWindowOrder(curIndex);
  143. }
  144. }
  145. }
  146. /**
  147. * Returns true if this window is the topmost VWindow
  148. *
  149. * @return
  150. */
  151. private boolean isActive() {
  152. return equals(getTopmostWindow());
  153. }
  154. private static VWindow getTopmostWindow() {
  155. return windowOrder.get(windowOrder.size() - 1);
  156. }
  157. /** For internal use only. May be removed or replaced in the future. */
  158. public void setWindowOrderAndPosition() {
  159. // This cannot be done in the constructor as the widgets are created in
  160. // a different order than on they should appear on screen
  161. if (windowOrder.contains(this)) {
  162. // Already set
  163. return;
  164. }
  165. final int order = windowOrder.size();
  166. setWindowOrder(order);
  167. windowOrder.add(this);
  168. setPopupPosition(order * STACKING_OFFSET_PIXELS, order
  169. * STACKING_OFFSET_PIXELS);
  170. }
  171. private void setWindowOrder(int order) {
  172. setZIndex(order + Z_INDEX);
  173. }
  174. @Override
  175. protected void setZIndex(int zIndex) {
  176. super.setZIndex(zIndex);
  177. if (vaadinModality) {
  178. DOM.setStyleAttribute(getModalityCurtain(), "zIndex", "" + zIndex);
  179. }
  180. }
  181. protected Element getModalityCurtain() {
  182. if (modalityCurtain == null) {
  183. modalityCurtain = DOM.createDiv();
  184. modalityCurtain.setClassName(CLASSNAME + "-modalitycurtain");
  185. }
  186. return modalityCurtain;
  187. }
  188. protected void constructDOM() {
  189. setStyleName(CLASSNAME);
  190. header = DOM.createDiv();
  191. DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader");
  192. headerText = DOM.createDiv();
  193. DOM.setElementProperty(headerText, "className", CLASSNAME + "-header");
  194. contents = DOM.createDiv();
  195. DOM.setElementProperty(contents, "className", CLASSNAME + "-contents");
  196. footer = DOM.createDiv();
  197. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
  198. resizeBox = DOM.createDiv();
  199. DOM.setElementProperty(resizeBox, "className", CLASSNAME + "-resizebox");
  200. closeBox = DOM.createDiv();
  201. maximizeRestoreBox = DOM.createDiv();
  202. DOM.setElementProperty(maximizeRestoreBox, "className", CLASSNAME
  203. + "-maximizebox");
  204. DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox");
  205. DOM.appendChild(footer, resizeBox);
  206. wrapper = DOM.createDiv();
  207. DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap");
  208. DOM.appendChild(wrapper, header);
  209. DOM.appendChild(wrapper, maximizeRestoreBox);
  210. DOM.appendChild(wrapper, closeBox);
  211. DOM.appendChild(header, headerText);
  212. DOM.appendChild(wrapper, contents);
  213. DOM.appendChild(wrapper, footer);
  214. DOM.appendChild(super.getContainerElement(), wrapper);
  215. sinkEvents(Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.TOUCHEVENTS
  216. | Event.ONCLICK | Event.ONLOSECAPTURE);
  217. setWidget(contentPanel);
  218. }
  219. /**
  220. * Calling this method will defer ordering algorithm, to order windows based
  221. * on servers bringToFront and modality instructions. Non changed windows
  222. * will be left intact.
  223. * <p>
  224. * For internal use only. May be removed or replaced in the future.
  225. */
  226. public static void deferOrdering() {
  227. if (!orderingDefered) {
  228. orderingDefered = true;
  229. Scheduler.get().scheduleFinally(new Command() {
  230. @Override
  231. public void execute() {
  232. doServerSideOrdering();
  233. VNotification.bringNotificationsToFront();
  234. }
  235. });
  236. }
  237. }
  238. private static void doServerSideOrdering() {
  239. orderingDefered = false;
  240. VWindow[] array = windowOrder.toArray(new VWindow[windowOrder.size()]);
  241. Arrays.sort(array, new Comparator<VWindow>() {
  242. @Override
  243. public int compare(VWindow o1, VWindow o2) {
  244. /*
  245. * Order by modality, then by bringtofront sequence.
  246. */
  247. if (o1.vaadinModality && !o2.vaadinModality) {
  248. return 1;
  249. } else if (!o1.vaadinModality && o2.vaadinModality) {
  250. return -1;
  251. } else if (o1.bringToFrontSequence > o2.bringToFrontSequence) {
  252. return 1;
  253. } else if (o1.bringToFrontSequence < o2.bringToFrontSequence) {
  254. return -1;
  255. } else {
  256. return 0;
  257. }
  258. }
  259. });
  260. for (int i = 0; i < array.length; i++) {
  261. VWindow w = array[i];
  262. if (w.bringToFrontSequence != -1 || w.vaadinModality) {
  263. w.bringToFront();
  264. w.bringToFrontSequence = -1;
  265. }
  266. }
  267. }
  268. @Override
  269. public void setVisible(boolean visible) {
  270. /*
  271. * Visibility with VWindow works differently than with other Paintables
  272. * in Vaadin. Invisible VWindows are not attached to DOM at all. Flag is
  273. * used to avoid visibility call from
  274. * ApplicationConnection.updateComponent();
  275. */
  276. if (!visibilityChangesDisabled) {
  277. super.setVisible(visible);
  278. }
  279. if (visible && BrowserInfo.get().isWebkit()) {
  280. Util.removeUnneededScrollbars(contents);
  281. updateContentsSize();
  282. positionOrSizeUpdated();
  283. }
  284. }
  285. /** For internal use only. May be removed or replaced in the future. */
  286. public void setDraggable(boolean draggable) {
  287. if (this.draggable == draggable) {
  288. return;
  289. }
  290. this.draggable = draggable;
  291. setCursorProperties();
  292. }
  293. private void setCursorProperties() {
  294. if (!draggable) {
  295. header.getStyle().setProperty("cursor", "default");
  296. footer.getStyle().setProperty("cursor", "default");
  297. } else {
  298. header.getStyle().setProperty("cursor", "");
  299. footer.getStyle().setProperty("cursor", "");
  300. }
  301. }
  302. /**
  303. * Sets the closable state of the window. Additionally hides/shows the close
  304. * button according to the new state.
  305. *
  306. * @param closable
  307. * true if the window can be closed by the user
  308. */
  309. public void setClosable(boolean closable) {
  310. if (this.closable == closable) {
  311. return;
  312. }
  313. this.closable = closable;
  314. if (closable) {
  315. DOM.setStyleAttribute(closeBox, "display", "");
  316. } else {
  317. DOM.setStyleAttribute(closeBox, "display", "none");
  318. }
  319. }
  320. /**
  321. * Returns the closable state of the sub window. If the sub window is
  322. * closable a decoration (typically an X) is shown to the user. By clicking
  323. * on the X the user can close the window.
  324. *
  325. * @return true if the sub window is closable
  326. */
  327. protected boolean isClosable() {
  328. return closable;
  329. }
  330. @Override
  331. public void show() {
  332. if (!windowOrder.contains(this)) {
  333. // This is needed if the window is hidden and then shown again.
  334. // Otherwise this VWindow is added to windowOrder in the
  335. // constructor.
  336. windowOrder.add(this);
  337. }
  338. if (vaadinModality) {
  339. showModalityCurtain();
  340. }
  341. super.show();
  342. }
  343. @Override
  344. public void hide() {
  345. /*
  346. * If the window has a RichTextArea and the RTA is focused at the time
  347. * of hiding in IE8 only the window will have some problems returning
  348. * the focus to the correct place. Curiously the focus will be returned
  349. * correctly if clicking on the "close" button in the window header but
  350. * closing the window from a button for example in the window will fail.
  351. * Symptom described in #10776
  352. *
  353. * The problematic part is that for the focus to be returned correctly
  354. * an input element needs to be focused in the root panel. Focusing some
  355. * other element apparently won't work.
  356. */
  357. if (BrowserInfo.get().isIE8()) {
  358. fixIE8FocusCaptureIssue();
  359. }
  360. if (vaadinModality) {
  361. hideModalityCurtain();
  362. }
  363. super.hide();
  364. // Remove window from windowOrder to avoid references being left
  365. // hanging.
  366. windowOrder.remove(this);
  367. }
  368. private void fixIE8FocusCaptureIssue() {
  369. Element e = DOM.createInputText();
  370. Style elemStyle = e.getStyle();
  371. elemStyle.setPosition(Position.ABSOLUTE);
  372. elemStyle.setTop(-10, Unit.PX);
  373. elemStyle.setWidth(0, Unit.PX);
  374. elemStyle.setHeight(0, Unit.PX);
  375. contentPanel.getElement().appendChild(e);
  376. e.focus();
  377. contentPanel.getElement().removeChild(e);
  378. }
  379. /** For internal use only. May be removed or replaced in the future. */
  380. public void setVaadinModality(boolean modality) {
  381. vaadinModality = modality;
  382. if (vaadinModality) {
  383. if (isAttached()) {
  384. showModalityCurtain();
  385. }
  386. deferOrdering();
  387. } else {
  388. if (modalityCurtain != null) {
  389. if (isAttached()) {
  390. hideModalityCurtain();
  391. }
  392. modalityCurtain = null;
  393. }
  394. }
  395. }
  396. private void showModalityCurtain() {
  397. DOM.setStyleAttribute(getModalityCurtain(), "zIndex",
  398. "" + (windowOrder.indexOf(this) + Z_INDEX));
  399. if (isShowing()) {
  400. getOverlayContainer().insertBefore(getModalityCurtain(),
  401. getElement());
  402. } else {
  403. getOverlayContainer().appendChild(getModalityCurtain());
  404. }
  405. }
  406. private void hideModalityCurtain() {
  407. modalityCurtain.removeFromParent();
  408. if (BrowserInfo.get().isIE()) {
  409. // IE leaks memory in certain cases unless we release the reference
  410. // (#9197)
  411. modalityCurtain = null;
  412. }
  413. }
  414. /*
  415. * Shows an empty div on top of all other content; used when moving, so that
  416. * iframes (etc) do not steal event.
  417. */
  418. private void showDraggingCurtain() {
  419. getElement().getParentElement().insertBefore(getDraggingCurtain(),
  420. getElement());
  421. }
  422. private void hideDraggingCurtain() {
  423. if (draggingCurtain != null) {
  424. draggingCurtain.removeFromParent();
  425. }
  426. }
  427. /*
  428. * Shows an empty div on top of all other content; used when resizing, so
  429. * that iframes (etc) do not steal event.
  430. */
  431. private void showResizingCurtain() {
  432. getElement().getParentElement().insertBefore(getResizingCurtain(),
  433. getElement());
  434. }
  435. private void hideResizingCurtain() {
  436. if (resizingCurtain != null) {
  437. resizingCurtain.removeFromParent();
  438. }
  439. }
  440. private Element getDraggingCurtain() {
  441. if (draggingCurtain == null) {
  442. draggingCurtain = createCurtain();
  443. draggingCurtain.setClassName(CLASSNAME + "-draggingCurtain");
  444. }
  445. return draggingCurtain;
  446. }
  447. private Element getResizingCurtain() {
  448. if (resizingCurtain == null) {
  449. resizingCurtain = createCurtain();
  450. resizingCurtain.setClassName(CLASSNAME + "-resizingCurtain");
  451. }
  452. return resizingCurtain;
  453. }
  454. private Element createCurtain() {
  455. Element curtain = DOM.createDiv();
  456. DOM.setStyleAttribute(curtain, "position", "absolute");
  457. DOM.setStyleAttribute(curtain, "top", "0px");
  458. DOM.setStyleAttribute(curtain, "left", "0px");
  459. DOM.setStyleAttribute(curtain, "width", "100%");
  460. DOM.setStyleAttribute(curtain, "height", "100%");
  461. DOM.setStyleAttribute(curtain, "zIndex", "" + VOverlay.Z_INDEX);
  462. return curtain;
  463. }
  464. /** For internal use only. May be removed or replaced in the future. */
  465. public void setResizable(boolean resizability) {
  466. resizable = resizability;
  467. if (resizability) {
  468. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
  469. DOM.setElementProperty(resizeBox, "className", CLASSNAME
  470. + "-resizebox");
  471. } else {
  472. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer "
  473. + CLASSNAME + "-footer-noresize");
  474. DOM.setElementProperty(resizeBox, "className", CLASSNAME
  475. + "-resizebox " + CLASSNAME + "-resizebox-disabled");
  476. }
  477. }
  478. public void updateMaximizeRestoreClassName(boolean visible,
  479. WindowMode windowMode) {
  480. String className;
  481. if (windowMode == WindowMode.MAXIMIZED) {
  482. className = CLASSNAME + "-restorebox";
  483. } else {
  484. className = CLASSNAME + "-maximizebox";
  485. }
  486. if (!visible) {
  487. className = className + " " + className + "-disabled";
  488. }
  489. maximizeRestoreBox.setClassName(className);
  490. }
  491. // TODO this will eventually be removed, currently used to avoid updating to
  492. // server side.
  493. public void setPopupPositionNoUpdate(int left, int top) {
  494. if (top < 0) {
  495. // ensure window is not moved out of browser window from top of the
  496. // screen
  497. top = 0;
  498. }
  499. super.setPopupPosition(left, top);
  500. }
  501. @Override
  502. public void setPopupPosition(int left, int top) {
  503. if (top < 0) {
  504. // ensure window is not moved out of browser window from top of the
  505. // screen
  506. top = 0;
  507. }
  508. super.setPopupPosition(left, top);
  509. if (left != uidlPositionX && client != null) {
  510. client.updateVariable(id, "positionx", left, false);
  511. uidlPositionX = left;
  512. }
  513. if (top != uidlPositionY && client != null) {
  514. client.updateVariable(id, "positiony", top, false);
  515. uidlPositionY = top;
  516. }
  517. }
  518. public void setCaption(String c) {
  519. setCaption(c, null);
  520. }
  521. public void setCaption(String c, String icon) {
  522. String html = Util.escapeHTML(c);
  523. if (icon != null) {
  524. icon = client.translateVaadinUri(icon);
  525. html = "<img src=\"" + Util.escapeAttribute(icon)
  526. + "\" class=\"v-icon\" />" + html;
  527. }
  528. DOM.setInnerHTML(headerText, html);
  529. }
  530. @Override
  531. protected Element getContainerElement() {
  532. // in GWT 1.5 this method is used in PopupPanel constructor
  533. if (contents == null) {
  534. return super.getContainerElement();
  535. }
  536. return contents;
  537. }
  538. private Event headerDragPending;
  539. @Override
  540. public void onBrowserEvent(final Event event) {
  541. boolean bubble = true;
  542. final int type = event.getTypeInt();
  543. final Element target = DOM.eventGetTarget(event);
  544. if (resizing || resizeBox == target) {
  545. onResizeEvent(event);
  546. bubble = false;
  547. } else if (isClosable() && target == closeBox) {
  548. if (type == Event.ONCLICK) {
  549. onCloseClick();
  550. }
  551. bubble = false;
  552. } else if (target == maximizeRestoreBox) {
  553. // handled in connector
  554. if (type != Event.ONCLICK) {
  555. bubble = false;
  556. }
  557. } else if (header.isOrHasChild(target) && !dragging) {
  558. // dblclick handled in connector
  559. if (type != Event.ONDBLCLICK && draggable) {
  560. if (type == Event.ONMOUSEDOWN) {
  561. headerDragPending = event;
  562. } else if (type == Event.ONMOUSEMOVE
  563. && headerDragPending != null) {
  564. // ie won't work unless this is set here
  565. dragging = true;
  566. onDragEvent(headerDragPending);
  567. onDragEvent(event);
  568. headerDragPending = null;
  569. } else {
  570. headerDragPending = null;
  571. }
  572. bubble = false;
  573. }
  574. } else if (dragging || !contents.isOrHasChild(target)) {
  575. onDragEvent(event);
  576. bubble = false;
  577. } else if (type == Event.ONCLICK) {
  578. // clicked inside window, ensure to be on top
  579. if (!isActive()) {
  580. bringToFront();
  581. }
  582. }
  583. /*
  584. * If clicking on other than the content, move focus to the window.
  585. * After that this windows e.g. gets all keyboard shortcuts.
  586. */
  587. if (type == Event.ONMOUSEDOWN
  588. && !contentPanel.getElement().isOrHasChild(target)
  589. && target != closeBox && target != maximizeRestoreBox) {
  590. contentPanel.focus();
  591. }
  592. if (!bubble) {
  593. event.stopPropagation();
  594. } else {
  595. // Super.onBrowserEvent takes care of Handlers added by the
  596. // ClickEventHandler
  597. super.onBrowserEvent(event);
  598. }
  599. }
  600. private void onCloseClick() {
  601. client.updateVariable(id, "close", true, true);
  602. }
  603. private void onResizeEvent(Event event) {
  604. if (resizable && Util.isTouchEventOrLeftMouseButton(event)) {
  605. switch (event.getTypeInt()) {
  606. case Event.ONMOUSEDOWN:
  607. case Event.ONTOUCHSTART:
  608. if (!isActive()) {
  609. bringToFront();
  610. }
  611. showResizingCurtain();
  612. if (BrowserInfo.get().isIE()) {
  613. DOM.setStyleAttribute(resizeBox, "visibility", "hidden");
  614. }
  615. resizing = true;
  616. startX = Util.getTouchOrMouseClientX(event);
  617. startY = Util.getTouchOrMouseClientY(event);
  618. origW = getElement().getOffsetWidth();
  619. origH = getElement().getOffsetHeight();
  620. DOM.setCapture(getElement());
  621. event.preventDefault();
  622. break;
  623. case Event.ONMOUSEUP:
  624. case Event.ONTOUCHEND:
  625. setSize(event, true);
  626. case Event.ONTOUCHCANCEL:
  627. DOM.releaseCapture(getElement());
  628. case Event.ONLOSECAPTURE:
  629. hideResizingCurtain();
  630. if (BrowserInfo.get().isIE()) {
  631. DOM.setStyleAttribute(resizeBox, "visibility", "");
  632. }
  633. resizing = false;
  634. break;
  635. case Event.ONMOUSEMOVE:
  636. case Event.ONTOUCHMOVE:
  637. if (resizing) {
  638. centered = false;
  639. setSize(event, false);
  640. event.preventDefault();
  641. }
  642. break;
  643. default:
  644. event.preventDefault();
  645. break;
  646. }
  647. }
  648. }
  649. /**
  650. * TODO check if we need to support this with touch based devices.
  651. *
  652. * Checks if the cursor was inside the browser content area when the event
  653. * happened.
  654. *
  655. * @param event
  656. * The event to be checked
  657. * @return true, if the cursor is inside the browser content area
  658. *
  659. * false, otherwise
  660. */
  661. private boolean cursorInsideBrowserContentArea(Event event) {
  662. if (event.getClientX() < 0 || event.getClientY() < 0) {
  663. // Outside to the left or above
  664. return false;
  665. }
  666. if (event.getClientX() > Window.getClientWidth()
  667. || event.getClientY() > Window.getClientHeight()) {
  668. // Outside to the right or below
  669. return false;
  670. }
  671. return true;
  672. }
  673. private void setSize(Event event, boolean updateVariables) {
  674. if (!cursorInsideBrowserContentArea(event)) {
  675. // Only drag while cursor is inside the browser client area
  676. return;
  677. }
  678. int w = Util.getTouchOrMouseClientX(event) - startX + origW;
  679. int h = Util.getTouchOrMouseClientY(event) - startY + origH;
  680. w = Math.max(w, getMinWidth());
  681. h = Math.max(h, getMinHeight());
  682. setWidth(w + "px");
  683. setHeight(h + "px");
  684. if (updateVariables) {
  685. // sending width back always as pixels, no need for unit
  686. client.updateVariable(id, "width", w, false);
  687. client.updateVariable(id, "height", h, immediate);
  688. }
  689. if (updateVariables || !resizeLazy) {
  690. // Resize has finished or is not lazy
  691. updateContentsSize();
  692. } else {
  693. // Lazy resize - wait for a while before re-rendering contents
  694. delayedContentsSizeUpdater.trigger();
  695. }
  696. }
  697. private int getMinHeight() {
  698. return getPixelValue(getElement().getStyle().getProperty("minHeight"));
  699. }
  700. private int getMinWidth() {
  701. return getPixelValue(getElement().getStyle().getProperty("minWidth"));
  702. }
  703. private static int getPixelValue(String size) {
  704. if (size == null || !size.endsWith("px")) {
  705. return -1;
  706. } else {
  707. return Integer.parseInt(size.substring(0, size.length() - 2));
  708. }
  709. }
  710. public void updateContentsSize() {
  711. LayoutManager layoutManager = getLayoutManager();
  712. layoutManager.setNeedsMeasure(ConnectorMap.get(client).getConnector(
  713. this));
  714. layoutManager.layoutNow();
  715. }
  716. @Override
  717. public void setWidth(String width) {
  718. // Override PopupPanel which sets the width to the contents
  719. getElement().getStyle().setProperty("width", width);
  720. // Update v-has-width in case undefined window is resized
  721. setStyleName("v-has-width", width != null && width.length() > 0);
  722. }
  723. @Override
  724. public void setHeight(String height) {
  725. // Override PopupPanel which sets the height to the contents
  726. getElement().getStyle().setProperty("height", height);
  727. // Update v-has-height in case undefined window is resized
  728. setStyleName("v-has-height", height != null && height.length() > 0);
  729. }
  730. private void onDragEvent(Event event) {
  731. if (!Util.isTouchEventOrLeftMouseButton(event)) {
  732. return;
  733. }
  734. switch (DOM.eventGetType(event)) {
  735. case Event.ONTOUCHSTART:
  736. if (event.getTouches().length() > 1) {
  737. return;
  738. }
  739. case Event.ONMOUSEDOWN:
  740. if (!isActive()) {
  741. bringToFront();
  742. }
  743. beginMovingWindow(event);
  744. break;
  745. case Event.ONMOUSEUP:
  746. case Event.ONTOUCHEND:
  747. case Event.ONTOUCHCANCEL:
  748. case Event.ONLOSECAPTURE:
  749. stopMovingWindow();
  750. break;
  751. case Event.ONMOUSEMOVE:
  752. case Event.ONTOUCHMOVE:
  753. moveWindow(event);
  754. break;
  755. default:
  756. break;
  757. }
  758. }
  759. private void moveWindow(Event event) {
  760. if (dragging) {
  761. centered = false;
  762. if (cursorInsideBrowserContentArea(event)) {
  763. // Only drag while cursor is inside the browser client area
  764. final int x = Util.getTouchOrMouseClientX(event) - startX
  765. + origX;
  766. final int y = Util.getTouchOrMouseClientY(event) - startY
  767. + origY;
  768. setPopupPosition(x, y);
  769. }
  770. DOM.eventPreventDefault(event);
  771. }
  772. }
  773. private void beginMovingWindow(Event event) {
  774. if (draggable) {
  775. showDraggingCurtain();
  776. dragging = true;
  777. startX = Util.getTouchOrMouseClientX(event);
  778. startY = Util.getTouchOrMouseClientY(event);
  779. origX = DOM.getAbsoluteLeft(getElement());
  780. origY = DOM.getAbsoluteTop(getElement());
  781. DOM.setCapture(getElement());
  782. DOM.eventPreventDefault(event);
  783. }
  784. }
  785. private void stopMovingWindow() {
  786. dragging = false;
  787. hideDraggingCurtain();
  788. DOM.releaseCapture(getElement());
  789. }
  790. @Override
  791. public boolean onEventPreview(Event event) {
  792. if (dragging) {
  793. onDragEvent(event);
  794. return false;
  795. } else if (resizing) {
  796. onResizeEvent(event);
  797. return false;
  798. }
  799. // TODO This is probably completely unnecessary as the modality curtain
  800. // prevents events from reaching other windows and any security check
  801. // must be done on the server side and not here.
  802. // The code here is also run many times as each VWindow has an event
  803. // preview but we cannot check only the current VWindow here (e.g.
  804. // if(isTopMost) {...}) because PopupPanel will cause all events that
  805. // are not cancelled here and target this window to be consume():d
  806. // meaning the event won't be sent to the rest of the preview handlers.
  807. if (getTopmostWindow().vaadinModality) {
  808. // Topmost window is modal. Cancel the event if it targets something
  809. // outside that window (except debug console...)
  810. if (DOM.getCaptureElement() != null) {
  811. // Allow events when capture is set
  812. return true;
  813. }
  814. final Element target = event.getEventTarget().cast();
  815. if (!DOM.isOrHasChild(getTopmostWindow().getElement(), target)) {
  816. // not within the modal window, but let's see if it's in the
  817. // debug window
  818. Widget w = Util.findWidget(target, null);
  819. while (w != null) {
  820. if (w instanceof VDebugWindow) {
  821. return true; // allow debug-window clicks
  822. } else if (ConnectorMap.get(client).isConnector(w)) {
  823. return false;
  824. }
  825. w = w.getParent();
  826. }
  827. return false;
  828. }
  829. }
  830. return true;
  831. }
  832. @Override
  833. public void addStyleDependentName(String styleSuffix) {
  834. // VWindow's getStyleElement() does not return the same element as
  835. // getElement(), so we need to override this.
  836. setStyleName(getElement(), getStylePrimaryName() + "-" + styleSuffix,
  837. true);
  838. }
  839. @Override
  840. public ShortcutActionHandler getShortcutActionHandler() {
  841. return shortcutHandler;
  842. }
  843. @Override
  844. public void onScroll(ScrollEvent event) {
  845. client.updateVariable(id, "scrollTop",
  846. contentPanel.getScrollPosition(), false);
  847. client.updateVariable(id, "scrollLeft",
  848. contentPanel.getHorizontalScrollPosition(), false);
  849. }
  850. @Override
  851. public void onKeyDown(KeyDownEvent event) {
  852. if (shortcutHandler != null) {
  853. shortcutHandler
  854. .handleKeyboardEvent(Event.as(event.getNativeEvent()));
  855. return;
  856. }
  857. }
  858. @Override
  859. public void onBlur(BlurEvent event) {
  860. if (client.hasEventListeners(this, EventId.BLUR)) {
  861. client.updateVariable(id, EventId.BLUR, "", true);
  862. }
  863. }
  864. @Override
  865. public void onFocus(FocusEvent event) {
  866. if (client.hasEventListeners(this, EventId.FOCUS)) {
  867. client.updateVariable(id, EventId.FOCUS, "", true);
  868. }
  869. }
  870. @Override
  871. public void focus() {
  872. contentPanel.focus();
  873. }
  874. private int getDecorationHeight() {
  875. LayoutManager lm = getLayoutManager();
  876. int headerHeight = lm.getOuterHeight(header);
  877. int footerHeight = lm.getOuterHeight(footer);
  878. return headerHeight + footerHeight;
  879. }
  880. private LayoutManager getLayoutManager() {
  881. return LayoutManager.get(client);
  882. }
  883. private int getDecorationWidth() {
  884. LayoutManager layoutManager = getLayoutManager();
  885. return layoutManager.getOuterWidth(getElement())
  886. - contentPanel.getElement().getOffsetWidth();
  887. }
  888. }