Replace the top menu bar with a tab panel and links

This removes the need for an implicit FocusPanel from the GWT library,
but still allows the user to implement keyboard navigation.

Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gerrit/client/Gerrit.java b/src/main/java/com/google/gerrit/client/Gerrit.java
index 0608d6d..28c3c4c 100644
--- a/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -33,10 +33,13 @@
 import com.google.gwt.user.client.History;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
 import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.MenuBar;
-import com.google.gwt.user.client.ui.MenuItem;
+import com.google.gwt.user.client.ui.InlineLabel;
 import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.TabPanel;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
 import com.google.gwtexpui.user.client.UserAgent;
 import com.google.gwtexpui.user.client.ViewSite;
 import com.google.gwtjsonrpc.client.JsonUtil;
@@ -64,7 +67,8 @@
   private static final ArrayList<SignedInListener> signedInListeners =
       new ArrayList<SignedInListener>();
 
-  private static LinkMenuBar menuBar;
+  private static TabPanel menuLeft;
+  private static LinkMenuBar menuRight;
   private static RootPanel siteHeader;
   private static RootPanel siteFooter;
   private static ViewSite<Screen> body;
@@ -159,9 +163,21 @@
     initHistoryHooks();
     populateBottomMenu();
 
-    final RootPanel topMenu = RootPanel.get("gerrit_topmenu");
-    menuBar = new LinkMenuBar();
-    topMenu.add(menuBar);
+    final RootPanel menuArea = RootPanel.get("gerrit_topmenu");
+    final Grid menuLine = new Grid(1, 3);
+    menuLeft = new TabPanel();
+    menuRight = new LinkMenuBar();
+    menuLeft.setStyleName("gerrit-topmenu-menuLeft");
+    menuRight.addStyleName("gerrit-topmenu-menuRight");
+    menuLine.setStyleName("gerrit-topmenu");
+    menuArea.add(menuLine);
+    menuLine.setWidget(0, 0, menuLeft);
+    menuLine.setWidget(0, 1, new FlowPanel());
+    menuLine.setWidget(0, 2, menuRight);
+    final CellFormatter fmt = menuLine.getCellFormatter();
+    fmt.setStyleName(0, 0, "gerrit-topmenu-TDmenu");
+    fmt.setStyleName(0, 1, "gerrit-topmenu-TDglue");
+    fmt.setStyleName(0, 2, "gerrit-topmenu-TDmenu");
 
     siteHeader = RootPanel.get("gerrit_header");
     siteFooter = RootPanel.get("gerrit_footer");
@@ -175,7 +191,7 @@
     };
     RootPanel.get("gerrit_body").add(body);
 
