Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

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