Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

TabSheet.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.io.Serializable;
  6. import java.lang.reflect.Method;
  7. import java.util.HashMap;
  8. import java.util.Iterator;
  9. import java.util.LinkedList;
  10. import java.util.Map;
  11. import com.vaadin.terminal.ErrorMessage;
  12. import com.vaadin.terminal.KeyMapper;
  13. import com.vaadin.terminal.PaintException;
  14. import com.vaadin.terminal.PaintTarget;
  15. import com.vaadin.terminal.Resource;
  16. import com.vaadin.terminal.Paintable.RepaintRequestListener;
  17. import com.vaadin.terminal.gwt.client.ui.VTabsheet;
  18. /**
  19. * Tabsheet component.
  20. *
  21. * @author IT Mill Ltd.
  22. * @version
  23. * @VERSION@
  24. * @since 3.0
  25. */
  26. @SuppressWarnings("serial")
  27. @ClientWidget(VTabsheet.class)
  28. public class TabSheet extends AbstractComponentContainer implements
  29. RepaintRequestListener {
  30. /**
  31. * Linked list of component tabs.
  32. */
  33. private final LinkedList components = new LinkedList();
  34. /**
  35. * Map containing information related to the tabs (caption, icon etc).
  36. */
  37. private final HashMap<Component, Tab> tabs = new HashMap<Component, Tab>();
  38. /**
  39. * Selected tab.
  40. */
  41. private Component selected = null;
  42. private final KeyMapper keyMapper = new KeyMapper();
  43. /**
  44. * Holds the value of property tabsHIdden.
  45. */
  46. private boolean tabsHidden;
  47. private LinkedList paintedTabs = new LinkedList();
  48. /**
  49. * Constructs a new Tabsheet. Tabsheet is immediate by default.
  50. */
  51. public TabSheet() {
  52. super();
  53. // expand horizontally by default
  54. setWidth(100, UNITS_PERCENTAGE);
  55. setImmediate(true);
  56. }
  57. /**
  58. * Gets the component container iterator for going trough all the components
  59. * in the container.
  60. *
  61. * @return the Iterator of the components inside the container.
  62. */
  63. public Iterator getComponentIterator() {
  64. return java.util.Collections.unmodifiableList(components).iterator();
  65. }
  66. /**
  67. * Removes the component from this container.
  68. *
  69. * @param c
  70. * the component to be removed.
  71. */
  72. @Override
  73. public void removeComponent(Component c) {
  74. if (c != null && components.contains(c)) {
  75. super.removeComponent(c);
  76. keyMapper.remove(c);
  77. components.remove(c);
  78. tabs.remove(c);
  79. if (c.equals(selected)) {
  80. if (components.isEmpty()) {
  81. selected = null;
  82. } else {
  83. selected = (Component) components.getFirst();
  84. fireSelectedTabChange();
  85. }
  86. }
  87. requestRepaint();
  88. }
  89. }
  90. /**
  91. * Adds a new tab into TabSheet. Components caption and icon are rendered
  92. * into tab.
  93. *
  94. * @param c
  95. * the component to be added.
  96. */
  97. @Override
  98. public void addComponent(Component c) {
  99. addTab(c);
  100. }
  101. /**
  102. * Adds a new tab into TabSheet.
  103. *
  104. * @param c
  105. * the component to be added onto tab.
  106. * @param caption
  107. * the caption to be set for the component and used rendered in
  108. * tab bar
  109. * @param icon
  110. * the icon to be set for the component and used rendered in tab
  111. * bar
  112. * @return the created tab
  113. */
  114. public Tab addTab(Component c, String caption, Resource icon) {
  115. if (c != null) {
  116. components.addLast(c);
  117. Tab tab = new TabSheetTabImpl(caption, icon);
  118. tabs.put(c, tab);
  119. if (selected == null) {
  120. selected = c;
  121. fireSelectedTabChange();
  122. }
  123. super.addComponent(c);
  124. requestRepaint();
  125. return tab;
  126. } else {
  127. return null;
  128. }
  129. }
  130. /**
  131. * Adds a new tab into TabSheet. Components caption and icon are rendered
  132. * into tab.
  133. *
  134. * @param c
  135. * the component to be added onto tab.
  136. * @return the created tab
  137. */
  138. public Tab addTab(Component c) {
  139. if (c != null) {
  140. return addTab(c, c.getCaption(), c.getIcon());
  141. }
  142. return null;
  143. }
  144. /**
  145. * Gets the component UIDL tag.
  146. *
  147. * @return the Component UIDL tag as string.
  148. */
  149. @Override
  150. public String getTag() {
  151. return "tabsheet";
  152. }
  153. /**
  154. * Moves all components from another container to this container. The
  155. * components are removed from the other container.
  156. *
  157. * @param source
  158. * the container components are removed from.
  159. */
  160. @Override
  161. public void moveComponentsFrom(ComponentContainer source) {
  162. for (final Iterator i = source.getComponentIterator(); i.hasNext();) {
  163. final Component c = (Component) i.next();
  164. String caption = null;
  165. Resource icon = null;
  166. if (TabSheet.class.isAssignableFrom(source.getClass())) {
  167. caption = ((TabSheet) source).getTabCaption(c);
  168. icon = ((TabSheet) source).getTabIcon(c);
  169. }
  170. source.removeComponent(c);
  171. addTab(c, caption, icon);
  172. }
  173. }
  174. /**
  175. * Paints the content of this component.
  176. *
  177. * @param event
  178. * the Paint Event.
  179. * @throws PaintException
  180. * if the paint operation failed.
  181. */
  182. @Override
  183. public void paintContent(PaintTarget target) throws PaintException {
  184. if (areTabsHidden()) {
  185. target.addAttribute("hidetabs", true);
  186. }
  187. target.startTag("tabs");
  188. for (final Iterator i = getComponentIterator(); i.hasNext();) {
  189. final Component component = (Component) i.next();
  190. Tab tab = tabs.get(component);
  191. /*
  192. * If we have no selection, if the current selection is invisible or
  193. * if the current selection is disabled (but the whole component is
  194. * not) we select this tab instead
  195. */
  196. Tab selectedTabInfo = null;
  197. if (selected != null) {
  198. selectedTabInfo = tabs.get(selected);
  199. }
  200. if (selected == null || selectedTabInfo == null
  201. || !selectedTabInfo.isVisible()
  202. || !selectedTabInfo.isEnabled()) {
  203. // The current selection is not valid so we need to change it
  204. if (tab.isEnabled() && tab.isVisible()) {
  205. selected = component;
  206. } else {
  207. /*
  208. * The current selection is not valid but this tab cannot be
  209. * selected either.
  210. */
  211. selected = null;
  212. }
  213. }
  214. target.startTag("tab");
  215. if (!tab.isEnabled() && tab.isVisible()) {
  216. target.addAttribute("disabled", true);
  217. }
  218. if (!tab.isVisible()) {
  219. target.addAttribute("hidden", true);
  220. }
  221. final Resource icon = tab.getIcon();
  222. if (icon != null) {
  223. target.addAttribute("icon", icon);
  224. }
  225. final String caption = tab.getCaption();
  226. if (caption != null && caption.length() > 0) {
  227. target.addAttribute("caption", caption);
  228. }
  229. final String description = tab.getDescription();
  230. if (description != null) {
  231. target.addAttribute("description", description);
  232. }
  233. final ErrorMessage componentError = tab.getComponentError();
  234. if (componentError != null) {
  235. componentError.paint(target);
  236. }
  237. target.addAttribute("key", keyMapper.key(component));
  238. if (component.equals(selected)) {
  239. target.addAttribute("selected", true);
  240. component.paint(target);
  241. paintedTabs.add(component);
  242. } else if (paintedTabs.contains(component)) {
  243. component.paint(target);
  244. } else {
  245. component.requestRepaintRequests();
  246. }
  247. target.endTag("tab");
  248. }
  249. target.endTag("tabs");
  250. if (selected != null) {
  251. target.addVariable(this, "selected", keyMapper.key(selected));
  252. }
  253. }
  254. /**
  255. * Are tabs hidden.
  256. *
  257. * @return the Property visibility.
  258. */
  259. public boolean areTabsHidden() {
  260. return tabsHidden;
  261. }
  262. /**
  263. * Setter for property tabsHidden.
  264. *
  265. * @param tabsHidden
  266. * True if the tabs should be hidden.
  267. */
  268. public void hideTabs(boolean tabsHidden) {
  269. this.tabsHidden = tabsHidden;
  270. requestRepaint();
  271. }
  272. /**
  273. * Gets the caption for a component.
  274. *
  275. * @param c
  276. * the component.
  277. * @deprecated Use {@link #getTab(Component)} and {@link Tab#getCaption()}
  278. * instead.
  279. */
  280. @Deprecated
  281. public String getTabCaption(Component c) {
  282. Tab info = tabs.get(c);
  283. if (info == null) {
  284. return "";
  285. } else {
  286. return info.getCaption();
  287. }
  288. }
  289. /**
  290. * Sets tabs captions.
  291. *
  292. * @param c
  293. * the component.
  294. * @param caption
  295. * the caption to set.
  296. * @deprecated Use {@link #getTab(Component)} and
  297. * {@link Tab#setCaption(String)} instead.
  298. */
  299. @Deprecated
  300. public void setTabCaption(Component c, String caption) {
  301. Tab info = tabs.get(c);
  302. if (info != null) {
  303. info.setCaption(caption);
  304. requestRepaint();
  305. }
  306. }
  307. /**
  308. * Gets the icon for a component.
  309. *
  310. * @param c
  311. * the component.
  312. * @deprecated Use {@link #getTab(Component)} and {@link Tab#getIcon()}
  313. * instead.
  314. */
  315. @Deprecated
  316. public Resource getTabIcon(Component c) {
  317. Tab info = tabs.get(c);
  318. if (info == null) {
  319. return null;
  320. } else {
  321. return info.getIcon();
  322. }
  323. }
  324. /**
  325. * Sets icon for the given component.
  326. *
  327. * Normally TabSheet uses icon from component
  328. *
  329. * @param c
  330. * the component
  331. * @param icon
  332. * the icon to set
  333. * @deprecated Use {@link #getTab(Component)} and
  334. * {@link Tab#setIcon(Resource)} instead.
  335. */
  336. @Deprecated
  337. public void setTabIcon(Component c, Resource icon) {
  338. Tab info = tabs.get(c);
  339. if (info != null) {
  340. info.setIcon(icon);
  341. requestRepaint();
  342. }
  343. }
  344. /**
  345. * Returns the Tab for the component. The Tab object can be used for setting
  346. * caption,icon, etc for the tab.
  347. *
  348. * @param c
  349. * the component
  350. * @return
  351. */
  352. public Tab getTab(Component c) {
  353. return tabs.get(c);
  354. }
  355. /**
  356. * Sets the selected tab.
  357. *
  358. * @param c
  359. */
  360. public void setSelectedTab(Component c) {
  361. if (c != null && components.contains(c) && !c.equals(selected)) {
  362. selected = c;
  363. fireSelectedTabChange();
  364. requestRepaint();
  365. }
  366. }
  367. /**
  368. * Gets the selected tab.
  369. *
  370. * @return the selected tab.
  371. */
  372. public Component getSelectedTab() {
  373. return selected;
  374. }
  375. /**
  376. * Invoked when the value of a variable has changed.
  377. *
  378. * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object,
  379. * java.util.Map)
  380. */
  381. @Override
  382. public void changeVariables(Object source, Map variables) {
  383. if (variables.containsKey("selected")) {
  384. setSelectedTab((Component) keyMapper.get((String) variables
  385. .get("selected")));
  386. }
  387. }
  388. /* Documented in superclass */
  389. public void replaceComponent(Component oldComponent, Component newComponent) {
  390. if (selected == oldComponent) {
  391. // keep selection w/o selectedTabChange event
  392. selected = newComponent;
  393. }
  394. Tab newTab = tabs.get(newComponent);
  395. Tab oldTab = tabs.get(oldComponent);
  396. // Gets the captions
  397. String oldCaption = null;
  398. Resource oldIcon = null;
  399. String newCaption = null;
  400. Resource newIcon = null;
  401. if (oldTab != null) {
  402. oldCaption = oldTab.getCaption();
  403. oldIcon = oldTab.getIcon();
  404. }
  405. if (newTab != null) {
  406. newCaption = newTab.getCaption();
  407. newIcon = newTab.getIcon();
  408. } else {
  409. newCaption = newComponent.getCaption();
  410. newIcon = newComponent.getIcon();
  411. }
  412. // Gets the locations
  413. int oldLocation = -1;
  414. int newLocation = -1;
  415. int location = 0;
  416. for (final Iterator i = components.iterator(); i.hasNext();) {
  417. final Component component = (Component) i.next();
  418. if (component == oldComponent) {
  419. oldLocation = location;
  420. }
  421. if (component == newComponent) {
  422. newLocation = location;
  423. }
  424. location++;
  425. }
  426. if (oldLocation == -1) {
  427. addComponent(newComponent);
  428. } else if (newLocation == -1) {
  429. removeComponent(oldComponent);
  430. keyMapper.remove(oldComponent);
  431. newTab = addTab(newComponent);
  432. components.remove(newComponent);
  433. components.add(oldLocation, newComponent);
  434. newTab.setCaption(oldCaption);
  435. newTab.setIcon(oldIcon);
  436. } else {
  437. if (oldLocation > newLocation) {
  438. components.remove(oldComponent);
  439. components.add(newLocation, oldComponent);
  440. components.remove(newComponent);
  441. components.add(oldLocation, newComponent);
  442. } else {
  443. components.remove(newComponent);
  444. components.add(oldLocation, newComponent);
  445. components.remove(oldComponent);
  446. components.add(newLocation, oldComponent);
  447. }
  448. if (newTab != null) {
  449. // This should always be true
  450. newTab.setCaption(oldCaption);
  451. newTab.setIcon(oldIcon);
  452. }
  453. if (oldTab != null) {
  454. // This should always be true
  455. oldTab.setCaption(newCaption);
  456. oldTab.setIcon(newIcon);
  457. }
  458. requestRepaint();
  459. }
  460. }
  461. /* Click event */
  462. private static final Method SELECTED_TAB_CHANGE_METHOD;
  463. static {
  464. try {
  465. SELECTED_TAB_CHANGE_METHOD = SelectedTabChangeListener.class
  466. .getDeclaredMethod("selectedTabChange",
  467. new Class[] { SelectedTabChangeEvent.class });
  468. } catch (final java.lang.NoSuchMethodException e) {
  469. // This should never happen
  470. throw new java.lang.RuntimeException(
  471. "Internal error finding methods in TabSheet");
  472. }
  473. }
  474. /**
  475. * Selected Tab Change event. This event is thrown, when the selected tab in
  476. * the tab sheet is changed.
  477. *
  478. * @author IT Mill Ltd.
  479. * @version
  480. * @VERSION@
  481. * @since 3.0
  482. */
  483. public class SelectedTabChangeEvent extends Component.Event {
  484. /**
  485. * New instance of selected tab change event
  486. *
  487. * @param source
  488. * the Source of the event.
  489. */
  490. public SelectedTabChangeEvent(Component source) {
  491. super(source);
  492. }
  493. /**
  494. * TabSheet where the event occurred.
  495. *
  496. * @return the Source of the event.
  497. */
  498. public TabSheet getTabSheet() {
  499. return (TabSheet) getSource();
  500. }
  501. }
  502. /**
  503. * Selected Tab Change Event listener
  504. *
  505. * @author IT Mill Ltd.
  506. *
  507. * @version
  508. * @VERSION@
  509. * @since 3.0
  510. */
  511. public interface SelectedTabChangeListener extends Serializable {
  512. /**
  513. * Visible tab in tab sheet has has been changed.
  514. *
  515. * @param event
  516. * the Selected tab change event.
  517. */
  518. public void selectedTabChange(SelectedTabChangeEvent event);
  519. }
  520. /**
  521. * Adds the selected tab change listener
  522. *
  523. * @param listener
  524. * the Listener to be added.
  525. */
  526. public void addListener(SelectedTabChangeListener listener) {
  527. addListener(SelectedTabChangeEvent.class, listener,
  528. SELECTED_TAB_CHANGE_METHOD);
  529. }
  530. /**
  531. * Removes the selected tab change listener
  532. *
  533. * @param listener
  534. * the Listener to be removed.
  535. */
  536. public void removeListener(SelectedTabChangeListener listener) {
  537. removeListener(SelectedTabChangeEvent.class, listener,
  538. SELECTED_TAB_CHANGE_METHOD);
  539. }
  540. /**
  541. * Emits the options change event.
  542. */
  543. protected void fireSelectedTabChange() {
  544. fireEvent(new SelectedTabChangeEvent(this));
  545. }
  546. /*
  547. * If child is not rendered on the client we need to repaint on child
  548. * repaint due the way captions and icons are handled.
  549. */
  550. public void repaintRequested(RepaintRequestEvent event) {
  551. if (!paintedTabs.contains(event.getPaintable())) {
  552. requestRepaint();
  553. }
  554. }
  555. @Override
  556. public void detach() {
  557. super.detach();
  558. paintedTabs.clear();
  559. }
  560. /**
  561. *
  562. */
  563. public interface Tab extends Serializable {
  564. /**
  565. * Returns the visible status for the tab.
  566. *
  567. * @return true for visible, false for hidden
  568. */
  569. public boolean isVisible();
  570. /**
  571. * Sets the visible status for the tab.
  572. *
  573. * @param visible
  574. * true for visible, false for hidden
  575. */
  576. public void setVisible(boolean visible);
  577. /**
  578. * Returns the enabled status for the tab.
  579. *
  580. * @return true for enabled, false for disabled
  581. */
  582. public boolean isEnabled();
  583. /**
  584. * Sets the enabled status for the tab.
  585. *
  586. * @param enabled
  587. * true for enabled, false for disabled
  588. */
  589. public void setEnabled(boolean enabled);
  590. /**
  591. * Sets the caption for the tab.
  592. *
  593. * @param caption
  594. * the caption to set
  595. */
  596. public void setCaption(String caption);
  597. /**
  598. * Gets the caption for the tab.
  599. *
  600. */
  601. public String getCaption();
  602. /**
  603. * Gets the icon for the tab.
  604. *
  605. */
  606. public Resource getIcon();
  607. /**
  608. * Sets the icon for the tab.
  609. *
  610. * @param icon
  611. * the icon to set
  612. */
  613. public void setIcon(Resource icon);
  614. /**
  615. * Gets the description for the tab. The description can be used to
  616. * briefly describe the state of the tab to the user.
  617. *
  618. * @return the description for the tab
  619. */
  620. public String getDescription();
  621. /**
  622. * Sets the description for the tab.
  623. *
  624. * @param description
  625. * the new description string for the tab.
  626. */
  627. public void setDescription(String description);
  628. public void setComponentError(ErrorMessage componentError);
  629. public ErrorMessage getComponentError();
  630. }
  631. /**
  632. * TabSheet's implementation of Tab
  633. *
  634. */
  635. public class TabSheetTabImpl implements Tab {
  636. private String caption = "";
  637. private Resource icon = null;
  638. private boolean enabled = true;
  639. private boolean visible = true;
  640. private String description = null;
  641. private ErrorMessage componentError = null;
  642. public TabSheetTabImpl(String caption, Resource icon) {
  643. if (caption == null) {
  644. caption = "";
  645. }
  646. this.caption = caption;
  647. this.icon = icon;
  648. }
  649. /**
  650. * Returns the tab caption. Can never be null.
  651. */
  652. public String getCaption() {
  653. return caption;
  654. }
  655. public void setCaption(String caption) {
  656. this.caption = caption;
  657. requestRepaint();
  658. }
  659. public Resource getIcon() {
  660. return icon;
  661. }
  662. public void setIcon(Resource icon) {
  663. this.icon = icon;
  664. requestRepaint();
  665. }
  666. public boolean isEnabled() {
  667. return enabled;
  668. }
  669. public void setEnabled(boolean enabled) {
  670. this.enabled = enabled;
  671. requestRepaint();
  672. }
  673. public boolean isVisible() {
  674. return visible;
  675. }
  676. public void setVisible(boolean visible) {
  677. this.visible = visible;
  678. requestRepaint();
  679. }
  680. public String getDescription() {
  681. return description;
  682. }
  683. public void setDescription(String description) {
  684. this.description = description;
  685. requestRepaint();
  686. }
  687. public ErrorMessage getComponentError() {
  688. return componentError;
  689. }
  690. public void setComponentError(ErrorMessage componentError) {
  691. this.componentError = componentError;
  692. requestRepaint();
  693. }
  694. }
  695. }