You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UI.java 70KB

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. }