Revision: 8454
Author: tomer...@google.com
Date: Mon Aug  2 09:47:24 2010
Log: Adds integral BiDi support to Label.java and its descendants. Introduces a new interface, HasAutoHorizontalAlignment.

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

http://code.google.com/p/google-web-toolkit/source/detail?r=8454

Added:
/trunk/user/src/com/google/gwt/user/client/ui/HasAutoHorizontalAlignment.java
 /trunk/user/test/com/google/gwt/user/client/ui/HTMLTest.java
 /trunk/user/test/com/google/gwt/user/client/ui/LabelTest.java
Modified:
 /trunk/user/src/com/google/gwt/i18n/shared/BidiFormatter.java
/trunk/user/src/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser.java
 /trunk/user/src/com/google/gwt/user/client/ui/HTML.java
 /trunk/user/src/com/google/gwt/user/client/ui/HasHorizontalAlignment.java
 /trunk/user/src/com/google/gwt/user/client/ui/InlineHTML.java
 /trunk/user/src/com/google/gwt/user/client/ui/InlineLabel.java
 /trunk/user/src/com/google/gwt/user/client/ui/Label.java
/trunk/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParserTest.java
 /trunk/user/test/com/google/gwt/user/UISuite.java

=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/user/client/ui/HasAutoHorizontalAlignment.java Mon Aug 2 09:47:24 2010
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+/**
+ * A widget that implements this interface can be configured to be aligned
+ * according to its contents' direction, in addition to the static alignment
+ * options offered by {...@link HasHorizontalAlignment}.
+ */
+public interface HasAutoHorizontalAlignment extends HasHorizontalAlignment {
+
+  /**
+   * Specifies that the widget's contents should be aligned left for LTR
+ * content, right for RTL content, and if the content's direction is DEFAULT,
+   * like {...@link #ALIGN_LOCALE_START}.
+   */
+  AutoHorizontalAlignmentConstant ALIGN_CONTENT_START =
+      new AutoHorizontalAlignmentConstant();
+
+  /**
+   * Specifies that the widget's contents should be aligned right for LTR
+ * content, left for RTL content, and if the content's direction is DEFAULT,
+   * like {...@link #ALIGN_LOCALE_END}.
+   */
+  AutoHorizontalAlignmentConstant ALIGN_CONTENT_END =
+      new AutoHorizontalAlignmentConstant();
+
+  /**
+   * Gets the horizontal auto-alignment setting. This may be one of the
+   * auto-alignment values above that depend on content direction (e.g.
+   * {...@link HasAutoHorizontalAlignment#ALIGN_CONTENT_START}), or one of the
+   * "static" {...@link HasHorizontalAlignment.HorizontalAlignmentConstant}
+ * alignment values (e.g. {...@link HasHorizontalAlignment#ALIGN_LOCALE_START}).
+   * It may be set by either {...@code setAutoHorizontalAlignment} or {...@code
+   * HasHorizontalAlignment#setHorizontalAlignment}. The default is null,
+ * indicating that no specific horizontal alignment has been set, allowing it
+   * to be determined by the usual HTML and CSS mechanisms.
+   *
+   * @return the current automatic horizontal alignment policy.
+   */
+  AutoHorizontalAlignmentConstant getAutoHorizontalAlignment();
+
+  /**
+   * Sets the horizontal alignment, allowing in addition to the "static"
+   * {...@link HasHorizontalAlignment.HorizontalAlignmentConstant} values, the
+ * "automatic" {...@link HasHorizontalAlignment.AutoHorizontalAlignmentConstant} + * values that depend on the content direction. Determines the values returned
+   * by both {...@link #getAutoHorizontalAlignment} and
+   * {...@link HasHorizontalAlignment#getHorizontalAlignment()}.
+   * <p> For the {...@code ALIGN_CONTENT_START} and {...@code 
ALIGN_CONTENT_END}
+   * values, sets the horizontal alignment (including the value of {...@code
+ * HasHorizontalAlignment#getHorizontalAlignment()}) to the start or end edge
+   * of the current content's direction, respectively, and continues to
+   * automatically update it whenever the content direction changes.
+   * <p> For other values, operates like {...@link #setHorizontalAlignment}.
+ * <p> For {...@code null}, the horizontal alignment is cleared, allowing it to + * be determined by the standard HTML mechanisms such as inheritance and CSS
+   * rules.
+   * @see HasHorizontalAlignment
+   *
+   * @param autoHorizontalAlignment the new automatic horizontal alignment
+   *        policy
+   */
+  void setAutoHorizontalAlignment(AutoHorizontalAlignmentConstant
+      autoHorizontalAlignment);
+}
=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/user/client/ui/HTMLTest.java Mon Aug 2 09:47:24 2010
@@ -0,0 +1,168 @@
+/*
+ * 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.Element;
+import com.google.gwt.i18n.client.BidiUtils;
+import com.google.gwt.i18n.client.HasDirection.Direction;
+import com.google.gwt.i18n.client.LocaleInfo;
+
+/**
+ * Tests {...@link HTML}.
+ * Note: tests only the direction and alignment logic.
+ */
+public class HTMLTest extends LabelTest {
+
+ private final String EN_HTML = "<b style=\"color: red;\">" + EN_TEXT + "</b>"; + private final String IW_HTML = "<b style=\"color: red;\">" + IW_TEXT + "</b>";
+  private HTML label;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.User";
+  }
+
+  // setDirection is deprecated; this only assures backwards compatibility.
+  public void testSetDirection() {
+    for (int i = 0; i < 2; i++) {
+      String id = i == 0 ? "div label: " : "span label: ";
+      label = HTML.wrap(i == 0 ? createAttachedDivElement() :
+          createAttachedSpanElement());
+      label.setDirection(Direction.RTL);
+      assertLabelDirection(id + "label's direction is incorrect after " +
+          "setDirection", Direction.RTL);
+
+      label.setText(EN_TEXT, Direction.LTR);
+      assertLabelDirection(id + "label's direction is incorrect after " +
+          "setText with a specific direction", Direction.LTR);
+
+      label.setText(EN_TEXT);
+ assertLabelDirection(id + "label's direction wasn't reverted to the " + + "direction set by last setDirection when calling setText with no " + + "direction argument and without a directionEstimator", Direction.RTL);
+      if (i == 1) {
+ // For span element, we also specifically assert that the direction of
+        // the topmost element matches the last setDirection.
+ assertEquals(id + "element's direction does not match the direction " + + "set by last setDirection when calling setText with no direction " +
+            "argument and without a directionEstimator", Direction.RTL,
+            BidiUtils.getDirectionOnElement(label.getElement()));
+      }
+    }
+  }
+
+  public void testSetDirectionEstimatorAndSetHtml() {
+    testSetDirectionEstimatorAndSetTextOrHtml(true);
+  }
+
+  public void testSetDirectionEstimatorAndSetText() {
+    testSetDirectionEstimatorAndSetTextOrHtml(false);
+  }
+
+  /**
+ * Asserts that both the {...@link Label#getContentDirection} and the physical
+   * dir attribute match the expected direction.
+   *
+   * @param message Assertion message
+   * @param expected Expected direction
+   */
+  private void assertLabelDirection(String message, Direction expected) {
+    assertTrue("attribute mismatch: " + message,
+        expected == getLabelDirection() ||
+        /* For inline elements, empty dir attribute is acceptable if LTR is
+         * expected and the locale is not RTL. */
+        isSpanWrapped() && getLabelDirection() == Direction.DEFAULT &&
+ expected == Direction.LTR && !LocaleInfo.getCurrentLocale().isRTL());
+
+    assertEquals("contentDir mismatch: " + message, expected,
+        label.getContentDirection());
+  }
+
+  private Direction getLabelDirection() {
+    Element element = isSpanWrapped() ?
+        label.getElement().getFirstChildElement() : label.getElement();
+
+    return BidiUtils.getDirectionOnElement(element);
+  }
+
+ // This will not work generally. It assumes that the label's content isn't
+  // consist of a span tag.
+  private boolean isSpanWrapped() {
+    Element inner = label.getElement().getFirstChildElement();
+    return inner != null && inner.getTagName().equalsIgnoreCase("span");
+  }
+
+  private void setLabelTextOrHtml(String content, boolean isHtml) {
+    if (isHtml) {
+      label.setHTML(content);
+    } else {
+      label.setText(content);
+    }
+  }
+
+ private void setLabelTextOrHtml(String content, Direction dir, boolean isHtml) {
+    if (isHtml) {
+      label.setHTML(content, dir);
+    } else {
+      label.setText(content, dir);
+    }
+  }
+
+  private void testSetDirectionEstimatorAndSetTextOrHtml(boolean isHtml) {
+    String enContent = isHtml ? EN_HTML : EN_TEXT;
+    String iwContent = isHtml ? IW_HTML : IW_TEXT;
+    for (int i = 0; i < 2; i++) {
+      String id = i == 0 ? "div label: " : "span label: ";
+      label = HTML.wrap(i == 0 ? createAttachedDivElement() :
+          createAttachedSpanElement());
+
+      setLabelTextOrHtml(enContent, isHtml);
+      assertLabelDirection(id + "label's direction is not DEFAULT upon " +
+          "standard initialization", Direction.DEFAULT);
+
+      setLabelTextOrHtml(iwContent, Direction.RTL, isHtml);
+ assertLabelDirection(id + "label's direction is not RTL after it was" +
+          " explicitly set to RTL", Direction.RTL);
+
+      setLabelTextOrHtml(enContent, isHtml);
+ assertLabelDirection(id + "label's direction was not specified, and no" + + " estimator specified, thus should return to initial value (DEFAULT)",
+          Direction.DEFAULT);
+
+      label.setDirectionEstimator(true);
+ assertLabelDirection(id + "label's direction wasn't instantly updated" +
+          " to LTR on switching direction estimation on", Direction.LTR);
+
+      setLabelTextOrHtml(iwContent, isHtml);
+ assertLabelDirection(id + "label's direction wasn't estimated as RTL",
+          Direction.RTL);
+
+      setLabelTextOrHtml(iwContent, Direction.LTR, isHtml);
+ assertLabelDirection(id + "label's direction is not LTR after it was" + + " explicitly set to LTR (direction estimation is on)", Direction.LTR);
+
+      setLabelTextOrHtml(iwContent, Direction.DEFAULT, isHtml);
+ assertLabelDirection(id + "label's direction is not DEFAULT after it" +
+          " was explicitly set to DEFAULT (direction estimation is on)",
+          Direction.DEFAULT);
+
+      assertEquals(id + "retreived html is incorrect", iwContent,
+          label.getHTML());
+      assertEquals(id + "retreived text is incorrect", IW_TEXT,
+          label.getText());
+    }
+  }
+}
=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/user/client/ui/LabelTest.java Mon Aug 2 09:47:24 2010
@@ -0,0 +1,167 @@
+/*
+ * 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.DivElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.SpanElement;
+import com.google.gwt.i18n.client.BidiUtils;
+import com.google.gwt.i18n.client.HasDirection.Direction;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.ui.HasHorizontalAlignment.AutoHorizontalAlignmentConstant; +import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
+
+/**
+ * Tests {...@link Label}.
+ * Note: tests only the alignment logic. direction logic is tested at
+ * {...@link HTMLTest}, and other stuff remains currently untested.
+ */
+public class LabelTest extends GWTTestCase {
+
+  protected final String EN_TEXT = "abc";
+  protected final String IW_TEXT = "\u05e0\u05e1\u05e2";
+  private Label label;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.User";
+  }
+
+  public void testSetAutoHorizontalAlignmentNoDirectionEstimator() {
+    Element elem = createAttachedDivElement();
+ // Initialize the div with a specific direction, to verify it remembers its
+    // original direction on setText with no direction argument.
+    BidiUtils.setDirectionOnElement(elem, Direction.LTR);
+    label = Label.wrap(elem);
+
+    label.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
+    assertAlign("horizontal alignment was set to left by " +
+        "setHorizontalAlignment, but is not",
+        HasHorizontalAlignment.ALIGN_LEFT);
+
+    label.setAutoHorizontalAlignment(null);
+    assertEquals("text-align is not empty after " +
+        "setAutoHorizontalAlignment(null)", "",
+        label.getElement().getStyle().getProperty("textAlign"));
+
+    label.setAutoHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
+    assertAlign("horizontal alignment was set to right by " +
+        "setAutoHorizontalAlignment, but is not",
+        HasHorizontalAlignment.ALIGN_RIGHT);
+
+    label.setText(IW_TEXT, Direction.RTL);
+    label.setAutoHorizontalAlignment(
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_END);
+ assertAlign("automatic horizontal alignment was set to ALIGN_CONTENT_END," +
+        " content was declared RTL", HasHorizontalAlignment.ALIGN_LEFT,
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_END);
+
+    label.setText(EN_TEXT);
+ assertAlign("automatic horizontal alignment was set to ALIGN_CONTENT_END," +
+        " content direction was reset to the original LTR after calling " +
+        "setText with no direction argument",
+        HasHorizontalAlignment.ALIGN_RIGHT,
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_END);
+
+    label.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_JUSTIFY);
+    assertAlign("horizontal alignment was set to justify by " +
+        "setHorizontalAlignment, but is not",
+        HasHorizontalAlignment.ALIGN_JUSTIFY);
+  }
+
+  public void testSetAutoHorizontalAlignmentWithDirectionEstimator() {
+    Element elem = createAttachedDivElement();
+ // Initialize the div with a specific direction, to verify it remembers its
+    // original direction on turning direction estimator off.
+    BidiUtils.setDirectionOnElement(elem, Direction.LTR);
+    label = Label.wrap(createAttachedDivElement());
+
+    label.setAutoHorizontalAlignment(
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_END);
+    label.setDirectionEstimator(true);
+    label.setText(IW_TEXT);
+ assertAlign("automatic horizontal alignment was set to ALIGN_CONTENT_END," +
+        " and content is supposedly estimated as RTL",
+        HasHorizontalAlignment.ALIGN_LEFT,
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_END);
+
+    label.setAutoHorizontalAlignment(
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_END);
+    assertAlign("automatic horizontal alignment was set (again) to " +
+        "ALIGN_CONTENT_END, and content is estimated as RTL",
+        HasHorizontalAlignment.ALIGN_LEFT,
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_END);
+
+    label.setAutoHorizontalAlignment(
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_START);
+    assertAlign("automatic horizontal alignment was set to " +
+        "ALIGN_CONTENT_START, content is estimated as RTL",
+        HasHorizontalAlignment.ALIGN_RIGHT,
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_START);
+
+    label.setDirectionEstimator(false);
+    assertAlign("direction was supposed to be reset to the original " +
+ "ALIGN_LEFT after turning off direction estimator, and automatic " +
+        "horizontal alignment was to ALIGN_CONTENT_START",
+        HasHorizontalAlignment.ALIGN_LEFT,
+        HasAutoHorizontalAlignment.ALIGN_CONTENT_START);
+  }
+
+  /**
+   * Create a div and attach it to the {...@link RootPanel}.
+   *
+   * @return the new div
+   */
+  protected Element createAttachedDivElement() {
+    DivElement elem = Document.get().createDivElement();
+    RootPanel.getBodyElement().appendChild(elem);
+    return elem;
+  }
+
+  /**
+   * Create a span and attach it to the {...@link RootPanel}.
+   *
+   * @return the new span
+   */
+  protected Element createAttachedSpanElement() {
+    SpanElement elem = Document.get().createSpanElement();
+    RootPanel.getBodyElement().appendChild(elem);
+    return elem;
+  }
+
+ private void assertAlign(String msg, HorizontalAlignmentConstant expected) {
+    assertAlign(msg, expected, expected);
+  }
+
+  /**
+   * Asserts that everything is fine with the alignment.
+   *
+   * @param msg assertion message
+   * @param expected expected horizontal alignment
+   * @param expectedAuto expected auto horizontal alignment
+   */
+ private void assertAlign(String msg, HorizontalAlignmentConstant expected,
+      AutoHorizontalAlignmentConstant expectedAuto) {
+    assertEquals(msg + " (text-align property value is incorrect)",
+        expected.getTextAlignString(),
+        label.getElement().getStyle().getProperty("textAlign"));
+ assertEquals(msg + " (getHorizontalAlignment return value is incorrect)",
+        expected, label.getHorizontalAlignment());
+ assertEquals(msg + " (getAutoHorizontalAlignment return value is incorrect)",
+        expectedAuto, label.getAutoHorizontalAlignment());
+  }
+}
=======================================
--- /trunk/user/src/com/google/gwt/i18n/shared/BidiFormatter.java Tue Jul 20 08:04:29 2010 +++ /trunk/user/src/com/google/gwt/i18n/shared/BidiFormatter.java Mon Aug 2 09:47:24 2010
@@ -17,6 +17,7 @@
 package com.google.gwt.i18n.shared;

 import com.google.gwt.i18n.client.HasDirection.Direction;
