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

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