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.

Panel.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.util.Iterator;
  6. import java.util.Map;
  7. import com.vaadin.event.Action;
  8. import com.vaadin.event.Action.Handler;
  9. import com.vaadin.event.ActionManager;
  10. import com.vaadin.event.MouseEvents.ClickEvent;
  11. import com.vaadin.event.MouseEvents.ClickListener;
  12. import com.vaadin.terminal.PaintException;
  13. import com.vaadin.terminal.PaintTarget;
  14. import com.vaadin.terminal.Scrollable;
  15. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  16. import com.vaadin.terminal.gwt.client.ui.PanelConnector;
  17. import com.vaadin.terminal.gwt.client.ui.PanelConnector.PanelState;
  18. import com.vaadin.ui.Component.Focusable;
  19. /**
  20. * Panel - a simple single component container.
  21. *
  22. * @author Vaadin Ltd.
  23. * @version
  24. * @VERSION@
  25. * @since 3.0
  26. */
  27. @SuppressWarnings("serial")
  28. @ClientWidget(PanelConnector.class)
  29. public class Panel extends AbstractComponentContainer implements Scrollable,
  30. ComponentContainer.ComponentAttachListener,
  31. ComponentContainer.ComponentDetachListener, Action.Notifier, Focusable {
  32. private static final String CLICK_EVENT = PanelConnector.CLICK_EVENT_IDENTIFIER;
  33. /**
  34. * Content of the panel.
  35. */
  36. private ComponentContainer content;
  37. /**
  38. * Keeps track of the Actions added to this component, and manages the
  39. * painting and handling as well.
  40. */
  41. protected ActionManager actionManager;
  42. /**
  43. * Creates a new empty panel. A VerticalLayout is used as content.
  44. */
  45. public Panel() {
  46. this((ComponentContainer) null);
  47. }
  48. /**
  49. * Creates a new empty panel which contains the given content. The content
  50. * cannot be null.
  51. *
  52. * @param content
  53. * the content for the panel.
  54. */
  55. public Panel(ComponentContainer content) {
  56. setContent(content);
  57. setWidth(100, Unit.PERCENTAGE);
  58. getState().setTabIndex(-1);
  59. }
  60. /**
  61. * Creates a new empty panel with caption. Default layout is used.
  62. *
  63. * @param caption
  64. * the caption used in the panel (HTML/XHTML).
  65. */
  66. public Panel(String caption) {
  67. this(caption, null);
  68. }
  69. /**
  70. * Creates a new empty panel with the given caption and content.
  71. *
  72. * @param caption
  73. * the caption of the panel (HTML/XHTML).
  74. * @param content
  75. * the content used in the panel.
  76. */
  77. public Panel(String caption, ComponentContainer content) {
  78. this(content);
  79. setCaption(caption);
  80. }
  81. /**
  82. * Sets the caption of the panel.
  83. *
  84. * Note that the caption is interpreted as HTML/XHTML and therefore care
  85. * should be taken not to enable HTML injection and XSS attacks using panel
  86. * captions. This behavior may change in future versions.
  87. *
  88. * @see AbstractComponent#setCaption(String)
  89. */
  90. @Override
  91. public void setCaption(String caption) {
  92. super.setCaption(caption);
  93. }
  94. /**
  95. * Gets the current layout of the panel.
  96. *
  97. * @return the Current layout of the panel.
  98. * @deprecated A Panel can now contain a ComponentContainer which is not
  99. * necessarily a Layout. Use {@link #getContent()} instead.
  100. */
  101. @Deprecated
  102. public Layout getLayout() {
  103. if (content instanceof Layout) {
  104. return (Layout) content;
  105. } else if (content == null) {
  106. return null;
  107. }
  108. throw new IllegalStateException(
  109. "Panel does not contain a Layout. Use getContent() instead of getLayout().");
  110. }
  111. /**
  112. * Sets the layout of the panel.
  113. *
  114. * If given layout is null, a VerticalLayout with margins set is used as a
  115. * default.
  116. *
  117. * Components from old layout are not moved to new layout by default
  118. * (changed in 5.2.2). Use function in Layout interface manually.
  119. *
  120. * @param newLayout
  121. * the New layout of the panel.
  122. * @deprecated A Panel can now contain a ComponentContainer which is not
  123. * necessarily a Layout. Use
  124. * {@link #setContent(ComponentContainer)} instead.
  125. */
  126. @Deprecated
  127. public void setLayout(Layout newLayout) {
  128. setContent(newLayout);
  129. }
  130. /**
  131. * Returns the content of the Panel.
  132. *
  133. * @return
  134. */
  135. public ComponentContainer getContent() {
  136. return content;
  137. }
  138. /**
  139. *
  140. * Set the content of the Panel. If null is given as the new content then a
  141. * layout is automatically created and set as the content.
  142. *
  143. * @param content
  144. * The new content
  145. */
  146. public void setContent(ComponentContainer newContent) {
  147. // If the content is null we create the default content
  148. if (newContent == null) {
  149. newContent = createDefaultContent();
  150. }
  151. // if (newContent == null) {
  152. // throw new IllegalArgumentException("Content cannot be null");
  153. // }
  154. if (newContent == content) {
  155. // don't set the same content twice
  156. return;
  157. }
  158. // detach old content if present
  159. if (content != null) {
  160. content.setParent(null);
  161. content.removeListener((ComponentContainer.ComponentAttachListener) this);
  162. content.removeListener((ComponentContainer.ComponentDetachListener) this);
  163. }
  164. // Sets the panel to be parent for the content
  165. newContent.setParent(this);
  166. // Sets the new content
  167. content = newContent;
  168. // Adds the event listeners for new content
  169. newContent
  170. .addListener((ComponentContainer.ComponentAttachListener) this);
  171. newContent
  172. .addListener((ComponentContainer.ComponentDetachListener) this);
  173. content = newContent;
  174. }
  175. /**
  176. * Create a ComponentContainer which is added by default to the Panel if
  177. * user does not specify any content.
  178. *
  179. * @return
  180. */
  181. private ComponentContainer createDefaultContent() {
  182. VerticalLayout layout = new VerticalLayout();
  183. // Force margins by default
  184. layout.setMargin(true);
  185. return layout;
  186. }
  187. /*
  188. * (non-Javadoc)
  189. *
  190. * @see
  191. * com.vaadin.ui.AbstractComponent#paintContent(com.vaadin.terminal.PaintTarget
  192. * )
  193. */
  194. @Override
  195. public void paintContent(PaintTarget target) throws PaintException {
  196. // This is needed for now for paint to be ever run for the child
  197. content.paint(target);
  198. if (actionManager != null) {
  199. actionManager.paintActions(null, target);
  200. }
  201. }
  202. @Override
  203. public void requestRepaintAll() {
  204. // Panel has odd structure, delegate to layout
  205. requestRepaint();
  206. if (getContent() != null) {
  207. getContent().requestRepaintAll();
  208. }
  209. }
  210. /**
  211. * Adds the component into this container.
  212. *
  213. * @param c
  214. * the component to be added.
  215. * @see com.vaadin.ui.AbstractComponentContainer#addComponent(com.vaadin.ui.Component)
  216. */
  217. @Override
  218. public void addComponent(Component c) {
  219. content.addComponent(c);
  220. // No repaint request is made as we except the underlying container to
  221. // request repaints
  222. }
  223. /**
  224. * Removes the component from this container.
  225. *
  226. * @param c
  227. * The component to be removed.
  228. * @see com.vaadin.ui.AbstractComponentContainer#removeComponent(com.vaadin.ui.Component)
  229. */
  230. @Override
  231. public void removeComponent(Component c) {
  232. content.removeComponent(c);
  233. // No repaint request is made as we except the underlying container to
  234. // request repaints
  235. }
  236. /**
  237. * Gets the component container iterator for going through all the
  238. * components in the container.
  239. *
  240. * @return the Iterator of the components inside the container.
  241. * @see com.vaadin.ui.ComponentContainer#getComponentIterator()
  242. */
  243. public Iterator<Component> getComponentIterator() {
  244. return content.getComponentIterator();
  245. }
  246. /**
  247. * Called when one or more variables handled by the implementing class are
  248. * changed.
  249. *
  250. * @see com.vaadin.terminal.VariableOwner#changeVariables(Object, Map)
  251. */
  252. @SuppressWarnings("unchecked")
  253. @Override
  254. public void changeVariables(Object source, Map<String, Object> variables) {
  255. super.changeVariables(source, variables);
  256. if (variables.containsKey(CLICK_EVENT)) {
  257. fireClick((Map<String, Object>) variables.get(CLICK_EVENT));
  258. }
  259. // Get new size
  260. final Integer newWidth = (Integer) variables.get("width");
  261. final Integer newHeight = (Integer) variables.get("height");
  262. if (newWidth != null && newWidth.intValue() != getWidth()) {
  263. setWidth(newWidth.intValue(), UNITS_PIXELS);
  264. }
  265. if (newHeight != null && newHeight.intValue() != getHeight()) {
  266. setHeight(newHeight.intValue(), UNITS_PIXELS);
  267. }
  268. // Scrolling
  269. final Integer newScrollX = (Integer) variables.get("scrollLeft");
  270. final Integer newScrollY = (Integer) variables.get("scrollTop");
  271. if (newScrollX != null && newScrollX.intValue() != getScrollLeft()) {
  272. // set internally, not to fire request repaint
  273. getState().setScrollLeft(newScrollX.intValue());
  274. }
  275. if (newScrollY != null && newScrollY.intValue() != getScrollTop()) {
  276. // set internally, not to fire request repaint
  277. getState().setScrollTop(newScrollY.intValue());
  278. }
  279. // Actions
  280. if (actionManager != null) {
  281. actionManager.handleActions(variables, this);
  282. }
  283. }
  284. /* Scrolling functionality */
  285. /*
  286. * (non-Javadoc)
  287. *
  288. * @see com.vaadin.terminal.Scrollable#setScrollable(boolean)
  289. */
  290. public int getScrollLeft() {
  291. return getState().getScrollLeft();
  292. }
  293. /*
  294. * (non-Javadoc)
  295. *
  296. * @see com.vaadin.terminal.Scrollable#setScrollable(boolean)
  297. */
  298. public int getScrollTop() {
  299. return getState().getScrollTop();
  300. }
  301. /*
  302. * (non-Javadoc)
  303. *
  304. * @see com.vaadin.terminal.Scrollable#setScrollLeft(int)
  305. */
  306. public void setScrollLeft(int scrollLeft) {
  307. if (scrollLeft < 0) {
  308. throw new IllegalArgumentException(
  309. "Scroll offset must be at least 0");
  310. }
  311. getState().setScrollLeft(scrollLeft);
  312. requestRepaint();
  313. }
  314. /*
  315. * (non-Javadoc)
  316. *
  317. * @see com.vaadin.terminal.Scrollable#setScrollTop(int)
  318. */
  319. public void setScrollTop(int scrollTop) {
  320. if (scrollTop < 0) {
  321. throw new IllegalArgumentException(
  322. "Scroll offset must be at least 0");
  323. }
  324. getState().setScrollTop(scrollTop);
  325. requestRepaint();
  326. }
  327. /* Documented in superclass */
  328. public void replaceComponent(Component oldComponent, Component newComponent) {
  329. content.replaceComponent(oldComponent, newComponent);
  330. }
  331. /**
  332. * A new component is attached to container.
  333. *
  334. * @see com.vaadin.ui.ComponentContainer.ComponentAttachListener#componentAttachedToContainer(com.vaadin.ui.ComponentContainer.ComponentAttachEvent)
  335. */
  336. public void componentAttachedToContainer(ComponentAttachEvent event) {
  337. if (event.getContainer() == content) {
  338. fireComponentAttachEvent(event.getAttachedComponent());
  339. }
  340. }
  341. /**
  342. * A component has been detached from container.
  343. *
  344. * @see com.vaadin.ui.ComponentContainer.ComponentDetachListener#componentDetachedFromContainer(com.vaadin.ui.ComponentContainer.ComponentDetachEvent)
  345. */
  346. public void componentDetachedFromContainer(ComponentDetachEvent event) {
  347. if (event.getContainer() == content) {
  348. fireComponentDetachEvent(event.getDetachedComponent());
  349. }
  350. }
  351. /**
  352. * Notifies the component that it is connected to an application.
  353. *
  354. * @see com.vaadin.ui.Component#attach()
  355. */
  356. @Override
  357. public void attach() {
  358. // can't call parent here as this is Panels hierarchy is a hack
  359. requestRepaint();
  360. if (content != null) {
  361. content.attach();
  362. }
  363. }
  364. /**
  365. * Notifies the component that it is detached from the application.
  366. *
  367. * @see com.vaadin.ui.Component#detach()
  368. */
  369. @Override
  370. public void detach() {
  371. // can't call parent here as this is Panels hierarchy is a hack
  372. if (content != null) {
  373. content.detach();
  374. }
  375. }
  376. /**
  377. * Removes all components from this container.
  378. *
  379. * @see com.vaadin.ui.ComponentContainer#removeAllComponents()
  380. */
  381. @Override
  382. public void removeAllComponents() {
  383. content.removeAllComponents();
  384. }
  385. /*
  386. * ACTIONS
  387. */
  388. @Override
  389. protected ActionManager getActionManager() {
  390. if (actionManager == null) {
  391. actionManager = new ActionManager(this);
  392. }
  393. return actionManager;
  394. }
  395. public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
  396. T action) {
  397. getActionManager().addAction(action);
  398. }
  399. public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
  400. T action) {
  401. if (actionManager != null) {
  402. actionManager.removeAction(action);
  403. }
  404. }
  405. public void addActionHandler(Handler actionHandler) {
  406. getActionManager().addActionHandler(actionHandler);
  407. }
  408. public void removeActionHandler(Handler actionHandler) {
  409. if (actionManager != null) {
  410. actionManager.removeActionHandler(actionHandler);
  411. }
  412. }
  413. /**
  414. * Removes all action handlers
  415. */
  416. public void removeAllActionHandlers() {
  417. if (actionManager != null) {
  418. actionManager.removeAllActionHandlers();
  419. }
  420. }
  421. /**
  422. * Add a click listener to the Panel. The listener is called whenever the
  423. * user clicks inside the Panel. Also when the click targets a component
  424. * inside the Panel, provided the targeted component does not prevent the
  425. * click event from propagating.
  426. *
  427. * Use {@link #removeListener(ClickListener)} to remove the listener.
  428. *
  429. * @param listener
  430. * The listener to add
  431. */
  432. public void addListener(ClickListener listener) {
  433. addListener(CLICK_EVENT, ClickEvent.class, listener,
  434. ClickListener.clickMethod);
  435. }
  436. /**
  437. * Remove a click listener from the Panel. The listener should earlier have
  438. * been added using {@link #addListener(ClickListener)}.
  439. *
  440. * @param listener
  441. * The listener to remove
  442. */
  443. public void removeListener(ClickListener listener) {
  444. removeListener(CLICK_EVENT, ClickEvent.class, listener);
  445. }
  446. /**
  447. * Fire a click event to all click listeners.
  448. *
  449. * @param object
  450. * The raw "value" of the variable change from the client side.
  451. */
  452. private void fireClick(Map<String, Object> parameters) {
  453. MouseEventDetails mouseDetails = MouseEventDetails
  454. .deSerialize((String) parameters.get("mouseDetails"));
  455. fireEvent(new ClickEvent(this, mouseDetails));
  456. }
  457. /**
  458. * {@inheritDoc}
  459. */
  460. public int getTabIndex() {
  461. return getState().getTabIndex();
  462. }
  463. /**
  464. * {@inheritDoc}
  465. */
  466. public void setTabIndex(int tabIndex) {
  467. getState().setTabIndex(tabIndex);
  468. requestRepaint();
  469. }
  470. /**
  471. * Moves keyboard focus to the component. {@see Focusable#focus()}
  472. *
  473. */
  474. @Override
  475. public void focus() {
  476. super.focus();
  477. }
  478. /*
  479. * (non-Javadoc)
  480. *
  481. * @see com.vaadin.ui.ComponentContainer#getComponentCount()
  482. */
  483. public int getComponentCount() {
  484. // This is so wrong... (#2924)
  485. return content.getComponentCount();
  486. }
  487. @Override
  488. public PanelState getState() {
  489. return (PanelState) super.getState();
  490. }
  491. @Override
  492. protected PanelState createState() {
  493. return new PanelState();
  494. }
  495. }