+import com.google.gwt.i18n.client.LocaleInfo;

 /**
  * Utility class for formatting text for display in a potentially
@@ -182,7 +183,31 @@
       boolean alwaysSpan) {
     return new BidiFormatter(contextDir, alwaysSpan);
   }
-
+
+  /**
+ * Factory for creating an instance of BidiFormatter whose context direction
+   * matches the current locale's direction. The default behavior of {...@link
+ * #spanWrap} and its variations is set to avoid span wrapping unless it's
+   * necessary ('dir' attribute needs to be set).
+   */
+  public static BidiFormatter getInstanceForCurrentLocale() {
+    return getInstanceForCurrentLocale(false);
+  }
+
+  /**
+ * Factory for creating an instance of BidiFormatter whose context direction + * matches the current locale's direction, and given the desired span wrapping
+   * behavior (see below).
+   *
+ * @param alwaysSpan Whether {...@link #spanWrap} (and its variations) should + * always use a 'span' tag, even when the input direction is neutral + * or matches the context, so that the DOM structure of the output
+   *          does not depend on the combination of directions
+   */
+ public static BidiFormatter getInstanceForCurrentLocale(boolean alwaysSpan) {
+    return getInstance(LocaleInfo.getCurrentLocale().isRTL(), alwaysSpan);
+  }
+
   private boolean alwaysSpan;
   private Direction contextDir;

