org.eclipse.jface.util.BidiUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jface.util.BidiUtils.java

Source

/*******************************************************************************
 * Copyright (c) 2012, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 ******************************************************************************/

package org.eclipse.jface.util;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.equinox.bidi.StructuredTextTypeHandlerFactory;
import org.eclipse.jface.internal.InternalPolicy;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SegmentListener;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;

/**
 * This class provides API to handle Base Text Direction (BTD) and
 * Structured Text support for SWT Text widgets.
 *
 * @since 3.9
 */
public final class BidiUtils {

    /**
     * Left-To-Right Base Text Direction.
     * @see #getTextDirection()
     */
    public static final String LEFT_TO_RIGHT = "ltr"; //$NON-NLS-1$

    /**
     * Right-To-Left Base Text Direction.
     * @see #getTextDirection()
     */
    public static final String RIGHT_TO_LEFT = "rtl";//$NON-NLS-1$

    /**
     * Auto (contextual) Base Text Direction.
     * @see #getTextDirection()
     */
    public static final String AUTO = "auto";//$NON-NLS-1$

    /**
     * Base Text Direction defined in {@link BidiUtils#getTextDirection()}
     * @see #getSegmentListener(String)
     * @see #applyBidiProcessing(Text, String)
     */
    public static final String BTD_DEFAULT = "default";//$NON-NLS-1$

    /**
     * Visual Left-To-Right Text Direction.
     * <p>
     * <b>Note:</b> This handling type is deprecated and should only be used
     * when interfacing with legacy systems that store data in visual order.
     *
     * @see <a
     *      href="http://www.w3.org/International/questions/qa-visual-vs-logical">http://www.w3.org/International/questions/qa-visual-vs-logical</a>
     * @see #getSegmentListener(String)
     * @see #applyBidiProcessing(Text, String)
     *
     * @since 3.11
     */
    public static final String VISUAL_LEFT_TO_RIGHT = "visualltr"; //$NON-NLS-1$

    /**
     * Visual Right-To-Left Text Direction
     * <p>
     * <b>Note:</b> This handling type is deprecated and should only be used
     * when interfacing with legacy systems that store data in visual order.
     *
     * @see <a
     *      href="http://www.w3.org/International/questions/qa-visual-vs-logical">http://www.w3.org/International/questions/qa-visual-vs-logical</a>
     * @see #getSegmentListener(String)
     * @see #applyBidiProcessing(Text, String)
     *
     * @since 3.11
     */
    public static final String VISUAL_RIGHT_TO_LEFT = "visualrtl";//$NON-NLS-1$

    /**
     * Segment listener for LTR Base Text Direction
     */
    private static final SegmentListener BASE_TEXT_DIRECTION_LTR = new BaseTextDirectionSegmentListener(
            LEFT_TO_RIGHT);

    /**
     * Segment listener for RTL Base Text Direction
     */
    private static final SegmentListener BASE_TEXT_DIRECTION_RTL = new BaseTextDirectionSegmentListener(
            RIGHT_TO_LEFT);

    /**
     * Segment listener for Auto (Contextual) Base Text Direction
     */
    private static final SegmentListener BASE_TEXT_DIRECTION_AUTO = new BaseTextDirectionSegmentListener(AUTO);

    /**
     * Segment listener for LTR Visual Text Direction
     */
    private static final SegmentListener VISUAL_TEXT_DIRECTION_LTR = new VisualTextDirectionSegmentListener(
            VISUAL_LEFT_TO_RIGHT);

    /**
     * Segment listener for RTL Visual Text Direction
     */
    private static final SegmentListener VISUAL_TEXT_DIRECTION_RTL = new VisualTextDirectionSegmentListener(
            VISUAL_RIGHT_TO_LEFT);

    /**
     * Listener cache. Map from structured text type id ({@link String}) to
     * structured text segment listener ({@link SegmentListener}).
     */
    private static final Map<String, SegmentListener> structuredTextSegmentListeners = new HashMap<>();

    /**
     * The LRE char
     */
    static final char LRE = 0x202A;

    /**
     * The LRM char
     */
    static final char LRM = 0x200E;

