Adding support for Direction.LINE_START/END to DockLayoutPanel and SplitLayoutPanel. The new DockLayoutPanelRtlTest revealed a bug in DOMRtlTest where the document body wasn't actually in RTL mode for the test, making the test completely bogus. I've updated the test to force the body in RTL mode, and I changed the test to handle the quirkiness of RTL mode.

Review at http://gwt-code-reviews.appspot.com/828801

Review by: jgw@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8714 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParser.java
index 1410b51..156828a 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParser.java
@@ -56,6 +56,8 @@
     DOCK_NAMES.put("south", "addSouth");
     DOCK_NAMES.put("east", "addEast");
     DOCK_NAMES.put("west", "addWest");
+    DOCK_NAMES.put("lineStart", "addLineStart");
+    DOCK_NAMES.put("lineEnd", "addLineEnd");
     DOCK_NAMES.put("center", "add");
   }
 
diff --git a/user/src/com/google/gwt/user/cellview/client/CellBrowser.java b/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
index 6365d2d..4f7dafd 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
@@ -790,11 +790,7 @@
 
     // Add the view to the LayoutPanel.
     SplitLayoutPanel splitPanel = getSplitLayoutPanel();
-    if (LocaleInfo.getCurrentLocale().isRTL()) {
-      splitPanel.insertEast(scrollable, defaultWidth, null);
-    } else {
-      splitPanel.insertWest(scrollable, defaultWidth, null);
-    }
+    splitPanel.insertLineStart(scrollable, defaultWidth, null);
     splitPanel.setWidgetMinSize(scrollable, minWidth);
     splitPanel.forceLayout();
 
diff --git a/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
index 4bbd7e3..fa780f5 100644
--- a/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
@@ -18,6 +18,7 @@
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.i18n.client.LocaleInfo;
 import com.google.gwt.layout.client.Layout;
 import com.google.gwt.layout.client.Layout.Layer;
 
@@ -126,7 +127,7 @@
 
   /**
    * Creates an empty dock panel.
-   * 
+   *
    * @param unit the unit to be used for layout
    */
   public DockLayoutPanel(Unit unit) {
@@ -140,7 +141,7 @@
   /**
    * Adds a widget at the center of the dock. No further widgets may be added
    * after this one.
-   * 
+   *
    * @param widget the widget to be added
    */
   @Override
@@ -150,7 +151,7 @@
 
   /**
    * Adds a widget to the east edge of the dock.
-   * 
+   *
    * @param widget the widget to be added
    * @param size the child widget's size
    */
@@ -159,8 +160,30 @@
   }
 
   /**
+   * Adds a widget to the end of the line. In LTR mode, the widget is added to
+   * the east. In RTL mode, the widget is added to the west.
+   *
+   * @param widget the widget to be added
+   * @param size the child widget's size
+   */
+  public void addLineEnd(Widget widget, double size) {
+    insert(widget, Direction.LINE_END, size, null);
+  }
+
+  /**
+   * Adds a widget to the start of the line. In LTR mode, the widget is added to
+   * the west. In RTL mode, the widget is added to the east.
+   *
+   * @param widget the widget to be added
+   * @param size the child widget's size
+   */
+  public void addLineStart(Widget widget, double size) {
+    insert(widget, Direction.LINE_START, size, null);
+  }
+
+  /**
    * Adds a widget to the north edge of the dock.
-   * 
+   *
    * @param widget the widget to be added
    * @param size the child widget's size
    */
@@ -170,7 +193,7 @@
 
   /**
    * Adds a widget to the south edge of the dock.
-   * 
+   *
    * @param widget the widget to be added
    * @param size the child widget's size
    */
@@ -180,7 +203,7 @@
 
   /**
    * Adds a widget to the west edge of the dock.
-   * 
+   *
    * @param widget the widget to be added
    * @param size the child widget's size
    */
@@ -205,7 +228,7 @@
 
   /**
    * Gets the container element wrapping the given child widget.
-   * 
+   *
    * @param child
    * @return the widget's container element
    */