@@ -623,3 +648,4 @@
return str.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\n", "<br>");
   }
 }
+
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser.java Fri Jul 2 04:56:18 2010 +++ /trunk/user/src/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser.java Mon Aug 2 09:47:24 2010
@@ -37,9 +37,11 @@
     values.put("LEFT", PREFIX + "LEFT");
     values.put("CENTER", PREFIX + "CENTER");
     values.put("RIGHT", PREFIX + "RIGHT");
+    values.put("JUSTIFY", PREFIX + "JUSTIFY");
     values.put("ALIGN_LEFT", PREFIX + "LEFT");
     values.put("ALIGN_CENTER", PREFIX + "CENTER");
     values.put("ALIGN_RIGHT", PREFIX + "RIGHT");
+    values.put("ALIGN_JUSTIFY", PREFIX + "JUSTIFY");
   }

   HorizontalAlignmentConstantParser(FieldReferenceConverter converter,
=======================================
--- /trunk/user/src/com/google/gwt/user/client/ui/HTML.java Tue Jul 8 12:08:40 2008 +++ /trunk/user/src/com/google/gwt/user/client/ui/HTML.java Mon Aug 2 09:47:24 2010
@@ -1,12 +1,12 @@
 /*
  * Copyright 2006 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
@@ -17,25 +17,26 @@

 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.client.HasDirection.Direction;

 /**
  * A widget that can contain arbitrary HTML.
- *
+ *
* This widget uses a &lt;div&gt; element, causing it to be displayed with block
  * layout.
- *
+ *
  * <p>
  * If you only need a simple label (text, but not HTML), then the
* {...@link com.google.gwt.user.client.ui.Label} widget is more appropriate, as it * disallows the use of HTML, which can lead to potential security issues if not
  * used properly.
  * </p>
- *
+ *
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-HTML { }</li>
  * </ul>
- *
+ *
  * <p>
  * <h3>Example</h3>
  * {...@example com.google.gwt.examples.HTMLExample}
@@ -46,11 +47,11 @@
   /**
* Creates an HTML widget that wraps an existing &lt;div&gt; or &lt;span&gt;
    * element.
-   *
+   *
* This element must already be attached to the document. If the element is
    * removed from the document, you must call
    * {...@link RootPanel#detachNow(Widget)}.
-   *
+   *
    * @param element the element to be wrapped
    */
   public static HTML wrap(Element element) {
@@ -76,18 +77,31 @@

   /**
    * Creates an HTML widget with the specified HTML contents.
-   *
+   *
    * @param html the new widget's HTML contents
    */
   public HTML(String html) {
     this();
     setHTML(html);
   }
+
+  /**
+   * Creates an HTML widget with the specified HTML contents and with the
+   * specified direction.
+   *
+   * @param html the new widget's HTML contents
+ * @param dir the content's direction. Note: {...@code Direction.DEFAULT} means
+   *        direction should be inherited from the widget's parent element.
+   */
+  public HTML(String html, Direction dir) {
+    this();
+    setHTML(html, dir);
+  }

   /**
* Creates an HTML widget with the specified contents, optionally treating it
    * as HTML, and optionally disabling word wrapping.
-   *
+   *
    * @param html the widget's contents
    * @param wordWrap <code>false</code> to disable word wrapping
    */
@@ -99,20 +113,41 @@
   /**
* This constructor may be used by subclasses to explicitly use an existing * element. This element must be either a &lt;div&gt; or &lt;span&gt; element.
-   *
+   *
    * @param element the element to be used
    */
   protected HTML(Element element) {
+    // super(element) asserts that element is either a &lt;div&gt; or
+    // &lt;span&gt;.
     super(element);
-    assert element.getTagName().equalsIgnoreCase("div")
-        || element.getTagName().equalsIgnoreCase("span");
   }

   public String getHTML() {
-    return getElement().getInnerHTML();
+    return getTextOrHtml(true);
   }

+  /**
+   * Sets the label's content to the given HTML.
+ * See {...@link #setText(String)} for details on potential effects on direction
+   * and alignment.
+   *
+   * @param html the new widget's HTML content
+   */
   public void setHTML(String html) {
-    getElement().setInnerHTML(html);
+    setTextOrHtml(html, true);
+  }
+
+
+  /**
+ * Sets the label's content to the given HTML, applying the given direction. + * See {...@link #setText(String, Direction)} for details on potential effects on
+   * alignment.
+   *
+   * @param html the new widget's HTML content
+ * @param dir the content's direction. Note: {...@code Direction.DEFAULT} means
+   *        direction should be inherited from the widget's parent element.
+   */
+  public void setHTML(String html, Direction dir) {
+    setTextOrHtml(html, dir, true);
   }
 }
=======================================
--- /trunk/user/src/com/google/gwt/user/client/ui/HasHorizontalAlignment.java Fri Jul 2 04:56:18 2010 +++ /trunk/user/src/com/google/gwt/user/client/ui/HasHorizontalAlignment.java Mon Aug 2 09:47:24 2010
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;

 import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.HasDirection.Direction;
 import com.google.gwt.i18n.client.LocaleInfo;

 /**
@@ -27,7 +28,7 @@
  * <p>
* The names of the static members of {...@link HorizontalAlignmentConstant}, as
  * well as simple alignment names (<code>left</code>, <code>center</code>,
- * <code>right</code>), can be used as values for a
+ * <code>right</code>, <code>justify</code>), can be used as values for a
* <code>horizontalAlignment</code> attribute of any widget that implements this * interface. (In fact, this will work for any widget method that takes a single
  * HorizontalAlignmentConstant value.)
@@ -42,9 +43,37 @@
 public interface HasHorizontalAlignment {

   /**
-   * Horizontal alignment constant.
+ * Type for values defined and used in {...@link HasAutoHorizontalAlignment}. + * Defined here so that HorizontalAlignmentConstant can be derived from it, + * thus allowing HasAutoHorizontalAlignment methods to accept and return both + * AutoHorizontalAlignmentConstant and HorizontalAlignmentConstant values -
+   * without allowing the methods defined here to accept or return
+   * AutoHorizontalAlignmentConstant values.
    */
-  public static class HorizontalAlignmentConstant {
+  public static class AutoHorizontalAlignmentConstant {
+ // The constructor is package-private to prevent uncontrolled inheritance
+    // and instantiation of this class.
+    AutoHorizontalAlignmentConstant() {
+    }
+  }
+
+  /**
+ * Possible return values for {...@link #getHorizontalAlignment}, and parameter
+   * values for {...@link #setHorizontalAlignment}.
+   */
+  public static class HorizontalAlignmentConstant extends
+      AutoHorizontalAlignmentConstant {
+
+    public static HorizontalAlignmentConstant endOf(Direction direction) {
+      return direction == Direction.LTR ? ALIGN_RIGHT :
+          direction == Direction.RTL ? ALIGN_LEFT : ALIGN_LOCALE_END;
+    }
+
+ public static HorizontalAlignmentConstant startOf(Direction direction) {
+      return direction == Direction.LTR ? ALIGN_LEFT :
+          direction == Direction.RTL ? ALIGN_RIGHT : ALIGN_LOCALE_START;
+    }
+
     private final String textAlignString;

     private HorizontalAlignmentConstant(String textAlignString) {
@@ -53,7 +82,7 @@

     /**
      * Gets the CSS 'text-align' string associated with this constant.
-     *
+     *
      * @return the CSS 'text-align' value
      */
     public String getTextAlignString() {
@@ -67,6 +96,12 @@
HorizontalAlignmentConstant ALIGN_CENTER = new HorizontalAlignmentConstant(
       "center");

+  /**
+   * Specifies that the widget's contents should be aligned as justify.
+   */
+ HorizontalAlignmentConstant ALIGN_JUSTIFY = new HorizontalAlignmentConstant(
+      "justify");
+
   /**
    * Specifies that the widget's contents should be aligned to the left.
    */
@@ -84,24 +119,47 @@
* the right. In a LTR layout, specifies that the widget's constants should be
    * aligned to the left.
    */
-  HorizontalAlignmentConstant ALIGN_DEFAULT = GWT.isClient()
+  HorizontalAlignmentConstant ALIGN_LOCALE_START = GWT.isClient()
       && LocaleInfo.getCurrentLocale().isRTL() ? ALIGN_RIGHT : ALIGN_LEFT;

+  /**
+ * In a RTL layout, specifies that the widget's contents should be aligned to + * the left. In a LTR layout, specifies that the widget's constants should be
+   * aligned to the right.
+   */
+  HorizontalAlignmentConstant ALIGN_LOCALE_END = GWT.isClient()
+      && LocaleInfo.getCurrentLocale().isRTL() ? ALIGN_LEFT : ALIGN_RIGHT;
+
+  /**
+   * Synonym of {...@link #ALIGN_LOCALE_START}.
+   */
+  HorizontalAlignmentConstant ALIGN_DEFAULT = ALIGN_LOCALE_START;
+
   /**
    * Gets the horizontal alignment.
-   *
-   * @return the current horizontal alignment.
+   *
+   * @return the current horizontal alignment (
+   *         {...@link HasHorizontalAlignment#ALIGN_LEFT},
+   *         {...@link HasHorizontalAlignment#ALIGN_CENTER},
+   *         {...@link HasHorizontalAlignment#ALIGN_RIGHT},
+   *         {...@link HasHorizontalAlignment#ALIGN_JUSTIFY}, or
+   *         null).
    */
   HorizontalAlignmentConstant getHorizontalAlignment();

   /**
    * Sets the horizontal alignment.
-   *
+   * <p> Use {...@code null} to clear horizontal alignment, allowing it to be
+   * determined by the standard HTML mechanisms such as inheritance and CSS
+   * rules.
+   *
    * @param align the horizontal alignment (
-   *          {...@link HasHorizontalAlignment#ALIGN_LEFT},
-   *          {...@link HasHorizontalAlignment#ALIGN_CENTER},
-   *          {...@link HasHorizontalAlignment#ALIGN_RIGHT}), or
-   *          {...@link HasHorizontalAlignment#ALIGN_DEFAULT}).
+   *         {...@link HasHorizontalAlignment#ALIGN_LEFT},
+   *         {...@link HasHorizontalAlignment#ALIGN_CENTER},
+   *         {...@link HasHorizontalAlignment#ALIGN_RIGHT},
+   *         {...@link HasHorizontalAlignment#ALIGN_JUSTIFY},
+   *         {...@link HasHorizontalAlignment#ALIGN_LOCALE_START}, or
+   *         {...@link HasHorizontalAlignment#ALIGN_LOCALE_END}).
    */
   void setHorizontalAlignment(HorizontalAlignmentConstant align);
 }
=======================================
--- /trunk/user/src/com/google/gwt/user/client/ui/InlineHTML.java Wed Oct 28 09:10:53 2009 +++ /trunk/user/src/com/google/gwt/user/client/ui/InlineHTML.java Mon Aug 2 09:47:24 2010
@@ -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
@@ -17,20 +17,21 @@

 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.client.HasDirection.Direction;

 /**
  * A widget that can contain arbitrary HTML.
- *
+ *
  * This widget uses a &lt;span&gt; element, causing it to be displayed with
  * inline layout.
- *
+ *
  * <p>
  * If you only need a simple label (text, but not HTML), then the
* {...@link com.google.gwt.user.client.ui.Label} widget is more appropriate, as it * disallows the use of HTML, which can lead to potential security issues if not
  * used properly.
  * </p>
- *
+ *
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-InlineHTML { }</li>
@@ -41,11 +42,11 @@
   /**
    * Creates an InlineHTML widget that wraps an existing &lt;div&gt; or
    * &lt;span&gt; element.
-   *
+   *
* This element must already be attached to the document. If the element is
    * removed from the document, you must call
    * {...@link RootPanel#detachNow(Widget)}.
-   *
+   *
    * @param element the element to be wrapped
    */
   public static InlineHTML wrap(Element element) {
@@ -71,23 +72,36 @@

   /**
    * Creates an HTML widget with the specified HTML contents.
-   *
+   *
    * @param html the new widget's HTML contents
    */
   public InlineHTML(String html) {
     this();
     setHTML(html);
   }
+
+  /**
+   * Creates an HTML widget with the specified HTML contents and with the
+   * specified direction.
+   *
+   * @param html the new widget's HTML contents
+ * @param dir the content's direction. Note: {...@code Direction.DEFAULT} means
+   *        direction should be inherited from the widget's parent element.
+   */
+  public InlineHTML(String html, Direction dir) {
+    this();
+    setHTML(html, dir);
+  }

   /**
* This constructor may be used by subclasses to explicitly use an existing * element. This element must be either a &lt;div&gt; &lt;span&gt; element.
-   *
+   *
    * @param element the element to be used
    */
   protected InlineHTML(Element element) {
+    // super(element) also asserts that element is either a &lt;div&gt; or
+    // &lt;span&gt;.
     super(element);
-    assert element.getTagName().equalsIgnoreCase("div")
-        || element.getTagName().equalsIgnoreCase("span");
   }
 }
=======================================
--- /trunk/user/src/com/google/gwt/user/client/ui/InlineLabel.java Wed Oct 28 09:10:53 2009 +++ /trunk/user/src/com/google/gwt/user/client/ui/InlineLabel.java Mon Aug 2 09:47:24 2010
@@ -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
@@ -17,13 +17,14 @@

 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.client.HasDirection.Direction;

 /**
  * A widget that contains arbitrary text, <i>not</i> interpreted as HTML.
- *
+ *
  * This widget uses a &lt;span&gt; element, causing it to be displayed with
  * inline layout.
- *
+ *
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-InlineLabel { }</li>
@@ -34,11 +35,11 @@
   /**
    * Creates a InlineLabel widget that wraps an existing &lt;div&gt; or
    * &lt;span&gt; element.
-   *
+   *
* This element must already be attached to the document. If the element is
    * removed from the document, you must call
    * {...@link RootPanel#detachNow(Widget)}.
-   *
+   *
    * @param element the element to be wrapped
    */
   public static InlineLabel wrap(Element element) {
@@ -64,23 +65,35 @@

   /**
    * Creates a label with the specified text.
-   *
+   *
    * @param text the new label's text
    */
   public InlineLabel(String text) {
     this();
     setText(text);
   }
+
+  /**
+   * Creates a label with the specified text and direction.
+   *
+   * @param text the new label's text
+   * @param dir the text's direction. Note: {...@code Direction.DEFAULT} means
+   *        direction should be inherited from the widget's parent element.
+   */
+  public InlineLabel(String text, Direction dir) {
+    this();
+    setText(text, dir);
+  }

   /**
* This constructor may be used by subclasses to explicitly use an existing * element. This element must be either a &lt;div&gt; &lt;span&gt; element.
-   *
+   *
    * @param element the element to be used
    */
   protected InlineLabel(Element element) {
+    // super(element) also asserts that element is either a &lt;div&gt; or
+    // &lt;span&gt;.
     super(element);
-    assert element.getTagName().equalsIgnoreCase("div")
-        || element.getTagName().equalsIgnoreCase("span");
   }
 }
=======================================
--- /trunk/user/src/com/google/gwt/user/client/ui/Label.java Fri Mar 20 11:33:42 2009 +++ /trunk/user/src/com/google/gwt/user/client/ui/Label.java Mon Aug 2 09:47:24 2010
@@ -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
@@ -36,36 +36,40 @@
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.i18n.client.BidiUtils;
 import com.google.gwt.i18n.client.HasDirection;
+import com.google.gwt.i18n.shared.BidiFormatter;
+import com.google.gwt.i18n.shared.DirectionEstimator;
+import com.google.gwt.i18n.shared.HasDirectionEstimator;
+import com.google.gwt.i18n.shared.WordCountDirectionEstimator;

 /**
  * A widget that contains arbitrary text, <i>not</i> interpreted as HTML.
- *
+ *
* This widget uses a &lt;div&gt; element, causing it to be displayed with block
  * layout.
- *
+ *
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-Label { }</li>
  * </ul>
- *
+ *
  * <p>
  * <h3>Example</h3>
  * {...@example com.google.gwt.examples.HTMLExample}
  * </p>
  */
 @SuppressWarnings("deprecation")
-public class Label extends Widget implements HasHorizontalAlignment, HasText,
-    HasWordWrap, HasDirection, HasClickHandlers, SourcesClickEvents,
-    SourcesMouseEvents, HasAllMouseHandlers {
+public class Label extends Widget implements HasText, HasWordWrap, HasDirection,
+    HasClickHandlers, SourcesClickEvents, SourcesMouseEvents,
+ HasAllMouseHandlers, HasDirectionEstimator, HasAutoHorizontalAlignment {

   /**
* Creates a Label widget that wraps an existing &lt;div&gt; or &lt;span&gt;
    * element.
-   *
+   *
* This element must already be attached to the document. If the element is
    * removed from the document, you must call
    * {...@link RootPanel#detachNow(Widget)}.
-   *
+   *
    * @param element the element to be wrapped
    */
   public static Label wrap(Element element) {
@@ -81,29 +85,96 @@
     return label;
   }

+  /**
+   * The widget's auto horizontal alignment policy.
+   * @see HasAutoHorizontalAlignment
+   */
+  private AutoHorizontalAlignmentConstant autoHorizontalAlignment;
+
+  /**
+   * The direction of the widget's content.
+   * Note: this may not match the direction of the widget's top DOM element
+   * ({...@code getElement()}).
+   * See {...@link #setTextOrHtml(String, Direction, boolean)} for details.
+   */
+  private Direction contentDir;
+
+  /**
+   * The widget's DirectionEstimator object.
+   */
+  private DirectionEstimator directionEstimator;
+
+  /**
+   * The widget's horizontal alignment.
+   */
   private HorizontalAlignmentConstant horzAlign;

+  /**
+   * The initial direction of the widget's element.
+   */
+  private Direction initialElementDir;
+
+  /**
+   * Whether the widget is inline (a &lt;span&gt; element).
+ * This is needed because direction is handled differently for inline elements
+   * and for non-inline elements.
+   * <p>
+   * In case Label supports types of elements other than span and div, this
+ * should get true for any element that is inline by default. Another approach + * could be calculating the element's display property, but this may have some
+   * overhead, and is problematic when the element is yet unattached.
+   */
+  private boolean isElementInline;
+
+  /**
+   * Whether the widget contains a nested &lt;span&gt; element used to
+   * indicate the content's direction.
+   * <p>
+ * The widget's top element is used for this purpose when it is a &lt;div&gt;, + * but doing so on an inline element often results in garbling what follows
+   * it. Thus, when the widget's top element is a &lt;span&gt;, a nested
+ * &lt;span&gt; must be used to carry the content's direction, with an LRM or
+   * RLM character afterwards to prevent the garbling.
+   */
+  private boolean isSpanWrapped;
+
   /**
    * Creates an empty label.
    */
   public Label() {
     setElement(Document.get().createDivElement());
     setStyleName("gwt-Label");
+    isElementInline = false;
+    isSpanWrapped = false;
+    contentDir = Direction.DEFAULT;
+    initialElementDir = Direction.DEFAULT;
   }

   /**
    * Creates a label with the specified text.
-   *
+   *
    * @param text the new label's text
    */
   public Label(String text) {
     this();
     setText(text);
   }
+
+  /**
+   * Creates a label with the specified text and direction.
+   *
+   * @param text the new label's text
+ * @param dir the text's direction. Note that {...@code DEFAULT} means direction
+   *        should be inherited from the widget's parent element.
+   */
+  public Label(String text, Direction dir) {
+    this();
+    setText(text, dir);
+  }

   /**
    * Creates a label with the specified text.
-   *
+   *
    * @param text the new label's text
    * @param wordWrap <code>false</code> to disable word wrapping
    */
@@ -115,13 +186,17 @@
   /**
* This constructor may be used by subclasses to explicitly use an existing * element. This element must be either a &lt;div&gt; or &lt;span&gt; element.
-   *
+   *
    * @param element the element to be used
    */
   protected Label(Element element) {
     setElement(element);
-    assert element.getTagName().equalsIgnoreCase("div")
-        || element.getTagName().equalsIgnoreCase("span");
+    String tagName = element.getTagName();
+    isElementInline = tagName.equalsIgnoreCase("span");
+    assert isElementInline || tagName.equalsIgnoreCase("div");
+    isSpanWrapped = false;
+    initialElementDir = BidiUtils.getDirectionOnElement(element);
+    contentDir = initialElementDir;
   }

   public HandlerRegistration addClickHandler(ClickHandler handler) {
@@ -141,9 +216,9 @@
   }

   /**
-   * @deprecated Use {...@link #addMouseOverHandler} {...@link
-   * #addMouseMoveHandler}, {...@link #addMouseDownHandler}, {...@link
-   * #addMouseUpHandler} and {...@link #addMouseOutHandler} instead
+   * @deprecated Use {...@link #addMouseOverHandler},
+   * {...@link #addMouseMoveHandler}, {...@link #addMouseDownHandler},
+   * {...@link #addMouseUpHandler} and {...@link #addMouseOutHandler} instead
    */
   @Deprecated
   public void addMouseListener(MouseListener listener) {
@@ -178,16 +253,39 @@
     ListenerWrapper.WrappedMouseWheelListener.add(this, listener);
   }

+  /**
+   * {...@inheritdoc}
+   */
+  public AutoHorizontalAlignmentConstant getAutoHorizontalAlignment() {
+    return autoHorizontalAlignment;
+  }
+
+  public Direction getContentDirection() {
+    return contentDir;
+  }
+
+  /**
+   * Gets the widget element's direction.
+   * @deprecated Use {...@link #getContentDirection} instead
+   */
+  @Deprecated
   public Direction getDirection() {
     return BidiUtils.getDirectionOnElement(getElement());
   }

+  public DirectionEstimator getDirectionEstimator() {
+    return directionEstimator;
+  }
+
+  /**
+   * {...@inheritdoc}
+   */
   public HorizontalAlignmentConstant getHorizontalAlignment() {
     return horzAlign;
   }

   public String getText() {
-    return getElement().getInnerText();
+    return getTextOrHtml(false);
   }

   public boolean getWordWrap() {
@@ -195,7 +293,7 @@
   }

   /**
- * @deprecated Use the {...@link HandlerRegistration#removeHandler} method on + * @deprecated Use the {...@link HandlerRegistration#removeHandler} method on
    * the object returned by {...@link #addClickHandler} instead
    */
   @Deprecated
@@ -221,21 +319,217 @@
     ListenerWrapper.WrappedMouseWheelListener.remove(this, listener);
   }

+  /**
+   * {...@inheritdoc}
+   */
+  public void setAutoHorizontalAlignment(AutoHorizontalAlignmentConstant
+      autoAlignment) {
+    autoHorizontalAlignment = autoAlignment;
+    updateHorizontalAlignment();
+  }
+
+  /**
+   * Sets the widget element's direction.
+   * @deprecated Use {...@link #setDirectionEstimator} and / or pass explicit
+   * direction to {...@link #setText} instead
+   */
+  @Deprecated
   public void setDirection(Direction direction) {
     BidiUtils.setDirectionOnElement(getElement(), direction);
+    initialElementDir = direction;
+
+ // For backwards compatibility, assure there's no span wrap, and update the
+    // content direction.
+    setInnerTextOrHtml(getTextOrHtml(true), true);
+    isSpanWrapped = false;
+    contentDir = initialElementDir;
+    updateHorizontalAlignment();
   }

+  /**
+   * {...@inheritdoc}
+   * <p>
+   * See note at {...@link #setDirectionEstimator(DirectionEstimator)}.
+   */
+  public void setDirectionEstimator(boolean enabled) {
+ setDirectionEstimator(enabled ? WordCountDirectionEstimator.get() : null);
+  }
+
+  /**
+   * {...@inheritdoc}
+   * <p>
+   * Note: if the widget already has non-empty content, this will update
+   * its direction according to the new estimator's result. This may cause
+   * flicker, and thus should be avoided; DirectionEstimator should be set
+   * before the widget has any content.
+   */
+ public void setDirectionEstimator(DirectionEstimator directionEstimator) {
+    this.directionEstimator = directionEstimator;
+    // Refresh appearance
+    setTextOrHtml(getTextOrHtml(true), true);
+  }
+
+  /**
+   * {...@inheritdoc}
+   *
+   * <p> Note: A subsequent call to {...@link #setAutoHorizontalAlignment} may
+   * override the horizontal alignment set by this method.
+ * <p> Note: For {...@code null}, the horizontal alignment is cleared, allowing + * it to be determined by the standard HTML mechanisms such as inheritance and
+   * CSS rules.
+   * @see #setAutoHorizontalAlignment
+   */
   public void setHorizontalAlignment(HorizontalAlignmentConstant align) {
-    horzAlign = align;
- getElement().getStyle().setProperty("textAlign", align.getTextAlignString());
+    setAutoHorizontalAlignment(align);
   }

+  /**
+   * Sets the label's content to the given text.
+   * <p>
+   * Doesn't change the widget's direction or horizontal alignment if
+ * {...@code directionEstimator} is null. Otherwise, the widget's direction is
+   * set using the estimator, and its alignment may therefore change as
+   * described in {...@link #setText(String, Direction)}.
+   *
+   * @param text the widget's new text
+   */
   public void setText(String text) {
-    getElement().setInnerText(text);
+    setTextOrHtml(text, false);
+  }
+
+  /**
+ * Sets the label's content to the given text, applying the given direction.
+   * <p>
+   * This will have the following effect on the horizontal alignment:
+   * <ul>
+   * <li> If the automatic alignment setting is ALIGN_CONTENT_START or
+ * ALIGN_CONTENT_END, the horizontal alignment will be set to match the start
+   * or end edge, respectively, of the new direction (the {...@code dir}
+   * parameter). If that is DEFAULT, the locale direction is used.
+   * <li> Otherwise, the horizontal alignment value is not changed, but the
+ * effective alignment may nevertheless change according to the usual HTML + * rules, i.e. it will match the start edge of the new direction if the widget
+   * element is a &lt;div&gt; and has no explicit alignment value even by
+   * inheritance.
+   * </ul>
+   *
+   * @param text the widget's new text
+   * @param dir the text's direction. Note: {...@code Direction.DEFAULT} means
+   *        direction should be inherited from the widget's parent element.
+   */
+  public void setText(String text, Direction dir) {
+    setTextOrHtml(text, dir, false);
   }

   public void setWordWrap(boolean wrap) {
     getElement().getStyle().setProperty("whiteSpace",
         wrap ? "normal" : "nowrap");
   }
-}
+
+  protected String getTextOrHtml(boolean isHtml) {
+    Element element = isSpanWrapped ? getElement().getFirstChildElement()
+        : getElement();
+    return isHtml ? element.getInnerHTML() : element.getInnerText();
+  }
+
+  /**
+ * Sets the label's content to the given value (either plain text or HTML). + * See {...@link #setText(String)} for details on potential effects on direction
+   * and alignment.
+   *
+   * @param content the widget's new content
+   * @param isHtml whether the content is HTML
+   */
+  protected void setTextOrHtml(String content, boolean isHtml) {
+    if (directionEstimator == null) {
+      isSpanWrapped = false;
+      setInnerTextOrHtml(content, isHtml);
+
+ // Preserves the initial direction of the widget. This is different from + // passing the direction parameter explicitly as DEFAULT, which forces the
+      // widget to inherit the direction from its parent.
+      if (contentDir != initialElementDir) {
+        contentDir = initialElementDir;
+        BidiUtils.setDirectionOnElement(getElement(), initialElementDir);
+        updateHorizontalAlignment();
+      }
+    } else {
+      setTextOrHtml(content, directionEstimator.estimateDirection(content,
+          isHtml), isHtml);
+    }
+  }
+
+  /**
+ * Sets the label's content to the given value (either plain text or HTML),
+   * applying the given direction.
+ * See {...@link #setText(String, Direction)} for details on potential effects on
+   * alignment.
+   * <p>
+   * Implementation details:
+   * <ul>
+   * <li> If the widget's element is a &lt;div&gt;, sets its dir attribute
+   * according to the given direction.
+ * <li> Otherwise (i.e. the widget's element is a &lt;span&gt;), the direction + * is set using a nested &lt;span dir=...&gt; element which holds the content + * of the widget. This nested span may be followed by a zero-width Unicode + * direction character (LRM or RLM). This manipulation is necessary to prevent + * garbling in case the direction of the widget is opposite to the direction + * of its context. See {...@link com.google.gwt.i18n.shared.BidiFormatter} for
+   * more details.
+   * </ul>
+   *
+   * @param content the widget's new content
+   * @param dir the content's direction
+   * @param isHtml whether the content is HTML
+   */
+ protected void setTextOrHtml(String content, Direction dir, boolean isHtml) {
+    contentDir = dir;
+
+    // Set the text and the direction.
+    if (isElementInline) {
+      isSpanWrapped = true;
+      getElement().setInnerHTML(BidiFormatter.getInstanceForCurrentLocale(
+ true /* alwaysSpan */).spanWrapWithKnownDir(dir, content, isHtml));
+    } else {
+      isSpanWrapped = false;
+      BidiUtils.setDirectionOnElement(getElement(), dir);
+      setInnerTextOrHtml(content, isHtml);
+    }
+
+    // Update the horizontal alignment if needed.
+    updateHorizontalAlignment();
+  }
+
+  private void setInnerTextOrHtml(String content, boolean isHtml) {
+    if (isHtml) {
+      getElement().setInnerHTML(content);
+    } else {
+      getElement().setInnerText(content);
+    }
+  }
+
+  /**
+   * Sets the horizontal alignment of the widget according to the current
+   * AutoHorizontalAlignment setting.
+   */
+  private void updateHorizontalAlignment() {
+    HorizontalAlignmentConstant align;
+    if (autoHorizontalAlignment == null) {
+      align = null;
+ } else if (autoHorizontalAlignment instanceof HorizontalAlignmentConstant) {
+      align = (HorizontalAlignmentConstant) autoHorizontalAlignment;
+    } else {
+      /* autoHorizontalAlignment is a truly automatic policy, i.e. either
+      ALIGN_CONTENT_START or ALIGN_CONTENT_END */
+      align = autoHorizontalAlignment == ALIGN_CONTENT_START ?
+          HorizontalAlignmentConstant.startOf(contentDir) :
+          HorizontalAlignmentConstant.endOf(contentDir);
+    }
+
+    if (align != horzAlign) {
+      horzAlign = align;
+ getElement().getStyle().setProperty("textAlign", horzAlign == null ? ""
+          : horzAlign.getTextAlignString());
+    }
+  }
+}
=======================================
--- /trunk/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParserTest.java Fri Jul 2 04:56:18 2010 +++ /trunk/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParserTest.java Mon Aug 2 09:47:24 2010
@@ -49,16 +49,19 @@
     assertEquals(HHA + ".ALIGN_LEFT", parser.parse("left"));
     assertEquals(HHA + ".ALIGN_CENTER", parser.parse("center"));
     assertEquals(HHA + ".ALIGN_RIGHT", parser.parse("right"));
+    assertEquals(HHA + ".ALIGN_JUSTIFY", parser.parse("justify"));
     // capitalized
     assertEquals(HHA + ".ALIGN_LEFT", parser.parse("Left"));
     assertEquals(HHA + ".ALIGN_CENTER", parser.parse("Center"));
     assertEquals(HHA + ".ALIGN_RIGHT", parser.parse("Right"));
+    assertEquals(HHA + ".ALIGN_JUSTIFY", parser.parse("Justify"));
   }

   public void testUglyNames() throws UnableToCompleteException {
     assertEquals(HHA + ".ALIGN_LEFT", parser.parse("ALIGN_LEFT"));
     assertEquals(HHA + ".ALIGN_CENTER", parser.parse("ALIGN_CENTER"));
     assertEquals(HHA + ".ALIGN_RIGHT", parser.parse("ALIGN_RIGHT"));
+    assertEquals(HHA + ".ALIGN_JUSTIFY", parser.parse("ALIGN_JUSTIFY"));
   }

   public void testBad() {
=======================================
--- /trunk/user/test/com/google/gwt/user/UISuite.java Mon Jun 7 12:20:31 2010 +++ /trunk/user/test/com/google/gwt/user/UISuite.java Mon Aug 2 09:47:24 2010
@@ -55,12 +55,14 @@
 import com.google.gwt.user.client.ui.FormPanelTest;
 import com.google.gwt.user.client.ui.GridTest;
 import com.google.gwt.user.client.ui.HTMLPanelTest;
+import com.google.gwt.user.client.ui.HTMLTest;
 import com.google.gwt.user.client.ui.HiddenTest;
 import com.google.gwt.user.client.ui.HistoryTest;
 import com.google.gwt.user.client.ui.HorizontalPanelTest;
 import com.google.gwt.user.client.ui.HorizontalSplitPanelTest;
 import com.google.gwt.user.client.ui.HyperlinkTest;
 import com.google.gwt.user.client.ui.ImageTest;
+import com.google.gwt.user.client.ui.LabelTest;
 import com.google.gwt.user.client.ui.LazyPanelTest;
 import com.google.gwt.user.client.ui.LinearPanelTest;
 import com.google.gwt.user.client.ui.ListBoxTest;
@@ -150,9 +152,11 @@
     suite.addTestSuite(HorizontalPanelTest.class);
     suite.addTestSuite(HorizontalSplitPanelTest.class);
     suite.addTestSuite(HTMLPanelTest.class);
+    suite.addTestSuite(HTMLTest.class);
     suite.addTestSuite(HyperlinkTest.class);
     suite.addTestSuite(ImageBundleGeneratorTest.class);
     suite.addTestSuite(ImageTest.class);
+    suite.addTestSuite(LabelTest.class);
     suite.addTestSuite(LayoutTest.class);
     suite.addTestSuite(LazyPanelTest.class);
     suite.addTestSuite(LinearPanelTest.class);

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to