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

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