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

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