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.

Window.java 50KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548
  1. /*
  2. * Copyright 2000-2016 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.ui;
  17. import java.io.Serializable;
  18. import java.lang.reflect.Method;
  19. import java.util.ArrayList;
  20. import java.util.Arrays;
  21. import java.util.Collection;
  22. import java.util.Collections;
  23. import java.util.List;
  24. import java.util.Map;
  25. import org.jsoup.nodes.Element;
  26. import org.jsoup.select.Elements;
  27. import com.vaadin.event.ConnectorEventListener;
  28. import com.vaadin.event.FieldEvents.BlurEvent;
  29. import com.vaadin.event.FieldEvents.BlurListener;
  30. import com.vaadin.event.FieldEvents.BlurNotifier;
  31. import com.vaadin.event.FieldEvents.FocusEvent;
  32. import com.vaadin.event.FieldEvents.FocusListener;
  33. import com.vaadin.event.FieldEvents.FocusNotifier;
  34. import com.vaadin.event.MouseEvents.ClickEvent;
  35. import com.vaadin.event.ShortcutAction;
  36. import com.vaadin.event.ShortcutAction.KeyCode;
  37. import com.vaadin.event.ShortcutAction.ModifierKey;
  38. import com.vaadin.event.ShortcutListener;
  39. import com.vaadin.server.PaintException;
  40. import com.vaadin.server.PaintTarget;
  41. import com.vaadin.shared.Connector;
  42. import com.vaadin.shared.EventId;
  43. import com.vaadin.shared.MouseEventDetails;
  44. import com.vaadin.shared.Registration;
  45. import com.vaadin.shared.ui.window.WindowMode;
  46. import com.vaadin.shared.ui.window.WindowRole;
  47. import com.vaadin.shared.ui.window.WindowServerRpc;
  48. import com.vaadin.shared.ui.window.WindowState;
  49. import com.vaadin.ui.declarative.DesignAttributeHandler;
  50. import com.vaadin.ui.declarative.DesignContext;
  51. import com.vaadin.ui.declarative.DesignException;
  52. import com.vaadin.util.ReflectTools;
  53. /**
  54. * A component that represents a floating popup window that can be added to a
  55. * {@link UI}. A window is added to a {@code UI} using
  56. * {@link UI#addWindow(Window)}.
  57. * <p>
  58. * The contents of a window is set using {@link #setContent(Component)} or by
  59. * using the {@link #Window(String, Component)} constructor.
  60. * </p>
  61. * <p>
  62. * A window can be positioned on the screen using absolute coordinates (pixels)
  63. * or set to be centered using {@link #center()}
  64. * </p>
  65. * <p>
  66. * The caption is displayed in the window header.
  67. * </p>
  68. * <p>
  69. * In Vaadin versions prior to 7.0.0, Window was also used as application level
  70. * windows. This function is now covered by the {@link UI} class.
  71. * </p>
  72. *
  73. * @author Vaadin Ltd.
  74. * @since 3.0
  75. */
  76. @SuppressWarnings({ "serial", "deprecation" })
  77. public class Window extends Panel
  78. implements FocusNotifier, BlurNotifier, LegacyComponent {
  79. private WindowServerRpc rpc = new WindowServerRpc() {
  80. @Override
  81. public void click(MouseEventDetails mouseDetails) {
  82. fireEvent(new ClickEvent(Window.this, mouseDetails));
  83. }
  84. @Override
  85. public void windowModeChanged(WindowMode newState) {
  86. setWindowMode(newState);
  87. }
  88. @Override
  89. public void windowMoved(int x, int y) {
  90. if (x != getState(false).positionX) {
  91. setPositionX(x);
  92. }
  93. if (y != getState(false).positionY) {
  94. setPositionY(y);
  95. }
  96. }
  97. };
  98. /**
  99. * Holds registered CloseShortcut instances for query and later removal
  100. */
  101. private List<CloseShortcut> closeShortcuts = new ArrayList<>(4);
  102. /**
  103. * Used to keep the window order position. Order position for unattached
  104. * window is {@code -1}.
  105. * <p>
  106. * Window with greatest order position value is on the top and window with 0
  107. * position value is on the bottom.
  108. */
  109. private int orderPosition = -1;
  110. /**
  111. * Creates a new, empty window
  112. */
  113. public Window() {
  114. this("", null);
  115. }
  116. /**
  117. * Creates a new, empty window with a given title.
  118. *
  119. * @param caption
  120. * the title of the window.
  121. */
  122. public Window(String caption) {
  123. this(caption, null);
  124. }
  125. /**
  126. * Creates a new, empty window with the given content and title.
  127. *
  128. * @param caption
  129. * the title of the window.
  130. * @param content
  131. * the contents of the window
  132. */
  133. public Window(String caption, Component content) {
  134. super(caption, content);
  135. registerRpc(rpc);
  136. setSizeUndefined();
  137. setCloseShortcut(KeyCode.ESCAPE);
  138. }
  139. /* ********************************************************************* */
  140. /*
  141. * (non-Javadoc)
  142. *
  143. * @see com.vaadin.ui.Panel#paintContent(com.vaadin.server.PaintTarget)
  144. */
  145. @Override
  146. public synchronized void paintContent(PaintTarget target)
  147. throws PaintException {
  148. if (bringToFront != null) {
  149. target.addAttribute("bringToFront", bringToFront.intValue());
  150. bringToFront = null;
  151. }
  152. // Contents of the window panel is painted
  153. super.paintContent(target);
  154. }
  155. /*
  156. * (non-Javadoc)
  157. *
  158. * @see com.vaadin.ui.AbstractComponent#setParent(com.vaadin.server.
  159. * ClientConnector )
  160. */
  161. @Override
  162. public void setParent(HasComponents parent) {
  163. if (parent == null || parent instanceof UI) {
  164. super.setParent(parent);
  165. } else {
  166. throw new IllegalArgumentException(
  167. "A Window can only be added to a UI using UI.addWindow(Window window)");
  168. }
  169. }
  170. /*
  171. * (non-Javadoc)
  172. *
  173. * @see com.vaadin.ui.Panel#changeVariables(java.lang.Object, java.util.Map)
  174. */
  175. @Override
  176. public void changeVariables(Object source, Map<String, Object> variables) {
  177. // TODO Are these for top level windows or sub windows?
  178. boolean sizeHasChanged = false;
  179. // size is handled in super class, but resize events only in windows ->
  180. // so detect if size change occurs before super.changeVariables()
  181. if (variables.containsKey("height") && (getHeightUnits() != Unit.PIXELS
  182. || (Integer) variables.get("height") != getHeight())) {
  183. sizeHasChanged = true;
  184. }
  185. if (variables.containsKey("width") && (getWidthUnits() != Unit.PIXELS
  186. || (Integer) variables.get("width") != getWidth())) {
  187. sizeHasChanged = true;
  188. }
  189. super.changeVariables(source, variables);
  190. // Positioning
  191. final Integer positionx = (Integer) variables.get("positionx");
  192. if (positionx != null) {
  193. final int x = positionx.intValue();
  194. // This is information from the client so it is already using the
  195. // position. No need to repaint.
  196. setPositionX(x < 0 ? -1 : x);
  197. }
  198. final Integer positiony = (Integer) variables.get("positiony");
  199. if (positiony != null) {
  200. final int y = positiony.intValue();
  201. // This is information from the client so it is already using the
  202. // position. No need to repaint.
  203. setPositionY(y < 0 ? -1 : y);
  204. }
  205. if (isClosable()) {
  206. // Closing
  207. final Boolean close = (Boolean) variables.get("close");
  208. if (close != null && close.booleanValue()) {
  209. close();
  210. }
  211. }
  212. // fire event if size has really changed
  213. if (sizeHasChanged) {
  214. fireResize();
  215. }
  216. if (variables.containsKey(FocusEvent.EVENT_ID)) {
  217. fireEvent(new FocusEvent(this));
  218. } else if (variables.containsKey(BlurEvent.EVENT_ID)) {
  219. fireEvent(new BlurEvent(this));
  220. }
  221. }
  222. /**
  223. * Method that handles window closing (from UI).
  224. *
  225. * <p>
  226. * By default, windows are removed from their respective UIs and thus
  227. * visually closed on browser-side.
  228. * </p>
  229. *
  230. * <p>
  231. * To react to a window being closed (after it is closed), register a
  232. * {@link CloseListener}.
  233. * </p>
  234. */
  235. public void close() {
  236. UI uI = getUI();
  237. // Don't do anything if not attached to a UI
  238. if (uI != null) {
  239. // window is removed from the UI
  240. uI.removeWindow(this);
  241. }
  242. }
  243. /**
  244. * Gets the distance of Window left border in pixels from left border of the
  245. * containing (main window) when the window is in {@link WindowMode#NORMAL}.
  246. *
  247. * @return the Distance of Window left border in pixels from left border of
  248. * the containing (main window).or -1 if unspecified
  249. * @since 4.0.0
  250. */
  251. public int getPositionX() {
  252. return getState(false).positionX;
  253. }
  254. /**
  255. * Sets the position of the window on the screen using
  256. * {@link #setPositionX(int)} and {@link #setPositionY(int)}
  257. *
  258. * @since 7.5
  259. * @param x
  260. * The new x coordinate for the window
  261. * @param y
  262. * The new y coordinate for the window
  263. */
  264. public void setPosition(int x, int y) {
  265. setPositionX(x);
  266. setPositionY(y);
  267. }
  268. /**
  269. * Sets the distance of Window left border in pixels from left border of the
  270. * containing (main window). Has effect only if in {@link WindowMode#NORMAL}
  271. * mode.
  272. *
  273. * @param positionX
  274. * the Distance of Window left border in pixels from left border
  275. * of the containing (main window). or -1 if unspecified.
  276. * @since 4.0.0
  277. */
  278. public void setPositionX(int positionX) {
  279. getState().positionX = positionX;
  280. getState().centered = false;
  281. }
  282. /**
  283. * Gets the distance of Window top border in pixels from top border of the
  284. * containing (main window) when the window is in {@link WindowMode#NORMAL}
  285. * state, or when next set to that state.
  286. *
  287. * @return Distance of Window top border in pixels from top border of the
  288. * containing (main window). or -1 if unspecified
  289. *
  290. * @since 4.0.0
  291. */
  292. public int getPositionY() {
  293. return getState(false).positionY;
  294. }
  295. /**
  296. * Returns the position of this window in the order of all open windows for
  297. * this UI.
  298. * <p>
  299. * Window with position 0 is on the bottom, and window with greatest
  300. * position is at the top. If window has no position (it's not yet attached
  301. * or hidden) then position is {@code -1}.
  302. *
  303. * @see UI#addWindowOrderUpdateListener(com.vaadin.ui.UI.WindowOrderUpdateListener)
  304. *
  305. * @since 8.0
  306. *
  307. * @return window order position.
  308. */
  309. public int getOrderPosition() {
  310. return orderPosition;
  311. }
  312. /**
  313. * Sets the distance of Window top border in pixels from top border of the
  314. * containing (main window). Has effect only if in {@link WindowMode#NORMAL}
  315. * mode.
  316. *
  317. * @param positionY
  318. * the Distance of Window top border in pixels from top border of
  319. * the containing (main window). or -1 if unspecified
  320. *
  321. * @since 4.0.0
  322. */
  323. public void setPositionY(int positionY) {
  324. getState().positionY = positionY;
  325. getState().centered = false;
  326. }
  327. private static final Method WINDOW_CLOSE_METHOD;
  328. static {
  329. try {
  330. WINDOW_CLOSE_METHOD = CloseListener.class
  331. .getDeclaredMethod("windowClose", CloseEvent.class);
  332. } catch (final java.lang.NoSuchMethodException e) {
  333. // This should never happen
  334. throw new java.lang.RuntimeException(
  335. "Internal error, window close method not found");
  336. }
  337. }
  338. public static class CloseEvent extends Component.Event {
  339. /**
  340. *
  341. * @param source
  342. */
  343. public CloseEvent(Component source) {
  344. super(source);
  345. }
  346. /**
  347. * Gets the Window.
  348. *
  349. * @return the window.
  350. */
  351. public Window getWindow() {
  352. return (Window) getSource();
  353. }
  354. }
  355. /**
  356. * Event which is fired when the window order position is changed.
  357. *
  358. * @see UI.WindowOrderUpdateEvent
  359. *
  360. * @author Vaadin Ltd
  361. *
  362. */
  363. public static class WindowOrderChangeEvent extends Component.Event {
  364. private final int order;
  365. public WindowOrderChangeEvent(Component source, int order) {
  366. super(source);
  367. this.order = order;
  368. }
  369. /**
  370. * Gets the Window.
  371. *
  372. * @return the window
  373. */
  374. public Window getWindow() {
  375. return (Window) getSource();
  376. }
  377. /**
  378. * Gets the new window order position.
  379. *
  380. * @return the new order position
  381. */
  382. public int getOrder() {
  383. return order;
  384. }
  385. }
  386. /**
  387. * An interface used for listening to Window order change events.
  388. *
  389. * @see UI.WindowOrderUpdateListener
  390. */
  391. @FunctionalInterface
  392. public interface WindowOrderChangeListener extends ConnectorEventListener {
  393. public static final Method windowOrderChangeMethod = ReflectTools
  394. .findMethod(WindowOrderChangeListener.class,
  395. "windowOrderChanged", WindowOrderChangeEvent.class);
  396. /**
  397. * Called when the window order position is changed. Use
  398. * {@link WindowOrderChangeEvent#getWindow()} to get a reference to the
  399. * {@link Window} whose order position is changed. Use
  400. * {@link WindowOrderChangeEvent#getOrder()} to get a new order
  401. * position.
  402. *
  403. * @param event
  404. */
  405. public void windowOrderChanged(WindowOrderChangeEvent event);
  406. }
  407. /**
  408. * Adds a WindowOrderChangeListener to the window.
  409. * <p>
  410. * The WindowOrderChangeEvent is fired when the order position is changed.
  411. * It can happen when some window (this or other) is brought to front or
  412. * detached.
  413. * <p>
  414. * The other way to listen positions of all windows in UI is
  415. * {@link UI#addWindowOrderUpdateListener(com.vaadin.ui.UI.WindowOrderUpdateListener)}
  416. *
  417. * @see UI#addWindowOrderUpdateListener(com.vaadin.ui.UI.WindowOrderUpdateListener)
  418. *
  419. * @param listener
  420. * the WindowModeChangeListener to add.
  421. * @since 8.0
  422. */
  423. public Registration addWindowOrderChangeListener(
  424. WindowOrderChangeListener listener) {
  425. addListener(EventId.WINDOW_ORDER, WindowOrderChangeEvent.class,
  426. listener, WindowOrderChangeListener.windowOrderChangeMethod);
  427. return () -> removeListener(EventId.WINDOW_ORDER,
  428. WindowOrderChangeEvent.class, listener);
  429. }
  430. protected void fireWindowOrderChange(Integer order) {
  431. if (order == null || orderPosition != order) {
  432. orderPosition = (order == null) ? -1 : order;
  433. fireEvent(new Window.WindowOrderChangeEvent(this,
  434. getOrderPosition()));
  435. }
  436. }
  437. /**
  438. * An interface used for listening to Window close events. Add the
  439. * CloseListener to a window and
  440. * {@link CloseListener#windowClose(CloseEvent)} will be called whenever the
  441. * user closes the window.
  442. *
  443. * <p>
  444. * Since Vaadin 6.5, removing a window using {@link #removeWindow(Window)}
  445. * fires the CloseListener.
  446. * </p>
  447. */
  448. @FunctionalInterface
  449. public interface CloseListener extends Serializable {
  450. /**
  451. * Called when the user closes a window. Use
  452. * {@link CloseEvent#getWindow()} to get a reference to the
  453. * {@link Window} that was closed.
  454. *
  455. * @param e
  456. * Event containing
  457. */
  458. public void windowClose(CloseEvent e);
  459. }
  460. /**
  461. * Adds a CloseListener to the window.
  462. *
  463. * For a window the CloseListener is fired when the user closes it (clicks
  464. * on the close button).
  465. *
  466. * For a browser level window the CloseListener is fired when the browser
  467. * level window is closed. Note that closing a browser level window does not
  468. * mean it will be destroyed. Also note that Opera does not send events like
  469. * all other browsers and therefore the close listener might not be called
  470. * if Opera is used.
  471. *
  472. * <p>
  473. * Since Vaadin 6.5, removing windows using {@link #removeWindow(Window)}
  474. * does fire the CloseListener.
  475. * </p>
  476. *
  477. * @param listener
  478. * the CloseListener to add, not null
  479. * @since 8.0
  480. */
  481. public Registration addCloseListener(CloseListener listener) {
  482. return addListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD);
  483. }
  484. /**
  485. * Removes the CloseListener from the window.
  486. *
  487. * <p>
  488. * For more information on CloseListeners see {@link CloseListener}.
  489. * </p>
  490. *
  491. * @param listener
  492. * the CloseListener to remove.
  493. */
  494. @Deprecated
  495. public void removeCloseListener(CloseListener listener) {
  496. removeListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD);
  497. }
  498. protected void fireClose() {
  499. fireEvent(new Window.CloseEvent(this));
  500. }
  501. /**
  502. * Event which is fired when the mode of the Window changes.
  503. *
  504. * @author Vaadin Ltd
  505. * @since 7.1
  506. *
  507. */
  508. public static class WindowModeChangeEvent extends Component.Event {
  509. private final WindowMode windowMode;
  510. /**
  511. *
  512. * @param source
  513. */
  514. public WindowModeChangeEvent(Component source, WindowMode windowMode) {
  515. super(source);
  516. this.windowMode = windowMode;
  517. }
  518. /**
  519. * Gets the Window.
  520. *
  521. * @return the window
  522. */
  523. public Window getWindow() {
  524. return (Window) getSource();
  525. }
  526. /**
  527. * Gets the new window mode.
  528. *
  529. * @return the new mode
  530. */
  531. public WindowMode getWindowMode() {
  532. return windowMode;
  533. }
  534. }
  535. /**
  536. * An interface used for listening to Window maximize / restore events. Add
  537. * the WindowModeChangeListener to a window and
  538. * {@link WindowModeChangeListener#windowModeChanged(WindowModeChangeEvent)}
  539. * will be called whenever the window is maximized (
  540. * {@link WindowMode#MAXIMIZED}) or restored ({@link WindowMode#NORMAL} ).
  541. */
  542. @FunctionalInterface
  543. public interface WindowModeChangeListener extends Serializable {
  544. public static final Method windowModeChangeMethod = ReflectTools
  545. .findMethod(WindowModeChangeListener.class, "windowModeChanged",
  546. WindowModeChangeEvent.class);
  547. /**
  548. * Called when the user maximizes / restores a window. Use
  549. * {@link WindowModeChangeEvent#getWindow()} to get a reference to the
  550. * {@link Window} that was maximized / restored. Use
  551. * {@link WindowModeChangeEvent#getWindowMode()} to get a reference to
  552. * the new state.
  553. *
  554. * @param event
  555. */
  556. public void windowModeChanged(WindowModeChangeEvent event);
  557. }
  558. /**
  559. * Adds a WindowModeChangeListener to the window.
  560. *
  561. * The WindowModeChangeEvent is fired when the user changed the display
  562. * state by clicking the maximize/restore button or by double clicking on
  563. * the window header. The event is also fired if the state is changed using
  564. * {@link #setWindowMode(WindowMode)}.
  565. *
  566. * @param listener
  567. * the WindowModeChangeListener to add.
  568. * @since 8.0
  569. */
  570. public Registration addWindowModeChangeListener(
  571. WindowModeChangeListener listener) {
  572. return addListener(WindowModeChangeEvent.class, listener,
  573. WindowModeChangeListener.windowModeChangeMethod);
  574. }
  575. /**
  576. * Removes the WindowModeChangeListener from the window.
  577. *
  578. * @param listener
  579. * the WindowModeChangeListener to remove.
  580. */
  581. @Deprecated
  582. public void removeWindowModeChangeListener(
  583. WindowModeChangeListener listener) {
  584. removeListener(WindowModeChangeEvent.class, listener,
  585. WindowModeChangeListener.windowModeChangeMethod);
  586. }
  587. protected void fireWindowWindowModeChange() {
  588. fireEvent(
  589. new Window.WindowModeChangeEvent(this, getState().windowMode));
  590. }
  591. /**
  592. * Method for the resize event.
  593. */
  594. private static final Method WINDOW_RESIZE_METHOD;
  595. static {
  596. try {
  597. WINDOW_RESIZE_METHOD = ResizeListener.class
  598. .getDeclaredMethod("windowResized", ResizeEvent.class);
  599. } catch (final java.lang.NoSuchMethodException e) {
  600. // This should never happen
  601. throw new java.lang.RuntimeException(
  602. "Internal error, window resized method not found");
  603. }
  604. }
  605. /**
  606. * Resize events are fired whenever the client-side fires a resize-event
  607. * (e.g. the browser window is resized). The frequency may vary across
  608. * browsers.
  609. */
  610. public static class ResizeEvent extends Component.Event {
  611. /**
  612. *
  613. * @param source
  614. */
  615. public ResizeEvent(Component source) {
  616. super(source);
  617. }
  618. /**
  619. * Get the window form which this event originated
  620. *
  621. * @return the window
  622. */
  623. public Window getWindow() {
  624. return (Window) getSource();
  625. }
  626. }
  627. /**
  628. * Listener for window resize events.
  629. *
  630. * @see com.vaadin.ui.Window.ResizeEvent
  631. */
  632. @FunctionalInterface
  633. public interface ResizeListener extends Serializable {
  634. public void windowResized(ResizeEvent e);
  635. }
  636. /**
  637. * Add a resize listener.
  638. *
  639. * @see Registration
  640. *
  641. * @param listener
  642. * the listener to add, not null
  643. * @return a registration object for removing the listener
  644. * @since 8.0
  645. */
  646. public Registration addResizeListener(ResizeListener listener) {
  647. return addListener(ResizeEvent.class, listener, WINDOW_RESIZE_METHOD);
  648. }
  649. /**
  650. * Remove a resize listener.
  651. *
  652. * @param listener
  653. */
  654. @Deprecated
  655. public void removeResizeListener(ResizeListener listener) {
  656. removeListener(ResizeEvent.class, listener);
  657. }
  658. /**
  659. * Fire the resize event.
  660. */
  661. protected void fireResize() {
  662. fireEvent(new ResizeEvent(this));
  663. }
  664. /**
  665. * Used to keep the right order of windows if multiple windows are brought
  666. * to front in a single changeset. If this is not used, the order is quite
  667. * random (depends on the order getting to dirty list. e.g. which window got
  668. * variable changes).
  669. */
  670. private Integer bringToFront = null;
  671. /**
  672. * If there are currently several windows visible, calling this method makes
  673. * this window topmost.
  674. * <p>
  675. * This method can only be called if this window connected a UI. Else an
  676. * illegal state exception is thrown. Also if there are modal windows and
  677. * this window is not modal, and illegal state exception is thrown.
  678. * <p>
  679. */
  680. public void bringToFront() {
  681. UI uI = getUI();
  682. if (uI == null) {
  683. throw new IllegalStateException(
  684. "Window must be attached to parent before calling bringToFront method.");
  685. }
  686. int maxBringToFront = -1;
  687. for (Window w : uI.getWindows()) {
  688. if (!isModal() && w.isModal()) {
  689. throw new IllegalStateException(
  690. "The UI contains modal windows, non-modal window cannot be brought to front.");
  691. }
  692. if (w.bringToFront != null) {
  693. maxBringToFront = Math.max(maxBringToFront,
  694. w.bringToFront.intValue());
  695. }
  696. }
  697. bringToFront = Integer.valueOf(maxBringToFront + 1);
  698. markAsDirty();
  699. }
  700. /**
  701. * Sets window modality. When a modal window is open, components outside
  702. * that window cannot be accessed.
  703. * <p>
  704. * Keyboard navigation is restricted by blocking the tab key at the top and
  705. * bottom of the window by activating the tab stop function internally.
  706. *
  707. * @param modal
  708. * true if modality is to be turned on
  709. */
  710. public void setModal(boolean modal) {
  711. getState().modal = modal;
  712. center();
  713. }
  714. /**
  715. * @return true if this window is modal.
  716. */
  717. public boolean isModal() {
  718. return getState(false).modal;
  719. }
  720. /**
  721. * Sets window resizable.
  722. *
  723. * @param resizable
  724. * true if resizability is to be turned on
  725. */
  726. public void setResizable(boolean resizable) {
  727. getState().resizable = resizable;
  728. }
  729. /**
  730. *
  731. * @return true if window is resizable by the end-user, otherwise false.
  732. */
  733. public boolean isResizable() {
  734. return getState(false).resizable;
  735. }
  736. /**
  737. *
  738. * @return true if a delay is used before recalculating sizes, false if
  739. * sizes are recalculated immediately.
  740. */
  741. public boolean isResizeLazy() {
  742. return getState(false).resizeLazy;
  743. }
  744. /**
  745. * Should resize operations be lazy, i.e. should there be a delay before
  746. * layout sizes are recalculated. Speeds up resize operations in slow UIs
  747. * with the penalty of slightly decreased usability.
  748. *
  749. * Note, some browser send false resize events for the browser window and
  750. * are therefore always lazy.
  751. *
  752. * @param resizeLazy
  753. * true to use a delay before recalculating sizes, false to
  754. * calculate immediately.
  755. */
  756. public void setResizeLazy(boolean resizeLazy) {
  757. getState().resizeLazy = resizeLazy;
  758. }
  759. /**
  760. * Sets this window to be centered relative to its parent window. Affects
  761. * windows only. If the window is resized as a result of the size of its
  762. * content changing, it will keep itself centered as long as its position is
  763. * not explicitly changed programmatically or by the user.
  764. * <p>
  765. * <b>NOTE:</b> This method has several issues as currently implemented.
  766. * Please refer to http://dev.vaadin.com/ticket/8971 for details.
  767. */
  768. public void center() {
  769. getState().centered = true;
  770. }
  771. /**
  772. * Returns the closable status of the window. If a window is closable, it
  773. * typically shows an X in the upper right corner. Clicking on the X sends a
  774. * close event to the server. Setting closable to false will remove the X
  775. * from the window and prevent the user from closing the window.
  776. *
  777. * @return true if the window can be closed by the user.
  778. */
  779. public boolean isClosable() {
  780. return getState(false).closable;
  781. }
  782. /**
  783. * Sets the closable status for the window. If a window is closable it
  784. * typically shows an X in the upper right corner. Clicking on the X sends a
  785. * close event to the server. Setting closable to false will remove the X
  786. * from the window and prevent the user from closing the window.
  787. *
  788. * @param closable
  789. * determines if the window can be closed by the user.
  790. */
  791. public void setClosable(boolean closable) {
  792. if (closable != isClosable()) {
  793. getState().closable = closable;
  794. }
  795. }
  796. /**
  797. * Indicates whether a window can be dragged or not. By default a window is
  798. * draggable.
  799. *
  800. * @return {@code true} if window is draggable; {@code false} if not
  801. */
  802. public boolean isDraggable() {
  803. return getState(false).draggable;
  804. }
  805. /**
  806. * Enables or disables that a window can be dragged (moved) by the user. By
  807. * default a window is draggable.
  808. * <p/>
  809. *
  810. * @param draggable
  811. * true if the window can be dragged by the user
  812. */
  813. public void setDraggable(boolean draggable) {
  814. getState().draggable = draggable;
  815. }
  816. /**
  817. * Gets the current mode of the window.
  818. *
  819. * @see WindowMode
  820. * @return the mode of the window.
  821. */
  822. public WindowMode getWindowMode() {
  823. return getState(false).windowMode;
  824. }
  825. /**
  826. * Sets the mode for the window
  827. *
  828. * @see WindowMode
  829. * @param windowMode
  830. * The new mode
  831. */
  832. public void setWindowMode(WindowMode windowMode) {
  833. if (windowMode != getWindowMode()) {
  834. getState().windowMode = windowMode;
  835. fireWindowWindowModeChange();
  836. }
  837. }
  838. /**
  839. * This is the old way of adding a keyboard shortcut to close a
  840. * {@link Window} - to preserve compatibility with existing code under the
  841. * new functionality, this method now first removes all registered close
  842. * shortcuts, then adds the default ESCAPE shortcut key, and then attempts
  843. * to add the shortcut provided as parameters to this method. This method,
  844. * and its companion {@link #removeCloseShortcut()}, are now considered
  845. * deprecated, as their main function is to preserve exact backwards
  846. * compatibility with old code. For all new code, use the new keyboard
  847. * shortcuts API: {@link #addCloseShortcut(int,int...)},
  848. * {@link #removeCloseShortcut(int,int...)},
  849. * {@link #removeAllCloseShortcuts()}, {@link #hasCloseShortcut(int,int...)}
  850. * and {@link #getCloseShortcuts()}.
  851. * <p>
  852. * Original description: Makes it possible to close the window by pressing
  853. * the given {@link KeyCode} and (optional) {@link ModifierKey}s.<br/>
  854. * Note that this shortcut only reacts while the window has focus, closing
  855. * itself - if you want to close a window from a UI, use
  856. * {@link UI#addAction(com.vaadin.event.Action)} of the UI instead.
  857. *
  858. * @param keyCode
  859. * the keycode for invoking the shortcut
  860. * @param modifiers
  861. * the (optional) modifiers for invoking the shortcut. Can be set
  862. * to null to be explicit about not having modifiers.
  863. *
  864. * @deprecated Use {@link #addCloseShortcut(int, int...)} instead.
  865. */
  866. @Deprecated
  867. public void setCloseShortcut(int keyCode, int... modifiers) {
  868. removeCloseShortcut();
  869. addCloseShortcut(keyCode, modifiers);
  870. }
  871. /**
  872. * Removes all keyboard shortcuts previously set with
  873. * {@link #setCloseShortcut(int, int...)} and
  874. * {@link #addCloseShortcut(int, int...)}, then adds the default
  875. * {@link KeyCode#ESCAPE} shortcut.
  876. * <p>
  877. * This is the old way of removing the (single) keyboard close shortcut, and
  878. * is retained only for exact backwards compatibility. For all new code, use
  879. * the new keyboard shortcuts API: {@link #addCloseShortcut(int,int...)},
  880. * {@link #removeCloseShortcut(int,int...)},
  881. * {@link #removeAllCloseShortcuts()}, {@link #hasCloseShortcut(int,int...)}
  882. * and {@link #getCloseShortcuts()}.
  883. *
  884. * @deprecated Use {@link #removeCloseShortcut(int, int...)} instead.
  885. */
  886. @Deprecated
  887. public void removeCloseShortcut() {
  888. for (CloseShortcut sc : closeShortcuts) {
  889. removeAction(sc);
  890. }
  891. closeShortcuts.clear();
  892. addCloseShortcut(KeyCode.ESCAPE);
  893. }
  894. /**
  895. * Adds a close shortcut - pressing this key while holding down all (if any)
  896. * modifiers specified while this Window is in focus will close the Window.
  897. *
  898. * @since 7.6
  899. * @param keyCode
  900. * the keycode for invoking the shortcut
  901. * @param modifiers
  902. * the (optional) modifiers for invoking the shortcut. Can be set
  903. * to null to be explicit about not having modifiers.
  904. */
  905. public void addCloseShortcut(int keyCode, int... modifiers) {
  906. // Ignore attempts to re-add existing shortcuts
  907. if (hasCloseShortcut(keyCode, modifiers)) {
  908. return;
  909. }
  910. // Actually add the shortcut
  911. CloseShortcut shortcut = new CloseShortcut(this, keyCode, modifiers);
  912. addAction(shortcut);
  913. closeShortcuts.add(shortcut);
  914. }
  915. /**
  916. * Removes a close shortcut previously added with
  917. * {@link #addCloseShortcut(int, int...)}.
  918. *
  919. * @since 7.6
  920. * @param keyCode
  921. * the keycode for invoking the shortcut
  922. * @param modifiers
  923. * the (optional) modifiers for invoking the shortcut. Can be set
  924. * to null to be explicit about not having modifiers.
  925. */
  926. public void removeCloseShortcut(int keyCode, int... modifiers) {
  927. for (CloseShortcut shortcut : closeShortcuts) {
  928. if (shortcut.equals(keyCode, modifiers)) {
  929. removeAction(shortcut);
  930. closeShortcuts.remove(shortcut);
  931. return;
  932. }
  933. }
  934. }
  935. /**
  936. * Removes all close shortcuts. This includes the default ESCAPE shortcut.
  937. * It is up to the user to add back any and all keyboard close shortcuts
  938. * they may require. For more fine-grained control over shortcuts, use
  939. * {@link #removeCloseShortcut(int, int...)}.
  940. *
  941. * @since 7.6
  942. */
  943. public void removeAllCloseShortcuts() {
  944. for (CloseShortcut shortcut : closeShortcuts) {
  945. removeAction(shortcut);
  946. }
  947. closeShortcuts.clear();
  948. }
  949. /**
  950. * Checks if a close window shortcut key has already been registered.
  951. *
  952. * @since 7.6
  953. * @param keyCode
  954. * the keycode for invoking the shortcut
  955. * @param modifiers
  956. * the (optional) modifiers for invoking the shortcut. Can be set
  957. * to null to be explicit about not having modifiers.
  958. * @return true, if an exactly matching shortcut has been registered.
  959. */
  960. public boolean hasCloseShortcut(int keyCode, int... modifiers) {
  961. for (CloseShortcut shortcut : closeShortcuts) {
  962. if (shortcut.equals(keyCode, modifiers)) {
  963. return true;
  964. }
  965. }
  966. return false;
  967. }
  968. /**
  969. * Returns an unmodifiable collection of {@link CloseShortcut} objects
  970. * currently registered with this {@link Window}. This method is provided
  971. * mainly so that users can implement their own serialization routines. To
  972. * check if a certain combination of keys has been registered as a close
  973. * shortcut, use the {@link #hasCloseShortcut(int, int...)} method instead.
  974. *
  975. * @since 7.6
  976. * @return an unmodifiable Collection of CloseShortcut objects.
  977. */
  978. public Collection<CloseShortcut> getCloseShortcuts() {
  979. return Collections.unmodifiableCollection(closeShortcuts);
  980. }
  981. /**
  982. * A {@link ShortcutListener} specifically made to define a keyboard
  983. * shortcut that closes the window.
  984. *
  985. * <pre>
  986. * <code>
  987. * // within the window using helper
  988. * window.setCloseShortcut(KeyCode.ESCAPE, null);
  989. *
  990. * // or globally
  991. * getUI().addAction(new Window.CloseShortcut(window, KeyCode.ESCAPE));
  992. * </code>
  993. * </pre>
  994. *
  995. */
  996. public static class CloseShortcut extends ShortcutListener {
  997. protected Window window;
  998. /**
  999. * Creates a keyboard shortcut for closing the given window using the
  1000. * shorthand notation defined in {@link ShortcutAction}.
  1001. *
  1002. * @param window
  1003. * to be closed when the shortcut is invoked
  1004. * @param shorthandCaption
  1005. * the caption with shortcut keycode and modifiers indicated
  1006. */
  1007. public CloseShortcut(Window window, String shorthandCaption) {
  1008. super(shorthandCaption);
  1009. this.window = window;
  1010. }
  1011. /**
  1012. * Creates a keyboard shortcut for closing the given window using the
  1013. * given {@link KeyCode} and {@link ModifierKey}s.
  1014. *
  1015. * @param window
  1016. * to be closed when the shortcut is invoked
  1017. * @param keyCode
  1018. * KeyCode to react to
  1019. * @param modifiers
  1020. * optional modifiers for shortcut
  1021. */
  1022. public CloseShortcut(Window window, int keyCode, int... modifiers) {
  1023. super(null, keyCode, modifiers);
  1024. this.window = window;
  1025. }
  1026. /**
  1027. * Creates a keyboard shortcut for closing the given window using the
  1028. * given {@link KeyCode}.
  1029. *
  1030. * @param window
  1031. * to be closed when the shortcut is invoked
  1032. * @param keyCode
  1033. * KeyCode to react to
  1034. */
  1035. public CloseShortcut(Window window, int keyCode) {
  1036. this(window, keyCode, null);
  1037. }
  1038. @Override
  1039. public void handleAction(Object sender, Object target) {
  1040. if (window.isClosable()) {
  1041. window.close();
  1042. }
  1043. }
  1044. public boolean equals(int keyCode, int... modifiers) {
  1045. if (keyCode != getKeyCode()) {
  1046. return false;
  1047. }
  1048. if (getModifiers() != null) {
  1049. int[] mods = null;
  1050. if (modifiers != null) {
  1051. // Modifiers provided by the parent ShortcutAction class
  1052. // are guaranteed to be sorted. We still need to sort
  1053. // the modifiers passed in as argument.
  1054. mods = Arrays.copyOf(modifiers, modifiers.length);
  1055. Arrays.sort(mods);
  1056. }
  1057. return Arrays.equals(mods, getModifiers());
  1058. }
  1059. return true;
  1060. }
  1061. }
  1062. /*
  1063. * (non-Javadoc)
  1064. *
  1065. * @see
  1066. * com.vaadin.event.FieldEvents.FocusNotifier#addFocusListener(com.vaadin
  1067. * .event.FieldEvents.FocusListener)
  1068. */
  1069. @Override
  1070. public Registration addFocusListener(FocusListener listener) {
  1071. return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
  1072. FocusListener.focusMethod);
  1073. }
  1074. /*
  1075. * (non-Javadoc)
  1076. *
  1077. * @see
  1078. * com.vaadin.event.FieldEvents.BlurNotifier#addBlurListener(com.vaadin.
  1079. * event.FieldEvents.BlurListener)
  1080. */
  1081. @Override
  1082. public Registration addBlurListener(BlurListener listener) {
  1083. return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
  1084. BlurListener.blurMethod);
  1085. }
  1086. /**
  1087. * {@inheritDoc}
  1088. *
  1089. * Cause the window to be brought on top of other windows and gain keyboard
  1090. * focus.
  1091. */
  1092. @Override
  1093. public void focus() {
  1094. /*
  1095. * When focusing a window it basically means it should be brought to the
  1096. * front. Instead of just moving the keyboard focus we focus the window
  1097. * and bring it top-most.
  1098. */
  1099. super.focus();
  1100. bringToFront();
  1101. }
  1102. @Override
  1103. protected WindowState getState() {
  1104. return (WindowState) super.getState();
  1105. }
  1106. @Override
  1107. protected WindowState getState(boolean markAsDirty) {
  1108. return (WindowState) super.getState(markAsDirty);
  1109. }
  1110. /**
  1111. * Allows to specify which components contain the description for the
  1112. * window. Text contained in these components will be read by assistive
  1113. * devices when it is opened.
  1114. *
  1115. * @param components
  1116. * the components to use as description
  1117. */
  1118. public void setAssistiveDescription(Component... components) {
  1119. if (components == null) {
  1120. throw new IllegalArgumentException(
  1121. "Parameter connectors must be non-null");
  1122. } else {
  1123. getState().contentDescription = components;
  1124. }
  1125. }
  1126. /**
  1127. * Gets the components that are used as assistive description. Text
  1128. * contained in these components will be read by assistive devices when the
  1129. * window is opened.
  1130. *
  1131. * @return array of previously set components
  1132. */
  1133. public Component[] getAssistiveDescription() {
  1134. Connector[] contentDescription = getState(false).contentDescription;
  1135. if (contentDescription == null) {
  1136. return null;
  1137. }
  1138. Component[] target = new Component[contentDescription.length];
  1139. System.arraycopy(contentDescription, 0, target, 0,
  1140. contentDescription.length);
  1141. return target;
  1142. }
  1143. /**
  1144. * Sets the accessibility prefix for the window caption.
  1145. *
  1146. * This prefix is read to assistive device users before the window caption,
  1147. * but not visible on the page.
  1148. *
  1149. * @param prefix
  1150. * String that is placed before the window caption
  1151. */
  1152. public void setAssistivePrefix(String prefix) {
  1153. getState().assistivePrefix = prefix;
  1154. }
  1155. /**
  1156. * Gets the accessibility prefix for the window caption.
  1157. *
  1158. * This prefix is read to assistive device users before the window caption,
  1159. * but not visible on the page.
  1160. *
  1161. * @return The accessibility prefix
  1162. */
  1163. public String getAssistivePrefix() {
  1164. return getState(false).assistivePrefix;
  1165. }
  1166. /**
  1167. * Sets the accessibility postfix for the window caption.
  1168. *
  1169. * This postfix is read to assistive device users after the window caption,
  1170. * but not visible on the page.
  1171. *
  1172. * @param assistivePostfix
  1173. * String that is placed after the window caption
  1174. */
  1175. public void setAssistivePostfix(String assistivePostfix) {
  1176. getState().assistivePostfix = assistivePostfix;
  1177. }
  1178. /**
  1179. * Gets the accessibility postfix for the window caption.
  1180. *
  1181. * This postfix is read to assistive device users after the window caption,
  1182. * but not visible on the page.
  1183. *
  1184. * @return The accessibility postfix
  1185. */
  1186. public String getAssistivePostfix() {
  1187. return getState(false).assistivePostfix;
  1188. }
  1189. /**
  1190. * Sets the WAI-ARIA role the window.
  1191. *
  1192. * This role defines how an assistive device handles a window. Available
  1193. * roles are alertdialog and dialog (@see
  1194. * <a href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
  1195. * Model</a>).
  1196. *
  1197. * The default role is dialog.
  1198. *
  1199. * @param role
  1200. * WAI-ARIA role to set for the window
  1201. */
  1202. public void setAssistiveRole(WindowRole role) {
  1203. getState().role = role;
  1204. }
  1205. /**
  1206. * Gets the WAI-ARIA role the window.
  1207. *
  1208. * This role defines how an assistive device handles a window. Available
  1209. * roles are alertdialog and dialog (@see
  1210. * <a href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
  1211. * Model</a>).
  1212. *
  1213. * @return WAI-ARIA role set for the window
  1214. */
  1215. public WindowRole getAssistiveRole() {
  1216. return getState(false).role;
  1217. }
  1218. /**
  1219. * Set if it should be prevented to set the focus to a component outside a
  1220. * non-modal window with the tab key.
  1221. * <p>
  1222. * This is meant to help users of assistive devices to not leaving the
  1223. * window unintentionally.
  1224. * <p>
  1225. * For modal windows, this function is activated automatically, while
  1226. * preserving the stored value of tabStop.
  1227. *
  1228. * @param tabStop
  1229. * true to keep the focus inside the window when reaching the top
  1230. * or bottom, false (default) to allow leaving the window
  1231. */
  1232. public void setTabStopEnabled(boolean tabStop) {
  1233. getState().assistiveTabStop = tabStop;
  1234. }
  1235. /**
  1236. * Get if it is prevented to leave a window with the tab key.
  1237. *
  1238. * @return true when the focus is limited to inside the window, false when
  1239. * focus can leave the window
  1240. */
  1241. public boolean isTabStopEnabled() {
  1242. return getState(false).assistiveTabStop;
  1243. }
  1244. /**
  1245. * Sets the message that is provided to users of assistive devices when the
  1246. * user reaches the top of the window when leaving a window with the tab key
  1247. * is prevented.
  1248. * <p>
  1249. * This message is not visible on the screen.
  1250. *
  1251. * @param topMessage
  1252. * String provided when the user navigates with Shift-Tab keys to
  1253. * the top of the window
  1254. */
  1255. public void setTabStopTopAssistiveText(String topMessage) {
  1256. getState().assistiveTabStopTopText = topMessage;
  1257. }
  1258. /**
  1259. * Sets the message that is provided to users of assistive devices when the
  1260. * user reaches the bottom of the window when leaving a window with the tab
  1261. * key is prevented.
  1262. * <p>
  1263. * This message is not visible on the screen.
  1264. *
  1265. * @param bottomMessage
  1266. * String provided when the user navigates with the Tab key to
  1267. * the bottom of the window
  1268. */
  1269. public void setTabStopBottomAssistiveText(String bottomMessage) {
  1270. getState().assistiveTabStopBottomText = bottomMessage;
  1271. }
  1272. /**
  1273. * Gets the message that is provided to users of assistive devices when the
  1274. * user reaches the top of the window when leaving a window with the tab key
  1275. * is prevented.
  1276. *
  1277. * @return the top message
  1278. */
  1279. public String getTabStopTopAssistiveText() {
  1280. return getState(false).assistiveTabStopTopText;
  1281. }
  1282. /**
  1283. * Gets the message that is provided to users of assistive devices when the
  1284. * user reaches the bottom of the window when leaving a window with the tab
  1285. * key is prevented.
  1286. *
  1287. * @return the bottom message
  1288. */
  1289. public String getTabStopBottomAssistiveText() {
  1290. return getState(false).assistiveTabStopBottomText;
  1291. }
  1292. @Override
  1293. public void readDesign(Element design, DesignContext context) {
  1294. super.readDesign(design, context);
  1295. if (design.hasAttr("center")) {
  1296. center();
  1297. }
  1298. if (design.hasAttr("position")) {
  1299. String[] position = design.attr("position").split(",");
  1300. setPositionX(Integer.parseInt(position[0]));
  1301. setPositionY(Integer.parseInt(position[1]));
  1302. }
  1303. // Parse shortcuts if defined, otherwise rely on default behavior
  1304. if (design.hasAttr("close-shortcut")) {
  1305. // Parse shortcuts
  1306. String[] shortcutStrings = DesignAttributeHandler
  1307. .readAttribute("close-shortcut", design.attributes(),
  1308. String.class)
  1309. .split("\\s+");
  1310. removeAllCloseShortcuts();
  1311. for (String part : shortcutStrings) {
  1312. if (!part.isEmpty()) {
  1313. ShortcutAction shortcut = DesignAttributeHandler
  1314. .getFormatter()
  1315. .parse(part.trim(), ShortcutAction.class);
  1316. addCloseShortcut(shortcut.getKeyCode(),
  1317. shortcut.getModifiers());
  1318. }
  1319. }
  1320. }
  1321. }
  1322. /**
  1323. * Reads the content and possible assistive descriptions from the list of
  1324. * child elements of a design. If an element has an
  1325. * {@code :assistive-description} attribute, adds the parsed component to
  1326. * the list of components used as the assistive description of this Window.
  1327. * Otherwise, sets the component as the content of this Window. If there are
  1328. * multiple non-description elements, throws a DesignException.
  1329. *
  1330. * @param children
  1331. * child elements in a design
  1332. * @param context
  1333. * the DesignContext instance used to parse the design
  1334. *
  1335. * @throws DesignException
  1336. * if there are multiple non-description child elements
  1337. * @throws DesignException
  1338. * if a child element could not be parsed as a Component
  1339. *
  1340. * @see #setContent(Component)
  1341. * @see #setAssistiveDescription(Component...)
  1342. */
  1343. @Override
  1344. protected void readDesignChildren(Elements children,
  1345. DesignContext context) {
  1346. List<Component> descriptions = new ArrayList<>();
  1347. Elements content = new Elements();
  1348. for (Element child : children) {
  1349. if (child.hasAttr(":assistive-description")) {
  1350. descriptions.add(context.readDesign(child));
  1351. } else {
  1352. content.add(child);
  1353. }
  1354. }
  1355. super.readDesignChildren(content, context);
  1356. setAssistiveDescription(
  1357. descriptions.toArray(new Component[descriptions.size()]));
  1358. }
  1359. @Override
  1360. public void writeDesign(Element design, DesignContext context) {
  1361. super.writeDesign(design, context);
  1362. Window def = context.getDefaultInstance(this);
  1363. if (getState().centered) {
  1364. design.attr("center", true);
  1365. }
  1366. DesignAttributeHandler.writeAttribute("position", design.attributes(),
  1367. getPosition(), def.getPosition(), String.class, context);
  1368. // Process keyboard shortcuts
  1369. if (closeShortcuts.size() == 1 && hasCloseShortcut(KeyCode.ESCAPE)) {
  1370. // By default, we won't write anything if we're relying on default
  1371. // shortcut behavior
  1372. } else {
  1373. // Dump all close shortcuts to a string...
  1374. String attrString = "";
  1375. // TODO: add canonical support for array data in XML attributes
  1376. for (CloseShortcut shortcut : closeShortcuts) {
  1377. String shortcutString = DesignAttributeHandler.getFormatter()
  1378. .format(shortcut, CloseShortcut.class);
  1379. attrString += shortcutString + " ";
  1380. }
  1381. // Write everything except the last "," to the design
  1382. DesignAttributeHandler.writeAttribute("close-shortcut",
  1383. design.attributes(), attrString.trim(), null, String.class,
  1384. context);
  1385. }
  1386. for (Component c : getAssistiveDescription()) {
  1387. Element child = context.createElement(c)
  1388. .attr(":assistive-description", true);
  1389. design.appendChild(child);
  1390. }
  1391. }
  1392. private String getPosition() {
  1393. return getPositionX() + "," + getPositionY();
  1394. }
  1395. @Override
  1396. protected Collection<String> getCustomAttributes() {
  1397. Collection<String> result = super.getCustomAttributes();
  1398. result.add("center");
  1399. result.add("position");
  1400. result.add("position-y");
  1401. result.add("position-x");
  1402. result.add("close-shortcut");
  1403. return result;
  1404. }
  1405. }