--- /dev/null
+package com.itmill.toolkit.terminal.gwt.client.ui;
+
+import java.util.Iterator;
+import java.util.Stack;
+
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.ui.MenuBar;
+import com.google.gwt.user.client.ui.MenuItem;
+import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
+import com.itmill.toolkit.terminal.gwt.client.Paintable;
+import com.itmill.toolkit.terminal.gwt.client.UIDL;
+
+public class IMenuBar extends MenuBar implements Paintable {
+
+ /** Set the CSS class name to allow styling. */
+ public static final String CLASSNAME = "i-menubar";
+
+ /** Component identifier in UIDL communications. */
+ protected String uidlId;
+
+ /** Reference to the server connection object. */
+ protected ApplicationConnection client;
+
+ /** A host reference for the Command objects */
+ protected final IMenuBar hostReference = this;
+
+ /**
+ * The constructor should first call super() to initialize the component and
+ * then handle any initialization relevant to IT Mill Toolkit.
+ */
+ public IMenuBar() {
+ // The superclass has a lot of relevant initialization
+ super();
+
+ // This method call of the Paintable interface sets the component
+ // style name in DOM tree
+ setStyleName(CLASSNAME);
+ }
+
+ /**
+ * This method must be implemented to update the client-side component from
+ * UIDL data received from server.
+ *
+ * This method is called when the page is loaded for the first time, and
+ * every time UI changes in the component are received from the server.
+ */
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ // This call should be made first. Ensure correct implementation,
+ // and let the containing layout manage caption, etc.
+ if (client.updateComponent(this, uidl, true)) {
+ return;
+ }
+
+ // Save reference to server connection object to be able to send
+ // user interaction later
+ this.client = client;
+
+ // Save the UIDL identifier for the component
+ uidlId = uidl.getId();
+
+ // Empty the menu every time it receives new information
+ if (!this.getItems().isEmpty()) {
+ this.clearItems();
+ }
+
+ /* Get tree received from server and actualize it in the GWT-MenuBar */
+
+ // For GWT 1.5
+ // this.setAnimationEnabled(uidl.getBooleanAttribute("animationEnabled"));
+ UIDL items = uidl.getChildUIDL(1);
+ Iterator itr = items.getChildIterator();
+ Stack iteratorStack = new Stack();
+ Stack menuStack = new Stack();
+ MenuBar currentMenu = this;
+
+ // Construct an empty command to be used when the item has no command
+ // associated
+ Command emptyCommand = new Command() {
+ public void execute() {
+ }
+ };
+
+ while (itr.hasNext()) {
+ UIDL item = (UIDL) itr.next();
+ MenuItem menuItem = null; // For receiving the item
+
+ String itemText = item.getStringAttribute("text");
+ final int itemId = item.getIntAttribute("id");
+
+ boolean itemHasCommand = item.getBooleanAttribute("command");
+
+ // Construct html from the text and the optional icon
+ if (!item.hasAttribute("icon")) {
+ itemText = "<p>" + itemText + "</p>";
+ } else {
+ itemText = "<p>"
+ + "<img src=\""
+ + client.translateToolkitUri(item
+ .getStringAttribute("icon")) + "\"</img>"
+ + itemText + "</p>";
+ }
+
+ // Check if we need to attach a command to this item
+ if (itemHasCommand) {
+ // Construct a command that fires onMenuClick(int) with the
+ // item's id-number
+ Command normalCommand = new Command() {
+ public void execute() {
+ hostReference.onMenuClick(itemId);
+ }
+ };
+
+ menuItem = currentMenu.addItem(itemText, true, normalCommand);
+
+ } else {
+ menuItem = currentMenu.addItem(itemText, true, emptyCommand);
+ }
+
+ if (item.getChildCount() > 0) {
+ menuStack.push(currentMenu);
+ iteratorStack.push(itr);
+ itr = item.getChildIterator();
+ currentMenu = new MenuBar(true);
+ menuItem.setSubMenu(currentMenu);
+ }
+
+ if (!itr.hasNext() && !iteratorStack.empty()) {
+ itr = (Iterator) iteratorStack.pop();
+ currentMenu = (MenuBar) menuStack.pop();
+ }
+ }// while
+
+ }// updateFromUIDL
+
+ /**
+ * This is called by the items in the menu and it communicates the
+ * information to the server
+ *
+ * @param clickedItemId
+ * id of the item that was clicked
+ */
+ public void onMenuClick(int clickedItemId) {
+ // Updating the state to the server can not be done before
+ // the server connection is known, i.e., before updateFromUIDL()
+ // has been called.
+ if (uidlId != null && client != null) {
+ // Communicate the user interaction parameters to server. This call
+ // will initiate an AJAX request to the server.
+ client.updateVariable(uidlId, "clickedId", clickedItemId, true);
+ }
+ }
+}// class IMenuBar
--- /dev/null
+package com.itmill.toolkit.tests.tickets;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.itmill.toolkit.Application;
+import com.itmill.toolkit.terminal.ThemeResource;
+import com.itmill.toolkit.ui.MenuBar;
+import com.itmill.toolkit.ui.Window;
+import com.itmill.toolkit.ui.MenuBar.Command;
+import com.itmill.toolkit.ui.MenuBar.Item;
+
+public class Ticket1598 extends Application {
+
+ Window main = new Window("MenuBar test");
+
+ final MenuBar menuBar = new MenuBar();
+
+ public void init() {
+ setMainWindow(main);
+ setTheme("default");
+
+ List itemList = new ArrayList();
+ // Populate the menubar
+ for (int i = 0; i < 5; i++) {
+ itemList.add(menuBar.addItem(new String("Menu " + i), null, null));
+ }
+
+ Item first = (Item) itemList.get(0);
+
+ for (int i = 0; i < 5; i++) {
+ first.addItem(new String("Submenu item" + i), null, new Command() {
+
+ public void menuSelected(Item selected) {
+ main.showNotification("Action " + selected.getText());
+ }
+ });
+ }
+
+ Item firstSecond = (Item) first.getChildren().get(1);
+
+ for (int i = 0; i < 3; i++) {
+ firstSecond.addItem(new String("Subsubmenu item" + i), null,
+ new Command() {
+
+ public void menuSelected(Item selected) {
+ main.showNotification("Action "
+ + selected.getText());
+ }
+ });
+ }
+
+ Item second = (Item) menuBar.getItems().get(1);
+
+ for (int i = 0; i < 5; i++) {
+ second.addItem(new String("Second submenu item" + i), null,
+ new Command() {
+
+ public void menuSelected(Item selected) {
+ main.showNotification("Action "
+ + selected.getText());
+ }
+ });
+ }
+
+ Item third = (Item) menuBar.getItems().get(2);
+ third.setIcon(new ThemeResource("icons/16/document.png"));
+
+ for (int i = 2; i <= 3; i++)
+ ((Item) menuBar.getItems().get(i)).setCommand(new Command() {
+
+ public void menuSelected(Item selectedItem) {
+ main.showNotification("Action " + selectedItem.getText());
+ }
+ });
+
+ final Item fourth = (Item) menuBar.getItems().get(3);
+ fourth.setText("Toggle animation");
+
+ fourth.setCommand(new Command() {
+ public void menuSelected(Item selected) {
+ menuBar
+ .addItemBefore("No animation yet...", null, null,
+ fourth);
+ // menuBar.setAnimation(!menuBar.hasAnimation());
+ }
+ });
+
+ final Item last = (Item) menuBar.getItems().get(menuBar.getSize() - 1);
+ last.setText("Remove me!");
+
+ // A command for removing the selected menuitem
+ Command removeCommand = new Command() {
+
+ public void menuSelected(Item selected) {
+ Item parent = selected.getParent();
+ if (parent != null) {
+ parent.removeChild(selected);
+ } else {
+ menuBar.removeItem(selected);
+ }
+ }
+ };
+
+ last.setCommand(removeCommand);
+
+ main.addComponent(menuBar);
+
+ }
+}
\ No newline at end of file
--- /dev/null
+package com.itmill.toolkit.ui;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import com.itmill.toolkit.terminal.PaintException;
+import com.itmill.toolkit.terminal.PaintTarget;
+import com.itmill.toolkit.terminal.Resource;
+
+/**
+ * The top-level menu class. This can contain MenuItems, which in turn can
+ * contain Submenus and MenuCommands.
+ *
+ */
+public class MenuBar extends AbstractComponent {
+
+ // Items of the top-level menu
+ private List menuItems;
+
+ // Number of items in this menu
+ private static int numberOfItems = 0;
+
+ private boolean animationEnabled;
+
+ /** Tag is the UIDL element name for client-server communications. */
+ public java.lang.String getTag() {
+ return "menubar";
+ }
+
+ /** Paint (serialize) the component for the client. */
+ public void paintContent(PaintTarget target) throws PaintException {
+
+ // Superclass writes any common attributes in the paint target.
+ super.paintContent(target);
+
+ // Stack for list iterators
+ Stack iteratorStack = new Stack();
+
+ target.startTag("options");
+ target.addAttribute("animationEnabled", animationEnabled);
+ target.endTag("options");
+ target.startTag("items");
+
+ Iterator itr = menuItems.iterator();
+
+ // This generates the tree from the contents of the menu
+ while (itr.hasNext()) {
+
+ Item item = (Item) itr.next();
+
+ target.startTag("item");
+
+ target.addAttribute("text", item.getText());
+ target.addAttribute("id", item.getId());
+
+ Command command = item.getCommand();
+ if (command != null) {
+ target.addAttribute("command", true);
+ } else {
+ target.addAttribute("command", false);
+ }
+
+ Resource icon = item.getIcon();
+ if (icon != null) {
+ target.addAttribute("icon", icon);
+ }
+
+ if (item.hasChildren()) {
+ iteratorStack.push(itr); // For later use
+
+ // Go through the children
+ itr = item.getChildren().iterator();
+ } else {
+ target.endTag("item"); // Item had no children, end description
+ }
+
+ if (!itr.hasNext() && !iteratorStack.empty()) { // The end submenu
+ itr = (Iterator) iteratorStack.pop();
+ target.endTag("item");
+ }
+
+ }
+
+ target.endTag("items");
+ }
+
+ /** Deserialize changes received from client. */
+ public void changeVariables(Object source, Map variables) {
+ Stack items = new Stack();
+ boolean found = false;
+
+ if (variables.containsKey("clickedId")) {
+
+ Integer clickedId = (Integer) variables.get("clickedId");
+ Iterator itr = this.getItems().iterator();
+ while (itr.hasNext()) {
+ items.push((Item) itr.next());
+ }
+
+ Item tmpItem = null;
+
+ // Go through all the items in the menu
+ while (!found && !items.empty()) {
+ tmpItem = (Item) items.pop();
+ found = (clickedId.intValue() == tmpItem.getId());
+
+ if (tmpItem.hasChildren()) {
+ itr = tmpItem.getChildren().iterator();
+ while (itr.hasNext()) {
+ items.push(itr.next());
+ }
+ }
+
+ }
+ // If we got the clicked item, launch the command.
+ if (found) {
+ tmpItem.getCommand().menuSelected(tmpItem);
+ }// while
+ }// if
+ }// changeVariables
+
+ /**
+ * Constructs an empty, horizontal menu
+ */
+ public MenuBar() {
+ menuItems = new ArrayList();
+ animationEnabled = false;
+ }
+
+ /**
+ * Add a new item to the menubar. Icon and command can be null, but a
+ * caption must be given.
+ *
+ * @param caption
+ * the text for the menu item
+ * @param icon
+ * the icon for the menu item
+ * @param command
+ * the command for the menu item
+ * @throws IllegalArgumentException
+ */
+ public MenuBar.Item addItem(String caption, Resource icon,
+ MenuBar.Command command) {
+ if (caption == null) {
+ throw new IllegalArgumentException("caption cannot be null");
+ }
+ Item newItem = new Item(caption, icon, command);
+ menuItems.add(newItem);
+ requestRepaint();
+
+ return newItem;
+
+ }
+
+ /**
+ * Add an item before some item. If the given item does not exist the item
+ * is added at the end of the menu. Icon and command can be null, but a
+ * caption must be given.
+ *
+ * @param caption
+ * the text for the menu item
+ * @param icon
+ * the icon for the menu item
+ * @param command
+ * the command for the menu item
+ * @param itemToAddBefore
+ * the item that will be after the new item
+ * @throws IllegalArgumentException
+ */
+ public MenuBar.Item addItemBefore(String caption, Resource icon,
+ MenuBar.Command command, MenuBar.Item itemToAddBefore) {
+ if (caption == null) {
+ throw new IllegalArgumentException("caption cannot be null");
+ }
+
+ Item newItem = new Item(caption, icon, command);
+ if (menuItems.contains(itemToAddBefore)) {
+ int index = menuItems.indexOf(itemToAddBefore);
+ menuItems.add(index, newItem);
+
+ } else {
+ menuItems.add(newItem);
+ }
+
+ requestRepaint();
+
+ return newItem;
+ }
+
+ /**
+ * Returns a list with all the MenuItem objects in the menubar
+ *
+ * @return a list containing the MenuItem objects in the menubar
+ */
+ public java.util.List getItems() {
+ return menuItems;
+ }
+
+ /**
+ * Remove first occurrence the specified item from the main menu
+ *
+ * @param item
+ * The item to be removed
+ */
+ public void removeItem(MenuBar.Item item) {
+ if (item != null) {
+ menuItems.remove(item);
+ }
+ requestRepaint();
+ }
+
+ /**
+ * Empty the menubar
+ */
+ public void removeItems() {
+ menuItems.clear();
+ requestRepaint();
+ }
+
+ /**
+ * Returns the size of the menu.
+ *
+ * @return The size of the menu
+ */
+ public int getSize() {
+ return menuItems.size();
+ }
+
+ /**
+ * Enable or disable animated menubar appearance. Animation is disabled by
+ * default.
+ *
+ * @param hasAnimation
+ */
+ public void setAnimation(boolean animation) {
+ animationEnabled = animation;
+ }
+
+ /**
+ * Returns true if the animation is enabled. Animation of this class is
+ * disabled by default.
+ *
+ * @return true if the animation is enabled
+ *
+ */
+ public boolean hasAnimation() {
+ return animationEnabled;
+ }
+
+ /**
+ * This interface contains the layer for menu commands of the MenuBar class .
+ * It's method will fire when the user clicks on the containing MenuItem.
+ * The selected item is given as an argument.
+ */
+ public interface Command {
+ public void menuSelected(MenuBar.Item selectedItem);
+ }
+
+ /**
+ * A composite class for menu items and submenus. You can set commands to be
+ * fired on user click by implementing the MenuBar.Command interface.
+ *
+ */
+ public class Item {
+
+ /** Private members * */
+ private int itsId;
+ private Command itsCommand;
+ private String itsText;
+ private List itsChildren;
+ private Resource itsIcon;
+ private Item itsParent;
+
+ /**
+ * Constructs a new menu item that can optionally have an icon and a
+ * command associated with it. Icon and command can be null, but a
+ * caption must be given.
+ *
+ * @param text
+ * The text associated with the command
+ * @param command
+ * The command to be fired
+ * @throws IllegalArgumentException
+ */
+ public Item(String caption, Resource icon, MenuBar.Command command) {
+ if (caption == null) {
+ throw new IllegalArgumentException("caption cannot be null");
+ }
+ itsId = ++numberOfItems;
+ itsText = caption;
+ itsIcon = icon;
+ itsCommand = command;
+ }
+
+ /**
+ * Checks if the item has children (if it is a submenu).
+ *
+ * @return True if this item has children
+ */
+ public boolean hasChildren() {
+ return itsChildren != null;
+ }
+
+ /**
+ * Add a new item inside this item, thus creating a submenu. Icon and
+ * command can be null, but a caption must be given.
+ *
+ * @param caption
+ * the text for the menu item
+ * @param icon
+ * the icon for the menu item
+ * @param command
+ * the command for the menu item
+ */
+ public MenuBar.Item addItem(String caption, Resource icon,
+ MenuBar.Command command) {
+ if (caption == null) {
+ throw new IllegalArgumentException("caption cannot be null");
+ }
+
+ if (itsChildren == null) {
+ itsChildren = new ArrayList();
+ }
+
+ Item newItem = new Item(caption, icon, command);
+
+ // The only place where the parent is set
+ newItem.setParent(this);
+ itsChildren.add(newItem);
+
+ requestRepaint();
+
+ return newItem;
+ }
+
+ /**
+ * Add an item before some item. If the given item does not exist the
+ * item is added at the end of the menu. Icon and command can be null,
+ * but a caption must be given.
+ *
+ * @param caption
+ * the text for the menu item
+ * @param icon
+ * the icon for the menu item
+ * @param command
+ * the command for the menu item
+ * @param itemToAddBefore
+ * the item that will be after the new item
+ *
+ */
+ public MenuBar.Item addItemBefore(String caption, Resource icon,
+ MenuBar.Command command, MenuBar.Item itemToAddBefore) {
+
+ Item newItem = null;
+
+ if (hasChildren() && itsChildren.contains(itemToAddBefore)) {
+ int index = itsChildren.indexOf(itemToAddBefore);
+ newItem = new Item(caption, icon, command);
+ newItem.setParent(this);
+ itsChildren.add(index, newItem);
+
+ } else {
+ newItem = addItem(caption, icon, command);
+ }
+
+ requestRepaint();
+
+ return newItem;
+ }
+
+ /**
+ * For the associated command.
+ *
+ * @return The associated command, or null if there is none
+ */
+ public Command getCommand() {
+ return itsCommand;
+ }
+
+ /**
+ * Gets the objects icon.
+ *
+ * @return The icon of the item, null if the item doesn't have an icon
+ */
+ public Resource getIcon() {
+ return itsIcon;
+ }
+
+ /**
+ * For the containing item. This will return null if the item is in the
+ * top-level menubar.
+ *
+ * @return The containing MenuBar.MenuItem, or null if there is none
+ */
+ public MenuBar.Item getParent() {
+ return itsParent;
+ }
+
+ /**
+ * This will return the children of this item or null if there are none.
+ *
+ * @return List of children items, or null if there are none
+ */
+ public java.util.List getChildren() {
+ return itsChildren;
+ }
+
+ /**
+ * Gets the objects text
+ *
+ * @return The text
+ */
+ public java.lang.String getText() {
+ return itsText;
+ }
+
+ /**
+ * Returns the number of children.
+ *
+ * @return The number of child items
+ */
+ public int getSize() {
+ return itsChildren.size();
+ }
+
+ /**
+ * Get the unique identifier for this item.
+ *
+ * @return The id of this item
+ */
+ public int getId() {
+ return itsId;
+ }
+
+ /**
+ * Set the command for this item. Set null to remove.
+ *
+ * @param command
+ * The MenuCommand of this item
+ */
+ public void setCommand(MenuBar.Command command) {
+ itsCommand = command;
+ }
+
+ /**
+ * Sets the icon. Set null to remove.
+ *
+ * @param icon
+ * The icon for this item
+ */
+ public void setIcon(Resource icon) {
+ itsIcon = icon;
+ requestRepaint();
+ }
+
+ /**
+ * Set the text of this object.
+ *
+ * @param text
+ * Text for this object
+ */
+ public void setText(java.lang.String text) {
+ if (text != null) {
+ itsText = text;
+ }
+ requestRepaint();
+ }
+
+ /**
+ * Remove the first occurrence of the item.
+ *
+ * @param item
+ * The item to be removed
+ */
+ public void removeChild(MenuBar.Item item) {
+ if (item != null && itsChildren != null) {
+ itsChildren.remove(item);
+ if (itsChildren.isEmpty()) {
+ itsChildren = null;
+ }
+ }
+ requestRepaint();
+ }
+
+ /**
+ * Empty the list of children items.
+ */
+ public void removeChildren() {
+ if (itsChildren != null) {
+ itsChildren.clear();
+ itsChildren = null;
+ }
+ requestRepaint();
+ }
+
+ /**
+ * Set the parent of this item. This is called by the addItem method.
+ *
+ * @param parent
+ * The parent item
+ */
+ protected void setParent(MenuBar.Item parent) {
+ itsParent = parent;
+ }
+
+ }// class MenuItem
+
+}// class MenuBar