@@ -216,7 +239,7 @@
 
   /**
    * Gets the layout direction of the given child widget.
-   * 
+   *
    * @param child the widget to be queried
    * @return the widget's layout direction, or <code>null</code> if it is not a
    *         child of this panel
@@ -232,7 +255,7 @@
   /**
    * Adds a widget to the east edge of the dock, inserting it before an existing
    * widget.
-   * 
+   *
    * @param widget the widget to be added
    * @param size the child widget's size
    * @param before the widget before which to insert the new child, or
@@ -243,9 +266,37 @@
   }
 
   /**
+   * Adds a widget to the start of the line, inserting it before an existing
+   * widget. In LTR mode, the widget is added to the east. In RTL mode, the
+   * widget is added to the west.
+   *
+   * @param widget the widget to be added
+   * @param size the child widget's size
+   * @param before the widget before which to insert the new child, or
+   *          <code>null</code> to append
+   */
+  public void insertLineEnd(Widget widget, double size, Widget before) {
+    insert(widget, Direction.LINE_END, size, before);
+  }
+
+  /**
+   * Adds a widget to the end of the line, inserting it before an existing
+   * widget. In LTR mode, the widget is added to the west. In RTL mode, the
+   * widget is added to the east.
+   *
+   * @param widget the widget to be added
+   * @param size the child widget's size
+   * @param before the widget before which to insert the new child, or
+   *          <code>null</code> to append
+   */
+  public void insertLineStart(Widget widget, double size, Widget before) {
+    insert(widget, Direction.LINE_START, size, before);
+  }
+
+  /**
    * Adds a widget to the north edge of the dock, inserting it before an
    * existing widget.
-   * 
+   *
    * @param widget the widget to be added
    * @param size the child widget's size
    * @param before the widget before which to insert the new child, or
@@ -258,7 +309,7 @@
   /**
    * Adds a widget to the south edge of the dock, inserting it before an
    * existing widget.
-   * 
+   *
    * @param widget the widget to be added
    * @param size the child widget's size
    * @param before the widget before which to insert the new child, or
@@ -271,7 +322,7 @@
   /**
    * Adds a widget to the west edge of the dock, inserting it before an existing
    * widget.
-   * 
+   *
    * @param widget the widget to be added
    * @param size the child widget's size
    * @param before the widget before which to insert the new child, or
@@ -304,7 +355,7 @@
 
     return removed;
   }
-  
+
   /**
    * Updates the size of the widget passed in as long as it is not the center
    * widget and updates the layout of the dock.
@@ -329,6 +380,27 @@
     return center;
   }
 
+  /**
+   * Resolve the specified direction based on the current locale. If the
+   * direction is {@link Direction#LINE_START} or {@link Direction#LINE_END},
+   * the return value will be one of {@link Direction#EAST} or
+   * {@link Direction#WEST} depending on the RTL mode of the locale. For all
+   * other directions, the specified value is returned.
+   *
+   * @param direction the specified direction
+   * @return the locale
+   */
+  protected Direction getResolvedDirection(Direction direction) {
+    if (direction == Direction.LINE_START) {
+      return LocaleInfo.getCurrentLocale().isRTL()
+          ? Direction.EAST : Direction.WEST;
+    } else if (direction == Direction.LINE_END) {
+      return LocaleInfo.getCurrentLocale().isRTL()
+          ? Direction.WEST : Direction.EAST;
+    }
+    return direction;
+  }
+
   protected Unit getUnit() {
     return unit;
   }
