Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075
  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, "access() task ignored "
  1435. + "because UI got detached after the task was "
  1436. + "enqueued. To suppress this message, change "
  1437. + "the task to implement {0} and make it handle "
  1438. + "{1}. Affected task: {2}",
  1439. new Object[] {
  1440. ErrorHandlingRunnable.class.getName(),
  1441. UIDetachedException.class.getName(),
  1442. runnable });
  1443. } else if (exception != null) {
  1444. /*
  1445. * If no ErrorHandlingRunnable, or if it threw an
  1446. * exception of its own.
  1447. */
  1448. ConnectorErrorEvent errorEvent = new ConnectorErrorEvent(
  1449. UI.this, exception);
  1450. ErrorHandler errorHandler = com.vaadin.server.ErrorEvent
  1451. .findErrorHandler(UI.this);
  1452. if (errorHandler == null && getSession() == null) {
  1453. /*
  1454. * Special case where findErrorHandler(UI) cannot
  1455. * find the session handler because the UI has
  1456. * recently been detached.
  1457. */
  1458. errorHandler = com.vaadin.server.ErrorEvent
  1459. .findErrorHandler(session);
  1460. }
  1461. if (errorHandler == null) {
  1462. errorHandler = new DefaultErrorHandler();
  1463. }
  1464. errorHandler.error(errorEvent);
  1465. }
  1466. } catch (Exception e) {
  1467. getLogger().log(Level.SEVERE, e.getMessage(), e);
  1468. }
  1469. }
  1470. });
  1471. }
  1472. /**
  1473. * Retrieves the object used for configuring tooltips.
  1474. *
  1475. * @return The instance used for tooltip configuration
  1476. */
  1477. public TooltipConfiguration getTooltipConfiguration() {
  1478. return tooltipConfiguration;
  1479. }
  1480. /**
  1481. * Retrieves the object used for configuring notifications.
  1482. *
  1483. * @return The instance used for notification configuration
  1484. */
  1485. public NotificationConfiguration getNotificationConfiguration() {
  1486. return notificationConfiguration;
  1487. }
  1488. /**
  1489. * Retrieves the object used for configuring the loading indicator.
  1490. *
  1491. * @return The instance used for configuring the loading indicator
  1492. */
  1493. public LoadingIndicatorConfiguration getLoadingIndicatorConfiguration() {
  1494. return loadingIndicatorConfiguration;
  1495. }
  1496. /**
  1497. * Pushes the pending changes and client RPC invocations of this UI to the
  1498. * client-side.
  1499. * <p>
  1500. * If push is enabled, but the push connection is not currently open, the
  1501. * push will be done when the connection is established.
  1502. * <p>
  1503. * As with all UI methods, the session must be locked when calling this
  1504. * method. It is also recommended that {@link UI#getCurrent()} is set up to
  1505. * return this UI since writing the response may invoke logic in any
  1506. * attached component or extension. The recommended way of fulfilling these
  1507. * conditions is to use {@link #access(Runnable)}.
  1508. *
  1509. * @throws IllegalStateException
  1510. * if push is disabled.
  1511. * @throws UIDetachedException
  1512. * if this UI is not attached to a session.
  1513. *
  1514. * @see #getPushConfiguration()
  1515. *
  1516. * @since 7.1
  1517. */
  1518. public void push() {
  1519. VaadinSession session = getSession();
  1520. if (session == null) {
  1521. throw new UIDetachedException("Cannot push a detached UI");
  1522. }
  1523. assert session.hasLock();
  1524. if (!getPushConfiguration().getPushMode().isEnabled()) {
  1525. throw new IllegalStateException("Push not enabled");
  1526. }
  1527. assert pushConnection != null;
  1528. /*
  1529. * Purge the pending access queue as it might mark a connector as dirty
  1530. * when the push would otherwise be ignored because there are no changes
  1531. * to push.
  1532. */
  1533. session.getService().runPendingAccessTasks(session);
  1534. if (!getConnectorTracker().hasDirtyConnectors()) {
  1535. // Do not push if there is nothing to push
  1536. return;
  1537. }
  1538. pushConnection.push();
  1539. }
  1540. /**
  1541. * Returns the internal push connection object used by this UI. This method
  1542. * should only be called by the framework.
  1543. * <p>
  1544. * This method is not intended to be overridden. If it is overridden, care
  1545. * should be taken since this method might be called in situations where
  1546. * {@link UI#getCurrent()} does not return this UI.
  1547. *
  1548. * @return the push connection used by this UI, or {@code null} if push is
  1549. * not available.
  1550. */
  1551. public PushConnection getPushConnection() {
  1552. assert !(getPushConfiguration().getPushMode().isEnabled()
  1553. && pushConnection == null);
  1554. return pushConnection;
  1555. }
  1556. /**
  1557. * Sets the internal push connection object used by this UI. This method
  1558. * should only be called by the framework.
  1559. * <p>
  1560. * The {@code pushConnection} argument must be non-null if and only if
  1561. * {@code getPushConfiguration().getPushMode().isEnabled()}.
  1562. *
  1563. * @param pushConnection
  1564. * the push connection to use for this UI
  1565. */
  1566. public void setPushConnection(PushConnection pushConnection) {
  1567. // If pushMode is disabled then there should never be a pushConnection;
  1568. // if enabled there should always be
  1569. assert (pushConnection == null)
  1570. ^ getPushConfiguration().getPushMode().isEnabled();
  1571. if (pushConnection == this.pushConnection) {
  1572. return;
  1573. }
  1574. if (this.pushConnection != null && this.pushConnection.isConnected()) {
  1575. this.pushConnection.disconnect();
  1576. }
  1577. this.pushConnection = pushConnection;
  1578. }
  1579. /**
  1580. * Sets the interval with which the UI should poll the server to see if
  1581. * there are any changes. Polling is disabled by default.
  1582. * <p>
  1583. * Note that it is possible to enable push and polling at the same time but
  1584. * it should not be done to avoid excessive server traffic.
  1585. * </p>
  1586. * <p>
  1587. * Add-on developers should note that this method is only meant for the
  1588. * application developer. An add-on should not set the poll interval
  1589. * directly, rather instruct the user to set it.
  1590. * </p>
  1591. *
  1592. * @param intervalInMillis
  1593. * The interval (in ms) with which the UI should poll the server
  1594. * or -1 to disable polling
  1595. */
  1596. public void setPollInterval(int intervalInMillis) {
  1597. getState().pollInterval = intervalInMillis;
  1598. }
  1599. /**
  1600. * Returns the interval with which the UI polls the server.
  1601. *
  1602. * @return The interval (in ms) with which the UI polls the server or -1 if
  1603. * polling is disabled
  1604. */
  1605. public int getPollInterval() {
  1606. return getState(false).pollInterval;
  1607. }
  1608. @Override
  1609. public Registration addPollListener(PollListener listener) {
  1610. return addListener(EventId.POLL, PollEvent.class, listener,
  1611. PollListener.POLL_METHOD);
  1612. }
  1613. @Override
  1614. @Deprecated
  1615. public void removePollListener(PollListener listener) {
  1616. removeListener(EventId.POLL, PollEvent.class, listener);
  1617. }
  1618. /**
  1619. * Retrieves the object used for configuring the push channel.
  1620. *
  1621. * @since 7.1
  1622. * @return The instance used for push configuration
  1623. */
  1624. public PushConfiguration getPushConfiguration() {
  1625. return pushConfiguration;
  1626. }
  1627. /**
  1628. * Retrieves the object used for configuring the reconnect dialog.
  1629. *
  1630. * @since 7.6
  1631. * @return The instance used for reconnect dialog configuration
  1632. */
  1633. public ReconnectDialogConfiguration getReconnectDialogConfiguration() {
  1634. return reconnectDialogConfiguration;
  1635. }
  1636. /**
  1637. * Get the label that is added to the container element, where tooltip,
  1638. * notification and dialogs are added to.
  1639. *
  1640. * @return the label of the container
  1641. */
  1642. public String getOverlayContainerLabel() {
  1643. return getState(false).overlayContainerLabel;
  1644. }
  1645. /**
  1646. * Sets the label that is added to the container element, where tooltip,
  1647. * notifications and dialogs are added to.
  1648. * <p>
  1649. * This is helpful for users of assistive devices, as this element is
  1650. * reachable for them.
  1651. * </p>
  1652. *
  1653. * @param overlayContainerLabel
  1654. * label to use for the container
  1655. */
  1656. public void setOverlayContainerLabel(String overlayContainerLabel) {
  1657. getState().overlayContainerLabel = overlayContainerLabel;
  1658. }
  1659. /**
  1660. * Returns the locale service which handles transmission of Locale data to
  1661. * the client.
  1662. *
  1663. * @since 7.1
  1664. * @return The LocaleService for this UI
  1665. */
  1666. public LocaleService getLocaleService() {
  1667. return localeService;
  1668. }
  1669. private static Logger getLogger() {
  1670. return Logger.getLogger(UI.class.getName());
  1671. }
  1672. /**
  1673. * Gets a string the uniquely distinguishes this UI instance based on where
  1674. * it is embedded. The embed identifier is based on the
  1675. * <code>window.name</code> DOM attribute of the browser window where the UI
  1676. * is displayed and the id of the div element where the UI is embedded.
  1677. *
  1678. * @since 7.2
  1679. * @return the embed id for this UI, or <code>null</code> if no id known
  1680. */
  1681. public String getEmbedId() {
  1682. return embedId;
  1683. }
  1684. /**
  1685. * Gets the last processed server message id.
  1686. *
  1687. * Used internally for communication tracking.
  1688. *
  1689. * @return lastProcessedServerMessageId the id of the last processed server
  1690. * message
  1691. * @since 7.6
  1692. */
  1693. public int getLastProcessedClientToServerId() {
  1694. return lastProcessedClientToServerId;
  1695. }
  1696. /**
  1697. * Sets the last processed server message id.
  1698. *
  1699. * Used internally for communication tracking.
  1700. *
  1701. * @param lastProcessedClientToServerId
  1702. * the id of the last processed server message
  1703. * @since 7.6
  1704. */
  1705. public void setLastProcessedClientToServerId(
  1706. int lastProcessedClientToServerId) {
  1707. this.lastProcessedClientToServerId = lastProcessedClientToServerId;
  1708. }
  1709. /**
  1710. * Adds a WindowOrderUpdateListener to the UI.
  1711. * <p>
  1712. * The WindowOrderUpdateEvent is fired when the order positions of windows
  1713. * are updated. It can happen when some window (this or other) is brought to
  1714. * front or detached.
  1715. * <p>
  1716. * The other way to listen window position for specific window is
  1717. * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)}
  1718. *
  1719. * @see Window#addWindowOrderChangeListener(WindowOrderChangeListener)
  1720. *
  1721. * @param listener
  1722. * the WindowModeChangeListener to add.
  1723. * @since 8.0
  1724. *
  1725. * @return a registration object for removing the listener
  1726. */
  1727. public Registration addWindowOrderUpdateListener(
  1728. WindowOrderUpdateListener listener) {
  1729. addListener(EventId.WINDOW_ORDER, WindowOrderUpdateEvent.class,
  1730. listener, WindowOrderUpdateListener.windowOrderUpdateMethod);
  1731. return () -> removeListener(EventId.WINDOW_ORDER,
  1732. WindowOrderUpdateEvent.class, listener);
  1733. }
  1734. /**
  1735. * Sets the drag source of an active HTML5 drag event.
  1736. *
  1737. * @param extension
  1738. * Extension of the drag source component.
  1739. * @see DragSourceExtension
  1740. * @since 8.1
  1741. */
  1742. public void setActiveDragSource(
  1743. DragSourceExtension<? extends AbstractComponent> extension) {
  1744. activeDragSource = extension;
  1745. }
  1746. /**
  1747. * Gets the drag source of an active HTML5 drag event.
  1748. *
  1749. * @return Extension of the drag source component if the drag event is
  1750. * active and originated from this UI, {@literal null} otherwise.
  1751. * @see DragSourceExtension
  1752. * @since 8.1
  1753. */
  1754. public DragSourceExtension<? extends AbstractComponent> getActiveDragSource() {
  1755. return activeDragSource;
  1756. }
  1757. /**
  1758. * Returns whether HTML5 DnD extensions {@link DragSourceExtension} and
  1759. * {@link DropTargetExtension} and alike should be enabled for mobile
  1760. * devices.
  1761. * <p>
  1762. * By default, it is disabled.
  1763. *
  1764. * @return {@code true} if enabled, {@code false} if not
  1765. * @since 8.1
  1766. * @see #setMobileHtml5DndEnabled(boolean)
  1767. */
  1768. public boolean isMobileHtml5DndEnabled() {
  1769. return getState(false).enableMobileHTML5DnD;
  1770. }
  1771. /**
  1772. * Enable or disable HTML5 DnD for mobile devices.
  1773. * <p>
  1774. * Usually you should enable the support in the {@link #init(VaadinRequest)}
  1775. * method. By default, it is disabled. This operation is NOOP when the user
  1776. * is not on a mobile device.
  1777. * <p>
  1778. * Changing this will effect all {@link DragSourceExtension} and
  1779. * {@link DropTargetExtension} (and subclasses) that have not yet been
  1780. * attached to the UI on the client side.
  1781. * <p>
  1782. * <em>NOTE: When disabling this after it has been enabled, it will not
  1783. * affect {@link DragSourceExtension} and {@link DropTargetExtension} (and
  1784. * subclasses) that have been previously added. Those extensions should be
  1785. * explicitly removed to make sure user cannot perform DnD operations
  1786. * anymore.</em>
  1787. *
  1788. * @param enabled
  1789. * {@code true} if enabled, {@code false} if not
  1790. * @since 8.1
  1791. */
  1792. public void setMobileHtml5DndEnabled(boolean enabled) {
  1793. if (getState(false).enableMobileHTML5DnD != enabled) {
  1794. getState().enableMobileHTML5DnD = enabled;
  1795. if (isMobileHtml5DndEnabled()) {
  1796. loadMobileHtml5DndPolyfill();
  1797. }
  1798. }
  1799. }
  1800. /**
  1801. * Load and initialize the mobile drag-drop-polyfill if needed and not yet
  1802. * done so.
  1803. */
  1804. private void loadMobileHtml5DndPolyfill() {
  1805. if (mobileHtml5DndPolyfillLoaded) {
  1806. return;
  1807. }
  1808. if (!getPage().getWebBrowser().isTouchDevice()) {
  1809. return;
  1810. }
  1811. mobileHtml5DndPolyfillLoaded = true;
  1812. String vaadinLocation = getSession().getService().getStaticFileLocation(
  1813. VaadinService.getCurrentRequest()) + "/VAADIN/";
  1814. getPage().addDependency(new Dependency(Type.JAVASCRIPT,
  1815. vaadinLocation + ApplicationConstants.MOBILE_DND_POLYFILL_JS));
  1816. getRpcProxy(PageClientRpc.class).initializeMobileHtml5DndPolyfill();
  1817. }
  1818. /**
  1819. * Event which is fired when the ordering of the windows is updated.
  1820. * <p>
  1821. * The other way to listen window position for specific window is
  1822. * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)}
  1823. *
  1824. * @see Window.WindowOrderChangeEvent
  1825. *
  1826. * @author Vaadin Ltd
  1827. * @since 8.0
  1828. *
  1829. */
  1830. public static class WindowOrderUpdateEvent extends Component.Event {
  1831. private final Collection<Window> windows;
  1832. public WindowOrderUpdateEvent(Component source,
  1833. Collection<Window> windows) {
  1834. super(source);
  1835. this.windows = windows;
  1836. }
  1837. /**
  1838. * Gets the windows in the order they appear in the UI: top most window
  1839. * is first, bottom one last.
  1840. *
  1841. * @return the windows collection
  1842. */
  1843. public Collection<Window> getWindows() {
  1844. return windows;
  1845. }
  1846. }
  1847. /**
  1848. * An interface used for listening to Windows order update events.
  1849. *
  1850. * @since 8.0
  1851. *
  1852. * @see Window.WindowOrderChangeEvent
  1853. */
  1854. @FunctionalInterface
  1855. public interface WindowOrderUpdateListener extends ConnectorEventListener {
  1856. public static final Method windowOrderUpdateMethod = ReflectTools
  1857. .findMethod(WindowOrderUpdateListener.class,
  1858. "windowOrderUpdated", WindowOrderUpdateEvent.class);
  1859. /**
  1860. * Called when the windows order positions are changed. Use
  1861. * {@link WindowOrderUpdateEvent#getWindows()} to get a reference to the
  1862. * {@link Window}s whose order positions are updated. Use
  1863. * {@link Window#getOrderPosition()} to get window position for specific
  1864. * window.
  1865. *
  1866. * @param event
  1867. */
  1868. public void windowOrderUpdated(WindowOrderUpdateEvent event);
  1869. }
  1870. }