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 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.util.Iterator;
  6. import java.util.LinkedList;
  7. import java.util.Map;
  8. import com.vaadin.event.Action;
  9. import com.vaadin.event.ShortcutAction;
  10. import com.vaadin.event.Action.Handler;
  11. import com.vaadin.event.MouseEvents.ClickEvent;
  12. import com.vaadin.event.MouseEvents.ClickListener;
  13. import com.vaadin.terminal.KeyMapper;
  14. import com.vaadin.terminal.PaintException;
  15. import com.vaadin.terminal.PaintTarget;
  16. import com.vaadin.terminal.Scrollable;
  17. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  18. import com.vaadin.terminal.gwt.client.ui.VPanel;
  19. /**
  20. * Panel - a simple single component container.
  21. *
  22. * @author IT Mill Ltd.
  23. * @version
  24. * @VERSION@
  25. * @since 3.0
  26. */
  27. @SuppressWarnings("serial")
  28. @ClientWidget(VPanel.class)
  29. public class Panel extends AbstractComponentContainer implements Scrollable,
  30. ComponentContainer.ComponentAttachListener,
  31. ComponentContainer.ComponentDetachListener, Action.Container {
  32. private static final String CLICK_EVENT = VPanel.CLICK_EVENT_IDENTIFIER;
  33. public static final String STYLE_LIGHT = "light";
  34. /**
  35. * Content of the panel.
  36. */
  37. private ComponentContainer content;
  38. /**
  39. * Scroll X position.
  40. */
  41. private int scrollOffsetX = 0;
  42. /**
  43. * Scroll Y position.
  44. */
  45. private int scrollOffsetY = 0;
  46. /**
  47. * Scrolling mode.
  48. */
  49. private boolean scrollable = false;
  50. /** List of action handlers */
  51. private LinkedList actionHandlers = null;
  52. /** Action mapper */
  53. private KeyMapper actionMapper = null;
  54. /**
  55. * Creates a new empty panel. A VerticalLayout is used as content.
  56. */
  57. public Panel() {
  58. this((ComponentContainer) null);
  59. }
  60. /**
  61. * Creates a new empty panel which contains the given content. The content
  62. * cannot be null.
  63. *
  64. * @param content
  65. * the content for the panel.
  66. */
  67. public Panel(ComponentContainer content) {
  68. setContent(content);
  69. setWidth(100, UNITS_PERCENTAGE);
  70. }
  71. /**
  72. * Creates a new empty panel with caption. Default layout is used.
  73. *
  74. * @param caption
  75. * the caption used in the panel.
  76. */
  77. public Panel(String caption) {
  78. this(caption, null);
  79. }
  80. /**
  81. * Creates a new empty panel with the given caption and content.
  82. *
  83. * @param caption
  84. * the caption of the panel.
  85. * @param content
  86. * the content used in the panel.
  87. */
  88. public Panel(String caption, ComponentContainer content) {
  89. this(content);
  90. setCaption(caption);
  91. }
  92. /**
  93. * Gets the current layout of the panel.
  94. *
  95. * @return the Current layout of the panel.
  96. * @deprecated A Panel can now contain a ComponentContainer which is not
  97. * necessarily a Layout. Use {@link #getContent()} instead.
  98. */
  99. @Deprecated
  100. public Layout getLayout() {
  101. if (content instanceof Layout) {
  102. return (Layout) content;
  103. } else if (content == null) {
  104. return null;
  105. }
  106. throw new IllegalStateException(
  107. "Panel does not contain a Layout. Use getContent() instead of getLayout().");
  108. }
  109. /**
  110. * Sets the layout of the panel.
  111. *
  112. * If given layout is null, a VerticalLayout with margins set is used as a
  113. * default.
  114. *
  115. * Components from old layout are not moved to new layout by default
  116. * (changed in 5.2.2). Use function in Layout interface manually.
  117. *
  118. * @param newLayout
  119. * the New layout of the panel.
  120. * @deprecated A Panel can now contain a ComponentContainer which is not
  121. * necessarily a Layout. Use
  122. * {@link #setContent(ComponentContainer)} instead.
  123. */
  124. @Deprecated
  125. public void setLayout(Layout newLayout) {
  126. setContent(newLayout);
  127. }
  128. /**
  129. * Returns the content of the Panel.
  130. *
  131. * @return
  132. */
  133. public ComponentContainer getContent() {
  134. return content;
  135. }
  136. /**
  137. *
  138. * Set the content of the Panel. If null is given as the new content then a
  139. * layout is automatically created and set as the content.
  140. *
  141. * @param content
  142. * The new content
  143. */
  144. public void setContent(ComponentContainer newContent) {
  145. // If the content is null we create the default content
  146. if (newContent == null) {
  147. newContent = createDefaultContent();
  148. }
  149. // if (newContent == null) {
  150. // throw new IllegalArgumentException("Content cannot be null");
  151. // }
  152. if (newContent == content) {
  153. // don't set the same content twice
  154. return;
  155. }
  156. // detach old content if present
  157. if (content != null) {
  158. content.setParent(null);
  159. content
  160. .removeListener((ComponentContainer.ComponentAttachListener) this);
  161. content
  162. .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. * Paints the content of this component.
  189. *
  190. * @param target
  191. * the Paint Event.
  192. * @throws PaintException
  193. * if the paint operation failed.
  194. */
  195. @Override
  196. public void paintContent(PaintTarget target) throws PaintException {
  197. content.paint(target);
  198. if (isScrollable()) {
  199. target.addVariable(this, "scrollLeft", getScrollLeft());
  200. target.addVariable(this, "scrollTop", getScrollTop());
  201. }
  202. target.addVariable(this, "action", "");
  203. target.startTag("actions");
  204. if (actionHandlers != null && !actionHandlers.isEmpty()) {
  205. for (final Iterator ahi = actionHandlers.iterator(); ahi.hasNext();) {
  206. final Action[] aa = ((Action.Handler) ahi.next()).getActions(
  207. null, this);
  208. if (aa != null) {
  209. for (int ai = 0; ai < aa.length; ai++) {
  210. final Action a = aa[ai];
  211. target.startTag("action");
  212. final String akey = actionMapper.key(aa[ai]);
  213. target.addAttribute("key", akey);
  214. if (a.getCaption() != null) {
  215. target.addAttribute("caption", a.getCaption());
  216. }
  217. if (a.getIcon() != null) {
  218. target.addAttribute("icon", a.getIcon());
  219. }
  220. if (a instanceof ShortcutAction) {
  221. final ShortcutAction sa = (ShortcutAction) a;
  222. target.addAttribute("kc", sa.getKeyCode());
  223. final int[] modifiers = sa.getModifiers();
  224. if (modifiers != null) {
  225. final String[] smodifiers = new String[modifiers.length];
  226. for (int i = 0; i < modifiers.length; i++) {
  227. smodifiers[i] = String
  228. .valueOf(modifiers[i]);
  229. }
  230. target.addAttribute("mk", smodifiers);
  231. }
  232. }
  233. target.endTag("action");
  234. }
  235. }
  236. }
  237. }
  238. target.endTag("actions");
  239. }
  240. @Override
  241. public void requestRepaintAll() {
  242. // Panel has odd structure, delegate to layout
  243. requestRepaint();
  244. if (getContent() != null) {
  245. getContent().requestRepaintAll();
  246. }
  247. }
  248. /**
  249. * Adds the component into this container.
  250. *
  251. * @param c
  252. * the component to be added.
  253. * @see com.vaadin.ui.AbstractComponentContainer#addComponent(com.vaadin.ui.Component)
  254. */
  255. @Override
  256. public void addComponent(Component c) {
  257. content.addComponent(c);
  258. // No repaint request is made as we except the underlying container to
  259. // request repaints
  260. }
  261. /**
  262. * Removes the component from this container.
  263. *
  264. * @param c
  265. * The component to be added.
  266. * @see com.vaadin.ui.AbstractComponentContainer#removeComponent(com.vaadin.ui.Component)
  267. */
  268. @Override
  269. public void removeComponent(Component c) {
  270. content.removeComponent(c);
  271. // No repaint request is made as we except the underlying container to
  272. // request repaints
  273. }
  274. /**
  275. * Gets the component container iterator for going trough all the components
  276. * in the container.
  277. *
  278. * @return the Iterator of the components inside the container.
  279. * @see com.vaadin.ui.ComponentContainer#getComponentIterator()
  280. */
  281. public Iterator getComponentIterator() {
  282. return content.getComponentIterator();
  283. }
  284. /**
  285. * Called when one or more variables handled by the implementing class are
  286. * changed.
  287. *
  288. * @see com.vaadin.terminal.VariableOwner#changeVariables(Object, Map)
  289. */
  290. @Override
  291. public void changeVariables(Object source, Map variables) {
  292. super.changeVariables(source, variables);
  293. if (variables.containsKey(CLICK_EVENT)) {
  294. fireClick(variables.get(CLICK_EVENT));
  295. }
  296. // Get new size
  297. final Integer newWidth = (Integer) variables.get("width");
  298. final Integer newHeight = (Integer) variables.get("height");
  299. if (newWidth != null && newWidth.intValue() != getWidth()) {
  300. setWidth(newWidth.intValue(), UNITS_PIXELS);
  301. }
  302. if (newHeight != null && newHeight.intValue() != getHeight()) {
  303. setHeight(newHeight.intValue(), UNITS_PIXELS);
  304. }
  305. // Scrolling
  306. final Integer newScrollX = (Integer) variables.get("scrollLeft");
  307. final Integer newScrollY = (Integer) variables.get("scrollTop");
  308. if (newScrollX != null && newScrollX.intValue() != getScrollLeft()) {
  309. // set internally, not to fire request repaint
  310. scrollOffsetX = newScrollX.intValue();
  311. }
  312. if (newScrollY != null && newScrollY.intValue() != getScrollTop()) {
  313. // set internally, not to fire request repaint
  314. scrollOffsetY = newScrollY.intValue();
  315. }
  316. // Actions
  317. if (variables.containsKey("action")) {
  318. final String key = (String) variables.get("action");
  319. final Action action = (Action) actionMapper.get(key);
  320. if (action != null && actionHandlers != null) {
  321. Object[] array = actionHandlers.toArray();
  322. for (int i = 0; i < array.length; i++) {
  323. ((Action.Handler) array[i])
  324. .handleAction(action, this, this);
  325. }
  326. }
  327. }
  328. }
  329. /* Scrolling functionality */
  330. /* Documented in interface */
  331. public int getScrollLeft() {
  332. return scrollOffsetX;
  333. }
  334. /**
  335. * @deprecated use getScrollLeft() instead
  336. */
  337. @Deprecated
  338. public int getScrollOffsetX() {
  339. return getScrollLeft();
  340. }
  341. /* Documented in interface */
  342. public int getScrollTop() {
  343. return scrollOffsetY;
  344. }
  345. /**
  346. * @deprecated use getScrollTop() instead
  347. */
  348. @Deprecated
  349. public int getScrollOffsetY() {
  350. return getScrollTop();
  351. }
  352. /* Documented in interface */
  353. public boolean isScrollable() {
  354. return scrollable;
  355. }
  356. /* Documented in interface */
  357. public void setScrollable(boolean isScrollingEnabled) {
  358. if (scrollable != isScrollingEnabled) {
  359. scrollable = isScrollingEnabled;
  360. requestRepaint();
  361. }
  362. }
  363. /* Documented in interface */
  364. public void setScrollLeft(int pixelsScrolled) {
  365. if (pixelsScrolled < 0) {
  366. throw new IllegalArgumentException(
  367. "Scroll offset must be at least 0");
  368. }
  369. if (scrollOffsetX != pixelsScrolled) {
  370. scrollOffsetX = pixelsScrolled;
  371. requestRepaint();
  372. }
  373. }
  374. /**
  375. * @deprecated use setScrollLeft() method instead
  376. */
  377. @Deprecated
  378. public void setScrollOffsetX(int pixels) {
  379. setScrollLeft(pixels);
  380. }
  381. /* Documented in interface */
  382. public void setScrollTop(int pixelsScrolledDown) {
  383. if (pixelsScrolledDown < 0) {
  384. throw new IllegalArgumentException(
  385. "Scroll offset must be at least 0");
  386. }
  387. if (scrollOffsetY != pixelsScrolledDown) {
  388. scrollOffsetY = pixelsScrolledDown;
  389. requestRepaint();
  390. }
  391. }
  392. /**
  393. * @deprecated use setScrollTop() method instead
  394. */
  395. @Deprecated
  396. public void setScrollOffsetY(int pixels) {
  397. setScrollTop(pixels);
  398. }
  399. /* Documented in superclass */
  400. public void replaceComponent(Component oldComponent, Component newComponent) {
  401. content.replaceComponent(oldComponent, newComponent);
  402. }
  403. /**
  404. * A new component is attached to container.
  405. *
  406. * @see com.vaadin.ui.ComponentContainer.ComponentAttachListener#componentAttachedToContainer(com.vaadin.ui.ComponentContainer.ComponentAttachEvent)
  407. */
  408. public void componentAttachedToContainer(ComponentAttachEvent event) {
  409. if (event.getContainer() == content) {
  410. fireComponentAttachEvent(event.getAttachedComponent());
  411. }
  412. }
  413. /**
  414. * A component has been detached from container.
  415. *
  416. * @see com.vaadin.ui.ComponentContainer.ComponentDetachListener#componentDetachedFromContainer(com.vaadin.ui.ComponentContainer.ComponentDetachEvent)
  417. */
  418. public void componentDetachedFromContainer(ComponentDetachEvent event) {
  419. if (event.getContainer() == content) {
  420. fireComponentDetachEvent(event.getDetachedComponent());
  421. }
  422. }
  423. /**
  424. * Notifies the component that it is connected to an application.
  425. *
  426. * @see com.vaadin.ui.Component#attach()
  427. */
  428. @Override
  429. public void attach() {
  430. // can't call parent here as this is Panels hierarchy is a hack
  431. requestRepaint();
  432. if (content != null) {
  433. content.attach();
  434. }
  435. }
  436. /**
  437. * Notifies the component that it is detached from the application.
  438. *
  439. * @see com.vaadin.ui.Component#detach()
  440. */
  441. @Override
  442. public void detach() {
  443. // can't call parent here as this is Panels hierarchy is a hack
  444. if (content != null) {
  445. content.detach();
  446. }
  447. }
  448. /**
  449. * Removes all components from this container.
  450. *
  451. * @see com.vaadin.ui.ComponentContainer#removeAllComponents()
  452. */
  453. @Override
  454. public void removeAllComponents() {
  455. content.removeAllComponents();
  456. }
  457. public void addActionHandler(Handler actionHandler) {
  458. if (actionHandler != null) {
  459. if (actionHandlers == null) {
  460. actionHandlers = new LinkedList();
  461. actionMapper = new KeyMapper();
  462. }
  463. if (!actionHandlers.contains(actionHandler)) {
  464. actionHandlers.add(actionHandler);
  465. requestRepaint();
  466. }
  467. }
  468. }
  469. /**
  470. * Removes an action handler.
  471. *
  472. * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler)
  473. */
  474. public void removeActionHandler(Action.Handler actionHandler) {
  475. if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
  476. actionHandlers.remove(actionHandler);
  477. if (actionHandlers.isEmpty()) {
  478. actionHandlers = null;
  479. actionMapper = null;
  480. }
  481. requestRepaint();
  482. }
  483. }
  484. public void addListener(ClickListener listener) {
  485. addListener(CLICK_EVENT, ClickEvent.class, listener,
  486. ClickListener.clickMethod);
  487. }
  488. public void removeListener(ClickListener listener) {
  489. removeListener(CLICK_EVENT, ClickEvent.class, listener);
  490. }
  491. /**
  492. * Fire a click event to all click listeners.
  493. *
  494. * @param object
  495. * The raw "value" of the variable change from the client side.
  496. */
  497. private void fireClick(Object object) {
  498. MouseEventDetails mouseDetails = MouseEventDetails
  499. .deserialize((String) object);
  500. fireEvent(new ClickEvent(this, mouseDetails));
  501. }
  502. }