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.

MenuBar.java 34KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.io.Serializable;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.Stack;
  24. import org.jsoup.nodes.Attributes;
  25. import org.jsoup.nodes.Element;
  26. import org.jsoup.nodes.Node;
  27. import org.jsoup.parser.Tag;
  28. import com.vaadin.server.PaintException;
  29. import com.vaadin.server.PaintTarget;
  30. import com.vaadin.server.Resource;
  31. import com.vaadin.shared.ui.menubar.MenuBarConstants;
  32. import com.vaadin.shared.ui.menubar.MenuBarState;
  33. import com.vaadin.ui.Component.Focusable;
  34. import com.vaadin.ui.declarative.DesignAttributeHandler;
  35. import com.vaadin.ui.declarative.DesignContext;
  36. /**
  37. * <p>
  38. * A class representing a horizontal menu bar. The menu can contain MenuItem
  39. * objects, which in turn can contain more MenuBars. These sub-level MenuBars
  40. * are represented as vertical menu.
  41. * </p>
  42. */
  43. @SuppressWarnings("serial")
  44. public class MenuBar extends AbstractComponent
  45. implements LegacyComponent, Focusable {
  46. // Items of the top-level menu
  47. private final List<MenuItem> menuItems;
  48. // Number of items in this menu
  49. private int numberOfItems = 0;
  50. private MenuItem moreItem;
  51. private boolean openRootOnHover;
  52. private boolean htmlContentAllowed;
  53. @Override
  54. protected MenuBarState getState() {
  55. return (MenuBarState) super.getState();
  56. }
  57. @Override
  58. protected MenuBarState getState(boolean markAsDirty) {
  59. return (MenuBarState) super.getState(markAsDirty);
  60. }
  61. /** Paint (serialise) the component for the client. */
  62. @Override
  63. public void paintContent(PaintTarget target) throws PaintException {
  64. target.addAttribute(MenuBarConstants.OPEN_ROOT_MENU_ON_HOWER,
  65. openRootOnHover);
  66. if (isHtmlContentAllowed()) {
  67. target.addAttribute(MenuBarConstants.HTML_CONTENT_ALLOWED, true);
  68. }
  69. target.startTag("options");
  70. if (getWidth() > -1) {
  71. target.startTag("moreItem");
  72. target.addAttribute("text", moreItem.getText());
  73. if (moreItem.getIcon() != null) {
  74. target.addAttribute("icon", moreItem.getIcon());
  75. }
  76. target.endTag("moreItem");
  77. }
  78. target.endTag("options");
  79. target.startTag("items");
  80. // This generates the tree from the contents of the menu
  81. for (MenuItem item : menuItems) {
  82. paintItem(target, item);
  83. }
  84. target.endTag("items");
  85. }
  86. private void paintItem(PaintTarget target, MenuItem item)
  87. throws PaintException {
  88. if (!item.isVisible()) {
  89. return;
  90. }
  91. target.startTag("item");
  92. target.addAttribute("id", item.getId());
  93. if (item.getStyleName() != null) {
  94. target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_STYLE,
  95. item.getStyleName());
  96. }
  97. if (item.isSeparator()) {
  98. target.addAttribute("separator", true);
  99. } else {
  100. target.addAttribute("text", item.getText());
  101. Command command = item.getCommand();
  102. if (command != null) {
  103. target.addAttribute("command", true);
  104. }
  105. Resource icon = item.getIcon();
  106. if (icon != null) {
  107. target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_ICON, icon);
  108. }
  109. if (!item.isEnabled()) {
  110. target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_DISABLED,
  111. true);
  112. }
  113. String description = item.getDescription();
  114. if (description != null && description.length() > 0) {
  115. target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_DESCRIPTION,
  116. description);
  117. }
  118. if (item.isCheckable()) {
  119. // if the "checked" attribute is present (either true or false),
  120. // the item is checkable
  121. target.addAttribute(MenuBarConstants.ATTRIBUTE_CHECKED,
  122. item.isChecked());
  123. }
  124. if (item.hasChildren()) {
  125. for (MenuItem child : item.getChildren()) {
  126. paintItem(target, child);
  127. }
  128. }
  129. }
  130. target.endTag("item");
  131. }
  132. /** Deserialize changes received from client. */
  133. @Override
  134. public void changeVariables(Object source, Map<String, Object> variables) {
  135. Stack<MenuItem> items = new Stack<MenuItem>();
  136. boolean found = false;
  137. if (variables.containsKey("clickedId")) {
  138. Integer clickedId = (Integer) variables.get("clickedId");
  139. Iterator<MenuItem> itr = getItems().iterator();
  140. while (itr.hasNext()) {
  141. items.push(itr.next());
  142. }
  143. MenuItem tmpItem = null;
  144. // Go through all the items in the menu
  145. while (!found && !items.empty()) {
  146. tmpItem = items.pop();
  147. found = (clickedId.intValue() == tmpItem.getId());
  148. if (tmpItem.hasChildren()) {
  149. itr = tmpItem.getChildren().iterator();
  150. while (itr.hasNext()) {
  151. items.push(itr.next());
  152. }
  153. }
  154. } // while
  155. // If we got the clicked item, launch the command.
  156. if (found && tmpItem.isEnabled()) {
  157. if (tmpItem.isCheckable()) {
  158. tmpItem.setChecked(!tmpItem.isChecked());
  159. }
  160. if (null != tmpItem.getCommand()) {
  161. tmpItem.getCommand().menuSelected(tmpItem);
  162. }
  163. }
  164. } // if
  165. }// changeVariables
  166. /**
  167. * Constructs an empty, horizontal menu
  168. */
  169. public MenuBar() {
  170. menuItems = new ArrayList<MenuItem>();
  171. setMoreMenuItem(null);
  172. }
  173. /**
  174. * Add a new item to the menu bar. Command can be null, but a caption must
  175. * be given.
  176. *
  177. * @param caption
  178. * the text for the menu item
  179. * @param command
  180. * the command for the menu item
  181. * @throws IllegalArgumentException
  182. */
  183. public MenuBar.MenuItem addItem(String caption, MenuBar.Command command) {
  184. return addItem(caption, null, command);
  185. }
  186. /**
  187. * Add a new item to the menu bar. Icon and command can be null, but a
  188. * caption must be given.
  189. *
  190. * @param caption
  191. * the text for the menu item
  192. * @param icon
  193. * the icon for the menu item
  194. * @param command
  195. * the command for the menu item
  196. * @throws IllegalArgumentException
  197. */
  198. public MenuBar.MenuItem addItem(String caption, Resource icon,
  199. MenuBar.Command command) {
  200. if (caption == null) {
  201. throw new IllegalArgumentException("caption cannot be null");
  202. }
  203. MenuItem newItem = new MenuItem(caption, icon, command);
  204. menuItems.add(newItem);
  205. markAsDirty();
  206. return newItem;
  207. }
  208. /**
  209. * Add an item before some item. If the given item does not exist the item
  210. * is added at the end of the menu. Icon and command can be null, but a
  211. * caption must be given.
  212. *
  213. * @param caption
  214. * the text for the menu item
  215. * @param icon
  216. * the icon for the menu item
  217. * @param command
  218. * the command for the menu item
  219. * @param itemToAddBefore
  220. * the item that will be after the new item
  221. * @throws IllegalArgumentException
  222. */
  223. public MenuBar.MenuItem addItemBefore(String caption, Resource icon,
  224. MenuBar.Command command, MenuBar.MenuItem itemToAddBefore) {
  225. if (caption == null) {
  226. throw new IllegalArgumentException("caption cannot be null");
  227. }
  228. MenuItem newItem = new MenuItem(caption, icon, command);
  229. if (menuItems.contains(itemToAddBefore)) {
  230. int index = menuItems.indexOf(itemToAddBefore);
  231. menuItems.add(index, newItem);
  232. } else {
  233. menuItems.add(newItem);
  234. }
  235. markAsDirty();
  236. return newItem;
  237. }
  238. /**
  239. * Returns a list with all the MenuItem objects in the menu bar
  240. *
  241. * @return a list containing the MenuItem objects in the menu bar
  242. */
  243. public List<MenuItem> getItems() {
  244. return menuItems;
  245. }
  246. /**
  247. * Remove first occurrence the specified item from the main menu
  248. *
  249. * @param item
  250. * The item to be removed
  251. */
  252. public void removeItem(MenuBar.MenuItem item) {
  253. if (item != null) {
  254. menuItems.remove(item);
  255. }
  256. markAsDirty();
  257. }
  258. /**
  259. * Empty the menu bar
  260. */
  261. public void removeItems() {
  262. menuItems.clear();
  263. markAsDirty();
  264. }
  265. /**
  266. * Returns the size of the menu.
  267. *
  268. * @return The size of the menu
  269. */
  270. public int getSize() {
  271. return menuItems.size();
  272. }
  273. /**
  274. * Set the item that is used when collapsing the top level menu. All
  275. * "overflowing" items will be added below this. The item command will be
  276. * ignored. If set to null, the default item with a downwards arrow is used.
  277. *
  278. * The item command (if specified) is ignored.
  279. *
  280. * @param item
  281. */
  282. public void setMoreMenuItem(MenuItem item) {
  283. if (item != null) {
  284. moreItem = item;
  285. } else {
  286. moreItem = new MenuItem("", null, null);
  287. }
  288. markAsDirty();
  289. }
  290. /**
  291. * Get the MenuItem used as the collapse menu item.
  292. *
  293. * @return
  294. */
  295. public MenuItem getMoreMenuItem() {
  296. return moreItem;
  297. }
  298. /**
  299. * Using this method menubar can be put into a special mode where top level
  300. * menus opens without clicking on the menu, but automatically when mouse
  301. * cursor is moved over the menu. In this mode the menu also closes itself
  302. * if the mouse is moved out of the opened menu.
  303. * <p>
  304. * Note, that on touch devices the menu still opens on a click event.
  305. *
  306. * @param autoOpenTopLevelMenu
  307. * true if menus should be opened without click, the default is
  308. * false
  309. */
  310. public void setAutoOpen(boolean autoOpenTopLevelMenu) {
  311. if (autoOpenTopLevelMenu != openRootOnHover) {
  312. openRootOnHover = autoOpenTopLevelMenu;
  313. markAsDirty();
  314. }
  315. }
  316. /**
  317. * Detects whether the menubar is in a mode where top level menus are
  318. * automatically opened when the mouse cursor is moved over the menu.
  319. * Normally root menu opens only by clicking on the menu. Submenus always
  320. * open automatically.
  321. *
  322. * @return true if the root menus open without click, the default is false
  323. */
  324. public boolean isAutoOpen() {
  325. return openRootOnHover;
  326. }
  327. /**
  328. * Sets whether html is allowed in the item captions. If set to true, the
  329. * captions are passed to the browser as html and the developer is
  330. * responsible for ensuring no harmful html is used. If set to false, the
  331. * content is passed to the browser as plain text.
  332. *
  333. * @param htmlContentAllowed
  334. * true if the captions are used as html, false if used as plain
  335. * text
  336. */
  337. public void setHtmlContentAllowed(boolean htmlContentAllowed) {
  338. this.htmlContentAllowed = htmlContentAllowed;
  339. markAsDirty();
  340. }
  341. /**
  342. * Checks whether item captions are interpreted as html or plain text.
  343. *
  344. * @return true if the captions are used as html, false if used as plain
  345. * text
  346. * @see #setHtmlContentAllowed(boolean)
  347. */
  348. public boolean isHtmlContentAllowed() {
  349. return htmlContentAllowed;
  350. }
  351. @Override
  352. public int getTabIndex() {
  353. return getState(false).tabIndex;
  354. }
  355. /*
  356. * (non-Javadoc)
  357. *
  358. * @see com.vaadin.ui.Component.Focusable#setTabIndex(int)
  359. */
  360. @Override
  361. public void setTabIndex(int tabIndex) {
  362. getState().tabIndex = tabIndex;
  363. }
  364. @Override
  365. public void focus() {
  366. // Overridden only to make public
  367. super.focus();
  368. }
  369. /**
  370. * This interface contains the layer for menu commands of the
  371. * {@link com.vaadin.ui.MenuBar} class. It's method will fire when the user
  372. * clicks on the containing {@link com.vaadin.ui.MenuBar.MenuItem}. The
  373. * selected item is given as an argument.
  374. */
  375. public interface Command extends Serializable {
  376. public void menuSelected(MenuBar.MenuItem selectedItem);
  377. }
  378. /**
  379. * A composite class for menu items and sub-menus. You can set commands to
  380. * be fired on user click by implementing the
  381. * {@link com.vaadin.ui.MenuBar.Command} interface. You can also add
  382. * multiple MenuItems to a MenuItem and create a sub-menu.
  383. *
  384. */
  385. public class MenuItem implements Serializable {
  386. /** Private members * */
  387. private final int itsId;
  388. private Command itsCommand;
  389. private String itsText;
  390. private List<MenuItem> itsChildren;
  391. private Resource itsIcon;
  392. private MenuItem itsParent;
  393. private boolean enabled = true;
  394. private boolean visible = true;
  395. private boolean isSeparator = false;
  396. private String styleName;
  397. private String description;
  398. private boolean checkable = false;
  399. private boolean checked = false;
  400. /**
  401. * Constructs a new menu item that can optionally have an icon and a
  402. * command associated with it. Icon and command can be null, but a
  403. * caption must be given.
  404. *
  405. * @param text
  406. * The text associated with the command
  407. * @param command
  408. * The command to be fired
  409. * @throws IllegalArgumentException
  410. */
  411. public MenuItem(String caption, Resource icon,
  412. MenuBar.Command command) {
  413. if (caption == null) {
  414. throw new IllegalArgumentException("caption cannot be null");
  415. }
  416. itsId = ++numberOfItems;
  417. itsText = caption;
  418. itsIcon = icon;
  419. itsCommand = command;
  420. }
  421. /**
  422. * Checks if the item has children (if it is a sub-menu).
  423. *
  424. * @return True if this item has children
  425. */
  426. public boolean hasChildren() {
  427. return !isSeparator() && itsChildren != null;
  428. }
  429. /**
  430. * Adds a separator to this menu. A separator is a way to visually group
  431. * items in a menu, to make it easier for users to find what they are
  432. * looking for in the menu.
  433. *
  434. * @author Jouni Koivuviita / Vaadin Ltd.
  435. * @since 6.2.0
  436. */
  437. public MenuBar.MenuItem addSeparator() {
  438. MenuItem item = addItem("", null, null);
  439. item.setSeparator(true);
  440. return item;
  441. }
  442. public MenuBar.MenuItem addSeparatorBefore(MenuItem itemToAddBefore) {
  443. MenuItem item = addItemBefore("", null, null, itemToAddBefore);
  444. item.setSeparator(true);
  445. return item;
  446. }
  447. /**
  448. * Add a new item inside this item, thus creating a sub-menu. Command
  449. * can be null, but a caption must be given.
  450. *
  451. * @param caption
  452. * the text for the menu item
  453. * @param command
  454. * the command for the menu item
  455. */
  456. public MenuBar.MenuItem addItem(String caption,
  457. MenuBar.Command command) {
  458. return addItem(caption, null, command);
  459. }
  460. /**
  461. * Add a new item inside this item, thus creating a sub-menu. Icon and
  462. * command can be null, but a caption must be given.
  463. *
  464. * @param caption
  465. * the text for the menu item
  466. * @param icon
  467. * the icon for the menu item
  468. * @param command
  469. * the command for the menu item
  470. * @throws IllegalStateException
  471. * If the item is checkable and thus cannot have children.
  472. */
  473. public MenuBar.MenuItem addItem(String caption, Resource icon,
  474. MenuBar.Command command) throws IllegalStateException {
  475. if (isSeparator()) {
  476. throw new UnsupportedOperationException(
  477. "Cannot add items to a separator");
  478. }
  479. if (isCheckable()) {
  480. throw new IllegalStateException(
  481. "A checkable item cannot have children");
  482. }
  483. if (caption == null) {
  484. throw new IllegalArgumentException("Caption cannot be null");
  485. }
  486. if (itsChildren == null) {
  487. itsChildren = new ArrayList<MenuItem>();
  488. }
  489. MenuItem newItem = new MenuItem(caption, icon, command);
  490. // The only place where the parent is set
  491. newItem.setParent(this);
  492. itsChildren.add(newItem);
  493. markAsDirty();
  494. return newItem;
  495. }
  496. /**
  497. * Add an item before some item. If the given item does not exist the
  498. * item is added at the end of the menu. Icon and command can be null,
  499. * but a caption must be given.
  500. *
  501. * @param caption
  502. * the text for the menu item
  503. * @param icon
  504. * the icon for the menu item
  505. * @param command
  506. * the command for the menu item
  507. * @param itemToAddBefore
  508. * the item that will be after the new item
  509. * @throws IllegalStateException
  510. * If the item is checkable and thus cannot have children.
  511. */
  512. public MenuBar.MenuItem addItemBefore(String caption, Resource icon,
  513. MenuBar.Command command, MenuBar.MenuItem itemToAddBefore)
  514. throws IllegalStateException {
  515. if (isCheckable()) {
  516. throw new IllegalStateException(
  517. "A checkable item cannot have children");
  518. }
  519. MenuItem newItem = null;
  520. if (hasChildren() && itsChildren.contains(itemToAddBefore)) {
  521. int index = itsChildren.indexOf(itemToAddBefore);
  522. newItem = new MenuItem(caption, icon, command);
  523. newItem.setParent(this);
  524. itsChildren.add(index, newItem);
  525. } else {
  526. newItem = addItem(caption, icon, command);
  527. }
  528. markAsDirty();
  529. return newItem;
  530. }
  531. /**
  532. * For the associated command.
  533. *
  534. * @return The associated command, or null if there is none
  535. */
  536. public Command getCommand() {
  537. return itsCommand;
  538. }
  539. /**
  540. * Gets the objects icon.
  541. *
  542. * @return The icon of the item, null if the item doesn't have an icon
  543. */
  544. public Resource getIcon() {
  545. return itsIcon;
  546. }
  547. /**
  548. * For the containing item. This will return null if the item is in the
  549. * top-level menu bar.
  550. *
  551. * @return The containing {@link com.vaadin.ui.MenuBar.MenuItem} , or
  552. * null if there is none
  553. */
  554. public MenuBar.MenuItem getParent() {
  555. return itsParent;
  556. }
  557. /**
  558. * This will return the children of this item or null if there are none.
  559. *
  560. * @return List of children items, or null if there are none
  561. */
  562. public List<MenuItem> getChildren() {
  563. return itsChildren;
  564. }
  565. /**
  566. * Gets the objects text
  567. *
  568. * @return The text
  569. */
  570. public java.lang.String getText() {
  571. return itsText;
  572. }
  573. /**
  574. * Returns the number of children.
  575. *
  576. * @return The number of child items
  577. */
  578. public int getSize() {
  579. if (itsChildren != null) {
  580. return itsChildren.size();
  581. }
  582. return -1;
  583. }
  584. /**
  585. * Get the unique identifier for this item.
  586. *
  587. * @return The id of this item
  588. */
  589. public int getId() {
  590. return itsId;
  591. }
  592. /**
  593. * Set the command for this item. Set null to remove.
  594. *
  595. * @param command
  596. * The MenuCommand of this item
  597. */
  598. public void setCommand(MenuBar.Command command) {
  599. itsCommand = command;
  600. }
  601. /**
  602. * Sets the icon. Set null to remove.
  603. *
  604. * @param icon
  605. * The icon for this item
  606. */
  607. public void setIcon(Resource icon) {
  608. itsIcon = icon;
  609. markAsDirty();
  610. }
  611. /**
  612. * Set the text of this object.
  613. *
  614. * @param text
  615. * Text for this object
  616. */
  617. public void setText(java.lang.String text) {
  618. if (text != null) {
  619. itsText = text;
  620. }
  621. markAsDirty();
  622. }
  623. /**
  624. * Remove the first occurrence of the item.
  625. *
  626. * @param item
  627. * The item to be removed
  628. */
  629. public void removeChild(MenuBar.MenuItem item) {
  630. if (item != null && itsChildren != null) {
  631. itsChildren.remove(item);
  632. if (itsChildren.isEmpty()) {
  633. itsChildren = null;
  634. }
  635. markAsDirty();
  636. }
  637. }
  638. /**
  639. * Empty the list of children items.
  640. */
  641. public void removeChildren() {
  642. if (itsChildren != null) {
  643. itsChildren.clear();
  644. itsChildren = null;
  645. markAsDirty();
  646. }
  647. }
  648. /**
  649. * Set the parent of this item. This is called by the addItem method.
  650. *
  651. * @param parent
  652. * The parent item
  653. */
  654. protected void setParent(MenuBar.MenuItem parent) {
  655. itsParent = parent;
  656. }
  657. public void setEnabled(boolean enabled) {
  658. this.enabled = enabled;
  659. markAsDirty();
  660. }
  661. public boolean isEnabled() {
  662. return enabled;
  663. }
  664. public void setVisible(boolean visible) {
  665. this.visible = visible;
  666. markAsDirty();
  667. }
  668. public boolean isVisible() {
  669. return visible;
  670. }
  671. private void setSeparator(boolean isSeparator) {
  672. this.isSeparator = isSeparator;
  673. markAsDirty();
  674. }
  675. public boolean isSeparator() {
  676. return isSeparator;
  677. }
  678. public void setStyleName(String styleName) {
  679. this.styleName = styleName;
  680. markAsDirty();
  681. }
  682. public String getStyleName() {
  683. return styleName;
  684. }
  685. /**
  686. * Sets the items's description. See {@link #getDescription()} for more
  687. * information on what the description is. This method will trigger a
  688. * {@link RepaintRequestEvent}.
  689. *
  690. * @param description
  691. * the new description string for the component.
  692. */
  693. public void setDescription(String description) {
  694. this.description = description;
  695. markAsDirty();
  696. }
  697. /**
  698. * <p>
  699. * Gets the items's description. The description can be used to briefly
  700. * describe the state of the item to the user. The description string
  701. * may contain certain XML tags:
  702. * </p>
  703. *
  704. * <p>
  705. * <table border=1>
  706. * <tr>
  707. * <td width=120><b>Tag</b></td>
  708. * <td width=120><b>Description</b></td>
  709. * <td width=120><b>Example</b></td>
  710. * </tr>
  711. * <tr>
  712. * <td>&lt;b></td>
  713. * <td>bold</td>
  714. * <td><b>bold text</b></td>
  715. * </tr>
  716. * <tr>
  717. * <td>&lt;i></td>
  718. * <td>italic</td>
  719. * <td><i>italic text</i></td>
  720. * </tr>
  721. * <tr>
  722. * <td>&lt;u></td>
  723. * <td>underlined</td>
  724. * <td><u>underlined text</u></td>
  725. * </tr>
  726. * <tr>
  727. * <td>&lt;br></td>
  728. * <td>linebreak</td>
  729. * <td>N/A</td>
  730. * </tr>
  731. * <tr>
  732. * <td>&lt;ul><br>
  733. * &lt;li>item1<br>
  734. * &lt;li>item1<br>
  735. * &lt;/ul></td>
  736. * <td>item list</td>
  737. * <td>
  738. * <ul>
  739. * <li>item1
  740. * <li>item2
  741. * </ul>
  742. * </td>
  743. * </tr>
  744. * </table>
  745. * </p>
  746. *
  747. * <p>
  748. * These tags may be nested.
  749. * </p>
  750. *
  751. * @return item's description <code>String</code>
  752. */
  753. public String getDescription() {
  754. return description;
  755. }
  756. /**
  757. * Gets the checkable state of the item - whether the item has checked
  758. * and unchecked states. If an item is checkable its checked state (as
  759. * returned by {@link #isChecked()}) is indicated in the UI.
  760. *
  761. * <p>
  762. * An item is not checkable by default.
  763. * </p>
  764. *
  765. * @return true if the item is checkable, false otherwise
  766. * @since 6.6.2
  767. */
  768. public boolean isCheckable() {
  769. return checkable;
  770. }
  771. /**
  772. * Sets the checkable state of the item. If an item is checkable its
  773. * checked state (as returned by {@link #isChecked()}) is indicated in
  774. * the UI.
  775. *
  776. * <p>
  777. * An item is not checkable by default.
  778. * </p>
  779. *
  780. * <p>
  781. * Items with sub items cannot be checkable.
  782. * </p>
  783. *
  784. * @param checkable
  785. * true if the item should be checkable, false otherwise
  786. * @throws IllegalStateException
  787. * If the item has children
  788. * @since 6.6.2
  789. */
  790. public void setCheckable(boolean checkable)
  791. throws IllegalStateException {
  792. if (hasChildren()) {
  793. throw new IllegalStateException(
  794. "A menu item with children cannot be checkable");
  795. }
  796. this.checkable = checkable;
  797. markAsDirty();
  798. }
  799. /**
  800. * Gets the checked state of the item (checked or unchecked). Only used
  801. * if the item is checkable (as indicated by {@link #isCheckable()}).
  802. * The checked state is indicated in the UI with the item, if the item
  803. * is checkable.
  804. *
  805. * <p>
  806. * An item is not checked by default.
  807. * </p>
  808. *
  809. * <p>
  810. * The CSS style corresponding to the checked state is "-checked".
  811. * </p>
  812. *
  813. * @return true if the item is checked, false otherwise
  814. * @since 6.6.2
  815. */
  816. public boolean isChecked() {
  817. return checked;
  818. }
  819. /**
  820. * Sets the checked state of the item. Only used if the item is
  821. * checkable (indicated by {@link #isCheckable()}). The checked state is
  822. * indicated in the UI with the item, if the item is checkable.
  823. *
  824. * <p>
  825. * An item is not checked by default.
  826. * </p>
  827. *
  828. * <p>
  829. * The CSS style corresponding to the checked state is "-checked".
  830. * </p>
  831. *
  832. * @return true if the item is checked, false otherwise
  833. * @since 6.6.2
  834. */
  835. public void setChecked(boolean checked) {
  836. this.checked = checked;
  837. markAsDirty();
  838. }
  839. }// class MenuItem
  840. @Override
  841. public void writeDesign(Element design, DesignContext designContext) {
  842. super.writeDesign(design, designContext);
  843. for (MenuItem item : getItems()) {
  844. design.appendChild(createMenuElement(item));
  845. }
  846. // in many cases there seems to be an empty more menu item
  847. if (getMoreMenuItem() != null
  848. && !getMoreMenuItem().getText().isEmpty()) {
  849. Element moreMenu = createMenuElement(getMoreMenuItem());
  850. moreMenu.attr("more", true);
  851. design.appendChild(moreMenu);
  852. }
  853. if (!htmlContentAllowed) {
  854. design.attr(DESIGN_ATTR_PLAIN_TEXT, true);
  855. }
  856. }
  857. protected Element createMenuElement(MenuItem item) {
  858. Element menuElement = new Element(Tag.valueOf("menu"), "");
  859. // Defaults
  860. MenuItem def = new MenuItem("", null, null);
  861. Attributes attr = menuElement.attributes();
  862. DesignAttributeHandler.writeAttribute("icon", attr, item.getIcon(),
  863. def.getIcon(), Resource.class);
  864. DesignAttributeHandler.writeAttribute("disabled", attr,
  865. !item.isEnabled(), !def.isEnabled(), boolean.class);
  866. DesignAttributeHandler.writeAttribute("visible", attr, item.isVisible(),
  867. def.isVisible(), boolean.class);
  868. DesignAttributeHandler.writeAttribute("separator", attr,
  869. item.isSeparator(), def.isSeparator(), boolean.class);
  870. DesignAttributeHandler.writeAttribute("checkable", attr,
  871. item.isCheckable(), def.isCheckable(), boolean.class);
  872. DesignAttributeHandler.writeAttribute("checked", attr, item.isChecked(),
  873. def.isChecked(), boolean.class);
  874. DesignAttributeHandler.writeAttribute("description", attr,
  875. item.getDescription(), def.getDescription(), String.class);
  876. DesignAttributeHandler.writeAttribute("style-name", attr,
  877. item.getStyleName(), def.getStyleName(), String.class);
  878. menuElement.append(item.getText());
  879. if (item.hasChildren()) {
  880. for (MenuItem subMenu : item.getChildren()) {
  881. menuElement.appendChild(createMenuElement(subMenu));
  882. }
  883. }
  884. return menuElement;
  885. }
  886. protected MenuItem readMenuElement(Element menuElement) {
  887. Resource icon = null;
  888. if (menuElement.hasAttr("icon")) {
  889. icon = DesignAttributeHandler.getFormatter()
  890. .parse(menuElement.attr("icon"), Resource.class);
  891. }
  892. String caption = "";
  893. List<Element> subMenus = new ArrayList<Element>();
  894. for (Node node : menuElement.childNodes()) {
  895. if (node instanceof Element
  896. && ((Element) node).tagName().equals("menu")) {
  897. subMenus.add((Element) node);
  898. } else {
  899. caption += node.toString();
  900. }
  901. }
  902. MenuItem menu = new MenuItem(caption.trim(), icon, null);
  903. Attributes attr = menuElement.attributes();
  904. if (menuElement.hasAttr("icon")) {
  905. menu.setIcon(DesignAttributeHandler.readAttribute("icon", attr,
  906. Resource.class));
  907. }
  908. if (menuElement.hasAttr("disabled")) {
  909. menu.setEnabled(!DesignAttributeHandler.readAttribute("disabled",
  910. attr, boolean.class));
  911. }
  912. if (menuElement.hasAttr("visible")) {
  913. menu.setVisible(DesignAttributeHandler.readAttribute("visible",
  914. attr, boolean.class));
  915. }
  916. if (menuElement.hasAttr("separator")) {
  917. menu.setSeparator(DesignAttributeHandler.readAttribute("separator",
  918. attr, boolean.class));
  919. }
  920. if (menuElement.hasAttr("checkable")) {
  921. menu.setCheckable(DesignAttributeHandler.readAttribute("checkable",
  922. attr, boolean.class));
  923. }
  924. if (menuElement.hasAttr("checked")) {
  925. menu.setChecked(DesignAttributeHandler.readAttribute("checked",
  926. attr, boolean.class));
  927. }
  928. if (menuElement.hasAttr("description")) {
  929. menu.setDescription(DesignAttributeHandler
  930. .readAttribute("description", attr, String.class));
  931. }
  932. if (menuElement.hasAttr("style-name")) {
  933. menu.setStyleName(DesignAttributeHandler.readAttribute("style-name",
  934. attr, String.class));
  935. }
  936. if (!subMenus.isEmpty()) {
  937. menu.itsChildren = new ArrayList<MenuItem>();
  938. }
  939. for (Element subMenu : subMenus) {
  940. MenuItem newItem = readMenuElement(subMenu);
  941. newItem.setParent(menu);
  942. menu.itsChildren.add(newItem);
  943. }
  944. return menu;
  945. }
  946. @Override
  947. public void readDesign(Element design, DesignContext designContext) {
  948. super.readDesign(design, designContext);
  949. for (Element itemElement : design.children()) {
  950. if (itemElement.tagName().equals("menu")) {
  951. MenuItem menuItem = readMenuElement(itemElement);
  952. if (itemElement.hasAttr("more")) {
  953. setMoreMenuItem(menuItem);
  954. } else {
  955. menuItems.add(menuItem);
  956. }
  957. }
  958. }
  959. setHtmlContentAllowed(!design.hasAttr(DESIGN_ATTR_PLAIN_TEXT));
  960. }
  961. @Override
  962. protected Collection<String> getCustomAttributes() {
  963. Collection<String> result = super.getCustomAttributes();
  964. result.add(DESIGN_ATTR_PLAIN_TEXT);
  965. result.add("html-content-allowed");
  966. return result;
  967. }
  968. /**
  969. * Returns the delay before executing update logic inside
  970. * {@link com.vaadin.client.ui.menubar.MenuBarConnector#updateFromUIDL(UIDL, ApplicationConnection)}
  971. * after mouseDownEvent
  972. *
  973. * @since 7.7.16
  974. */
  975. public int getDelayMs() {
  976. return getState(false).delayMs;
  977. }
  978. /**
  979. * Set the delay before executing update logic inside
  980. * {@link com.vaadin.client.ui.menubar.MenuBarConnector#updateFromUIDL(UIDL, ApplicationConnection)}
  981. * after mouseDownEvent
  982. *
  983. * @since 7.7.16
  984. */
  985. public void setDelayMs(int delayMs) {
  986. getState().delayMs = delayMs;
  987. }
  988. }// class MenuBar