All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.user.client.ui.DirectionalTextHelper Maven / Gradle / Ivy

There is a newer version: 2.12.1
Show newest version
/*
 * 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.shared.BidiFormatter;
import com.google.gwt.i18n.shared.DirectionEstimator;
import com.google.gwt.i18n.shared.HasDirectionEstimator;
import com.google.gwt.i18n.shared.WordCountDirectionEstimator;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.annotations.IsSafeHtml;
import com.google.gwt.safehtml.shared.annotations.SuppressIsSafeHtmlCastCheck;

/**
 * A helper class for displaying bidi (i.e. potentially opposite-direction) text 
 * or HTML in an element.
 * Note: this class assumes that callers perform all their text/html and
 * direction manipulations through it alone.
 */
public class DirectionalTextHelper implements HasDirectionEstimator {

  /**
   * A default direction estimator instance.
   */
  public static final DirectionEstimator DEFAULT_DIRECTION_ESTIMATOR =
      WordCountDirectionEstimator.get();

  /**
   * The DirectionEstimator object.
   */
  private DirectionEstimator directionEstimator;

  /**
   * The target element.
   */
  private final Element element;

  /**
   * The initial direction of the element.
   */
  private Direction initialElementDir;

  /**
   * Whether direction was explicitly set on the last {@code setTextOrHtml}
   * call. If so, {@link #setDirectionEstimator} will refrain from modifying the
   * direction until {@link #setTextOrHtml} is called without specifying an
   * explicit direction.
   */
  private boolean isDirectionExplicitlySet;

  /**
   * Whether the element is inline (e.g. a <span> element, but not a block
   * element like <div>).
   * This is needed because direction is handled differently for inline elements
   * and for non-inline elements.
   */
  private final boolean isElementInline;