    /**
     * The PDF char
     */
    static final char PDF = 0x202C;

    /**
     * The RLE char
     */
    static final char RLE = 0x202B;

    /**
     * The LRO char
     */
    static final char LRO = 0x202D;

    /**
     * The RLO char
     */
    static final char RLO = 0x202E;

    private static boolean bidiSupport = false;
    private static String textDirection = "";//$NON-NLS-1$

    private BidiUtils() {
        // no instances
    }

    /**
     * Returns the Base Text Direction. Possible values are:
     * <ul>
     * <li>{@link BidiUtils#LEFT_TO_RIGHT}</li>
     * <li>{@link BidiUtils#RIGHT_TO_LEFT}</li>
     * <li>{@link BidiUtils#AUTO}</li>
     * <li><code>null</code> (no direction set)</li>
     * </ul>
     *
     * @return the base text direction
     */
    public static String getTextDirection() {
        return textDirection;
    }

    /**
     * Sets the Base Text Direction. Possible values are:
     * <ul>
     * <li>{@link BidiUtils#LEFT_TO_RIGHT}</li>
     * <li>{@link BidiUtils#RIGHT_TO_LEFT}</li>
     * <li>{@link BidiUtils#AUTO}</li>
     * <li><code>null</code> (no default direction)</li>
     * </ul>
     *
     * @param direction the text direction to set
     * @throws IllegalArgumentException if <code>direction</code> is not legal
     */
    public static void setTextDirection(String direction) {
        if (direction == null || LEFT_TO_RIGHT.equals(direction) || RIGHT_TO_LEFT.equals(direction)
                || AUTO.equals(direction)) {
            textDirection = direction;
        } else {
            throw new IllegalArgumentException(direction);
        }
    }

    /**
     * Returns whether bidi support is enabled.
     *
     * @return <code>true</code> iff bidi support is enabled
     */
    public static boolean getBidiSupport() {
        return bidiSupport;
    }

    /**
     * Enables or disables bidi support.
     *
     * @param bidi <code>true</code> to enable bidi support, <code>false</code> to disable
     */
    public static void setBidiSupport(boolean bidi) {
        bidiSupport = bidi;
    }

    /**
     * Applies bidi processing to the given text field.
     *
     * <p>
     * Possible values for <code>handlingType</code> are:
     * <ul>
     * <li>{@link BidiUtils#LEFT_TO_RIGHT}</li>
     * <li>{@link BidiUtils#RIGHT_TO_LEFT}</li>
     * <li>{@link BidiUtils#AUTO}</li>
     * <li>{@link BidiUtils#BTD_DEFAULT}</li>
     * <li>{@link BidiUtils#VISUAL_LEFT_TO_RIGHT}</li>
     * <li>{@link BidiUtils#VISUAL_RIGHT_TO_LEFT}</li>
     * <li>the <code>String</code> constants in
     * {@link StructuredTextTypeHandlerFactory}</li>
     * <li>if OSGi is running, the types that have been contributed to the
     * <code>org.eclipse.equinox.bidi.bidiTypes</code> extension point.</li>
     * </ul>
     * <p>
     * The 3 values {@link #LEFT_TO_RIGHT}, {@link #RIGHT_TO_LEFT}, and
     * {@link #AUTO} are usable whether {@link #getBidiSupport() bidi support}
     * is enabled or disabled.
     * <p>
     * The remaining values only have an effect if bidi support is enabled.
     * <p>
     * The 4 first values {@link #LEFT_TO_RIGHT}, {@link #RIGHT_TO_LEFT},
     * {@link #AUTO}, and {@link #BTD_DEFAULT} are for Base Text Direction (BTD)
     * handling. The remaining values are for Structured Text handling.
     * <p>
     * <strong>Note:</strong> If this method is called on a text control, then
     * {@link #applyTextDirection(Control, String)} must not be called on the
     * same control.
     * <p>
     * <strong>Note:</strong> The Structured Text handling only works if the
     * <code>org.eclipse.equinox.bidi</code> bundle is on the classpath!
     * </p>
     *
     * <p>
     * <strong>Note:</strong>
     * {@link org.eclipse.swt.widgets.Text#addSegmentListener(SegmentListener)}
     * is currently only implemented on Windows and GTK, so this method won't
     * have an effect on Cocoa.
     *
     * @param field
     *            the text field
     * @param handlingType
     *            the type of handling
     * @throws IllegalArgumentException
     *             if <code>handlingType</code> is not a known type identifier
     */
    public static void applyBidiProcessing(Text field, String handlingType) {
        SegmentListener listener = getSegmentListener(handlingType);
        if (listener != null) {
            field.addSegmentListener(listener);
            if (InternalPolicy.DEBUG_BIDI_UTILS) {
                int color = 0;
                if (LEFT_TO_RIGHT.equals(handlingType)) {
                    color = SWT.COLOR_RED;
                } else if (RIGHT_TO_LEFT.equals(handlingType)) {
                    color = SWT.COLOR_GREEN;
                } else if (BTD_DEFAULT.equals(handlingType)) {
                    color = SWT.COLOR_YELLOW;
                } else if (AUTO.equals(handlingType)) {
                    color = SWT.COLOR_MAGENTA;
                } else {
                    color = SWT.COLOR_CYAN;
                }
                field.setBackground(field.getDisplay().getSystemColor(color));
                if (field.getMessage().length() == 0) {
                    field.setMessage('<' + handlingType + '>');
                }
                if (field.getToolTipText() == null) {
                    field.setToolTipText('<' + handlingType + '>');
                }
            }
        }
    }

    /**
     * Applies bidi processing to the given styled text field.
     *
     * <p>
     * Possible values for <code>handlingType</code> are:
     * <ul>
     * <li>{@link BidiUtils#LEFT_TO_RIGHT}</li>
     * <li>{@link BidiUtils#RIGHT_TO_LEFT}</li>
     * <li>{@link BidiUtils#AUTO}</li>
     * <li>{@link BidiUtils#BTD_DEFAULT}</li>
     * <li>{@link BidiUtils#VISUAL_LEFT_TO_RIGHT}</li>
     * <li>{@link BidiUtils#VISUAL_RIGHT_TO_LEFT}</li>
     * <li>the <code>String</code> constants in
     * {@link StructuredTextTypeHandlerFactory}</li>
     * <li>if OSGi is running, the types that have been contributed to the
     * <code>org.eclipse.equinox.bidi.bidiTypes</code> extension point.</li>
     * </ul>
     * <p>
     * The 3 values {@link #LEFT_TO_RIGHT}, {@link #RIGHT_TO_LEFT}, and
     * {@link #AUTO} are usable whether {@link #getBidiSupport() bidi support}
     * is enabled or disabled.
     * <p>
     * The remaining values only have an effect if bidi support is enabled.
     * <p>
     * The 4 first values {@link #LEFT_TO_RIGHT}, {@link #RIGHT_TO_LEFT},
     * {@link #AUTO}, and {@link #BTD_DEFAULT} are for Base Text Direction (BTD)
     * handling. The remaining values are for Structured Text handling.
     * <p>
     * <strong>Note:</strong> If this method is called on a text control, then
     * {@link #applyTextDirection(Control, String)} must not be called on the
     * same control.
     * <p>
     * <strong>Note:</strong> The Structured Text handling only works if the
     * <code>org.eclipse.equinox.bidi</code> bundle is on the classpath!
     * </p>
     *
     * @param field
     *            the styled text field
     * @param handlingType
     *            the type of handling
     * @throws IllegalArgumentException
     *             if <code>handlingType</code> is not a known type identifier
     */
    public static void applyBidiProcessing(StyledText field, String handlingType) {
        final SegmentListener listener = getSegmentListener(handlingType);
        if (listener != null) {
            field.addBidiSegmentListener(listener::getSegments);
        }
    }

    /**
     * Applies bidi processing to the given combo.
     *
     * <p>
     * Possible values for <code>handlingType</code> are:
     * <ul>
     * <li>{@link BidiUtils#LEFT_TO_RIGHT}</li>
     * <li>{@link BidiUtils#RIGHT_TO_LEFT}</li>
     * <li>{@link BidiUtils#AUTO}</li>
     * <li>{@link BidiUtils#BTD_DEFAULT}</li>
     * <li>{@link BidiUtils#VISUAL_LEFT_TO_RIGHT}</li>
     * <li>{@link BidiUtils#VISUAL_RIGHT_TO_LEFT}</li>
     * <li>the <code>String</code> constants in
     * {@link StructuredTextTypeHandlerFactory}</li>
     * <li>if OSGi is running, the types that have been contributed to the
     * <code>org.eclipse.equinox.bidi.bidiTypes</code> extension point.</li>
     * </ul>
     * <p>
     * The 3 values {@link #LEFT_TO_RIGHT}, {@link #RIGHT_TO_LEFT}, and
     * {@link #AUTO} are usable whether {@link #getBidiSupport() bidi support}
     * is enabled or disabled.
     * <p>
     * The remaining values only have an effect if bidi support is enabled.
     * <p>
     * The 4 first values {@link #LEFT_TO_RIGHT}, {@link #RIGHT_TO_LEFT},
     * {@link #AUTO}, and {@link #BTD_DEFAULT} are for Base Text Direction (BTD)
     * handling. The remaining values are for Structured Text handling.
     * <p>
     * <strong>Note:</strong> If this method is called on a combo control, then
     * {@link #applyTextDirection(Control, String)} must not be called on the
     * same control.
     * <p>
     * <strong>Note:</strong> The Structured Text handling only works if the
     * <code>org.eclipse.equinox.bidi</code> bundle is on the classpath!
     * </p>
     *
     * <p>
     * <strong>Note:</strong>
     * {@link org.eclipse.swt.widgets.Combo#addSegmentListener(SegmentListener)}
     * is currently only implemented on Windows so this method won't have an
     * effect on Cocoa and GTK.
     *
     * @param combo
     *            the combo field
     * @param handlingType
     *            the type of handling
     * @throws IllegalArgumentException
     *             if <code>handlingType</code> is not a known type identifier
     * @since 3.10
     */
    public static void applyBidiProcessing(Combo combo, String handlingType) {
        SegmentListener listener = getSegmentListener(handlingType);
        if (listener != null) {
            combo.addSegmentListener(listener);
            if (InternalPolicy.DEBUG_BIDI_UTILS) {
                int color = 0;
                if (LEFT_TO_RIGHT.equals(handlingType)) {
                    color = SWT.COLOR_RED;
                } else if (RIGHT_TO_LEFT.equals(handlingType)) {
                    color = SWT.COLOR_GREEN;
                } else if (BTD_DEFAULT.equals(handlingType)) {
                    color = SWT.COLOR_YELLOW;
                } else if (AUTO.equals(handlingType)) {
                    color = SWT.COLOR_MAGENTA;
                } else {
                    color = SWT.COLOR_CYAN;
                }
                combo.setBackground(combo.getDisplay().getSystemColor(color));
                if (combo.getToolTipText() == null) {
                    combo.setToolTipText('<' + handlingType + '>');
                }
            }
        }
    }