-    JsonUtil.addRpcStatusListener(new RpcStatus(topMenu));
+    JsonUtil.addRpcStatusListener(new RpcStatus(menuArea));
     SYSTEM_SVC.loadGerritConfig(new GerritCallback<GerritConfig>() {
       public void onSuccess(final GerritConfig result) {
         Common.setGerritConfig(result);
@@ -314,39 +330,39 @@
   }
 
   public static void refreshMenuBar() {
-    menuBar.clearItems();
+    menuLeft.clear();
+    menuRight.clear();
 
     final boolean signedIn = isSignedIn();
-    MenuBar m;
+    LinkMenuBar m;
 
-    m = new MenuBar(true);
+    m = new LinkMenuBar();
     addLink(m, C.menuAllOpen(), Link.ALL_OPEN);
     addLink(m, C.menuAllMerged(), Link.ALL_MERGED);
     addLink(m, C.menuAllAbandoned(), Link.ALL_ABANDONED);
-    menuBar.addItem(C.menuAll(), m);
+    menuLeft.add(m, C.menuAll());
 
     if (signedIn) {
-      m = new MenuBar(true);
+      m = new LinkMenuBar();
       addLink(m, C.menuMyChanges(), Link.MINE);
       addLink(m, C.menyMyDrafts(), Link.MINE_DRAFTS);
       addLink(m, C.menuMyStarredChanges(), Link.MINE_STARRED);
-      addLink(m, C.menuSettings(), Link.SETTINGS);
-      menuBar.addItem(C.menuMine(), m);
+      menuLeft.add(m, C.menuMine());
+      menuLeft.selectTab(1);
+    } else {
+      menuLeft.selectTab(0);
     }
 
     if (signedIn) {
-      m = new MenuBar(true);
+      m = new LinkMenuBar();
       addLink(m, C.menuGroups(), Link.ADMIN_GROUPS);
       addLink(m, C.menuProjects(), Link.ADMIN_PROJECTS);
-      menuBar.addItem(C.menuAdmin(), m);
+      menuLeft.add(m, C.menuAdmin());
     }
 
-    menuBar.lastInGroup();
-    menuBar.addGlue();
-
     if (signedIn) {
       whoAmI();
-      menuBar.addItem(new LinkMenuItem(C.menuSettings(), Link.SETTINGS));
+      addLink(menuRight, C.menuSettings(), Link.SETTINGS);
       boolean signout = false;
       switch (Common.getGerritConfig().getLoginType()) {
         case HTTP:
@@ -358,7 +374,7 @@
           break;
       }
       if (signout || (GWT.isClient() && !GWT.isScript())) {
-        menuBar.addItem(C.menuSignOut(), new Command() {
+        menuRight.addItem(C.menuSignOut(), new Command() {
           public void execute() {
             doSignOut();
           }
@@ -371,7 +387,7 @@
 
         case OPENID:
         default:
-          menuBar.addItem(C.menuSignIn(), new Command() {
+          menuRight.addItem(C.menuSignIn(), new Command() {
             public void execute() {
               doSignIn();
             }
@@ -379,14 +395,13 @@
           break;
       }
       if (GWT.isClient() && !GWT.isScript()) {
-        menuBar.addItem("Become", new Command() {
+        menuRight.addItem("Become", new Command() {
           public void execute() {
             Window.Location.assign(GWT.getHostPageBaseURL() + "become");
           }
         });
       }
     }
-    menuBar.lastInGroup();
 
     final boolean view = myAccount == null || myAccount.isShowSiteHeader();
     if (siteHeader != null) {
@@ -399,12 +414,12 @@
 
   private static void whoAmI() {
     final String name = FormatUtil.nameEmail(getUserAccount());
-    final MenuItem me = menuBar.addItem(name, (Command) null);
-    me.removeStyleName("gwt-MenuItem");
-    me.addStyleName("gerrit-MenuBarUserName");
+    final InlineLabel l = new InlineLabel(name);
+    l.setStyleName("gerrit-MenuBarUserName");
+    menuRight.add(l);
   }
 
-  private static void addLink(final MenuBar m, final String text,
+  private static void addLink(final LinkMenuBar m, final String text,
       final String historyToken) {
     m.addItem(new LinkMenuItem(text, historyToken));
   }
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java b/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
index 6793efa..d41db2a 100644
--- a/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
+++ b/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
@@ -328,7 +328,6 @@
     });
     m.addItem(Util.C.messageExpandAll(), new ExpandAllCommand(c, true));
     m.addItem(Util.C.messageCollapseAll(), new ExpandAllCommand(c, false));
-    m.lastInGroup();
     return m;
   }
 
diff --git a/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java b/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
new file mode 100644
index 0000000..e2dc555
--- /dev/null
+++ b/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.client.ui;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.ui.Accessibility;
+import com.google.gwt.user.client.ui.Anchor;
+
+public class CommandMenuItem extends Anchor implements ClickHandler {
+  private final Command command;
+
+  public CommandMenuItem(final String text, final Command cmd) {
+    super(text);
+    setStyleName("gerrit-MenuItem");
+    Accessibility.setRole(getElement(), Accessibility.ROLE_MENUITEM);
+    addClickHandler(this);
+    command = cmd;
+  }
+
+  @Override
+  public void onClick(final ClickEvent event) {
+    command.execute();
+  }
+}
diff --git a/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java b/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
index a0055f4..0fef53b 100644
--- a/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
+++ b/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
@@ -14,44 +14,44 @@
 
 package com.google.gerrit.client.ui;
 
-import com.google.gwt.user.client.ui.MenuBar;
-import com.google.gwt.user.client.ui.MenuItem;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.ui.Accessibility;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Widget;
 
-/** A GWT MenuBar, rendering its items as through they were normal links. */
-public class LinkMenuBar extends MenuBar {
+public class LinkMenuBar extends Composite {
+  private final FlowPanel body;
+
   public LinkMenuBar() {
+    body = new FlowPanel();
+    initWidget(body);
     setStyleName("gerrit-LinkMenuBar");
+    Accessibility.setRole(getElement(), Accessibility.ROLE_MENUBAR);
   }
 
-  @Override
-  public MenuItem addItem(final MenuItem item) {
-    item.addStyleDependentName("NormalItem");
-    return super.addItem(item);
+  public void addItem(final String text, final Command imp) {
+    add(new CommandMenuItem(text, imp));
   }
 
-  /**
-   * Add a cell to fill the screen width.
-   * <p>
-   * The glue has 100% width, forcing the browser to align out the next element
-   * as far right as possible. If there is exactly 1 glue in the menu bar, the
-   * bar is split into a left and right section. If there are 2 glues, the bar
-   * will be split into thirds.
-   */
-  public void addGlue() {
-    addSeparator().setStyleName("gerrit-FillMenuCenter");
+  public void addItem(final CommandMenuItem i) {
+    add(i);
   }
 
-  /**
-   * Mark this item as the last in its group, so it has no border.
-   * <p>
-   * Usually this is used just before {@link #addGlue()} and after the last item
-   * has been added.
-   */
-  public void lastInGroup() {
-    if (!getItems().isEmpty()) {
-      final MenuItem i = getItems().get(getItems().size() - 1);
-      i.removeStyleDependentName("NormalItem");
-      i.addStyleDependentName("LastItem");
+  public void addItem(final LinkMenuItem i) {
+    add(i);
+  }
+
+  public void clear() {
+    body.clear();
+  }
+
+  public void add(final Widget i) {
+    if (body.getWidgetCount() > 0) {
+      final Widget p = body.getWidget(body.getWidgetCount() - 1);
+      p.addStyleName("gerrit-LinkMenuItem-NotLast");
     }
+    i.addStyleName("gerrit-LinkMenuItem");
+    body.add(i);
   }
 }
diff --git a/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java b/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
index df98c67..ab4da2a 100644
--- a/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
+++ b/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
@@ -14,35 +14,13 @@
 
 package com.google.gerrit.client.ui;
 
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.MenuItem;
+import com.google.gwt.user.client.ui.Accessibility;
+import com.google.gwt.user.client.ui.Hyperlink;
 
-/**
- * A GWT {@link MenuItem} that uses a normal HTML link widget for its UI.
- * <p>
- * Using this widget instead of MenuItem permits the menu item to have the
- * standard right-click "Open in new window" and "Open in new tab" feature found
- * in popular browsers.
- */
-public class LinkMenuItem extends MenuItem {
-  /**
-   * Creates a hyperlink with its text and target history token specified.
-   * 
-   * @param text the hyperlink's text
-   * @param targetHistoryToken the history token to which it will link
-   */
+public class LinkMenuItem extends Hyperlink {
   public LinkMenuItem(final String text, final String targetHistoryToken) {
-    super("", new Command() {
-      public void execute() {
-        History.newItem(targetHistoryToken);
-      }
-    });
-    final Element a = DOM.createAnchor();
-    DOM.setElementProperty(a, "href", "#" + targetHistoryToken);
-    DOM.setInnerText(a, text);
-    DOM.appendChild(getElement(), a);
+    super(text, targetHistoryToken);
+    setStyleName("gerrit-MenuItem");
+    Accessibility.setRole(getElement(), Accessibility.ROLE_MENUITEM);
   }
 }
diff --git a/src/main/java/com/google/gerrit/public/gerrit.css b/src/main/java/com/google/gerrit/public/gerrit.css
index da6de82..78daa09 100644
--- a/src/main/java/com/google/gerrit/public/gerrit.css
+++ b/src/main/java/com/google/gerrit/public/gerrit.css
@@ -3,12 +3,7 @@
   padding-top: 5px;
   padding-left: 5px;
   padding-right: 5px;
-  height: 15px;
-}
-#gerrit_endtopmenu {
-  clear: both;
-  height: 5px;
-  border-bottom: 1px solid #B0BDCC;
+  background: #d4e9a9;
 }
 #gerrit_body {
   font-size: 11pt;
@@ -72,35 +67,71 @@
 
 
 /** Menu **/
-.gwt-MenuItem a,
-.gwt-MenuItem a:visited,
-.gwt-MenuItem a:hover {
-  color: #2a5db0;
-}
-.gerrit-LinkMenuBar .gwt-MenuItem {
+.gerrit-LinkMenuBar {
   font-size: 9pt;
-  color: #2a5db0;
-  text-decoration: underline;
+  display: inline;
+  white-space: nowrap;
+}
+.gerrit-MenuItem {
+  display: inline;
   padding-left: 5px;
   padding-right: 5px;
-  white-space: nowrap;
-  cursor: pointer;
-  cursor: hand;
 }
-.gerrit-LinkMenuBar .gwt-MenuItem-NormalItem {
+.gerrit-LinkMenuItem-NotLast {
   border-right: 1px solid black;
 }
-.gerrit-LinkMenuBar .gwt-MenuItem-LastItem {
-}
-.gerrit-FillMenuCenter {
+
+.gerrit-topmenu {
   width: 100%;
 }
-.gwt-MenuItem .gwt-Hyperlink {
-  font-size: 9pt;
-  white-space: nowrap;
+.gerrit-topmenu-TDmenu {
+  vertical-align: top;
+}
+.gerrit-topmenu-TDglue {
+  width: 100%;
+}
+.gerrit-topmenu-menuLeft {
+  width: 300px;
+  border-left: 1px solid #d4e9a9;
+  border-right: 1px solid #d4e9a9;
+  border-bottom: 1px solid #d4e9a9;
+}
+.gerrit-topmenu-menuLeft .gwt-TabBarFirst {
+  display: none;
+}
+.gerrit-topmenu-menuLeft .gwt-TabBarItem {
+  margin: 0px;
+  background: #d4e9a9;
+  padding-top: 0px;
+  padding-bottom: 1px;
+  padding-left: 1em;
+  padding-right: 1em;
+  border-right: 1px solid black;
+}
+.gerrit-topmenu-menuLeft .gwt-TabBarItem-selected {
+  background: #ffffcc;
+}
+.gerrit-topmenu-menuLeft .gwt-TabBarRest {
+  background: #d4e9a9;
+}
+.gerrit-topmenu-menuLeft .gwt-TabPanelBottom {
+  background: #d4e9a9;
+  border-top: 1px solid black;
+  border-left: none;
+  border-right: none;
+  border-bottom: none;
+  padding: 1px;
+}
+.gerrit-topmenu-menuLeft .gerrit-MenuItem {
+  padding-left: 1em;
+  padding-right: 1em;
+  border-right: none;
+}
+
+.gerrit-topmenu-menuRight {
+  float: right;
 }
 .gerrit-MenuBarUserName {
-  font-size: 9pt;
   font-weight: bold;
   padding-left: 5px;
   padding-right: 5px;
diff --git a/src/main/webapp/WEB-INF/Gerrit.html b/src/main/webapp/WEB-INF/Gerrit.html
index 87dd74a..424cbb0 100644
--- a/src/main/webapp/WEB-INF/Gerrit.html
+++ b/src/main/webapp/WEB-INF/Gerrit.html
@@ -10,7 +10,6 @@
   </head>
   <body>
     <div id="gerrit_topmenu"></div>
-    <div id="gerrit_endtopmenu"></div>
     <div id="gerrit_header"></div>
     <div id="gerrit_startinggerrit" style="margin-left: 10px;">
       <p>Loading <a href="http://code.google.com/p/gerrit/" target="_blank">Gerrit Code Review</a> ...</p>