  /**
   * Whether the element contains a nested <span> element used to
   * indicate the content's direction.
   * 

* The element itself is used for this purpose when it is a block element * (i.e. !isElementInline), but doing so on an inline element often results in * garbling what follows it. Thus, when the element is inline, a nested * <span> must be used to carry the content's direction, with an LRM or * RLM character afterwards to prevent the garbling. */ private boolean isSpanWrapped; /** * The direction of the element's content. * Note: this may not match the direction attribute of the element itself. * See * {@link #setText(String, com.google.gwt.i18n.client.HasDirection.Direction, boolean) * setText(String, Direction, boolean)} * for details. */ private Direction textDir; /** * @param element The widget's element holding text. * @param isElementInline Whether the element is an inline element. */ public DirectionalTextHelper(Element element, boolean isElementInline) { this.element = element; this.isElementInline = isElementInline; isSpanWrapped = false; this.initialElementDir = BidiUtils.getDirectionOnElement(element); textDir = initialElementDir; // setDirectionEstimator shouldn't refresh appearance of initial empty text. isDirectionExplicitlySet = true; } public DirectionEstimator getDirectionEstimator() { return directionEstimator; } public Direction getTextDirection() { return textDir; } /** * Get the inner text of the element, taking the inner span wrap into * consideration, if needed. * * @return the text */ public String getText() { return getTextOrHtml(false /* isHtml */); } /** * Get the inner html of the element, taking the inner span wrap into * consideration, if needed. * * @return the html */ public String getHtml() { return getTextOrHtml(true /* isHtml */); } /** * Get the inner text or html of the element, taking the inner span wrap into consideration, if * needed. Prefer using {@link #getText} or {@link #getHtml} instead of this method. * * @param isHtml true to get the inner html, false to get the inner text * @return the text or html */ public String getTextOrHtml(boolean isHtml) { Element elem = isSpanWrapped ? element.getFirstChildElement() : element; return isHtml ? elem.getInnerHTML() : elem.getInnerText(); } /** * Provides implementation for HasDirection's method setDirection (normally * deprecated), dealing with backwards compatibility issues. * @deprecated */ @Deprecated @SuppressIsSafeHtmlCastCheck public void setDirection(Direction direction) { BidiUtils.setDirectionOnElement(element, direction); initialElementDir = direction; /* * For backwards compatibility, assure there's no span wrap, and update the * content direction. */ setInnerTextOrHtml(getHtml(), true); // TODO: mXSS? isSpanWrapped = false; textDir = initialElementDir; isDirectionExplicitlySet = true; } /** * See note at * {@link #setDirectionEstimator(com.google.gwt.i18n.shared.DirectionEstimator)}. */ public void setDirectionEstimator(boolean enabled) { setDirectionEstimator(enabled ? DEFAULT_DIRECTION_ESTIMATOR : null); } /** * Note: if the element 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 element has any content. */ @SuppressIsSafeHtmlCastCheck public void setDirectionEstimator(DirectionEstimator directionEstimator) { this.directionEstimator = directionEstimator; /* * Refresh appearance unless direction was explicitly set on last * setTextOrHtml call. */ if (!isDirectionExplicitlySet) { setHtml(getHtml()); // TODO: mXSS } } /** * Sets the element's content to the given value (plain text). * If direction estimation is off, the direction is verified to match the * element's initial direction. Otherwise, the direction is affected as * described at * {@link #setText(String, com.google.gwt.i18n.client.HasDirection.Direction) * setText(String, Direction)}. * * @param content the element's new content */ @SuppressIsSafeHtmlCastCheck public void setText(String content) { setTextOrHtml(content, false /* isHtml */); } /** * Sets the element's content to the given value (html). * If direction estimation is off, the direction is verified to match the * element's initial direction. Otherwise, the direction is affected as * described at * {@link #setHtml(String, com.google.gwt.i18n.client.HasDirection.Direction) * setHtml(String, Direction)}. * * @param content the element's new content */ public void setHtml(SafeHtml content) { setHtml(content.asString()); } /** * Sets the element's content to the given value (html). * If direction estimation is off, the direction is verified to match the * element's initial direction. Otherwise, the direction is affected as * described at * {@link #setHtml(String, com.google.gwt.i18n.client.HasDirection.Direction) * setHtml(String, Direction)}. * * @param content the element's new content */ public void setHtml(@IsSafeHtml String content) { setTextOrHtml(content, true /* isHtml */); } /** * Sets the element's content to the given value (either plain text or HTML). * Prefer using {@link #setText(String) setText} or {@link #setHtml(String) setHtml} instead of * this method. * * @param content the element's new content * @param isHtml whether the content is HTML */ public void setTextOrHtml(@IsSafeHtml String content, boolean isHtml) { if (directionEstimator == null) { isSpanWrapped = false; setInnerTextOrHtml(content, isHtml); /* * Preserves the initial direction of the element. This is different from * passing the direction parameter explicitly as DEFAULT, which forces the * element to inherit the direction from its parent. */ if (textDir != initialElementDir) { textDir = initialElementDir; BidiUtils.setDirectionOnElement(element, initialElementDir); } } else { setTextOrHtml(content, directionEstimator.estimateDirection(content, isHtml), isHtml); } isDirectionExplicitlySet = false; } /** * Sets the element's content to the given value (plain text), applying the * given direction. *

* Implementation details: *

    *
  • If the element is a block element, sets its dir attribute according * to the given direction. *
  • Otherwise (i.e. the element is inline), the direction is set using a * nested <span dir=...> element which holds the content of the element. * 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 element is opposite to the direction of its * context. See {@link com.google.gwt.i18n.shared.BidiFormatter} for more * details. *
* * @param content the element's new content * @param dir the content's direction */ @SuppressIsSafeHtmlCastCheck public void setText(String content, Direction dir) { setTextOrHtml(content, dir, false /* isHtml */); } /** * Sets the element's content to the given value (html), applying the given * direction. *

* Implementation details: *

    *
  • If the element is a block element, sets its dir attribute according * to the given direction. *
  • Otherwise (i.e. the element is inline), the direction is set using a * nested <span dir=...> element which holds the content of the element. * 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 element is opposite to the direction of its * context. See {@link com.google.gwt.i18n.shared.BidiFormatter} for more * details. *
* * @param content the element's new content * @param dir the content's direction */ public void setHtml(SafeHtml content, Direction dir) { setHtml(content.asString(), dir); } /** * Sets the element's content to the given value (html), applying the given * direction. *

* Implementation details: *

    *
  • If the element is a block element, sets its dir attribute according * to the given direction. *
  • Otherwise (i.e. the element is inline), the direction is set using a * nested <span dir=...> element which holds the content of the element. * 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 element is opposite to the direction of its * context. See {@link com.google.gwt.i18n.shared.BidiFormatter} for more * details. *
* * @param content the element's new content * @param dir the content's direction */ public void setHtml(@IsSafeHtml String content, Direction dir) { setTextOrHtml(content, dir, true /* isHtml */); } /** * Sets the element's content to the given value (either plain text or HTML), * applying the given direction. Prefer using * {@link #setText(String, com.google.gwt.i18n.client.HasDirection.Direction) setText} or * {@link #setHtml(String, com.google.gwt.i18n.client.HasDirection.Direction) setHtml} * instead of this method. * * @param content the element's new content * @param dir the content's direction * @param isHtml whether the content is HTML */ public void setTextOrHtml(@IsSafeHtml String content, Direction dir, boolean isHtml) { textDir = dir; // Set the text and the direction. if (isElementInline) { isSpanWrapped = true; element.setInnerHTML(BidiFormatter.getInstanceForCurrentLocale( true /* alwaysSpan */).spanWrapWithKnownDir(dir, content, isHtml)); } else { isSpanWrapped = false; BidiUtils.setDirectionOnElement(element, dir); setInnerTextOrHtml(content, isHtml); } isDirectionExplicitlySet = true; } private void setInnerTextOrHtml(@IsSafeHtml String content, boolean isHtml) { if (isHtml) { element.setInnerHTML(content); } else { element.setInnerText(content); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy