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.

UI.java 70KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074
  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 static java.nio.charset.StandardCharsets.UTF_8;
  18. import java.io.ByteArrayOutputStream;
  19. import java.io.IOException;
  20. import java.lang.reflect.Method;
  21. import java.net.URI;
  22. import java.util.ArrayList;
  23. import java.util.Collection;
  24. import java.util.Collections;
  25. import java.util.Iterator;
  26. import java.util.LinkedHashMap;
  27. import java.util.LinkedHashSet;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.Map.Entry;
  31. import java.util.Objects;
  32. import java.util.concurrent.Future;
  33. import java.util.logging.Level;
  34. import java.util.logging.Logger;
  35. import com.vaadin.annotations.PreserveOnRefresh;
  36. import com.vaadin.event.Action;
  37. import com.vaadin.event.Action.Handler;
  38. import com.vaadin.event.ActionManager;
  39. import com.vaadin.event.ConnectorEventListener;
  40. import com.vaadin.event.MouseEvents.ClickEvent;
  41. import com.vaadin.event.MouseEvents.ClickListener;
  42. import com.vaadin.event.UIEvents.PollEvent;
  43. import com.vaadin.event.UIEvents.PollListener;
  44. import com.vaadin.event.UIEvents.PollNotifier;
  45. import com.vaadin.navigator.Navigator;
  46. import com.vaadin.navigator.PushStateNavigation;
  47. import com.vaadin.server.ClientConnector;
  48. import com.vaadin.server.ComponentSizeValidator;
  49. import com.vaadin.server.ComponentSizeValidator.InvalidLayout;
  50. import com.vaadin.server.DefaultErrorHandler;
  51. import com.vaadin.server.ErrorHandler;
  52. import com.vaadin.server.ErrorHandlingRunnable;
  53. import com.vaadin.server.LocaleService;
  54. import com.vaadin.server.Page;
  55. import com.vaadin.server.PaintException;
  56. import com.vaadin.server.PaintTarget;
  57. import com.vaadin.server.UIProvider;
  58. import com.vaadin.server.VaadinRequest;
  59. import com.vaadin.server.VaadinService;
  60. import com.vaadin.server.VaadinServlet;
  61. import com.vaadin.server.VaadinSession;
  62. import com.vaadin.server.VaadinSession.State;
  63. import com.vaadin.server.communication.PushConnection;
  64. import com.vaadin.shared.ApplicationConstants;
  65. import com.vaadin.shared.Connector;
  66. import com.vaadin.shared.EventId;
  67. import com.vaadin.shared.MouseEventDetails;
  68. import com.vaadin.shared.Registration;
  69. import com.vaadin.shared.communication.PushMode;
  70. import com.vaadin.shared.ui.WindowOrderRpc;
  71. import com.vaadin.shared.ui.ui.DebugWindowClientRpc;
  72. import com.vaadin.shared.ui.ui.DebugWindowServerRpc;
  73. import com.vaadin.shared.ui.ui.PageClientRpc;
  74. import com.vaadin.shared.ui.ui.ScrollClientRpc;
  75. import com.vaadin.shared.ui.ui.UIClientRpc;
  76. import com.vaadin.shared.ui.ui.UIConstants;
  77. import com.vaadin.shared.ui.ui.UIServerRpc;
  78. import com.vaadin.shared.ui.ui.UIState;
  79. import com.vaadin.ui.Component.Focusable;
  80. import com.vaadin.ui.Dependency.Type;
  81. import com.vaadin.ui.Window.WindowOrderChangeListener;
  82. import com.vaadin.ui.declarative.Design;
  83. import com.vaadin.ui.dnd.DragSourceExtension;
  84. import com.vaadin.ui.dnd.DropTargetExtension;
  85. import com.vaadin.util.ConnectorHelper;
  86. import com.vaadin.util.CurrentInstance;
  87. import com.vaadin.util.ReflectTools;
  88. /**
  89. * The topmost component in any component hierarchy. There is one UI for every
  90. * Vaadin instance in a browser window. A UI may either represent an entire
  91. * browser window (or tab) or some part of a html page where a Vaadin
  92. * application is embedded.
  93. * <p>
  94. * The UI is the server side entry point for various client side features that
  95. * are not represented as components added to a layout, e.g notifications, sub
  96. * windows, and executing javascript in the browser.
  97. * </p>
  98. * <p>
  99. * When a new UI instance is needed, typically because the user opens a URL in a
  100. * browser window which points to e.g. {@link VaadinServlet}, all
  101. * {@link UIProvider}s registered to the current {@link VaadinSession} are
  102. * queried for the UI class that should be used. The selection is by default
  103. * based on the <code>UI</code> init parameter from web.xml.
  104. * </p>
  105. * <p>
  106. * After a UI has been created by the application, it is initialized using
  107. * {@link #init(VaadinRequest)}. This method is intended to be overridden by the
  108. * developer to add components to the user interface and initialize
  109. * non-component functionality. The component hierarchy must be initialized by
  110. * passing a {@link Component} with the main layout or other content of the view
  111. * to {@link #setContent(Component)} or to the constructor of the UI.
  112. * </p>
  113. *
  114. * @see #init(VaadinRequest)
  115. * @see UIProvider
  116. *
  117. * @since 7.0
  118. */
  119. public abstract class UI extends AbstractSingleComponentContainer
  120. implements Action.Notifier, PollNotifier, LegacyComponent, Focusable {
  121. /**
  122. * The application to which this UI belongs
  123. */
  124. private volatile VaadinSession session;
  125. /**
  126. * List of windows in this UI.
  127. */
  128. private final LinkedHashSet<Window> windows = new LinkedHashSet<>();
  129. /**
  130. * The component that should be scrolled into view after the next repaint.
  131. * Null if nothing should be scrolled into view.
  132. */
  133. private Component scrollIntoView;
  134. /**
  135. * The id of this UI, used to find the server side instance of the UI form
  136. * which a request originates. A negative value indicates that the UI id has
  137. * not yet been assigned by the Application.
  138. *
  139. * @see VaadinSession#getNextUIid()
  140. */
  141. private int uiId = -1;
  142. /**
  143. * Keeps track of the Actions added to this component, and manages the
  144. * painting and handling as well.
  145. */
  146. protected ActionManager actionManager;
  147. private ConnectorTracker connectorTracker = new ConnectorTracker(this);
  148. private Page page = new Page(this, getState(false).pageState);
  149. private LoadingIndicatorConfiguration loadingIndicatorConfiguration = new LoadingIndicatorConfigurationImpl(
  150. this);
  151. /**
  152. * Scroll Y position.
  153. */
  154. private int scrollTop = 0;
  155. /**
  156. * Scroll X position
  157. */
  158. private int scrollLeft = 0;
  159. private UIServerRpc rpc = new UIServerRpc() {
  160. @Override
  161. public void click(MouseEventDetails mouseDetails) {
  162. fireEvent(new ClickEvent(UI.this, mouseDetails));
  163. }
  164. @Override
  165. public void resize(int viewWidth, int viewHeight, int windowWidth,
  166. int windowHeight) {
  167. // TODO We're not doing anything with the view dimensions
  168. getPage().updateBrowserWindowSize(windowWidth, windowHeight, true);
  169. }
  170. @Override
  171. public void scroll(int scrollTop, int scrollLeft) {
  172. UI.this.scrollTop = scrollTop;
  173. UI.this.scrollLeft = scrollLeft;
  174. }
  175. @Override
  176. public void poll() {
  177. fireEvent(new PollEvent(UI.this));
  178. }
  179. @Override
  180. public void popstate(String uri) {
  181. getPage().updateLocation(uri, true, true);
  182. }
  183. };
  184. private DebugWindowServerRpc debugRpc = new DebugWindowServerRpc() {
  185. @Override
  186. public void showServerDebugInfo(Connector connector) {
  187. String info = ConnectorHelper
  188. .getDebugInformation((ClientConnector) connector);
  189. getLogger().info(info);
  190. }
  191. @Override
  192. public void analyzeLayouts() {
  193. // TODO Move to client side
  194. List<InvalidLayout> invalidSizes = ComponentSizeValidator
  195. .validateLayouts(UI.this);
  196. StringBuilder json = new StringBuilder();
  197. json.append("{\"invalidLayouts\":");
  198. json.append('[');
  199. if (invalidSizes != null) {
  200. boolean first = true;
  201. for (InvalidLayout invalidSize : invalidSizes) {
  202. if (!first) {
  203. json.append(',');
  204. } else {
  205. first = false;
  206. }
  207. invalidSize.reportErrors(json, System.err);
  208. }
  209. }
  210. json.append("]}");
  211. getRpcProxy(DebugWindowClientRpc.class)
  212. .reportLayoutProblems(json.toString());
  213. }
  214. @Override
  215. public void showServerDesign(Connector connector) {
  216. if (!(connector instanceof Component)) {
  217. getLogger().severe("Tried to output declarative design for "
  218. + connector + ", which is not a component");
  219. return;
  220. }
  221. if (connector instanceof UI) {
  222. // We want to see the content of the UI, so we can add it to
  223. // another UI or component container
  224. connector = ((UI) connector).getContent();
  225. }
  226. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  227. try {
  228. Design.write((Component) connector, baos);
  229. getLogger().info("Design for " + connector
  230. + " requested from debug window:\n"
  231. + baos.toString(UTF_8.name()));
  232. } catch (IOException e) {
  233. getLogger().log(Level.WARNING,
  234. "Error producing design for " + connector, e);
  235. }
  236. }
  237. };
  238. private WindowOrderRpc windowOrderRpc = windowOrders -> {
  239. Map<Integer, Window> orders = new LinkedHashMap<>();
  240. for (Entry<Integer, Connector> entry : windowOrders.entrySet()) {
  241. if (entry.getValue() instanceof Window) {
  242. orders.put(entry.getKey(), (Window) entry.getValue());
  243. }
  244. }
  245. fireWindowOrder(orders);
  246. };
  247. /**
  248. * Timestamp keeping track of the last heartbeat of this UI. Updated to the
  249. * current time whenever the application receives a heartbeat or UIDL
  250. * request from the client for this UI.
  251. */
  252. private long lastHeartbeatTimestamp = System.currentTimeMillis();
  253. private boolean closing = false;
  254. private TooltipConfiguration tooltipConfiguration = new TooltipConfigurationImpl(
  255. this);
  256. private PushConfiguration pushConfiguration = new PushConfigurationImpl(
  257. this);
  258. private ReconnectDialogConfiguration reconnectDialogConfiguration = new ReconnectDialogConfigurationImpl(
  259. this);
  260. private NotificationConfiguration notificationConfiguration = new NotificationConfigurationImpl(
  261. this);
  262. /**
  263. * Tracks which message from the client should come next. First message from
  264. * the client has id 0.
  265. */
  266. private int lastProcessedClientToServerId = -1;
  267. /**
  268. * Stores the extension of the active drag source component
  269. */
  270. private DragSourceExtension<? extends AbstractComponent> activeDragSource;
  271. /**
  272. * Creates a new empty UI without a caption. The content of the UI must be
  273. * set by calling {@link #setContent(Component)} before using the UI.
  274. */
  275. public UI() {
  276. this(null);
  277. }
  278. /**
  279. * Creates a new UI with the given component (often a layout) as its
  280. * content.
  281. *
  282. * @param content
  283. * the component to use as this UIs content.
  284. *
  285. * @see #setContent(Component)
  286. */
  287. public UI(Component content) {
  288. registerRpc(rpc);
  289. registerRpc(debugRpc);
  290. registerRpc(windowOrderRpc);
  291. setSizeFull();
  292. setContent(content);
  293. }
  294. @Override
  295. protected UIState getState() {
  296. return (UIState) super.getState();
  297. }
  298. @Override
  299. protected UIState getState(boolean markAsDirty) {
  300. return (UIState) super.getState(markAsDirty);
  301. }
  302. @Override
  303. public Class<? extends UIState> getStateType() {
  304. // This is a workaround for a problem with creating the correct state
  305. // object during build
  306. return UIState.class;
  307. }
  308. /**
  309. * Overridden to return a value instead of referring to the parent.
  310. *
  311. * @return this UI
  312. *
  313. * @see com.vaadin.ui.AbstractComponent#getUI()
  314. */
  315. @Override
  316. public UI getUI() {
  317. return this;
  318. }
  319. /**
  320. * Gets the application object to which the component is attached.
  321. *
  322. * <p>
  323. * The method will return {@code null} if the component is not currently
  324. * attached to an application.
  325. * </p>
  326. *
  327. * <p>
  328. * Getting a null value is often a problem in constructors of regular
  329. * components and in the initializers of custom composite components. A
  330. * standard workaround is to use {@link VaadinSession#getCurrent()} to
  331. * retrieve the application instance that the current request relates to.
  332. * Another way is to move the problematic initialization to
  333. * {@link #attach()}, as described in the documentation of the method.
  334. * </p>
  335. *
  336. * @return the parent application of the component or <code>null</code>.
  337. * @see #attach()
  338. */
  339. @Override
  340. public VaadinSession getSession() {
  341. return session;
  342. }
  343. @Override
  344. public void paintContent(PaintTarget target) throws PaintException {
  345. page.paintContent(target);
  346. if (scrollIntoView != null) {
  347. target.addAttribute("scrollTo", scrollIntoView);
  348. scrollIntoView = null;
  349. }
  350. if (pendingFocus != null) {
  351. // ensure focused component is still attached to this main window
  352. if (equals(pendingFocus.getUI()) || (pendingFocus.getUI() != null
  353. && equals(pendingFocus.getUI().getParent()))) {
  354. target.addAttribute("focused", pendingFocus);
  355. }
  356. pendingFocus = null;
  357. }
  358. if (actionManager != null) {
  359. actionManager.paintActions(null, target);
  360. }
  361. if (isResizeLazy()) {
  362. target.addAttribute(UIConstants.RESIZE_LAZY, true);
  363. }
  364. }
  365. /**
  366. * Fire a click event to all click listeners.
  367. *
  368. * @param object
  369. * The raw "value" of the variable change from the client side.
  370. */
  371. private void fireClick(Map<String, Object> parameters) {
  372. MouseEventDetails mouseDetails = MouseEventDetails
  373. .deSerialize((String) parameters.get("mouseDetails"));
  374. fireEvent(new ClickEvent(this, mouseDetails));
  375. }
  376. /**
  377. * Fire a window order event.
  378. *
  379. * @param windows
  380. * The windows with their orders whose order has been updated.
  381. */
  382. private void fireWindowOrder(Map<Integer, Window> windows) {
  383. for (Entry<Integer, Window> entry : windows.entrySet()) {
  384. entry.getValue().fireWindowOrderChange(entry.getKey());
  385. }
  386. fireEvent(new WindowOrderUpdateEvent(this, windows.values()));
  387. }
  388. @Override
  389. @SuppressWarnings("unchecked")
  390. public void changeVariables(Object source, Map<String, Object> variables) {
  391. if (variables.containsKey(EventId.CLICK_EVENT_IDENTIFIER)) {
  392. fireClick((Map<String, Object>) variables
  393. .get(EventId.CLICK_EVENT_IDENTIFIER));
  394. }
  395. // Actions
  396. if (actionManager != null) {
  397. actionManager.handleActions(variables, this);
  398. }
  399. }
  400. /*
  401. * (non-Javadoc)
  402. *
  403. * @see com.vaadin.ui.HasComponents#iterator()
  404. */
  405. @Override
  406. public Iterator<Component> iterator() {
  407. // TODO could directly create some kind of combined iterator instead of
  408. // creating a new ArrayList
  409. List<Component> components = new ArrayList<>();
  410. if (getContent() != null) {
  411. components.add(getContent());
  412. }
  413. components.addAll(windows);
  414. return Collections.unmodifiableCollection(components).iterator();
  415. }
  416. /*
  417. * (non-Javadoc)
  418. *
  419. * @see com.vaadin.ui.ComponentContainer#getComponentCount()
  420. */
  421. @Override
  422. public int getComponentCount() {
  423. return windows.size() + (getContent() == null ? 0 : 1);
  424. }
  425. /**
  426. * Sets the session to which this UI is assigned.
  427. * <p>
  428. * This method is for internal use by the framework. To explicitly close a
  429. * UI, see {@link #close()}.
  430. * </p>
  431. *
  432. * @param session
  433. * the session to set
  434. *
  435. * @throws IllegalStateException
  436. * if the session has already been set
  437. *
  438. * @see #getSession()
  439. */
  440. public void setSession(VaadinSession session) {
  441. if (session == null && this.session == null) {
  442. throw new IllegalStateException(
  443. "Session should never be set to null when UI.session is already null");
  444. } else if (session != null && this.session != null) {
  445. throw new IllegalStateException(
  446. "Session has already been set. Old session: "
  447. + getSessionDetails(this.session)
  448. + ". New session: " + getSessionDetails(session)
  449. + ".");
  450. } else {
  451. if (session == null) {
  452. try {
  453. detach();
  454. } catch (Exception e) {
  455. getLogger().log(Level.WARNING,
  456. "Error while detaching UI from session", e);
  457. }
  458. // Disable push when the UI is detached. Otherwise the
  459. // push connection and possibly VaadinSession will live
  460. // on.
  461. getPushConfiguration().setPushMode(PushMode.DISABLED);
  462. new Thread(() -> {
  463. // This intentionally does disconnect without locking
  464. // the VaadinSession to avoid deadlocks where the server
  465. // uses a lock for the websocket connection
  466. // See https://dev.vaadin.com/ticket/18436
  467. // The underlying problem is
  468. // https://dev.vaadin.com/ticket/16919
  469. setPushConnection(null);
  470. }).start();
  471. }
  472. this.session = session;
  473. }
  474. if (session != null) {
  475. attach();
  476. }
  477. }
  478. private static String getSessionDetails(VaadinSession session) {
  479. if (session == null) {
  480. return null;
  481. } else {
  482. return session + " for " + session.getService().getServiceName();
  483. }
  484. }
  485. /**
  486. * Gets the id of the UI, used to identify this UI within its application
  487. * when processing requests. The UI id should be present in every request to
  488. * the server that originates from this UI.
  489. * {@link VaadinService#findUI(VaadinRequest)} uses this id to find the
  490. * route to which the request belongs.
  491. * <p>
  492. * This method is not intended to be overridden. If it is overridden, care
  493. * should be taken since this method might be called in situations where
  494. * {@link UI#getCurrent()} does not return this UI.
  495. *
  496. * @return the id of this UI
  497. */
  498. public int getUIId() {
  499. return uiId;
  500. }
  501. /**
  502. * Adds a window as a subwindow inside this UI. To open a new browser window
  503. * or tab, you should instead use a {@link UIProvider}.
  504. *
  505. * @param window
  506. * @throws IllegalArgumentException
  507. * if the window is already added to an application
  508. * @throws NullPointerException
  509. * if the given <code>Window</code> is <code>null</code>.
  510. */
  511. public void addWindow(Window window)
  512. throws IllegalArgumentException, NullPointerException {
  513. if (window == null) {
  514. throw new NullPointerException("Argument must not be null");
  515. }
  516. if (window.isAttached()) {
  517. throw new IllegalArgumentException(
  518. "Window is already attached to an application.");
  519. }
  520. attachWindow(window);
  521. }
  522. /**
  523. * Helper method to attach a window.
  524. *
  525. * @param w
  526. * the window to add
  527. */
  528. private void attachWindow(Window w) {
  529. windows.add(w);
  530. w.setParent(this);
  531. fireComponentAttachEvent(w);
  532. markAsDirty();
  533. }
  534. /**
  535. * Remove the given subwindow from this UI.
  536. *
  537. * Since Vaadin 6.5, {@link Window.CloseListener}s are called also when
  538. * explicitly removing a window by calling this method.
  539. *
  540. * Since Vaadin 6.5, returns a boolean indicating if the window was removed
  541. * or not.
  542. *
  543. * @param window
  544. * Window to be removed.
  545. * @return true if the subwindow was removed, false otherwise
  546. */
  547. public boolean removeWindow(Window window) {
  548. if (!windows.remove(window)) {
  549. // Window window is not a subwindow of this UI.
  550. return false;
  551. }
  552. window.setParent(null);
  553. markAsDirty();
  554. window.fireClose();
  555. fireComponentDetachEvent(window);
  556. fireWindowOrder(Collections.singletonMap(-1, window));
  557. return true;
  558. }
  559. /**
  560. * Gets all the windows added to this UI.
  561. *
  562. * @return an unmodifiable collection of windows
  563. */
  564. public Collection<Window> getWindows() {
  565. return Collections.unmodifiableCollection(windows);
  566. }
  567. @Override
  568. public void focus() {
  569. super.focus();
  570. }
  571. /**
  572. * Component that should be focused after the next repaint. Null if no focus
  573. * change should take place.
  574. */
  575. private Focusable pendingFocus;
  576. private boolean resizeLazy = false;
  577. private Navigator navigator;
  578. private PushConnection pushConnection = null;
  579. private LocaleService localeService = new LocaleService(this,
  580. getState(false).localeServiceState);
  581. private String embedId;
  582. private String uiPathInfo;
  583. private String uiRootPath;
  584. private boolean mobileHtml5DndPolyfillLoaded;
  585. /**
  586. * This method is used by Component.Focusable objects to request focus to
  587. * themselves. Focus renders must be handled at window level (instead of
  588. * Component.Focusable) due we want the last focused component to be focused
  589. * in client too. Not the one that is rendered last (the case we'd get if
  590. * implemented in Focusable only).
  591. *
  592. * To focus component from Vaadin application, use Focusable.focus(). See
  593. * {@link Focusable}.
  594. *
  595. * @param focusable
  596. * to be focused on next paint
  597. */
  598. public void setFocusedComponent(Focusable focusable) {
  599. pendingFocus = focusable;
  600. markAsDirty();
  601. }
  602. /**
  603. * Scrolls any component between the component and UI to a suitable position
  604. * so the component is visible to the user. The given component must belong
  605. * to this UI.
  606. *
  607. * @param component
  608. * the component to be scrolled into view
  609. * @throws IllegalArgumentException
  610. * if {@code component} does not belong to this UI
  611. */
  612. public void scrollIntoView(Component component)
  613. throws IllegalArgumentException {
  614. if (component.getUI() != this) {
  615. throw new IllegalArgumentException(
  616. "The component where to scroll must belong to this UI.");
  617. }
  618. scrollIntoView = component;
  619. markAsDirty();
  620. }
  621. /**
  622. * Internal initialization method, should not be overridden. This method is
  623. * not declared as final because that would break compatibility with e.g.
  624. * CDI.
  625. *
  626. * @param request
  627. * the initialization request
  628. * @param uiId
  629. * the id of the new ui
  630. * @param embedId
  631. * the embed id of this UI, or <code>null</code> if no id is
  632. * known
  633. *
  634. * @see #getUIId()
  635. * @see #getEmbedId()
  636. */
  637. public void doInit(VaadinRequest request, int uiId, String embedId) {
  638. if (this.uiId != -1) {
  639. String message = "This UI instance is already initialized (as UI id "
  640. + this.uiId
  641. + ") and can therefore not be initialized again (as UI id "
  642. + uiId + "). ";
  643. if (getSession() != null
  644. && !getSession().equals(VaadinSession.getCurrent())) {
  645. message += "Furthermore, it is already attached to another VaadinSession. ";
  646. }
  647. message += "Please make sure you are not accidentally reusing an old UI instance.";
  648. throw new IllegalStateException(message);
  649. }
  650. this.uiId = uiId;
  651. this.embedId = embedId;
  652. // Actual theme - used for finding CustomLayout templates
  653. setTheme(request.getParameter("theme"));
  654. getPage().init(request);
  655. String uiPathInfo = (String) request
  656. .getAttribute(ApplicationConstants.UI_ROOT_PATH);
  657. if (uiPathInfo != null) {
  658. setUiPathInfo(uiPathInfo);
  659. }
  660. if (getSession() != null && getSession().getConfiguration() != null
  661. && getSession().getConfiguration().isSendUrlsAsParameters()
  662. && getPage().getLocation() != null) {
  663. // By default the root is the URL from client
  664. String uiRootPath = getPage().getLocation().getPath();
  665. if (uiPathInfo != null && uiRootPath.contains(uiPathInfo)) {
  666. // String everything from the URL after uiPathInfo
  667. // This will remove the navigation state from the URL
  668. uiRootPath = uiRootPath.substring(0,
  669. uiRootPath.indexOf(uiPathInfo) + uiPathInfo.length());
  670. } else if (request.getPathInfo() != null) {
  671. // uiRootPath does not match the uiPathInfo
  672. // This can happen for example when embedding a Vaadin UI
  673. String pathInfo = request.getPathInfo();
  674. if (uiRootPath.endsWith(pathInfo)) {
  675. uiRootPath = uiRootPath.substring(0,
  676. uiRootPath.length() - pathInfo.length());
  677. }
  678. }
  679. // Store the URL as the UI Root Path
  680. setUiRootPath(uiRootPath);
  681. }
  682. // Call the init overridden by the application developer
  683. init(request);
  684. Navigator navigator = getNavigator();
  685. if (navigator != null) {
  686. // Kickstart navigation if a navigator was attached in init()
  687. navigator.navigateTo(navigator.getState());
  688. }
  689. }
  690. private void setUiRootPath(String uiRootPath) {
  691. this.uiRootPath = uiRootPath;
  692. }
  693. /**
  694. * Gets the part of path (from browser's URL) that points to this UI.
  695. * Basically the same as the value from {@link Page#getLocation()}, but
  696. * without possible view identifiers or path parameters.
  697. *
  698. * @return the part of path (from browser's URL) that points to this UI,
  699. * without possible view identifiers or path parameters
  700. *
  701. * @since 8.2
  702. */
  703. public String getUiRootPath() {
  704. return uiRootPath;
  705. }
  706. private void setUiPathInfo(String uiPathInfo) {
  707. this.uiPathInfo = uiPathInfo;
  708. }
  709. /**
  710. * Gets the path info part of the request that is used to detect the UI.
  711. * This is defined during UI init by certain {@link UIProvider UIProviders}
  712. * that map different UIs to different URIs, like Vaadin Spring. This
  713. * information is used by the {@link Navigator} when the {@link UI} is
  714. * annotated with {@link PushStateNavigation}.
  715. * <p>
  716. * For example if the UI is accessed through
  717. * {@code http://example.com/MyUI/mainview/parameter=1} the path info would
  718. * be {@code /MyUI}.
  719. *
  720. * @return the path info part of the request; {@code null} if no request
  721. * from client has been processed
  722. *
  723. * @since 8.2
  724. */
  725. public String getUiPathInfo() {
  726. return uiPathInfo;
  727. }
  728. /**
  729. * Initializes this UI. This method is intended to be overridden by
  730. * subclasses to build the view and configure non-component functionality.
  731. * Performing the initialization in a constructor is not suggested as the
  732. * state of the UI is not properly set up when the constructor is invoked.
  733. * <p>
  734. * The {@link VaadinRequest} can be used to get information about the
  735. * request that caused this UI to be created.
  736. * </p>
  737. *
  738. * @param request
  739. * the Vaadin request that caused this UI to be created
  740. */
  741. protected abstract void init(VaadinRequest request);
  742. /**
  743. * Internal reinitialization method, should not be overridden.
  744. *
  745. * @since 7.2
  746. * @param request
  747. * the request that caused this UI to be reloaded
  748. */
  749. public void doRefresh(VaadinRequest request) {
  750. // This is a horrible hack. We want to have the most recent location and
  751. // browser window size available in refresh(), but we want to call
  752. // listeners, if any, only after refresh(). So we momentarily assign the
  753. // old values back before setting the new values again to ensure the
  754. // events are properly fired.
  755. Page page = getPage();
  756. URI oldLocation = page.getLocation();
  757. int oldWidth = page.getBrowserWindowWidth();
  758. int oldHeight = page.getBrowserWindowHeight();
  759. page.init(request);
  760. // Reset heartbeat timeout to avoid surprise if it's almost expired
  761. setLastHeartbeatTimestamp(System.currentTimeMillis());
  762. refresh(request);
  763. URI newLocation = page.getLocation();
  764. int newWidth = page.getBrowserWindowWidth();
  765. int newHeight = page.getBrowserWindowHeight();
  766. page.updateLocation(oldLocation.toString(), false, false);
  767. page.updateBrowserWindowSize(oldWidth, oldHeight, false);
  768. page.updateLocation(newLocation.toString(), true, false);
  769. page.updateBrowserWindowSize(newWidth, newHeight, true);
  770. // Navigate if there is navigator, this is needed in case of
  771. // PushStateNavigation. Call navigateTo only if state have
  772. // truly changed
  773. Navigator navigator = getNavigator();
  774. if (navigator != null
  775. && !Objects.equals(navigator.getCurrentNavigationState(),
  776. navigator.getState())) {
  777. navigator.navigateTo(navigator.getState());
  778. }
  779. }
  780. /**
  781. * Reinitializes this UI after a browser refresh if the UI is set to be
  782. * preserved on refresh, typically using the {@link PreserveOnRefresh}
  783. * annotation. This method is intended to be overridden by subclasses if
  784. * needed; the default implementation is empty.
  785. * <p>
  786. * The {@link VaadinRequest} can be used to get information about the
  787. * request that caused this UI to be reloaded.
  788. *
  789. * @since 7.2
  790. * @param request
  791. * the request that caused this UI to be reloaded
  792. */
  793. protected void refresh(VaadinRequest request) {
  794. }
  795. /**
  796. * Sets the thread local for the current UI. This method is used by the
  797. * framework to set the current application whenever a new request is
  798. * processed and it is cleared when the request has been processed.
  799. * <p>
  800. * The application developer can also use this method to define the current
  801. * UI outside the normal request handling, e.g. when initiating custom
  802. * background threads.
  803. * <p>
  804. * The UI is stored using a weak reference to avoid leaking memory in case
  805. * it is not explicitly cleared.
  806. *
  807. * @param ui
  808. * the UI to register as the current UI
  809. *
  810. * @see #getCurrent()
  811. * @see ThreadLocal
  812. */
  813. public static void setCurrent(UI ui) {
  814. CurrentInstance.set(UI.class, ui);
  815. }
  816. /**
  817. * Gets the currently used UI. The current UI is automatically defined when
  818. * processing requests to the server. In other cases, (e.g. from background
  819. * threads), the current UI is not automatically defined.
  820. * <p>
  821. * The UI is stored using a weak reference to avoid leaking memory in case
  822. * it is not explicitly cleared.
  823. *
  824. * @return the current UI instance if available, otherwise <code>null</code>
  825. *
  826. * @see #setCurrent(UI)
  827. */
  828. public static UI getCurrent() {
  829. return CurrentInstance.get(UI.class);
  830. }
  831. /**
  832. * Set top offset to which the UI should scroll to.
  833. *
  834. * @param scrollTop
  835. */
  836. public void setScrollTop(int scrollTop) {
  837. if (scrollTop < 0) {
  838. throw new IllegalArgumentException(
  839. "Scroll offset must be at least 0");
  840. }
  841. if (this.scrollTop != scrollTop) {
  842. this.scrollTop = scrollTop;
  843. getRpcProxy(ScrollClientRpc.class).setScrollTop(scrollTop);
  844. }
  845. }
  846. public int getScrollTop() {
  847. return scrollTop;
  848. }
  849. /**
  850. * Set left offset to which the UI should scroll to.
  851. *
  852. * @param scrollLeft
  853. */
  854. public void setScrollLeft(int scrollLeft) {
  855. if (scrollLeft < 0) {
  856. throw new IllegalArgumentException(
  857. "Scroll offset must be at least 0");
  858. }
  859. if (this.scrollLeft != scrollLeft) {
  860. this.scrollLeft = scrollLeft;
  861. getRpcProxy(ScrollClientRpc.class).setScrollLeft(scrollLeft);
  862. }
  863. }
  864. public int getScrollLeft() {
  865. return scrollLeft;
  866. }
  867. @Override
  868. protected ActionManager getActionManager() {
  869. if (actionManager == null) {
  870. actionManager = new ActionManager(this);
  871. }
  872. return actionManager;
  873. }
  874. @Override
  875. public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
  876. T action) {
  877. getActionManager().addAction(action);
  878. }
  879. @Override
  880. public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
  881. T action) {
  882. if (actionManager != null) {
  883. actionManager.removeAction(action);
  884. }
  885. }
  886. @Override
  887. public void addActionHandler(Handler actionHandler) {
  888. getActionManager().addActionHandler(actionHandler);
  889. }
  890. @Override
  891. public void removeActionHandler(Handler actionHandler) {
  892. if (actionManager != null) {
  893. actionManager.removeActionHandler(actionHandler);
  894. }
  895. }
  896. /**
  897. * Should resize operations be lazy, i.e. should there be a delay before
  898. * layout sizes are recalculated and resize events are sent to the server.
  899. * Speeds up resize operations in slow UIs with the penalty of slightly
  900. * decreased usability.
  901. * <p>
  902. * Default value: <code>false</code>
  903. * </p>
  904. * <p>
  905. * When there are active window resize listeners, lazy resize mode should be
  906. * used to avoid a large number of events during resize.
  907. * </p>
  908. *
  909. * @param resizeLazy
  910. * true to use a delay before recalculating sizes, false to
  911. * calculate immediately.
  912. */
  913. public void setResizeLazy(boolean resizeLazy) {
  914. this.resizeLazy = resizeLazy;
  915. markAsDirty();
  916. }
  917. /**
  918. * Checks whether lazy resize is enabled.
  919. *
  920. * @return <code>true</code> if lazy resize is enabled, <code>false</code>
  921. * if lazy resize is not enabled
  922. */
  923. public boolean isResizeLazy() {
  924. return resizeLazy;
  925. }
  926. /**
  927. * Add a click listener to the UI. The listener is called whenever the user
  928. * clicks inside the UI. Also when the click targets a component inside the
  929. * UI, provided the targeted component does not prevent the click event from
  930. * propagating.
  931. *
  932. * @see Registration
  933. *
  934. * @param listener
  935. * The listener to add, not null
  936. * @return a registration object for removing the listener
  937. * @since 8.0
  938. */
  939. public Registration addClickListener(ClickListener listener) {
  940. return addListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class,
  941. listener, ClickListener.clickMethod);
  942. }
  943. /**
  944. * Remove a click listener from the UI. The listener should earlier have
  945. * been added using {@link #addListener(ClickListener)}.
  946. *
  947. * @param listener
  948. * The listener to remove
  949. *
  950. * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the
  951. * registration object returned from
  952. * {@link #removeClickListener(ClickListener)}.
  953. */
  954. @Deprecated
  955. public void removeClickListener(ClickListener listener) {
  956. removeListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class,
  957. listener);
  958. }
  959. @Override
  960. public boolean isConnectorEnabled() {
  961. // TODO How can a UI be invisible? What does it mean?
  962. return isVisible() && isEnabled();
  963. }
  964. public ConnectorTracker getConnectorTracker() {
  965. return connectorTracker;
  966. }
  967. public Page getPage() {
  968. return page;
  969. }
  970. /**
  971. * Returns the navigator attached to this UI or null if there is no
  972. * navigator.
  973. *
  974. * @return
  975. */
  976. public Navigator getNavigator() {
  977. return navigator;
  978. }
  979. /**
  980. * For internal use only.
  981. *
  982. * @param navigator
  983. */
  984. public void setNavigator(Navigator navigator) {
  985. this.navigator = navigator;
  986. }
  987. /**
  988. * Setting the caption of a UI is not supported. To set the title of the
  989. * HTML page, use Page.setTitle
  990. *
  991. * @deprecated As of 7.0, use {@link Page#setTitle(String)}
  992. */
  993. @Override
  994. @Deprecated
  995. public void setCaption(String caption) {
  996. throw new UnsupportedOperationException(
  997. "You can not set the title of a UI. To set the title of the HTML page, use Page.setTitle");
  998. }
  999. /**
  1000. * Shows a notification message on the middle of the UI. The message
  1001. * automatically disappears ("humanized message").
  1002. *
  1003. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  1004. * rendered as html.
  1005. *
  1006. * @see #showNotification(Notification)
  1007. * @see Notification
  1008. *
  1009. * @param caption
  1010. * The message
  1011. *
  1012. * @deprecated As of 7.0, use Notification.show instead but be aware that
  1013. * Notification.show does not allow HTML.
  1014. */
  1015. @Deprecated
  1016. public void showNotification(String caption) {
  1017. Notification notification = new Notification(caption);
  1018. notification.setHtmlContentAllowed(true);// Backwards compatibility
  1019. getPage().showNotification(notification);
  1020. }
  1021. /**
  1022. * Shows a notification message the UI. The position and behavior of the
  1023. * message depends on the type, which is one of the basic types defined in
  1024. * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
  1025. *
  1026. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  1027. * rendered as html.
  1028. *
  1029. * @see #showNotification(Notification)
  1030. * @see Notification
  1031. *
  1032. * @param caption
  1033. * The message
  1034. * @param type
  1035. * The message type
  1036. *
  1037. * @deprecated As of 7.0, use Notification.show instead but be aware that
  1038. * Notification.show does not allow HTML.
  1039. */
  1040. @Deprecated
  1041. public void showNotification(String caption, Notification.Type type) {
  1042. Notification notification = new Notification(caption, type);
  1043. notification.setHtmlContentAllowed(true);// Backwards compatibility
  1044. getPage().showNotification(notification);
  1045. }
  1046. /**
  1047. * Shows a notification consisting of a bigger caption and a smaller
  1048. * description on the middle of the UI. The message automatically disappears
  1049. * ("humanized message").
  1050. *
  1051. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  1052. * description are rendered as html.
  1053. *
  1054. * @see #showNotification(Notification)
  1055. * @see Notification
  1056. *
  1057. * @param caption
  1058. * The caption of the message
  1059. * @param description
  1060. * The message description
  1061. *
  1062. * @deprecated As of 7.0, use new Notification(...).show(Page) instead but
  1063. * be aware that HTML by default not allowed.
  1064. */
  1065. @Deprecated
  1066. public void showNotification(String caption, String description) {
  1067. Notification notification = new Notification(caption, description);
  1068. notification.setHtmlContentAllowed(true);// Backwards compatibility
  1069. getPage().showNotification(notification);
  1070. }
  1071. /**
  1072. * Shows a notification consisting of a bigger caption and a smaller
  1073. * description. The position and behavior of the message depends on the
  1074. * type, which is one of the basic types defined in {@link Notification} ,
  1075. * for instance Notification.TYPE_WARNING_MESSAGE.
  1076. *
  1077. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  1078. * description are rendered as html.
  1079. *
  1080. * @see #showNotification(Notification)
  1081. * @see Notification
  1082. *
  1083. * @param caption
  1084. * The caption of the message
  1085. * @param description
  1086. * The message description
  1087. * @param type
  1088. * The message type
  1089. *
  1090. * @deprecated As of 7.0, use new Notification(...).show(Page) instead but
  1091. * be aware that HTML by default not allowed.
  1092. */
  1093. @Deprecated
  1094. public void showNotification(String caption, String description,
  1095. Notification.Type type) {
  1096. Notification notification = new Notification(caption, description,
  1097. type);
  1098. notification.setHtmlContentAllowed(true);// Backwards compatibility
  1099. getPage().showNotification(notification);
  1100. }
  1101. /**
  1102. * Shows a notification consisting of a bigger caption and a smaller
  1103. * description. The position and behavior of the message depends on the
  1104. * type, which is one of the basic types defined in {@link Notification} ,
  1105. * for instance Notification.TYPE_WARNING_MESSAGE.
  1106. *
  1107. * Care should be taken to avoid XSS vulnerabilities if html content is
  1108. * allowed.
  1109. *
  1110. * @see #showNotification(Notification)
  1111. * @see Notification
  1112. *
  1113. * @param caption
  1114. * The message caption
  1115. * @param description
  1116. * The message description
  1117. * @param type
  1118. * The type of message
  1119. * @param htmlContentAllowed
  1120. * Whether html in the caption and description should be
  1121. * displayed as html or as plain text
  1122. *
  1123. * @deprecated As of 7.0, use new Notification(...).show(Page).
  1124. */
  1125. @Deprecated
  1126. public void showNotification(String caption, String description,
  1127. Notification.Type type, boolean htmlContentAllowed) {
  1128. getPage().showNotification(new Notification(caption, description, type,
  1129. htmlContentAllowed));
  1130. }
  1131. /**
  1132. * Shows a notification message.
  1133. *
  1134. * @see Notification
  1135. * @see #showNotification(String)
  1136. * @see #showNotification(String, int)
  1137. * @see #showNotification(String, String)
  1138. * @see #showNotification(String, String, int)
  1139. *
  1140. * @param notification
  1141. * The notification message to show
  1142. *
  1143. * @deprecated As of 7.0, use Notification.show instead
  1144. */
  1145. @Deprecated
  1146. public void showNotification(Notification notification) {
  1147. getPage().showNotification(notification);
  1148. }
  1149. /**
  1150. * Returns the timestamp of the last received heartbeat for this UI.
  1151. * <p>
  1152. * This method is not intended to be overridden. If it is overridden, care
  1153. * should be taken since this method might be called in situations where
  1154. * {@link UI#getCurrent()} does not return this UI.
  1155. *
  1156. * @see VaadinService#closeInactiveUIs(VaadinSession)
  1157. *
  1158. * @return The time the last heartbeat request occurred, in milliseconds
  1159. * since the epoch.
  1160. */
  1161. public long getLastHeartbeatTimestamp() {
  1162. return lastHeartbeatTimestamp;
  1163. }
  1164. /**
  1165. * Sets the last heartbeat request timestamp for this UI. Called by the
  1166. * framework whenever the application receives a valid heartbeat request for
  1167. * this UI.
  1168. * <p>
  1169. * This method is not intended to be overridden. If it is overridden, care
  1170. * should be taken since this method might be called in situations where
  1171. * {@link UI#getCurrent()} does not return this UI.
  1172. *
  1173. * @param lastHeartbeat
  1174. * The time the last heartbeat request occurred, in milliseconds
  1175. * since the epoch.
  1176. */
  1177. public void setLastHeartbeatTimestamp(long lastHeartbeat) {
  1178. lastHeartbeatTimestamp = lastHeartbeat;
  1179. }
  1180. /**
  1181. * Gets the theme currently in use by this UI.
  1182. *
  1183. * @return the theme name
  1184. */
  1185. public String getTheme() {
  1186. return getState(false).theme;
  1187. }
  1188. /**
  1189. * Sets the theme currently in use by this UI
  1190. * <p>
  1191. * Calling this method will remove the old theme (CSS file) from the
  1192. * application and add the new theme.
  1193. * <p>
  1194. * Note that this method is NOT SAFE to call in a portal environment or
  1195. * other environment where there are multiple UIs on the same page. The old
  1196. * CSS file will be removed even if there are other UIs on the page which
  1197. * are still using it.
  1198. *
  1199. * @since 7.3
  1200. * @param theme
  1201. * The new theme name
  1202. */
  1203. public void setTheme(String theme) {
  1204. if (theme == null) {
  1205. getState().theme = null;
  1206. } else {
  1207. getState().theme = VaadinServlet.stripSpecialChars(theme);
  1208. }
  1209. }
  1210. /**
  1211. * Marks this UI to be {@link #detach() detached} from the session at the
  1212. * end of the current request, or the next request if there is no current
  1213. * request (if called from a background thread, for instance.)
  1214. * <p>
  1215. * The UI is detached after the response is sent, so in the current request
  1216. * it can still update the client side normally. However, after the response
  1217. * any new requests from the client side to this UI will cause an error, so
  1218. * usually the client should be asked, for instance, to reload the page
  1219. * (serving a fresh UI instance), to close the page, or to navigate
  1220. * somewhere else.
  1221. * <p>
  1222. * Note that this method is strictly for users to explicitly signal the
  1223. * framework that the UI should be detached. Overriding it is not a reliable
  1224. * way to catch UIs that are to be detached. Instead, {@code UI.detach()}
  1225. * should be overridden or a {@link DetachListener} used.
  1226. */
  1227. public void close() {
  1228. closing = true;
  1229. boolean sessionExpired = (session == null
  1230. || session.getState() != State.OPEN);
  1231. getRpcProxy(UIClientRpc.class).uiClosed(sessionExpired);
  1232. if (getPushConnection() != null) {
  1233. // Push the Rpc to the client. The connection will be closed when
  1234. // the UI is detached and cleaned up.
  1235. // Can't use UI.push() directly since it checks for a valid session
  1236. if (session != null) {
  1237. session.getService().runPendingAccessTasks(session);
  1238. }
  1239. getPushConnection().push();
  1240. }
  1241. }
  1242. /**
  1243. * Returns whether this UI is marked as closed and is to be detached.
  1244. * <p>
  1245. * This method is not intended to be overridden. If it is overridden, care
  1246. * should be taken since this method might be called in situations where
  1247. * {@link UI#getCurrent()} does not return this UI.
  1248. *
  1249. * @see #close()
  1250. *
  1251. * @return whether this UI is closing.
  1252. */
  1253. public boolean isClosing() {
  1254. return closing;
  1255. }
  1256. /**
  1257. * Called after the UI is added to the session. A UI instance is attached
  1258. * exactly once, before its {@link #init(VaadinRequest) init} method is
  1259. * called.
  1260. *
  1261. * @see Component#attach
  1262. */
  1263. @Override
  1264. public void attach() {
  1265. super.attach();
  1266. getLocaleService().addLocale(getLocale());
  1267. }
  1268. /**
  1269. * Called before the UI is removed from the session. A UI instance is
  1270. * detached exactly once, either:
  1271. * <ul>
  1272. * <li>after it is explicitly {@link #close() closed}.
  1273. * <li>when its session is closed or expires
  1274. * <li>after three missed heartbeat requests.
  1275. * </ul>
  1276. * <p>
  1277. * Note that when a UI is detached, any changes made in the {@code detach}
  1278. * methods of any children or {@link DetachListener}s that would be
  1279. * communicated to the client are silently ignored.
  1280. */
  1281. @Override
  1282. public void detach() {
  1283. super.detach();
  1284. }
  1285. /*
  1286. * (non-Javadoc)
  1287. *
  1288. * @see
  1289. * com.vaadin.ui.AbstractSingleComponentContainer#setContent(com.vaadin.
  1290. * ui.Component)
  1291. */
  1292. @Override
  1293. public void setContent(Component content) {
  1294. if (content instanceof Window) {
  1295. throw new IllegalArgumentException(
  1296. "A Window cannot be added using setContent. Use addWindow(Window window) instead");
  1297. }
  1298. super.setContent(content);
  1299. }
  1300. @Override
  1301. public void setTabIndex(int tabIndex) {
  1302. getState().tabIndex = tabIndex;
  1303. }
  1304. @Override
  1305. public int getTabIndex() {
  1306. return getState(false).tabIndex;
  1307. }
  1308. /**
  1309. * Locks the session of this UI and runs the provided Runnable right away.
  1310. * <p>
  1311. * It is generally recommended to use {@link #access(Runnable)} instead of
  1312. * this method for accessing a session from a different thread as
  1313. * {@link #access(Runnable)} can be used while holding the lock of another
  1314. * session. To avoid causing deadlocks, this methods throws an exception if
  1315. * it is detected than another session is also locked by the current thread.
  1316. * </p>
  1317. * <p>
  1318. * This method behaves differently than {@link #access(Runnable)} in some
  1319. * situations:
  1320. * <ul>
  1321. * <li>If the current thread is currently holding the lock of the session,
  1322. * {@link #accessSynchronously(Runnable)} runs the task right away whereas
  1323. * {@link #access(Runnable)} defers the task to a later point in time.</li>
  1324. * <li>If some other thread is currently holding the lock for the session,
  1325. * {@link #accessSynchronously(Runnable)} blocks while waiting for the lock
  1326. * to be available whereas {@link #access(Runnable)} defers the task to a
  1327. * later point in time.</li>
  1328. * </ul>
  1329. * </p>
  1330. *
  1331. * @since 7.1
  1332. *
  1333. * @param runnable
  1334. * the runnable which accesses the UI
  1335. * @throws UIDetachedException
  1336. * if the UI is not attached to a session (and locking can
  1337. * therefore not be done)
  1338. * @throws IllegalStateException
  1339. * if the current thread holds the lock for another session
  1340. *
  1341. * @see #access(Runnable)
  1342. * @see VaadinSession#accessSynchronously(Runnable)
  1343. */
  1344. public void accessSynchronously(Runnable runnable)
  1345. throws UIDetachedException {
  1346. Map<Class<?>, CurrentInstance> old = null;
  1347. VaadinSession session = getSession();
  1348. if (session == null) {
  1349. throw new UIDetachedException();
  1350. }
  1351. VaadinService.verifyNoOtherSessionLocked(session);
  1352. session.lock();
  1353. try {
  1354. if (getSession() == null) {
  1355. // UI was detached after fetching the session but before we
  1356. // acquired the lock.
  1357. throw new UIDetachedException();
  1358. }
  1359. old = CurrentInstance.setCurrent(this);
  1360. runnable.run();
  1361. } finally {
  1362. session.unlock();
  1363. if (old != null) {
  1364. CurrentInstance.restoreInstances(old);
  1365. }
  1366. }
  1367. }
  1368. /**
  1369. * Provides exclusive access to this UI from outside a request handling
  1370. * thread.
  1371. * <p>
  1372. * The given runnable is executed while holding the session lock to ensure
  1373. * exclusive access to this UI. If the session is not locked, the lock will
  1374. * be acquired and the runnable is run right away. If the session is
  1375. * currently locked, the runnable will be run before that lock is released.
  1376. * </p>
  1377. * <p>
  1378. * RPC handlers for components inside this UI do not need to use this method
  1379. * as the session is automatically locked by the framework during RPC
  1380. * handling.
  1381. * </p>
  1382. * <p>
  1383. * Please note that the runnable might be invoked on a different thread or
  1384. * later on the current thread, which means that custom thread locals might
  1385. * not have the expected values when the command is executed.
  1386. * {@link UI#getCurrent()}, {@link VaadinSession#getCurrent()} and
  1387. * {@link VaadinService#getCurrent()} are set according to this UI before
  1388. * executing the command. Other standard CurrentInstance values such as
  1389. * {@link VaadinService#getCurrentRequest()} and
  1390. * {@link VaadinService#getCurrentResponse()} will not be defined.
  1391. * </p>
  1392. * <p>
  1393. * The returned future can be used to check for task completion and to
  1394. * cancel the task.
  1395. * </p>
  1396. *
  1397. * @see #getCurrent()
  1398. * @see #accessSynchronously(Runnable)
  1399. * @see VaadinSession#access(Runnable)
  1400. * @see VaadinSession#lock()
  1401. *
  1402. * @since 7.1
  1403. *
  1404. * @param runnable
  1405. * the runnable which accesses the UI
  1406. * @throws UIDetachedException
  1407. * if the UI is not attached to a session (and locking can
  1408. * therefore not be done)
  1409. * @return a future that can be used to check for task completion and to
  1410. * cancel the task
  1411. */
  1412. public Future<Void> access(final Runnable runnable) {
  1413. VaadinSession session = getSession();
  1414. if (session == null) {
  1415. throw new UIDetachedException();
  1416. }
  1417. return session.access(new ErrorHandlingRunnable() {
  1418. @Override
  1419. public void run() {
  1420. accessSynchronously(runnable);
  1421. }
  1422. @Override
  1423. public void handleError(Exception exception) {
  1424. try {
  1425. exception = ErrorHandlingRunnable.processException(runnable,
  1426. exception);
  1427. if (exception instanceof UIDetachedException) {
  1428. assert session != null;
  1429. /*
  1430. * UI was detached after access was run, but before
  1431. * accessSynchronously. Furthermore, there wasn't an
  1432. * ErrorHandlingRunnable that handled the exception.
  1433. */
  1434. getLogger().log(Level.WARNING,
  1435. "access() task ignored because UI got detached after the task was enqueued."
  1436. + " To suppress this message, change the task to implement {} and make it handle {}."
  1437. + " Affected task: {}",
  1438. new Object[] {
  1439. ErrorHandlingRunnable.class.getName(),
  1440. UIDetachedException.class.getName(),
  1441. runnable });
  1442. } else if (exception != null) {
  1443. /*
  1444. * If no ErrorHandlingRunnable, or if it threw an
  1445. * exception of its own.
  1446. */
  1447. ConnectorErrorEvent errorEvent = new ConnectorErrorEvent(
  1448. UI.this, exception);
  1449. ErrorHandler errorHandler = com.vaadin.server.ErrorEvent
  1450. .findErrorHandler(UI.this);
  1451. if (errorHandler == null && getSession() == null) {
  1452. /*
  1453. * Special case where findErrorHandler(UI) cannot
  1454. * find the session handler because the UI has
  1455. * recently been detached.
  1456. */
  1457. errorHandler = com.vaadin.server.ErrorEvent
  1458. .findErrorHandler(session);
  1459. }
  1460. if (errorHandler == null) {
  1461. errorHandler = new DefaultErrorHandler();
  1462. }
  1463. errorHandler.error(errorEvent);
  1464. }
  1465. } catch (Exception e) {
  1466. getLogger().log(Level.SEVERE, e.getMessage(), e);
  1467. }
  1468. }
  1469. });
  1470. }
  1471. /**
  1472. * Retrieves the object used for configuring tooltips.
  1473. *
  1474. * @return The instance used for tooltip configuration
  1475. */
  1476. public TooltipConfiguration getTooltipConfiguration() {
  1477. return tooltipConfiguration;
  1478. }
  1479. /**
  1480. * Retrieves the object used for configuring notifications.
  1481. *
  1482. * @return The instance used for notification configuration
  1483. */
  1484. public NotificationConfiguration getNotificationConfiguration() {
  1485. return notificationConfiguration;
  1486. }
  1487. /**
  1488. * Retrieves the object used for configuring the loading indicator.
  1489. *
  1490. * @return The instance used for configuring the loading indicator
  1491. */
  1492. public LoadingIndicatorConfiguration getLoadingIndicatorConfiguration() {
  1493. return loadingIndicatorConfiguration;
  1494. }
  1495. /**
  1496. * Pushes the pending changes and client RPC invocations of this UI to the
  1497. * client-side.
  1498. * <p>
  1499. * If push is enabled, but the push connection is not currently open, the
  1500. * push will be done when the connection is established.
  1501. * <p>
  1502. * As with all UI methods, the session must be locked when calling this
  1503. * method. It is also recommended that {@link UI#getCurrent()} is set up to
  1504. * return this UI since writing the response may invoke logic in any
  1505. * attached component or extension. The recommended way of fulfilling these
  1506. * conditions is to use {@link #access(Runnable)}.
  1507. *
  1508. * @throws IllegalStateException
  1509. * if push is disabled.
  1510. * @throws UIDetachedException
  1511. * if this UI is not attached to a session.
  1512. *
  1513. * @see #getPushConfiguration()
  1514. *
  1515. * @since 7.1
  1516. */
  1517. public void push() {
  1518. VaadinSession session = getSession();
  1519. if (session == null) {
  1520. throw new UIDetachedException("Cannot push a detached UI");
  1521. }
  1522. assert session.hasLock();
  1523. if (!getPushConfiguration().getPushMode().isEnabled()) {
  1524. throw new IllegalStateException("Push not enabled");
  1525. }
  1526. assert pushConnection != null;
  1527. /*
  1528. * Purge the pending access queue as it might mark a connector as dirty
  1529. * when the push would otherwise be ignored because there are no changes
  1530. * to push.
  1531. */
  1532. session.getService().runPendingAccessTasks(session);
  1533. if (!getConnectorTracker().hasDirtyConnectors()) {
  1534. // Do not push if there is nothing to push
  1535. return;
  1536. }
  1537. pushConnection.push();
  1538. }
  1539. /**
  1540. * Returns the internal push connection object used by this UI. This method
  1541. * should only be called by the framework.
  1542. * <p>
  1543. * This method is not intended to be overridden. If it is overridden, care
  1544. * should be taken since this method might be called in situations where
  1545. * {@link UI#getCurrent()} does not return this UI.
  1546. *
  1547. * @return the push connection used by this UI, or {@code null} if push is
  1548. * not available.
  1549. */
  1550. public PushConnection getPushConnection() {
  1551. assert !(getPushConfiguration().getPushMode().isEnabled()
  1552. && pushConnection == null);
  1553. return pushConnection;
  1554. }
  1555. /**
  1556. * Sets the internal push connection object used by this UI. This method
  1557. * should only be called by the framework.
  1558. * <p>
  1559. * The {@code pushConnection} argument must be non-null if and only if
  1560. * {@code getPushConfiguration().getPushMode().isEnabled()}.
  1561. *
  1562. * @param pushConnection
  1563. * the push connection to use for this UI
  1564. */
  1565. public void setPushConnection(PushConnection pushConnection) {
  1566. // If pushMode is disabled then there should never be a pushConnection;
  1567. // if enabled there should always be
  1568. assert (pushConnection == null)
  1569. ^ getPushConfiguration().getPushMode().isEnabled();
  1570. if (pushConnection == this.pushConnection) {
  1571. return;
  1572. }
  1573. if (this.pushConnection != null && this.pushConnection.isConnected()) {
  1574. this.pushConnection.disconnect();
  1575. }
  1576. this.pushConnection = pushConnection;
  1577. }
  1578. /**
  1579. * Sets the interval with which the UI should poll the server to see if
  1580. * there are any changes. Polling is disabled by default.
  1581. * <p>
  1582. * Note that it is possible to enable push and polling at the same time but
  1583. * it should not be done to avoid excessive server traffic.
  1584. * </p>
  1585. * <p>
  1586. * Add-on developers should note that this method is only meant for the
  1587. * application developer. An add-on should not set the poll interval
  1588. * directly, rather instruct the user to set it.
  1589. * </p>
  1590. *
  1591. * @param intervalInMillis
  1592. * The interval (in ms) with which the UI should poll the server
  1593. * or -1 to disable polling
  1594. */
  1595. public void setPollInterval(int intervalInMillis) {
  1596. getState().pollInterval = intervalInMillis;
  1597. }
  1598. /**
  1599. * Returns the interval with which the UI polls the server.
  1600. *
  1601. * @return The interval (in ms) with which the UI polls the server or -1 if
  1602. * polling is disabled
  1603. */
  1604. public int getPollInterval() {
  1605. return getState(false).pollInterval;
  1606. }
  1607. @Override
  1608. public Registration addPollListener(PollListener listener) {
  1609. return addListener(EventId.POLL, PollEvent.class, listener,
  1610. PollListener.POLL_METHOD);
  1611. }
  1612. @Override
  1613. @Deprecated
  1614. public void removePollListener(PollListener listener) {
  1615. removeListener(EventId.POLL, PollEvent.class, listener);
  1616. }
  1617. /**
  1618. * Retrieves the object used for configuring the push channel.
  1619. *
  1620. * @since 7.1
  1621. * @return The instance used for push configuration
  1622. */
  1623. public PushConfiguration getPushConfiguration() {
  1624. return pushConfiguration;
  1625. }
  1626. /**
  1627. * Retrieves the object used for configuring the reconnect dialog.
  1628. *
  1629. * @since 7.6
  1630. * @return The instance used for reconnect dialog configuration
  1631. */
  1632. public ReconnectDialogConfiguration getReconnectDialogConfiguration() {
  1633. return reconnectDialogConfiguration;
  1634. }
  1635. /**
  1636. * Get the label that is added to the container element, where tooltip,
  1637. * notification and dialogs are added to.
  1638. *
  1639. * @return the label of the container
  1640. */
  1641. public String getOverlayContainerLabel() {
  1642. return getState(false).overlayContainerLabel;
  1643. }
  1644. /**
  1645. * Sets the label that is added to the container element, where tooltip,
  1646. * notifications and dialogs are added to.
  1647. * <p>
  1648. * This is helpful for users of assistive devices, as this element is
  1649. * reachable for them.
  1650. * </p>
  1651. *
  1652. * @param overlayContainerLabel
  1653. * label to use for the container
  1654. */
  1655. public void setOverlayContainerLabel(String overlayContainerLabel) {
  1656. getState().overlayContainerLabel = overlayContainerLabel;
  1657. }
  1658. /**
  1659. * Returns the locale service which handles transmission of Locale data to
  1660. * the client.
  1661. *
  1662. * @since 7.1
  1663. * @return The LocaleService for this UI
  1664. */
  1665. public LocaleService getLocaleService() {
  1666. return localeService;
  1667. }
  1668. private static Logger getLogger() {
  1669. return Logger.getLogger(UI.class.getName());
  1670. }
  1671. /**
  1672. * Gets a string the uniquely distinguishes this UI instance based on where
  1673. * it is embedded. The embed identifier is based on the
  1674. * <code>window.name</code> DOM attribute of the browser window where the UI
  1675. * is displayed and the id of the div element where the UI is embedded.
  1676. *
  1677. * @since 7.2
  1678. * @return the embed id for this UI, or <code>null</code> if no id known
  1679. */
  1680. public String getEmbedId() {
  1681. return embedId;
  1682. }
  1683. /**
  1684. * Gets the last processed server message id.
  1685. *
  1686. * Used internally for communication tracking.
  1687. *
  1688. * @return lastProcessedServerMessageId the id of the last processed server
  1689. * message
  1690. * @since 7.6
  1691. */
  1692. public int getLastProcessedClientToServerId() {
  1693. return lastProcessedClientToServerId;
  1694. }
  1695. /**
  1696. * Sets the last processed server message id.
  1697. *
  1698. * Used internally for communication tracking.
  1699. *
  1700. * @param lastProcessedClientToServerId
  1701. * the id of the last processed server message
  1702. * @since 7.6
  1703. */
  1704. public void setLastProcessedClientToServerId(
  1705. int lastProcessedClientToServerId) {
  1706. this.lastProcessedClientToServerId = lastProcessedClientToServerId;
  1707. }
  1708. /**
  1709. * Adds a WindowOrderUpdateListener to the UI.
  1710. * <p>
  1711. * The WindowOrderUpdateEvent is fired when the order positions of windows
  1712. * are updated. It can happen when some window (this or other) is brought to
  1713. * front or detached.
  1714. * <p>
  1715. * The other way to listen window position for specific window is
  1716. * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)}
  1717. *
  1718. * @see Window#addWindowOrderChangeListener(WindowOrderChangeListener)
  1719. *
  1720. * @param listener
  1721. * the WindowModeChangeListener to add.
  1722. * @since 8.0
  1723. *
  1724. * @return a registration object for removing the listener
  1725. */
  1726. public Registration addWindowOrderUpdateListener(
  1727. WindowOrderUpdateListener listener) {
  1728. addListener(EventId.WINDOW_ORDER, WindowOrderUpdateEvent.class,
  1729. listener, WindowOrderUpdateListener.windowOrderUpdateMethod);
  1730. return () -> removeListener(EventId.WINDOW_ORDER,
  1731. WindowOrderUpdateEvent.class, listener);
  1732. }
  1733. /**
  1734. * Sets the drag source of an active HTML5 drag event.
  1735. *
  1736. * @param extension
  1737. * Extension of the drag source component.
  1738. * @see DragSourceExtension
  1739. * @since 8.1
  1740. */
  1741. public void setActiveDragSource(
  1742. DragSourceExtension<? extends AbstractComponent> extension) {
  1743. activeDragSource = extension;
  1744. }
  1745. /**
  1746. * Gets the drag source of an active HTML5 drag event.
  1747. *
  1748. * @return Extension of the drag source component if the drag event is
  1749. * active and originated from this UI, {@literal null} otherwise.
  1750. * @see DragSourceExtension
  1751. * @since 8.1
  1752. */
  1753. public DragSourceExtension<? extends AbstractComponent> getActiveDragSource() {
  1754. return activeDragSource;
  1755. }
  1756. /**
  1757. * Returns whether HTML5 DnD extensions {@link DragSourceExtension} and
  1758. * {@link DropTargetExtension} and alike should be enabled for mobile
  1759. * devices.
  1760. * <p>
  1761. * By default, it is disabled.
  1762. *
  1763. * @return {@code true} if enabled, {@code false} if not
  1764. * @since 8.1
  1765. * @see #setMobileHtml5DndEnabled(boolean)
  1766. */
  1767. public boolean isMobileHtml5DndEnabled() {
  1768. return getState(false).enableMobileHTML5DnD;
  1769. }
  1770. /**
  1771. * Enable or disable HTML5 DnD for mobile devices.
  1772. * <p>
  1773. * Usually you should enable the support in the {@link #init(VaadinRequest)}
  1774. * method. By default, it is disabled. This operation is NOOP when the user
  1775. * is not on a mobile device.
  1776. * <p>
  1777. * Changing this will effect all {@link DragSourceExtension} and
  1778. * {@link DropTargetExtension} (and subclasses) that have not yet been
  1779. * attached to the UI on the client side.
  1780. * <p>
  1781. * <em>NOTE: When disabling this after it has been enabled, it will not
  1782. * affect {@link DragSourceExtension} and {@link DropTargetExtension} (and
  1783. * subclasses) that have been previously added. Those extensions should be
  1784. * explicitly removed to make sure user cannot perform DnD operations
  1785. * anymore.</em>
  1786. *
  1787. * @param enabled
  1788. * {@code true} if enabled, {@code false} if not
  1789. * @since 8.1
  1790. */
  1791. public void setMobileHtml5DndEnabled(boolean enabled) {
  1792. if (getState(false).enableMobileHTML5DnD != enabled) {
  1793. getState().enableMobileHTML5DnD = enabled;
  1794. if (isMobileHtml5DndEnabled()) {
  1795. loadMobileHtml5DndPolyfill();
  1796. }
  1797. }
  1798. }
  1799. /**
  1800. * Load and initialize the mobile drag-drop-polyfill if needed and not yet
  1801. * done so.
  1802. */
  1803. private void loadMobileHtml5DndPolyfill() {
  1804. if (mobileHtml5DndPolyfillLoaded) {
  1805. return;
  1806. }
  1807. if (!getPage().getWebBrowser().isTouchDevice()) {
  1808. return;
  1809. }
  1810. mobileHtml5DndPolyfillLoaded = true;
  1811. String vaadinLocation = getSession().getService().getStaticFileLocation(
  1812. VaadinService.getCurrentRequest()) + "/VAADIN/";
  1813. getPage().addDependency(new Dependency(Type.JAVASCRIPT,
  1814. vaadinLocation + ApplicationConstants.MOBILE_DND_POLYFILL_JS));
  1815. getRpcProxy(PageClientRpc.class).initializeMobileHtml5DndPolyfill();
  1816. }
  1817. /**
  1818. * Event which is fired when the ordering of the windows is updated.
  1819. * <p>
  1820. * The other way to listen window position for specific window is
  1821. * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)}
  1822. *
  1823. * @see Window.WindowOrderChangeEvent
  1824. *
  1825. * @author Vaadin Ltd
  1826. * @since 8.0
  1827. *
  1828. */
  1829. public static class WindowOrderUpdateEvent extends Component.Event {
  1830. private final Collection<Window> windows;
  1831. public WindowOrderUpdateEvent(Component source,
  1832. Collection<Window> windows) {
  1833. super(source);
  1834. this.windows = windows;
  1835. }
  1836. /**
  1837. * Gets the windows in the order they appear in the UI: top most window
  1838. * is first, bottom one last.
  1839. *
  1840. * @return the windows collection
  1841. */
  1842. public Collection<Window> getWindows() {
  1843. return windows;
  1844. }
  1845. }
  1846. /**
  1847. * An interface used for listening to Windows order update events.
  1848. *
  1849. * @since 8.0
  1850. *
  1851. * @see Window.WindowOrderChangeEvent
  1852. */
  1853. @FunctionalInterface
  1854. public interface WindowOrderUpdateListener extends ConnectorEventListener {
  1855. public static final Method windowOrderUpdateMethod = ReflectTools
  1856. .findMethod(WindowOrderUpdateListener.class,
  1857. "windowOrderUpdated", WindowOrderUpdateEvent.class);
  1858. /**
  1859. * Called when the windows order positions are changed. Use
  1860. * {@link WindowOrderUpdateEvent#getWindows()} to get a reference to the
  1861. * {@link Window}s whose order positions are updated. Use
  1862. * {@link Window#getOrderPosition()} to get window position for specific
  1863. * window.
  1864. *
  1865. * @param event
  1866. */
  1867. public void windowOrderUpdated(WindowOrderUpdateEvent event);
  1868. }
  1869. }