@@ -337,7 +409,7 @@
    * Adds a widget to the specified edge of the dock. If the widget is already a
    * child of this panel, this method behaves as though {@link #remove(Widget)}
    * had already been called.
-   * 
+   *
    * @param widget the widget to be added
    * @param direction the widget's direction in the dock
    * @param before the widget before which to insert the new child, or
@@ -398,13 +470,16 @@
   }
 
   private void doLayout() {
-    double left = 0, top = 0, right = 0, bottom = 0;
+    double left = 0;
+    double top = 0;
+    double right = 0;
+    double bottom = 0;
 
     for (Widget child : getChildren()) {
       LayoutData data = (LayoutData) child.getLayoutData();
       Layer layer = data.layer;
 
-      switch (data.direction) {
+      switch (getResolvedDirection(data.direction)) {
         case NORTH:
           layer.setLeftRight(left, unit, right, unit);
           layer.setTopHeight(top, unit, data.size, unit);
diff --git a/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
index b7277d7..9bd75d5 100644
--- a/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
@@ -274,7 +274,7 @@
 
     LayoutData layout = (LayoutData) widget.getLayoutData();
     Splitter splitter = null;
-    switch (layout.direction) {
+    switch (getResolvedDirection(layout.direction)) {
       case WEST:
         splitter = new HSplitter(widget, false);
         break;
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index e31de10..69992ec 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -45,6 +45,8 @@
 import com.google.gwt.user.client.ui.DelegatingKeyboardListenerCollectionTest;
 import com.google.gwt.user.client.ui.DialogBoxTest;
 import com.google.gwt.user.client.ui.DisclosurePanelTest;
+import com.google.gwt.user.client.ui.DockLayoutPanelRtlTest;
+import com.google.gwt.user.client.ui.DockLayoutPanelTest;
 import com.google.gwt.user.client.ui.DockPanelTest;
 import com.google.gwt.user.client.ui.ElementWrappingTest;
 import com.google.gwt.user.client.ui.FastStringMapTest;
@@ -136,6 +138,8 @@
     suite.addTestSuite(DelegatingKeyboardListenerCollectionTest.class);
     suite.addTestSuite(DialogBoxTest.class);
     suite.addTestSuite(DisclosurePanelTest.class);
+    suite.addTestSuite(DockLayoutPanelRtlTest.class);
+    suite.addTestSuite(DockLayoutPanelTest.class);
     suite.addTestSuite(DockPanelTest.class);
     suite.addTestSuite(DOMTest.class);
     suite.addTestSuite(DOMRtlTest.class);
diff --git a/user/test/com/google/gwt/user/client/ui/DOMRtlTest.java b/user/test/com/google/gwt/user/client/ui/DOMRtlTest.java
index cd3d939..172c4bb 100644
--- a/user/test/com/google/gwt/user/client/ui/DOMRtlTest.java
+++ b/user/test/com/google/gwt/user/client/ui/DOMRtlTest.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2008 Google Inc.
- * 
+ *
  * 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
@@ -15,7 +15,6 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.dom.client.Document;
 import com.google.gwt.i18n.client.LocaleInfo;
 import com.google.gwt.junit.DoNotRunWith;
 import com.google.gwt.junit.Platform;
@@ -40,6 +39,9 @@
    */
   @DoNotRunWith({Platform.HtmlUnitBug})
   public void testGetAbsolutePositionWhenScrolled() {
+    // Force the document body into RTL mode.
+    RootPanel.get();
+
     assertTrue(LocaleInfo.getCurrentLocale().isRTL());
     final Element outer = DOM.createDiv();
     final Element inner = DOM.createDiv();
@@ -58,14 +60,19 @@
     inner.getStyle().setPropertyPx("height", 300);
     outer.appendChild(inner);
     inner.setInnerText(":-)");
+    int absLeftStart = inner.getAbsoluteLeft();
 
-    // Check the position when scrolled
-    outer.setScrollLeft(50);
-    assertEquals(outer.getScrollLeft(), 50);
-    int absLeft = inner.getAbsoluteLeft() - Document.get().getBodyOffsetLeft();
-    // TODO (jlabanca): FF2 incorrectly reports the absolute left as 49.  When
-    // we drop FF2 support, the only valid return value is 50.
-    assertTrue(50 == absLeft || 49 == absLeft);
+    // Check the position when scrolled. In RTL mode, the absolute position of
+    // the inner element depends on the position of the scrollbar of the outer
+    // element. Some browsers render the scrollbar on the right even in RTL
+    // mode, which pushes the inner element about 15 pixels to the left. In
+    // order to work around this ambiguity, we compare the old scroll
+    // position to the new scroll position, but do not assume the absolute
+    // position.
+    outer.setScrollLeft(-50);
+    assertEquals(outer.getScrollLeft(), -50);
+    int absLeftScrolled = inner.getAbsoluteLeft();
+    assertEquals(50, absLeftScrolled - absLeftStart);
 
     // Cleanup test
     RootPanel.getBodyElement().removeChild(outer);
diff --git a/user/test/com/google/gwt/user/client/ui/DockLayoutPanelRtlTest.java b/user/test/com/google/gwt/user/client/ui/DockLayoutPanelRtlTest.java
new file mode 100644
index 0000000..46ddcf0
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DockLayoutPanelRtlTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * 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.gwt.user.client.ui;
+
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.user.client.ui.DockLayoutPanel.Direction;
+
+/**
+ * Tests for {@link DockLayoutPanel}.
+ */
+public class DockLayoutPanelRtlTest extends WidgetTestBase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.UserTestRtl";
+  }
+
+  public void testGetResolvedDirection() {
+    DockLayoutPanel panel = createDockLayoutPanel();
+    assertEquals(
+        Direction.EAST, panel.getResolvedDirection(Direction.LINE_START));
+    assertEquals(
+        Direction.WEST, panel.getResolvedDirection(Direction.LINE_END));
+  }
+
+  protected DockLayoutPanel createDockLayoutPanel() {
+    return new DockLayoutPanel(Unit.PX);
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/DockLayoutPanelTest.java b/user/test/com/google/gwt/user/client/ui/DockLayoutPanelTest.java
new file mode 100644
index 0000000..d53e7db
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DockLayoutPanelTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * 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.gwt.user.client.ui;
+
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.user.client.ui.DockLayoutPanel.Direction;
+
+/**
+ * Tests for {@link DockLayoutPanel}.
+ */
+public class DockLayoutPanelTest extends WidgetTestBase {
+
+  public void testGetResolvedDirection() {
+    DockLayoutPanel panel = createDockLayoutPanel();
+    assertEquals(
+        Direction.WEST, panel.getResolvedDirection(Direction.LINE_START));
+    assertEquals(
+        Direction.EAST, panel.getResolvedDirection(Direction.LINE_END));
+  }
+
+  public void testAddLineEnd() {
+    DockLayoutPanel panel = createDockLayoutPanel();
+    Widget widget = new Label();
+    panel.addLineEnd(widget, 10);
+    assertEquals(Direction.LINE_END, panel.getWidgetDirection(widget));
+  }
+
+  public void testAddLineStart() {
+    DockLayoutPanel panel = createDockLayoutPanel();
+    Widget widget = new Label();
+    panel.addLineStart(widget, 10);
+    assertEquals(Direction.LINE_START, panel.getWidgetDirection(widget));
+  }
+
+  public void testInsertLineEnd() {
+    DockLayoutPanel panel = createDockLayoutPanel();
+    Widget widget = new Label();
+    panel.insertLineEnd(widget, 10, null);
+    assertEquals(Direction.LINE_END, panel.getWidgetDirection(widget));
+  }
+
+  public void testInsertLineStart() {
+    DockLayoutPanel panel = createDockLayoutPanel();
+    Widget widget = new Label();
+    panel.insertLineStart(widget, 10, null);
+    assertEquals(Direction.LINE_START, panel.getWidgetDirection(widget));
+  }
+
+  protected DockLayoutPanel createDockLayoutPanel() {
+    return new DockLayoutPanel(Unit.PX);
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/SplitLayoutPanelTest.java b/user/test/com/google/gwt/user/client/ui/SplitLayoutPanelTest.java
index 391ca0a..8b90ade 100644
--- a/user/test/com/google/gwt/user/client/ui/SplitLayoutPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/SplitLayoutPanelTest.java
@@ -18,7 +18,7 @@
 /**
  * Tests for {@link SplitLayoutPanel}.
  */
-public class SplitLayoutPanelTest extends WidgetTestBase {
+public class SplitLayoutPanelTest extends DockLayoutPanelTest {
 
   static class Adder implements HasWidgetsTester.WidgetAdder {
     public void addChild(HasWidgets container, Widget child) {
@@ -132,4 +132,9 @@
     assertEquals(l0, children.get(0));
     assertEquals(splitter0, children.get(1));
   }
+  
+  @Override
+  protected DockLayoutPanel createDockLayoutPanel() {
+    return new SplitLayoutPanel();
+  }
 }