view org.eclipse.draw2d/src/org/eclipse/draw2d/text/BidiProcessor.d @ 16:dbfb303e8fb0

first complete successful compile (win-only)
author Frank Benoit <benoit@tionex.de>
date Wed, 18 Mar 2009 08:56:47 +0100
parents bc29606a740c
children
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2004, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 * Port to the D programming language:
 *     Frank Benoit <benoit@tionex.de>
 *******************************************************************************/
module org.eclipse.draw2d.text.BidiProcessor;

import java.lang.all;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.TextLayout;
import org.eclipse.draw2d.text.FlowFigure;
import org.eclipse.draw2d.text.BidiInfo;
import org.eclipse.draw2d.text.FlowUtilities;

class Bidi{
    public static bool requiresBidi( char[], int, int ){
        return false;
    }
}

/**
 * A helper class for a BlockFlow that does Bidi evaluation of all the text in that block.
 * <p>
 * WARNING: This class is for INTERNAL use only.
 * @author Pratik Shah
 * @since 3.1
 */
public final class BidiProcessor {

/*
 * $TODO Workaround for Carbon.  AWT DLL cannot start properly on carbon.
 * Waiting for bug 82104
 */
private static bool isMacOS(){
    return SWT.getPlatform().equals("carbon"); //$NON-NLS-1$
}

/**
 * A helper class to hold information about contributions made to this processor.
 *
 * @author Pratik Shah
 * @since 3.1
 */
private static class BidiEntry {
    int begin, end;
    FlowFigure fig;
    this(FlowFigure fig, int offset, int length) {
        this.fig = fig;
        this.begin = offset;
        this.end = offset + length;
    }
}

/**
 * A singleton instance.
 */
private static BidiProcessor INSTANCE_;
public static BidiProcessor INSTANCE(){
    if( INSTANCE_ is null ){
        synchronized( BidiProcessor.classinfo ){
            if( INSTANCE_ is null ){
                INSTANCE_ = new BidiProcessor();
            }
        }
    }
    return INSTANCE_;
}

private StringBuffer bidiText;
private List list;
private int orientation = SWT.LEFT_TO_RIGHT;

private this() {
    list = new ArrayList();
}

/**
 * Records a String contribution for this bidi context. Contributions are
 * concatenated (in the order that they were contributed) to make the final
 * String which will determine the bidi info for all contributors.
 * @param fig the figure that is contributing the given text
 * @param str the text contributed by the given figure
 * @see #addControlChar(char)
 */
public void add(FlowFigure fig, String str) {
    //We are currently tracking empty contributions ("")
    list.add(new BidiEntry(fig, bidiText.length(), str.length));
    bidiText.append(str);
}

/**
 * Records a character contribution for this bidi context. Contributions are
 * concatenated (in the order that they were contributed) to make the final
 * String which will determine the bidi info for all contributors.
 * @param fig the figure that is contributing the given text
 * @param c the character being added
 * @see #addControlChar(char)
 */
public void add(FlowFigure fig, dchar c) {
    auto str = dcharToString(c);
    list.add(new BidiEntry(fig, bidiText.length(), str.length));
    bidiText.append(str);
}

/**
 * This methods allows FlowFigures to contribute text that may effect the bidi evaluation,
 * but is not text that is visible on the screen.  The bidi level of such text is
 * reported back to the contributing figure.
 *
 * @param c the control character
 */
public void addControlChar(dchar c) {
    bidiText.append(dcharToString(c));
}

/**
 * Breaks the given int array into bidi levels for each figure based on their
 * contributed text, and assigns those levels to each figure.  Also determines
 * if shaping needs to occur between figures and sets the appendJoiner, prependJoiner
 * accordingly.
 *
 * @param levels the calculated levels of all the text in the block
 */
private void assignResults(int[] levels) {
    BidiEntry prevEntry = null, entry = null;
    BidiInfo prevInfo = null, info = null;
    int end = 2, start = 0;
    for (int i = 0; i < list.size(); i++) {
        entry = cast(BidiEntry)list.get(i);
        info = new BidiInfo();

        while (levels[end] < entry.end)
            end += 2;

        int levelInfo[];
        if (end is start) {
            levelInfo = new int[1];
            if (prevInfo !is null)
                levelInfo[0] = prevInfo.levelInfo[prevInfo.levelInfo.length - 1];
            else
                levelInfo[0] = (orientation is SWT.LEFT_TO_RIGHT) ? 0 : 1;
        } else {
            levelInfo = new int[end - start - 1];
            System.arraycopy(levels, start + 1, levelInfo, 0, levelInfo.length);
        }
        for (int j = 1; j < levelInfo.length; j += 2)
            levelInfo[j] -= entry.begin;
        info.levelInfo = levelInfo;

        // Compare current and previous for joiners, and commit previous BidiInfo.
        if (prevEntry !is null) {
            if (// if we started in the middle of a level run
                    levels[start] < entry.begin
                    // and the level run is odd
                    && levels[start + 1] % 2 is 1
                    // and the first character of this figure is Arabic
                    && isJoiner(entry.begin)
                    // and the last character of the previous figure was Arabic
                    && isPrecedingJoiner(entry.begin))
                prevInfo.trailingJoiner = info.leadingJoiner = true;
            prevEntry.fig.setBidiInfo(prevInfo);
        }
        prevEntry = entry;
        prevInfo = info;
        if (entry.end is levels[end])
            start = end;
        else
            start = end - 2;
    }
    if (entry !is null)
        entry.fig.setBidiInfo(info);
}

private bool isJoiner(int begin) {
    return begin < bidiText.length && isJoiningCharacter(bidiText.slice()[begin.. $].firstCodePoint());
}

/**
 * @param the character to be evaluated
 * @return <code>true</code> if the given character is Arabic or ZWJ
 */
private bool isJoiningCharacter(dchar c) {
    return false;
    //FIXME: SWT Missing functionality
//     return Character.getDirectionality(c) is Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC
//             || c is BidiChars.ZWJ;
}

private bool isPrecedingJoiner(int begin) {
    return begin > 0 && isJoiningCharacter(bidiText.slice()[begin - 1..$].firstCodePoint());
}

/**
 * Processes the contributed text, determines the Bidi levels, and assigns them to
 * the FlowFigures that made thet contributions. This class is for INTERNAL use
 * only. Shaping of visually contiguous Arabic characters that are split in different
 * figures is also handled. This method will do nothing if the contributed text does not
 * require Bidi  evaluation. All contributions are discarded at the end of this method.
 */
public void process() {
    try {
        if (bidiText.length is 0 || isMacOS)
            return;
        char[] chars = bidiText.slice().dup;

        if (orientation !is SWT.RIGHT_TO_LEFT
                && !Bidi.requiresBidi(chars, 0, chars.length - 1))
            return;

        int[] levels = new int[15];
        TextLayout layout = FlowUtilities.getTextLayout();

        layout.setOrientation(orientation);
        layout.setText(bidiText.toString());
        int j = 0, offset, prevLevel = -1;
        for (offset = 0; offset < chars.length; offset++) {
            int newLevel = layout.getLevel(offset);
            if (newLevel !is prevLevel) {
                if (j + 3 > levels.length) {
                    int temp[] = levels;
                    levels = new int[levels.length * 2 + 1];
                    System.arraycopy(temp, 0, levels, 0, temp.length);
                }
                levels[j++] = offset;
                levels[j++] = newLevel;
                prevLevel = newLevel;
            }
        }
        levels[j++] = offset;

        if (j !is levels.length) {
            int[] newLevels = new int[j];
            System.arraycopy(levels, 0, newLevels, 0, j);
            levels = newLevels;
        }
        assignResults(levels);

        // reset the orientation of the layout, in case it was set to RTL
        layout.setOrientation(SWT.LEFT_TO_RIGHT);
    } finally {
        //will cause the fields to be reset for the next string to be processed
        bidiText = null;
        list.clear();
    }
}

/**
 * Sets the paragraph embedding.  The given orientation will be used on TextLayout
 * when determining the Bidi levels.
 *
 * @param newOrientation SWT.LEFT_TO_RIGHT or SWT.RIGHT_TO_LEFT
 */
public void setOrientation(int newOrientation) {
    bidiText = new StringBuffer();
    list.clear();
    orientation = newOrientation;
}

}