    /**
     * Returns a segment listener for the given <code>handlingType</code> that
     * can e.g. be passed to {@link Text#addSegmentListener(SegmentListener)}.
     *
     * <p>
     * <strong>Note:</strong> The Structured Text handling only works if the
     * <code>org.eclipse.equinox.bidi</code> bundle is on the classpath!
     * </p>
     *
     * @param handlingType
     *            the handling type as specified in
     *            {@link #applyBidiProcessing(Text, String)}
     * @return the segment listener, or <code>null</code> if no handling is
     *         required
     * @throws IllegalArgumentException
     *             if <code>handlingType</code> is not a known type identifier
     * @see #applyBidiProcessing(Text, String)
     */
    public static SegmentListener getSegmentListener(String handlingType) {
        SegmentListener listener = null;
        if (LEFT_TO_RIGHT.equals(handlingType)) {
            listener = BASE_TEXT_DIRECTION_LTR;
        } else if (RIGHT_TO_LEFT.equals(handlingType)) {
            listener = BASE_TEXT_DIRECTION_RTL;
        } else if (AUTO.equals(handlingType)) {
            listener = BASE_TEXT_DIRECTION_AUTO;

        } else if (getBidiSupport()) {
            if (BTD_DEFAULT.equals(handlingType)) {
                if (LEFT_TO_RIGHT.equals(getTextDirection())) {
                    listener = BASE_TEXT_DIRECTION_LTR;
                } else if (RIGHT_TO_LEFT.equals(getTextDirection())) {
                    listener = BASE_TEXT_DIRECTION_RTL;
                } else if (AUTO.equals(getTextDirection())) {
                    listener = BASE_TEXT_DIRECTION_AUTO;
                }
            } else if (VISUAL_LEFT_TO_RIGHT.equals(handlingType)) {
                listener = VISUAL_TEXT_DIRECTION_LTR;
            } else if (VISUAL_RIGHT_TO_LEFT.equals(handlingType)) {
                listener = VISUAL_TEXT_DIRECTION_RTL;
            } else {
                Object handler = structuredTextSegmentListeners.get(handlingType);
                if (handler != null) {
                    listener = (SegmentListener) handler;
                } else {
                    listener = new StructuredTextSegmentListener(handlingType);
                    structuredTextSegmentListeners.put(handlingType, listener);
                }
            }
        }
        return listener;
    }

    /**
     * Applies a Base Text Direction to the given control (and its descendants,
     * if it's a {@link Composite}).
     *
     * <p>
     * Possible values for <code>textDirection</code> are:
     * <ul>
     * <li>{@link BidiUtils#LEFT_TO_RIGHT}</li>
     * <li>{@link BidiUtils#RIGHT_TO_LEFT}</li>
     * <li>{@link BidiUtils#AUTO}</li>
     * <li>{@link BidiUtils#BTD_DEFAULT}</li>
     * </ul>
     * <p>
     * The 3 values {@link #LEFT_TO_RIGHT}, {@link #RIGHT_TO_LEFT}, and
     * {@link BidiUtils#AUTO} are usable whether {@link #getBidiSupport() bidi
     * support} is enabled or disabled.
     * <p>
     * The remaining value {@link BidiUtils#BTD_DEFAULT} only has an effect if
     * bidi support is enabled.
     *
     * <p>
     * <strong>Note:</strong> If this method is called on a control, then no
     * <code>applyBidiProcessing</code> method must be called on the same
     * control.
     * <p>
     * <strong>Note:</strong>
     * {@link org.eclipse.swt.widgets.Control#setTextDirection(int)} is
     * currently only implemented on Windows, so the direction won't be
     * inherited by descendants on GTK and Cocoa.
     * <p>
     *
     * @param control
     *            the control
     * @param textDirection
     *            the text direction
     */
    public static void applyTextDirection(Control control, String textDirection) {
        int textDir = 0;

        if (LEFT_TO_RIGHT.equals(textDirection)) {
            textDir = SWT.LEFT_TO_RIGHT;
        } else if (RIGHT_TO_LEFT.equals(textDirection)) {
            textDir = SWT.RIGHT_TO_LEFT;
        } else if (AUTO.equals(textDirection)) {
            textDir = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
        } else if (getBidiSupport() && BTD_DEFAULT.equals(textDirection)) {
            if (LEFT_TO_RIGHT.equals(getTextDirection())) {
                textDir = SWT.LEFT_TO_RIGHT;
            } else if (RIGHT_TO_LEFT.equals(getTextDirection())) {
                textDir = SWT.RIGHT_TO_LEFT;
            } else if (AUTO.equals(getTextDirection())) {
                textDir = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
            }
        }

        if (control instanceof Text && textDir != 0) {
            applyBidiProcessing((Text) control, textDirection);
        } else if (control instanceof StyledText && textDir != 0) {
            applyBidiProcessing((StyledText) control, textDirection);
        } else if (control instanceof Combo && textDir != 0) {
            applyBidiProcessing((Combo) control, textDirection);
        } else if (textDir != 0) {
            control.setTextDirection(textDir);
        }
    }
}