Make the MenuBar work more like standard MenuBars, and adds the following features:
1. on a mouse click for a submenu the focus remains on the clicked item rather than the first item of the submenu
2. a menu item may have both a command and a submenu associated with it
3. clicking a menu item with an open submenu closes it

Patch by: svachalek
Review by: jlabanca
Issue: 2459, 2718



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3512 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/MenuBar.java b/user/src/com/google/gwt/user/client/ui/MenuBar.java
index 6e8a642..2ed2c17 100644
--- a/user/src/com/google/gwt/user/client/ui/MenuBar.java
+++ b/user/src/com/google/gwt/user/client/ui/MenuBar.java
@@ -515,107 +515,44 @@
    * if the item's command should be fired, <code>false</code> otherwise.
    */
   void doItemAction(final MenuItem item, boolean fireCommand) {
-    // If the given item is already showing its menu, we're done.
-    if ((shownChildMenu != null) && (item.getSubMenu() == shownChildMenu)) {
-      return;
-    }
-
-    // If another item is showing its menu, then hide it.
-    if (shownChildMenu != null) {
-      shownChildMenu.onHide();
-      popup.hide();
-    }
-
-    // If the item has no popup, optionally fire its command.
-    if ((item != null) && (item.getSubMenu() == null)) {
-      if (fireCommand) {
-        // Close this menu and all of its parents.
-        closeAllParents();
-
-        // Fire the item's command.
-        Command cmd = item.getCommand();
-        if (cmd != null) {
-          DeferredCommand.addCommand(cmd);
-        }
-      }
-      return;
-    }
-
     // Ensure that the item is selected.
     selectItem(item);
 
-    if (item == null) {
-      return;
-    }
-    
-    // Create a new popup for this item, and position it next to
-    // the item (below if this is a horizontal menu bar, to the
-    // right if it's a vertical bar).
-    popup = new DecoratedPopupPanel(true, false, "menuPopup") {
-      {
-        setWidget(item.getSubMenu());
-        item.getSubMenu().onShow();
-      }
-
-      @Override
-      public boolean onEventPreview(Event event) {
-        // Hook the popup panel's event preview. We use this to keep it from
-        // auto-hiding when the parent menu is clicked.
-        switch (DOM.eventGetType(event)) {
-          case Event.ONCLICK:
-            // If the event target is part of the parent menu, suppress the
-            // event altogether.
-            Element target = DOM.eventGetTarget(event);
-            Element parentMenuElement = item.getParentMenu().getElement();
-            if (DOM.isOrHasChild(parentMenuElement, target)) {
-              return false;
-            }
-            break;
-        }
-
-        return super.onEventPreview(event);
-      }
-    };
-    popup.setAnimationType(AnimationType.ONE_WAY_CORNER);
-    popup.setAnimationEnabled(isAnimationEnabled);
-    popup.setStyleName(STYLENAME_DEFAULT + "Popup");
-    String primaryStyleName = getStylePrimaryName();
-    if (!STYLENAME_DEFAULT.equals(primaryStyleName)) {
-      popup.addStyleName(primaryStyleName + "Popup");
-    }
-    popup.addPopupListener(this);
-
-    shownChildMenu = item.getSubMenu();
-    item.getSubMenu().parentMenu = this;
-
-    // Show the popup, ensuring that the menubar's event preview remains on top
-    // of the popup's.
-    popup.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
-          
-      public void setPosition(int offsetWidth, int offsetHeight) {
+    if (item != null) {
+      // if the command should be fired and the item has one, fire it
+      if (fireCommand && item.getCommand() != null) {
+        // Close this menu and all of its parents.
+        closeAllParents();
+  
+        // Fire the item's command.
+        Command cmd = item.getCommand();
+        DeferredCommand.addCommand(cmd);
         
-        // depending on the bidi direction position a menu on the left or right
-        // of its base item
-        if (LocaleInfo.getCurrentLocale().isRTL()) {                                   
-          if (vertical) {
-            popup.setPopupPosition(MenuBar.this.getAbsoluteLeft() - offsetWidth + 1,
-                item.getAbsoluteTop());
-          } else {
-            popup.setPopupPosition(item.getAbsoluteLeft() + item.getOffsetWidth() - offsetWidth,
-                MenuBar.this.getAbsoluteTop() + MenuBar.this.getOffsetHeight() - 1);
-          }
-        } else {
-          if (vertical) {
-            popup.setPopupPosition(MenuBar.this.getAbsoluteLeft() + MenuBar.this.getOffsetWidth() - 1,
-                item.getAbsoluteTop());
-          } else {
-            popup.setPopupPosition(item.getAbsoluteLeft(), 
-                MenuBar.this.getAbsoluteTop() + MenuBar.this.getOffsetHeight() - 1);
-          }
+        // hide any open submenus of this item
+        if (shownChildMenu != null) {
+          shownChildMenu.onHide();
+          popup.hide();
+          shownChildMenu = null;
+          selectItem(null);
+        }
+      } else if (item.getSubMenu() != null) {
+        if (shownChildMenu == null) {
+          // open this submenu
+          openPopup(item);
+        } else if (item.getSubMenu() != shownChildMenu) {
+          // close the other submenu and open this one
+          shownChildMenu.onHide();
+          popup.hide();
+          openPopup(item);
+        } else if (fireCommand) {
+          // close this submenu
+          shownChildMenu.onHide();
+          popup.hide();
+          shownChildMenu = null;
+          selectItem(null);
         }
       }
-    });
-    shownChildMenu.focus();
+    }
   }
 
   void itemOver(MenuItem item) {
@@ -629,6 +566,7 @@
 
     // Style the item selected when the mouse enters.
     selectItem(item);
+    focus();
 
     // If child menus are being shown, or this menu is itself
     // a child menu, automatically show an item's child menu
@@ -824,8 +762,13 @@
     if (vertical) {
       selectNextItem();
     } else {
-      if (selectedItem.getSubMenu() != null) {
-        doItemAction(selectedItem, false);
+      if (selectedItem.getSubMenu() != null 
+          && !selectedItem.getSubMenu().getItems().isEmpty()
+          && (shownChildMenu == null || shownChildMenu.getSelectedItem() == null)) {
+        if (shownChildMenu == null) {
+          doItemAction(selectedItem, false);
+        }
+        selectedItem.getSubMenu().focus();
       } else if (parentMenu != null) {
         if (parentMenu.vertical) {
           parentMenu.selectNextItem();
@@ -844,8 +787,13 @@
     if (!vertical) {
       selectNextItem();
     } else {
-      if ((shownChildMenu == null) && (selectedItem.getSubMenu() != null)) {
-        doItemAction(selectedItem, false);
+      if (selectedItem.getSubMenu() != null 
+          && !selectedItem.getSubMenu().getItems().isEmpty()
+          && (shownChildMenu == null || shownChildMenu.getSelectedItem() == null)) {
+        if (shownChildMenu == null) {
+          doItemAction(selectedItem, false);
+        }
+        selectedItem.getSubMenu().focus();
       } else if (parentMenu != null) {
         if (!parentMenu.vertical) {
           parentMenu.selectNextItem();
@@ -902,10 +850,81 @@
    * This method is called when a menu bar is shown.
    */
   private void onShow() {
-    // Select the first item when a menu is shown.
-    if (items.size() > 0) {
-      selectItem(items.get(0));
+    // clear the selection; a keyboard user can cursor down to the first item
+    selectItem(null);
+  }
+
+  private void openPopup(final MenuItem item) {
+    // Create a new popup for this item, and position it next to
+    // the item (below if this is a horizontal menu bar, to the
+    // right if it's a vertical bar).
+    popup = new DecoratedPopupPanel(true, false, "menuPopup") {
+      {
+        setWidget(item.getSubMenu());
+        item.getSubMenu().onShow();
+      }
+  
+      @Override
+      public boolean onEventPreview(Event event) {
+        // Hook the popup panel's event preview. We use this to keep it from
+        // auto-hiding when the parent menu is clicked.
+        switch (DOM.eventGetType(event)) {
+          case Event.ONMOUSEDOWN:
+            // If the event target is part of the parent menu, suppress the
+            // event altogether.
+            Element target = DOM.eventGetTarget(event);
+            Element parentMenuElement = item.getParentMenu().getElement();
+            if (DOM.isOrHasChild(parentMenuElement, target)) {
+              return false;
+            }
+            boolean cancel = super.onEventPreview(event);
+            if (cancel) {
+              selectItem(null);
+            }
+            return cancel;
+        }
+        return super.onEventPreview(event);
+      }
+    };
+    popup.setAnimationType(AnimationType.ONE_WAY_CORNER);
+    popup.setAnimationEnabled(isAnimationEnabled);
+    popup.setStyleName(STYLENAME_DEFAULT + "Popup");
+    String primaryStyleName = getStylePrimaryName();
+    if (!STYLENAME_DEFAULT.equals(primaryStyleName)) {
+      popup.addStyleName(primaryStyleName + "Popup");
     }
+    popup.addPopupListener(this);
+  
+    shownChildMenu = item.getSubMenu();
+    item.getSubMenu().parentMenu = this;
+  
+    // Show the popup, ensuring that the menubar's event preview remains on top
+    // of the popup's.
+    popup.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
+          
+      public void setPosition(int offsetWidth, int offsetHeight) {
+        
+        // depending on the bidi direction position a menu on the left or right
+        // of its base item
+        if (LocaleInfo.getCurrentLocale().isRTL()) {                                   
+          if (vertical) {
+            popup.setPopupPosition(MenuBar.this.getAbsoluteLeft() - offsetWidth + 1,
+                item.getAbsoluteTop());
+          } else {
+            popup.setPopupPosition(item.getAbsoluteLeft() + item.getOffsetWidth() - offsetWidth,
+                MenuBar.this.getAbsoluteTop() + MenuBar.this.getOffsetHeight() - 1);
+          }
+        } else {
+          if (vertical) {
+            popup.setPopupPosition(MenuBar.this.getAbsoluteLeft() + MenuBar.this.getOffsetWidth() - 1,
+                item.getAbsoluteTop());
+          } else {
+            popup.setPopupPosition(item.getAbsoluteLeft(), 
+                MenuBar.this.getAbsoluteTop() + MenuBar.this.getOffsetHeight() - 1);
+          }
+        }
+      }
+    });
   }
 
   /**