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.

Root.java 50KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.io.Serializable;
  6. import java.lang.reflect.Method;
  7. import java.net.MalformedURLException;
  8. import java.net.URL;
  9. import java.util.ArrayList;
  10. import java.util.Collection;
  11. import java.util.Collections;
  12. import java.util.Iterator;
  13. import java.util.LinkedHashSet;
  14. import java.util.LinkedList;
  15. import java.util.List;
  16. import java.util.Map;
  17. import com.vaadin.Application;
  18. import com.vaadin.annotations.EagerInit;
  19. import com.vaadin.event.Action;
  20. import com.vaadin.event.Action.Handler;
  21. import com.vaadin.event.ActionManager;
  22. import com.vaadin.event.MouseEvents.ClickEvent;
  23. import com.vaadin.event.MouseEvents.ClickListener;
  24. import com.vaadin.terminal.PaintException;
  25. import com.vaadin.terminal.PaintTarget;
  26. import com.vaadin.terminal.Resource;
  27. import com.vaadin.terminal.WrappedRequest;
  28. import com.vaadin.terminal.WrappedRequest.BrowserDetails;
  29. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  30. import com.vaadin.terminal.gwt.client.ui.VNotification;
  31. import com.vaadin.terminal.gwt.client.ui.VView;
  32. import com.vaadin.tools.ReflectTools;
  33. import com.vaadin.ui.Window.CloseListener;
  34. /**
  35. * The topmost component in any component hierarchy. There is one root for every
  36. * Vaadin instance in a browser window. A root may either represent an entire
  37. * browser window (or tab) or some part of a html page where a Vaadin
  38. * application is embedded.
  39. * <p>
  40. * The root is the server side entry point for various client side features that
  41. * are not represented as components added to a layout, e.g notifications, sub
  42. * windows, and executing javascript in the browser.
  43. * </p>
  44. * <p>
  45. * When a new application instance is needed, typically because the user opens
  46. * the application in a browser window,
  47. * {@link Application#gerRoot(WrappedRequest)} is invoked to get a root. That
  48. * method does by default create a root according to the
  49. * {@value Application#ROOT_PARAMETER} parameter from web.xml.
  50. * </p>
  51. * <p>
  52. * After a root has been created by the application, it is initialized using
  53. * {@link #init(WrappedRequest)}. This method is intended to be overridden by
  54. * the developer to add components to the user interface and initialize
  55. * non-component functionality. The component hierarchy is initialized by
  56. * passing a {@link ComponentContainer} with the main layout of the view to
  57. * {@link #setContent(ComponentContainer)}.
  58. * </p>
  59. * <p>
  60. * If a {@link EagerInit} annotation is present on a class extending
  61. * <code>Root</code>, the framework will use a faster initialization method
  62. * which will not ensure that {@link BrowserDetails} are present in the
  63. * {@link WrappedRequest} passed to the init method.
  64. * </p>
  65. *
  66. * @see #init(WrappedRequest)
  67. * @see Application#getRoot(WrappedRequest)
  68. *
  69. * @since 7.0
  70. */
  71. // @ClientWidget(View.class) - Can't have annotation because of eager
  72. // classloaders in application servers and hard coded logic in client side code
  73. public abstract class Root extends AbstractComponentContainer implements
  74. Action.Container, Action.Notifier {
  75. /**
  76. * Listener that gets notified when the size of the browser window
  77. * containing the root has changed.
  78. *
  79. * @see Root#addListener(BrowserWindowResizeListener)
  80. */
  81. public interface BrowserWindowResizeListener extends Serializable {
  82. /**
  83. * Invoked when the browser window containing a Root has been resized.
  84. *
  85. * @param event
  86. * a browser window resize event
  87. */
  88. public void browserWindowResized(BrowserWindowResizeEvent event);
  89. }
  90. /**
  91. * Event that is fired when a browser window containing a root is resized.
  92. */
  93. public class BrowserWindowResizeEvent extends Component.Event {
  94. private final int width;
  95. private final int height;
  96. /**
  97. * Creates a new event
  98. *
  99. * @param source
  100. * the root for which the browser window has been resized
  101. * @param width
  102. * the new width of the browser window
  103. * @param height
  104. * the new height of the browser window
  105. */
  106. public BrowserWindowResizeEvent(Root source, int width, int height) {
  107. super(source);
  108. this.width = width;
  109. this.height = height;
  110. }
  111. @Override
  112. public Root getSource() {
  113. return (Root) super.getSource();
  114. }
  115. /**
  116. * Gets the new browser window height
  117. *
  118. * @return an integer with the new pixel height of the browser window
  119. */
  120. public int getHeight() {
  121. return height;
  122. }
  123. /**
  124. * Gets the new browser window width
  125. *
  126. * @return an integer with the new pixel width of the browser window
  127. */
  128. public int getWidth() {
  129. return width;
  130. }
  131. }
  132. private static final Method BROWSWER_RESIZE_METHOD = ReflectTools
  133. .findMethod(BrowserWindowResizeListener.class,
  134. "browserWindowResized", BrowserWindowResizeEvent.class);
  135. /**
  136. * Listener that listens changes in URI fragment.
  137. */
  138. public interface FragmentChangedListener extends Serializable {
  139. public void fragmentChanged(FragmentChangedEvent event);
  140. }
  141. /**
  142. * Event fired when uri fragment changes.
  143. */
  144. public class FragmentChangedEvent extends Component.Event {
  145. /**
  146. * The new uri fragment
  147. */
  148. private final String fragment;
  149. /**
  150. * Creates a new instance of UriFragmentReader change event.
  151. *
  152. * @param source
  153. * the Source of the event.
  154. */
  155. public FragmentChangedEvent(Root source, String fragment) {
  156. super(source);
  157. this.fragment = fragment;
  158. }
  159. /**
  160. * Gets the root in which the fragment has changed.
  161. *
  162. * @return the root in which the fragment has changed
  163. */
  164. public Root getRoot() {
  165. return (Root) getComponent();
  166. }
  167. /**
  168. * Get the new fragment
  169. *
  170. * @return the new fragment
  171. */
  172. public String getFragment() {
  173. return fragment;
  174. }
  175. }
  176. /**
  177. * Helper class to emulate the main window from Vaadin 6 using roots. This
  178. * class should be used in the same way as Window used as a browser level
  179. * window in Vaadin 6 with {@link com.vaadin.Application.LegacyApplication}
  180. */
  181. @Deprecated
  182. @EagerInit
  183. public static class LegacyWindow extends Root {
  184. private String name;
  185. /**
  186. * Create a new legacy window
  187. */
  188. public LegacyWindow() {
  189. super();
  190. }
  191. /**
  192. * Creates a new legacy window with the given caption
  193. *
  194. * @param caption
  195. * the caption of the window
  196. */
  197. public LegacyWindow(String caption) {
  198. super(caption);
  199. }
  200. /**
  201. * Creates a legacy window with the given caption and content layout
  202. *
  203. * @param caption
  204. * @param content
  205. */
  206. public LegacyWindow(String caption, ComponentContainer content) {
  207. super(caption, content);
  208. }
  209. @Override
  210. protected void init(WrappedRequest request) {
  211. // Just empty
  212. }
  213. /**
  214. * Gets the unique name of the window. The name of the window is used to
  215. * uniquely identify it.
  216. * <p>
  217. * The name also determines the URL that can be used for direct access
  218. * to a window. All windows can be accessed through
  219. * {@code http://host:port/app/win} where {@code http://host:port/app}
  220. * is the application URL (as returned by {@link Application#getURL()}
  221. * and {@code win} is the window name.
  222. * </p>
  223. * <p>
  224. * Note! Portlets do not support direct window access through URLs.
  225. * </p>
  226. *
  227. * @return the Name of the Window.
  228. */
  229. public String getName() {
  230. return name;
  231. }
  232. /**
  233. * Sets the unique name of the window. The name of the window is used to
  234. * uniquely identify it inside the application.
  235. * <p>
  236. * The name also determines the URL that can be used for direct access
  237. * to a window. All windows can be accessed through
  238. * {@code http://host:port/app/win} where {@code http://host:port/app}
  239. * is the application URL (as returned by {@link Application#getURL()}
  240. * and {@code win} is the window name.
  241. * </p>
  242. * <p>
  243. * This method can only be called before the window is added to an
  244. * application.
  245. * <p>
  246. * Note! Portlets do not support direct window access through URLs.
  247. * </p>
  248. *
  249. * @param name
  250. * the new name for the window or null if the application
  251. * should automatically assign a name to it
  252. * @throws IllegalStateException
  253. * if the window is attached to an application
  254. */
  255. public void setName(String name) {
  256. this.name = name;
  257. // The name can not be changed in application
  258. if (getApplication() != null) {
  259. throw new IllegalStateException(
  260. "Window name can not be changed while "
  261. + "the window is in application");
  262. }
  263. }
  264. /**
  265. * Gets the full URL of the window. The returned URL is window specific
  266. * and can be used to directly refer to the window.
  267. * <p>
  268. * Note! This method can not be used for portlets.
  269. * </p>
  270. *
  271. * @return the URL of the window or null if the window is not attached
  272. * to an application
  273. */
  274. public URL getURL() {
  275. Application application = getApplication();
  276. if (application == null) {
  277. return null;
  278. }
  279. try {
  280. return new URL(application.getURL(), getName() + "/");
  281. } catch (MalformedURLException e) {
  282. throw new RuntimeException(
  283. "Internal problem getting window URL, please report");
  284. }
  285. }
  286. }
  287. private static final Method FRAGMENT_CHANGED_METHOD;
  288. static {
  289. try {
  290. FRAGMENT_CHANGED_METHOD = FragmentChangedListener.class
  291. .getDeclaredMethod("fragmentChanged",
  292. new Class[] { FragmentChangedEvent.class });
  293. } catch (final java.lang.NoSuchMethodException e) {
  294. // This should never happen
  295. throw new java.lang.RuntimeException(
  296. "Internal error finding methods in FragmentChangedListener");
  297. }
  298. }
  299. /**
  300. * A border style used for opening resources in a window without a border.
  301. */
  302. public static final int BORDER_NONE = 0;
  303. /**
  304. * A border style used for opening resources in a window with a minimal
  305. * border.
  306. */
  307. public static final int BORDER_MINIMAL = 1;
  308. /**
  309. * A border style that indicates that the default border style should be
  310. * used when opening resources.
  311. */
  312. public static final int BORDER_DEFAULT = 2;
  313. /**
  314. * The container in which the component hierarchy of the root starts.
  315. */
  316. private ComponentContainer content;
  317. /**
  318. * The application to which this root belongs
  319. */
  320. private Application application;
  321. /**
  322. * A list of notifications that are waiting to be sent to the client.
  323. * Cleared (set to null) when the notifications have been sent.
  324. */
  325. private List<Notification> notifications;
  326. /**
  327. * A list of javascript commands that are waiting to be sent to the client.
  328. * Cleared (set to null) when the commands have been sent.
  329. */
  330. private List<String> jsExecQueue = null;
  331. /**
  332. * List of windows in this root.
  333. */
  334. private final LinkedHashSet<Window> windows = new LinkedHashSet<Window>();
  335. /**
  336. * Resources to be opened automatically on next repaint. The list is
  337. * automatically cleared when it has been sent to the client.
  338. */
  339. private final LinkedList<OpenResource> openList = new LinkedList<OpenResource>();
  340. /**
  341. * The component that should be scrolled into view after the next repaint.
  342. * Null if nothing should be scrolled into view.
  343. */
  344. private Component scrollIntoView;
  345. /**
  346. * The id of this root, used to find the server side instance of the root
  347. * form which a request originates. A negative value indicates that the root
  348. * id has not yet been assigned by the Application.
  349. *
  350. * @see Application#nextRootId
  351. */
  352. private int rootId = -1;
  353. /**
  354. * Keeps track of the Actions added to this component, and manages the
  355. * painting and handling as well.
  356. */
  357. protected ActionManager actionManager;
  358. /**
  359. * Thread local for keeping track of the current root.
  360. */
  361. private static final ThreadLocal<Root> currentRoot = new ThreadLocal<Root>();
  362. private int browserWindowWidth = -1;
  363. private int browserWindowHeight = -1;
  364. /** Identifies the click event */
  365. private static final String CLICK_EVENT_ID = VView.CLICK_EVENT_ID;
  366. private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker(
  367. this);
  368. /**
  369. * Creates a new empty root without a caption. This root will have a
  370. * {@link VerticalLayout} with margins enabled as its content.
  371. */
  372. public Root() {
  373. // Nothing to do here?
  374. }
  375. /**
  376. * Creates a new root with the given component container as its content.
  377. *
  378. * @param content
  379. * the content container to use as this roots content.
  380. *
  381. * @see #setContent(ComponentContainer)
  382. */
  383. public Root(ComponentContainer content) {
  384. setContent(content);
  385. }
  386. /**
  387. * Creates a new empty root with the given caption. This root will have a
  388. * {@link VerticalLayout} with margins enabled as its content.
  389. *
  390. * @param caption
  391. * the caption of the root, used as the page title if there's
  392. * nothing but the application on the web page
  393. *
  394. * @see #setCaption(String)
  395. */
  396. public Root(String caption) {
  397. setCaption(caption);
  398. }
  399. /**
  400. * Creates a new root with the given caption and content.
  401. *
  402. * @param caption
  403. * the caption of the root, used as the page title if there's
  404. * nothing but the application on the web page
  405. * @param content
  406. * the content container to use as this roots content.
  407. *
  408. * @see #setContent(ComponentContainer)
  409. * @see #setCaption(String)
  410. */
  411. public Root(String caption, ComponentContainer content) {
  412. this(content);
  413. setCaption(caption);
  414. }
  415. /**
  416. * Overridden to return a value instead of referring to the parent.
  417. *
  418. * @return this root
  419. *
  420. * @see com.vaadin.ui.AbstractComponent#getRoot()
  421. */
  422. @Override
  423. public Root getRoot() {
  424. return this;
  425. }
  426. public void replaceComponent(Component oldComponent, Component newComponent) {
  427. throw new UnsupportedOperationException();
  428. }
  429. @Override
  430. public Application getApplication() {
  431. return application;
  432. }
  433. @Override
  434. public void paintContent(PaintTarget target) throws PaintException {
  435. // Open requested resource
  436. synchronized (openList) {
  437. if (!openList.isEmpty()) {
  438. for (final Iterator<OpenResource> i = openList.iterator(); i
  439. .hasNext();) {
  440. (i.next()).paintContent(target);
  441. }
  442. openList.clear();
  443. }
  444. }
  445. ComponentContainer content = getContent();
  446. if (content != null) {
  447. content.paint(target);
  448. }
  449. // Paint subwindows
  450. for (final Iterator<Window> i = windows.iterator(); i.hasNext();) {
  451. final Window w = i.next();
  452. w.paint(target);
  453. }
  454. // Paint notifications
  455. if (notifications != null) {
  456. target.startTag("notifications");
  457. for (final Iterator<Notification> it = notifications.iterator(); it
  458. .hasNext();) {
  459. final Notification n = it.next();
  460. target.startTag("notification");
  461. if (n.getCaption() != null) {
  462. target.addAttribute(
  463. VNotification.ATTRIBUTE_NOTIFICATION_CAPTION,
  464. n.getCaption());
  465. }
  466. if (n.getDescription() != null) {
  467. target.addAttribute(
  468. VNotification.ATTRIBUTE_NOTIFICATION_MESSAGE,
  469. n.getDescription());
  470. }
  471. if (n.getIcon() != null) {
  472. target.addAttribute(
  473. VNotification.ATTRIBUTE_NOTIFICATION_ICON,
  474. n.getIcon());
  475. }
  476. if (!n.isHtmlContentAllowed()) {
  477. target.addAttribute(
  478. VView.NOTIFICATION_HTML_CONTENT_NOT_ALLOWED, true);
  479. }
  480. target.addAttribute(
  481. VNotification.ATTRIBUTE_NOTIFICATION_POSITION,
  482. n.getPosition());
  483. target.addAttribute(VNotification.ATTRIBUTE_NOTIFICATION_DELAY,
  484. n.getDelayMsec());
  485. if (n.getStyleName() != null) {
  486. target.addAttribute(
  487. VNotification.ATTRIBUTE_NOTIFICATION_STYLE,
  488. n.getStyleName());
  489. }
  490. target.endTag("notification");
  491. }
  492. target.endTag("notifications");
  493. notifications = null;
  494. }
  495. // Add executable javascripts if needed
  496. if (jsExecQueue != null) {
  497. for (String script : jsExecQueue) {
  498. target.startTag("execJS");
  499. target.addAttribute("script", script);
  500. target.endTag("execJS");
  501. }
  502. jsExecQueue = null;
  503. }
  504. if (scrollIntoView != null) {
  505. target.addAttribute("scrollTo", scrollIntoView);
  506. scrollIntoView = null;
  507. }
  508. if (pendingFocus != null) {
  509. // ensure focused component is still attached to this main window
  510. if (pendingFocus.getRoot() == this
  511. || (pendingFocus.getRoot() != null && pendingFocus
  512. .getRoot().getParent() == this)) {
  513. target.addAttribute("focused", pendingFocus);
  514. }
  515. pendingFocus = null;
  516. }
  517. if (actionManager != null) {
  518. actionManager.paintActions(null, target);
  519. }
  520. if (fragment != null) {
  521. target.addAttribute(VView.FRAGMENT_VARIABLE, fragment);
  522. }
  523. if (isResizeLazy()) {
  524. target.addAttribute(VView.RESIZE_LAZY, true);
  525. }
  526. }
  527. /**
  528. * Fire a click event to all click listeners.
  529. *
  530. * @param object
  531. * The raw "value" of the variable change from the client side.
  532. */
  533. private void fireClick(Map<String, Object> parameters) {
  534. MouseEventDetails mouseDetails = MouseEventDetails
  535. .deSerialize((String) parameters.get("mouseDetails"));
  536. fireEvent(new ClickEvent(this, mouseDetails));
  537. }
  538. @SuppressWarnings("unchecked")
  539. @Override
  540. public void changeVariables(Object source, Map<String, Object> variables) {
  541. super.changeVariables(source, variables);
  542. if (variables.containsKey(CLICK_EVENT_ID)) {
  543. fireClick((Map<String, Object>) variables.get(CLICK_EVENT_ID));
  544. }
  545. // Actions
  546. if (actionManager != null) {
  547. actionManager.handleActions(variables, this);
  548. }
  549. if (variables.containsKey(VView.FRAGMENT_VARIABLE)) {
  550. String fragment = (String) variables.get(VView.FRAGMENT_VARIABLE);
  551. setFragment(fragment, true);
  552. }
  553. boolean sendResizeEvent = false;
  554. if (variables.containsKey("height")) {
  555. browserWindowHeight = ((Integer) variables.get("height"))
  556. .intValue();
  557. sendResizeEvent = true;
  558. }
  559. if (variables.containsKey("width")) {
  560. browserWindowWidth = ((Integer) variables.get("width")).intValue();
  561. sendResizeEvent = true;
  562. }
  563. if (sendResizeEvent) {
  564. fireEvent(new BrowserWindowResizeEvent(this, browserWindowWidth,
  565. browserWindowHeight));
  566. }
  567. }
  568. /*
  569. * (non-Javadoc)
  570. *
  571. * @see com.vaadin.ui.ComponentContainer#getComponentIterator()
  572. */
  573. public Iterator<Component> getComponentIterator() {
  574. return Collections.singleton((Component) getContent()).iterator();
  575. }
  576. /*
  577. * (non-Javadoc)
  578. *
  579. * @see com.vaadin.ui.ComponentContainer#getComponentCount()
  580. */
  581. public int getComponentCount() {
  582. return getContent() == null ? 0 : 1;
  583. }
  584. /**
  585. * Sets the application to which this root is assigned. It is not legal to
  586. * change the application once it has been set nor to set a
  587. * <code>null</code> application.
  588. * <p>
  589. * This method is mainly intended for internal use by the framework.
  590. * </p>
  591. *
  592. * @param application
  593. * the application to set
  594. *
  595. * @throws IllegalStateException
  596. * if the application has already been set
  597. *
  598. * @see #getApplication()
  599. */
  600. public void setApplication(Application application) {
  601. if ((application == null) == (this.application == null)) {
  602. throw new IllegalStateException("Application has already been set");
  603. } else {
  604. this.application = application;
  605. }
  606. if (application != null) {
  607. attach();
  608. } else {
  609. detach();
  610. }
  611. }
  612. /**
  613. * Sets the id of this root within its application. The root id is used to
  614. * route requests to the right root.
  615. * <p>
  616. * This method is mainly intended for internal use by the framework.
  617. * </p>
  618. *
  619. * @param rootId
  620. * the id of this root
  621. *
  622. * @throws IllegalStateException
  623. * if the root id has already been set
  624. *
  625. * @see #getRootId()
  626. */
  627. public void setRootId(int rootId) {
  628. if (this.rootId != -1) {
  629. throw new IllegalStateException("Root id has already been defined");
  630. }
  631. this.rootId = rootId;
  632. }
  633. /**
  634. * Gets the id of the root, used to identify this root within its
  635. * application when processing requests. The root id should be present in
  636. * every request to the server that originates from this root.
  637. * {@link Application#getRootForRequest(WrappedRequest)} uses this id to
  638. * find the route to which the request belongs.
  639. *
  640. * @return
  641. */
  642. public int getRootId() {
  643. return rootId;
  644. }
  645. /**
  646. * Adds a window as a subwindow inside this root. To open a new browser
  647. * window or tab, you should instead use {@link open(Resource)} with an url
  648. * pointing to this application and ensure
  649. * {@link Application#getRoot(WrappedRequest)} returns an appropriate root
  650. * for the request.
  651. *
  652. * @param window
  653. * @throws IllegalArgumentException
  654. * if the window is already added to an application
  655. * @throws NullPointerException
  656. * if the given <code>Window</code> is <code>null</code>.
  657. */
  658. public void addWindow(Window window) throws IllegalArgumentException,
  659. NullPointerException {
  660. if (window == null) {
  661. throw new NullPointerException("Argument must not be null");
  662. }
  663. if (window.getApplication() != null) {
  664. throw new IllegalArgumentException(
  665. "Window is already attached to an application.");
  666. }
  667. attachWindow(window);
  668. }
  669. /**
  670. * Helper method to attach a window.
  671. *
  672. * @param w
  673. * the window to add
  674. */
  675. private void attachWindow(Window w) {
  676. windows.add(w);
  677. w.setParent(this);
  678. requestRepaint();
  679. }
  680. /**
  681. * Remove the given subwindow from this root.
  682. *
  683. * Since Vaadin 6.5, {@link CloseListener}s are called also when explicitly
  684. * removing a window by calling this method.
  685. *
  686. * Since Vaadin 6.5, returns a boolean indicating if the window was removed
  687. * or not.
  688. *
  689. * @param window
  690. * Window to be removed.
  691. * @return true if the subwindow was removed, false otherwise
  692. */
  693. public boolean removeWindow(Window window) {
  694. if (!windows.remove(window)) {
  695. // Window window is not a subwindow of this root.
  696. return false;
  697. }
  698. window.setParent(null);
  699. window.fireClose();
  700. requestRepaint();
  701. return true;
  702. }
  703. /**
  704. * Gets all the windows added to this root.
  705. *
  706. * @return an unmodifiable collection of windows
  707. */
  708. public Collection<Window> getWindows() {
  709. return Collections.unmodifiableCollection(windows);
  710. }
  711. @Override
  712. public void focus() {
  713. super.focus();
  714. }
  715. /**
  716. * Component that should be focused after the next repaint. Null if no focus
  717. * change should take place.
  718. */
  719. private Focusable pendingFocus;
  720. /**
  721. * The current URI fragment.
  722. */
  723. private String fragment;
  724. private boolean resizeLazy = false;
  725. /**
  726. * This method is used by Component.Focusable objects to request focus to
  727. * themselves. Focus renders must be handled at window level (instead of
  728. * Component.Focusable) due we want the last focused component to be focused
  729. * in client too. Not the one that is rendered last (the case we'd get if
  730. * implemented in Focusable only).
  731. *
  732. * To focus component from Vaadin application, use Focusable.focus(). See
  733. * {@link Focusable}.
  734. *
  735. * @param focusable
  736. * to be focused on next paint
  737. */
  738. public void setFocusedComponent(Focusable focusable) {
  739. pendingFocus = focusable;
  740. requestRepaint();
  741. }
  742. /**
  743. * Shows a notification message on the middle of the root. The message
  744. * automatically disappears ("humanized message").
  745. *
  746. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  747. * rendered as html.
  748. *
  749. * @see #showNotification(Notification)
  750. * @see Notification
  751. *
  752. * @param caption
  753. * The message
  754. */
  755. public void showNotification(String caption) {
  756. addNotification(new Notification(caption));
  757. }
  758. /**
  759. * Shows a notification message the root. The position and behavior of the
  760. * message depends on the type, which is one of the basic types defined in
  761. * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
  762. *
  763. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  764. * rendered as html.
  765. *
  766. * @see #showNotification(Notification)
  767. * @see Notification
  768. *
  769. * @param caption
  770. * The message
  771. * @param type
  772. * The message type
  773. */
  774. public void showNotification(String caption, int type) {
  775. addNotification(new Notification(caption, type));
  776. }
  777. /**
  778. * Shows a notification consisting of a bigger caption and a smaller
  779. * description on the middle of the root. The message automatically
  780. * disappears ("humanized message").
  781. *
  782. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  783. * description are rendered as html.
  784. *
  785. * @see #showNotification(Notification)
  786. * @see Notification
  787. *
  788. * @param caption
  789. * The caption of the message
  790. * @param description
  791. * The message description
  792. *
  793. */
  794. public void showNotification(String caption, String description) {
  795. addNotification(new Notification(caption, description));
  796. }
  797. /**
  798. * Shows a notification consisting of a bigger caption and a smaller
  799. * description. The position and behavior of the message depends on the
  800. * type, which is one of the basic types defined in {@link Notification},
  801. * for instance Notification.TYPE_WARNING_MESSAGE.
  802. *
  803. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  804. * description are rendered as html.
  805. *
  806. * @see #showNotification(Notification)
  807. * @see Notification
  808. *
  809. * @param caption
  810. * The caption of the message
  811. * @param description
  812. * The message description
  813. * @param type
  814. * The message type
  815. */
  816. public void showNotification(String caption, String description, int type) {
  817. addNotification(new Notification(caption, description, type));
  818. }
  819. /**
  820. * Shows a notification consisting of a bigger caption and a smaller
  821. * description. The position and behavior of the message depends on the
  822. * type, which is one of the basic types defined in {@link Notification},
  823. * for instance Notification.TYPE_WARNING_MESSAGE.
  824. *
  825. * Care should be taken to avoid XSS vulnerabilities if html content is
  826. * allowed.
  827. *
  828. * @see #showNotification(Notification)
  829. * @see Notification
  830. *
  831. * @param caption
  832. * The message caption
  833. * @param description
  834. * The message description
  835. * @param type
  836. * The type of message
  837. * @param htmlContentAllowed
  838. * Whether html in the caption and description should be
  839. * displayed as html or as plain text
  840. */
  841. public void showNotification(String caption, String description, int type,
  842. boolean htmlContentAllowed) {
  843. addNotification(new Notification(caption, description, type,
  844. htmlContentAllowed));
  845. }
  846. /**
  847. * Shows a notification message.
  848. *
  849. * @see Notification
  850. * @see #showNotification(String)
  851. * @see #showNotification(String, int)
  852. * @see #showNotification(String, String)
  853. * @see #showNotification(String, String, int)
  854. *
  855. * @param notification
  856. * The notification message to show
  857. */
  858. public void showNotification(Notification notification) {
  859. addNotification(notification);
  860. }
  861. /**
  862. * Internal helper method to actually add a notification.
  863. *
  864. * @param notification
  865. * the notification to add
  866. */
  867. private void addNotification(Notification notification) {
  868. if (notifications == null) {
  869. notifications = new LinkedList<Notification>();
  870. }
  871. notifications.add(notification);
  872. requestRepaint();
  873. }
  874. /**
  875. * Executes JavaScript in this root.
  876. *
  877. * <p>
  878. * This method allows one to inject javascript from the server to client. A
  879. * client implementation is not required to implement this functionality,
  880. * but currently all web-based clients do implement this.
  881. * </p>
  882. *
  883. * <p>
  884. * Executing javascript this way often leads to cross-browser compatibility
  885. * issues and regressions that are hard to resolve. Use of this method
  886. * should be avoided and instead it is recommended to create new widgets
  887. * with GWT. For more info on creating own, reusable client-side widgets in
  888. * Java, read the corresponding chapter in Book of Vaadin.
  889. * </p>
  890. *
  891. * @param script
  892. * JavaScript snippet that will be executed.
  893. */
  894. public void executeJavaScript(String script) {
  895. if (jsExecQueue == null) {
  896. jsExecQueue = new ArrayList<String>();
  897. }
  898. jsExecQueue.add(script);
  899. requestRepaint();
  900. }
  901. /**
  902. * Scrolls any component between the component and root to a suitable
  903. * position so the component is visible to the user. The given component
  904. * must belong to this root.
  905. *
  906. * @param component
  907. * the component to be scrolled into view
  908. * @throws IllegalArgumentException
  909. * if {@code component} does not belong to this root
  910. */
  911. public void scrollIntoView(Component component)
  912. throws IllegalArgumentException {
  913. if (component.getRoot() != this) {
  914. throw new IllegalArgumentException(
  915. "The component where to scroll must belong to this root.");
  916. }
  917. scrollIntoView = component;
  918. requestRepaint();
  919. }
  920. /**
  921. * Gets the content of this root. The content is a component container that
  922. * serves as the outermost item of the visual contents of this root.
  923. *
  924. * @return a component container to use as content
  925. *
  926. * @see #setContent(ComponentContainer)
  927. * @see #createDefaultLayout()
  928. */
  929. public ComponentContainer getContent() {
  930. if (content == null) {
  931. setContent(createDefaultLayout());
  932. }
  933. return content;
  934. }
  935. /**
  936. * Helper method to create the default content layout that is used if no
  937. * content has not been explicitly defined.
  938. *
  939. * @return a newly created layout
  940. */
  941. private static VerticalLayout createDefaultLayout() {
  942. VerticalLayout layout = new VerticalLayout();
  943. layout.setMargin(true);
  944. return layout;
  945. }
  946. /**
  947. * Sets the content of this root. The content is a component container that
  948. * serves as the outermost item of the visual contents of this root. If no
  949. * content has been set, a {@link VerticalLayout} with margins enabled will
  950. * be used by default - see {@link #createDefaultLayout()}. The content can
  951. * also be set in a constructor.
  952. *
  953. * @return a component container to use as content
  954. *
  955. * @see #Root(ComponentContainer)
  956. * @see #createDefaultLayout()
  957. */
  958. public void setContent(ComponentContainer content) {
  959. if (this.content != null) {
  960. super.removeComponent(this.content);
  961. }
  962. this.content = content;
  963. if (content != null) {
  964. super.addComponent(content);
  965. }
  966. }
  967. /**
  968. * Adds a component to this root. The component is not added directly to the
  969. * root, but instead to the content container ({@link #getContent()}).
  970. *
  971. * @param component
  972. * the component to add to this root
  973. *
  974. * @see #getContent()
  975. */
  976. @Override
  977. public void addComponent(Component component) {
  978. getContent().addComponent(component);
  979. }
  980. /**
  981. * This implementation removes the component from the content container (
  982. * {@link #getContent()}) instead of from the actual root.
  983. */
  984. @Override
  985. public void removeComponent(Component component) {
  986. getContent().removeComponent(component);
  987. }
  988. /**
  989. * This implementation removes the components from the content container (
  990. * {@link #getContent()}) instead of from the actual root.
  991. */
  992. @Override
  993. public void removeAllComponents() {
  994. getContent().removeAllComponents();
  995. }
  996. /**
  997. * Internal initialization method, should not be overridden. This method is
  998. * not declared as final because that would break compatibility with e.g.
  999. * CDI.
  1000. *
  1001. * @param request
  1002. * the initialization request
  1003. */
  1004. public void doInit(WrappedRequest request) {
  1005. BrowserDetails browserDetails = request.getBrowserDetails();
  1006. if (browserDetails != null) {
  1007. fragment = browserDetails.getUriFragment();
  1008. }
  1009. // Call the init overridden by the application developer
  1010. init(request);
  1011. }
  1012. /**
  1013. * Initializes this root. This method is intended to be overridden by
  1014. * subclasses to build the view and configure non-component functionality.
  1015. * Performing the initialization in a constructor is not suggested as the
  1016. * state of the root is not properly set up when the constructor is invoked.
  1017. * <p>
  1018. * The {@link WrappedRequest} can be used to get information about the
  1019. * request that caused this root to be created. By default, the
  1020. * {@link BrowserDetails} will be available in the request. If the browser
  1021. * details are not required, loading the application in the browser can take
  1022. * some shortcuts giving a faster initial rendering. This can be indicated
  1023. * by adding the {@link EagerInit} annotation to the Root class.
  1024. * </p>
  1025. *
  1026. * @param request
  1027. * the wrapped request that caused this root to be created
  1028. */
  1029. protected abstract void init(WrappedRequest request);
  1030. /**
  1031. * Sets the thread local for the current root. This method is used by the
  1032. * framework to set the current application whenever a new request is
  1033. * processed and it is cleared when the request has been processed.
  1034. * <p>
  1035. * The application developer can also use this method to define the current
  1036. * root outside the normal request handling, e.g. when initiating custom
  1037. * background threads.
  1038. * </p>
  1039. *
  1040. * @param root
  1041. * the root to register as the current root
  1042. *
  1043. * @see #getCurrentRoot()
  1044. * @see ThreadLocal
  1045. */
  1046. public static void setCurrentRoot(Root root) {
  1047. currentRoot.set(root);
  1048. }
  1049. /**
  1050. * Gets the currently used root. The current root is automatically defined
  1051. * when processing requests to the server. In other cases, (e.g. from
  1052. * background threads), the current root is not automatically defined.
  1053. *
  1054. * @return the current root instance if available, otherwise
  1055. * <code>null</code>
  1056. *
  1057. * @see #setCurrentRoot(Root)
  1058. */
  1059. public static Root getCurrentRoot() {
  1060. return currentRoot.get();
  1061. }
  1062. /**
  1063. * Opens the given resource in this root. The contents of this Root is
  1064. * replaced by the {@code Resource}.
  1065. *
  1066. * @param resource
  1067. * the resource to show in this root
  1068. */
  1069. public void open(Resource resource) {
  1070. synchronized (openList) {
  1071. if (!openList.contains(resource)) {
  1072. openList.add(new OpenResource(resource, null, -1, -1,
  1073. BORDER_DEFAULT));
  1074. }
  1075. }
  1076. requestRepaint();
  1077. }
  1078. /* ********************************************************************* */
  1079. /**
  1080. * Opens the given resource in a window with the given name.
  1081. * <p>
  1082. * The supplied {@code windowName} is used as the target name in a
  1083. * window.open call in the client. This means that special values such as
  1084. * "_blank", "_self", "_top", "_parent" have special meaning. An empty or
  1085. * <code>null</code> window name is also a special case.
  1086. * </p>
  1087. * <p>
  1088. * "", null and "_self" as {@code windowName} all causes the resource to be
  1089. * opened in the current window, replacing any old contents. For
  1090. * downloadable content you should avoid "_self" as "_self" causes the
  1091. * client to skip rendering of any other changes as it considers them
  1092. * irrelevant (the page will be replaced by the resource). This can speed up
  1093. * the opening of a resource, but it might also put the client side into an
  1094. * inconsistent state if the window content is not completely replaced e.g.,
  1095. * if the resource is downloaded instead of displayed in the browser.
  1096. * </p>
  1097. * <p>
  1098. * "_blank" as {@code windowName} causes the resource to always be opened in
  1099. * a new window or tab (depends on the browser and browser settings).
  1100. * </p>
  1101. * <p>
  1102. * "_top" and "_parent" as {@code windowName} works as specified by the HTML
  1103. * standard.
  1104. * </p>
  1105. * <p>
  1106. * Any other {@code windowName} will open the resource in a window with that
  1107. * name, either by opening a new window/tab in the browser or by replacing
  1108. * the contents of an existing window with that name.
  1109. * </p>
  1110. *
  1111. * @param resource
  1112. * the resource.
  1113. * @param windowName
  1114. * the name of the window.
  1115. */
  1116. public void open(Resource resource, String windowName) {
  1117. synchronized (openList) {
  1118. if (!openList.contains(resource)) {
  1119. openList.add(new OpenResource(resource, windowName, -1, -1,
  1120. BORDER_DEFAULT));
  1121. }
  1122. }
  1123. requestRepaint();
  1124. }
  1125. /**
  1126. * Opens the given resource in a window with the given size, border and
  1127. * name. For more information on the meaning of {@code windowName}, see
  1128. * {@link #open(Resource, String)}.
  1129. *
  1130. * @param resource
  1131. * the resource.
  1132. * @param windowName
  1133. * the name of the window.
  1134. * @param width
  1135. * the width of the window in pixels
  1136. * @param height
  1137. * the height of the window in pixels
  1138. * @param border
  1139. * the border style of the window. See {@link #BORDER_NONE
  1140. * Window.BORDER_* constants}
  1141. */
  1142. public void open(Resource resource, String windowName, int width,
  1143. int height, int border) {
  1144. synchronized (openList) {
  1145. if (!openList.contains(resource)) {
  1146. openList.add(new OpenResource(resource, windowName, width,
  1147. height, border));
  1148. }
  1149. }
  1150. requestRepaint();
  1151. }
  1152. /**
  1153. * Private class for storing properties related to opening resources.
  1154. */
  1155. private class OpenResource implements Serializable {
  1156. /**
  1157. * The resource to open
  1158. */
  1159. private final Resource resource;
  1160. /**
  1161. * The name of the target window
  1162. */
  1163. private final String name;
  1164. /**
  1165. * The width of the target window
  1166. */
  1167. private final int width;
  1168. /**
  1169. * The height of the target window
  1170. */
  1171. private final int height;
  1172. /**
  1173. * The border style of the target window
  1174. */
  1175. private final int border;
  1176. /**
  1177. * Creates a new open resource.
  1178. *
  1179. * @param resource
  1180. * The resource to open
  1181. * @param name
  1182. * The name of the target window
  1183. * @param width
  1184. * The width of the target window
  1185. * @param height
  1186. * The height of the target window
  1187. * @param border
  1188. * The border style of the target window
  1189. */
  1190. private OpenResource(Resource resource, String name, int width,
  1191. int height, int border) {
  1192. this.resource = resource;
  1193. this.name = name;
  1194. this.width = width;
  1195. this.height = height;
  1196. this.border = border;
  1197. }
  1198. /**
  1199. * Paints the open request. Should be painted inside the window.
  1200. *
  1201. * @param target
  1202. * the paint target
  1203. * @throws PaintException
  1204. * if the paint operation fails
  1205. */
  1206. private void paintContent(PaintTarget target) throws PaintException {
  1207. target.startTag("open");
  1208. target.addAttribute("src", resource);
  1209. if (name != null && name.length() > 0) {
  1210. target.addAttribute("name", name);
  1211. }
  1212. if (width >= 0) {
  1213. target.addAttribute("width", width);
  1214. }
  1215. if (height >= 0) {
  1216. target.addAttribute("height", height);
  1217. }
  1218. switch (border) {
  1219. case BORDER_MINIMAL:
  1220. target.addAttribute("border", "minimal");
  1221. break;
  1222. case BORDER_NONE:
  1223. target.addAttribute("border", "none");
  1224. break;
  1225. }
  1226. target.endTag("open");
  1227. }
  1228. }
  1229. public void setScrollTop(int scrollTop) {
  1230. throw new RuntimeException("Not yet implemented");
  1231. }
  1232. @Override
  1233. protected ActionManager getActionManager() {
  1234. if (actionManager == null) {
  1235. actionManager = new ActionManager(this);
  1236. }
  1237. return actionManager;
  1238. }
  1239. public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
  1240. T action) {
  1241. getActionManager().addAction(action);
  1242. }
  1243. public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
  1244. T action) {
  1245. if (actionManager != null) {
  1246. actionManager.removeAction(action);
  1247. }
  1248. }
  1249. public void addActionHandler(Handler actionHandler) {
  1250. getActionManager().addActionHandler(actionHandler);
  1251. }
  1252. public void removeActionHandler(Handler actionHandler) {
  1253. if (actionManager != null) {
  1254. actionManager.removeActionHandler(actionHandler);
  1255. }
  1256. }
  1257. /**
  1258. * Should resize operations be lazy, i.e. should there be a delay before
  1259. * layout sizes are recalculated. Speeds up resize operations in slow UIs
  1260. * with the penalty of slightly decreased usability.
  1261. * <p>
  1262. * Default value: <code>false</code>
  1263. *
  1264. * @param resizeLazy
  1265. * true to use a delay before recalculating sizes, false to
  1266. * calculate immediately.
  1267. */
  1268. public void setResizeLazy(boolean resizeLazy) {
  1269. this.resizeLazy = resizeLazy;
  1270. requestRepaint();
  1271. }
  1272. /**
  1273. * Checks whether lazy resize is enabled.
  1274. *
  1275. * @return <code>true</code> if lazy resize is enabled, <code>false</code>
  1276. * if lazy resize is not enabled
  1277. */
  1278. public boolean isResizeLazy() {
  1279. return resizeLazy;
  1280. }
  1281. /**
  1282. * Add a click listener to the Root. The listener is called whenever the
  1283. * user clicks inside the Root. Also when the click targets a component
  1284. * inside the Root, provided the targeted component does not prevent the
  1285. * click event from propagating.
  1286. *
  1287. * Use {@link #removeListener(ClickListener)} to remove the listener.
  1288. *
  1289. * @param listener
  1290. * The listener to add
  1291. */
  1292. public void addListener(ClickListener listener) {
  1293. addListener(CLICK_EVENT_ID, ClickEvent.class, listener,
  1294. ClickListener.clickMethod);
  1295. }
  1296. /**
  1297. * Remove a click listener from the Root. The listener should earlier have
  1298. * been added using {@link #addListener(ClickListener)}.
  1299. *
  1300. * @param listener
  1301. * The listener to remove
  1302. */
  1303. public void removeListener(ClickListener listener) {
  1304. removeListener(CLICK_EVENT_ID, ClickEvent.class, listener);
  1305. }
  1306. public void addListener(FragmentChangedListener listener) {
  1307. addListener(FragmentChangedEvent.class, listener,
  1308. FRAGMENT_CHANGED_METHOD);
  1309. }
  1310. public void removeListener(FragmentChangedListener listener) {
  1311. removeListener(FragmentChangedEvent.class, listener,
  1312. FRAGMENT_CHANGED_METHOD);
  1313. }
  1314. /**
  1315. * Sets URI fragment. Optionally fires a {@link FragmentChangedEvent}
  1316. *
  1317. * @param newFragment
  1318. * id of the new fragment
  1319. * @param fireEvent
  1320. * true to fire event
  1321. * @see FragmentChangedEvent
  1322. * @see FragmentChangedListener
  1323. */
  1324. public void setFragment(String newFragment, boolean fireEvents) {
  1325. if (newFragment == null) {
  1326. throw new NullPointerException("The fragment may not be null");
  1327. }
  1328. if (!newFragment.equals(fragment)) {
  1329. fragment = newFragment;
  1330. if (fireEvents) {
  1331. fireEvent(new FragmentChangedEvent(this, newFragment));
  1332. }
  1333. requestRepaint();
  1334. }
  1335. }
  1336. /**
  1337. * Sets URI fragment. This method fires a {@link FragmentChangedEvent}
  1338. *
  1339. * @param newFragment
  1340. * id of the new fragment
  1341. * @see FragmentChangedEvent
  1342. * @see FragmentChangedListener
  1343. */
  1344. public void setFragment(String newFragment) {
  1345. setFragment(newFragment, true);
  1346. }
  1347. /**
  1348. * Gets currently set URI fragment.
  1349. * <p>
  1350. * To listen changes in fragment, hook a {@link FragmentChangedListener}.
  1351. *
  1352. * @return the current fragment in browser uri or null if not known
  1353. */
  1354. public String getFragment() {
  1355. return fragment;
  1356. }
  1357. /**
  1358. * Adds a new {@link BrowserWindowResizeListener} to this root. The listener
  1359. * will be notified whenever the browser window within which this root
  1360. * resides is resized.
  1361. *
  1362. * @param resizeListener
  1363. * the listener to add
  1364. *
  1365. * @see BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent)
  1366. * @see #setResizeLazy(boolean)
  1367. */
  1368. public void addListener(BrowserWindowResizeListener resizeListener) {
  1369. addListener(BrowserWindowResizeEvent.class, resizeListener,
  1370. BROWSWER_RESIZE_METHOD);
  1371. }
  1372. /**
  1373. * Removes a {@link BrowserWindowResizeListener} from this root. The
  1374. * listener will no longer be notified when the browser window is resized.
  1375. *
  1376. * @param resizeListener
  1377. * the listener to remove
  1378. */
  1379. public void removeListener(BrowserWindowResizeListener resizeListener) {
  1380. removeListener(BrowserWindowResizeEvent.class, resizeListener,
  1381. BROWSWER_RESIZE_METHOD);
  1382. }
  1383. /**
  1384. * Gets the last known height of the browser window in which this root
  1385. * resides.
  1386. *
  1387. * @return the browser window height in pixels
  1388. */
  1389. public int getBrowserWindowHeight() {
  1390. return browserWindowHeight;
  1391. }
  1392. /**
  1393. * Gets the last known width of the browser window in which this root
  1394. * resides.
  1395. *
  1396. * @return the browser window width in pixels
  1397. */
  1398. public int getBrowserWindowWidth() {
  1399. return browserWindowWidth;
  1400. }
  1401. /**
  1402. * Notifies the child components and windows that the root is attached to
  1403. * the application.
  1404. */
  1405. @Override
  1406. public void attach() {
  1407. super.attach();
  1408. for (Window w : windows) {
  1409. w.attach();
  1410. }
  1411. }
  1412. /**
  1413. * Notifies the child components and windows that the root is detached from
  1414. * the application.
  1415. */
  1416. @Override
  1417. public void detach() {
  1418. super.detach();
  1419. for (Window w : windows) {
  1420. w.detach();
  1421. }
  1422. }
  1423. @Override
  1424. public boolean isConnectorEnabled() {
  1425. // TODO How can a Root be invisible? What does it mean?
  1426. return isVisible() && isEnabled();
  1427. }
  1428. public DirtyConnectorTracker getDirtyConnectorTracker() {
  1429. return dirtyConnectorTracker;
  1430. }
  1431. public void componentAttached(Component component) {
  1432. getDirtyConnectorTracker().componentAttached(component);
  1433. }
  1434. public void componentDetached(Component component) {
  1435. getDirtyConnectorTracker().componentDetached(component);
  1436. }
  1437. }