diff dwtx/draw2d/text/LineRoot.d @ 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/LineRoot.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,275 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.draw2d.text.LineRoot;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.LineBox;
+import dwtx.draw2d.text.FlowBox;
+import dwtx.draw2d.text.NestedLine;
+import dwtx.draw2d.text.ContentBox;
+import dwtx.draw2d.text.InlineFlow;
+
+/**
+ * LineRoot is the top-most container on a line of text displayed in Draw2d.  Hence, a
+ * LineRoot can tell you of things like the highest ascent or descent on a line, which
+ * is required to display selection and such.  All
+ * {@link dwtx.draw2d.text.ContentBox fragments} know of the LineRoot they belong
+ * to.
+ * @author Randy Hudson
+ * @author Pratik Shah
+ * @since 3.1
+ */
+public class LineRoot
+    : LineBox
+{
+
+private int baseline;
+private bool isMirrored;
+
+/**
+ * Constructor
+ * @param isMirrored <code>true</code> if the line is to be displayed in a mirrored control
+ */
+public this(bool isMirrored) {
+    this.isMirrored = isMirrored;
+}
+
+/**
+ * @see dwtx.draw2d.text.CompositeBox#add(dwtx.draw2d.text.FlowBox)
+ */
+public void add(FlowBox child) {
+    super.add(child);
+    child.setLineRoot(this);
+}
+
+private void bidiCommit() {
+    int xLocation = getX();
+    BidiLevelNode root = new BidiLevelNode();
+    List branches = new ArrayList();
+    // branches does not include this LineRoot; all the non-leaf child fragments of a
+    // parent will be listed before the parent itself in this list
+    buildBidiTree(this, root, branches);
+    List result = new ArrayList();
+    root.emit(result);
+    int i = isMirrored ? result.size() - 1 : 0;
+    while (i >= 0 && i < result.size()) {
+        FlowBox box = cast(FlowBox)result.get(i);
+        box.setX(xLocation);
+        xLocation += box.getWidth();
+        i += isMirrored ? -1 : 1;
+    }
+    // set the bounds of the composite boxes, and break overlapping ones into two
+    layoutNestedLines(branches);
+}
+
+private void buildBidiTree(FlowBox box, BidiLevelNode node, List branches) {
+    if ( auto lb = cast(LineBox)box ) {
+        List children = lb.getFragments();
+        for (int i = 0; i < children.size(); i++)
+            buildBidiTree(cast(FlowBox)children.get(i), node, branches);
+        if (box !is this)
+            branches.add(box);
+    } else {
+        ContentBox leafBox = cast(ContentBox)box;
+        while (leafBox.getBidiLevel() < node.level)
+            node = node.pop();
+        while (leafBox.getBidiLevel() > node.level)
+            node = node.push();
+        node.add(leafBox);
+    }
+}
+
+/**
+ * Committing a LineRoot will position its children correctly. All children boxes are made
+ * to have the same baseline, and are laid out according to the Unicode BiDi Algorithm,
+ * or left-to-right if Bidi is not necessary.
+ */
+public void commit() {
+    if (requiresBidi())
+        bidiCommit();
+    else
+        contiguousCommit(this, getX());
+}
+
+/**
+ * A LineRoot cannot be targetted.
+ * @see dwtx.draw2d.text.FlowBox#containsPoint(int, int)
+ */
+public bool containsPoint(int x, int y) {
+    return false;
+}
+
+/*
+ * Simply lays out all fragments from left-to-right in the order in which they're
+ * contained.
+ */
+private void contiguousCommit(FlowBox box, int x) {
+    box.setX(x);
+    if (auto lb = cast(LineBox)box ) {
+        List fragments = lb.getFragments();
+        int i = isMirrored ? fragments.size() - 1 : 0;
+        while (i >= 0 && i < fragments.size()) {
+            FlowBox child = cast(FlowBox)fragments.get(i);
+            contiguousCommit(child, x);
+            x += child.getWidth();
+            i += isMirrored ? -1 : 1;
+        }
+    }
+}
+
+private Result findParent(NestedLine line, List branches, int afterIndex) {
+    for (int i = afterIndex + 1; i < branches.size(); i++) {
+        NestedLine box = cast(NestedLine)branches.get(i);
+        int index = box.getFragments().indexOf(line);
+        if (index >= 0)
+            return new Result(box, index);
+    }
+    return new Result(this, getFragments().indexOf(line));
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowBox#getBaseline()
+ */
+public int getBaseline() {
+    return baseline;
+}
+
+LineRoot getLineRoot() {
+    return this;
+}
+
+int getVisibleBottom() {
+    return baseline + contentDescent;
+}
+
+int getVisibleTop() {
+    return baseline - contentAscent;
+}
+
+private void layoutNestedLines(List branches) {
+    for (int i = 0; i < branches.size(); i++) {
+        NestedLine parent = cast(NestedLine)branches.get(i);
+        FlowBox prevChild = null;
+        Rectangle bounds = null;
+        List frags = parent.getFragments();
+        for (int j = 0; j < frags.size(); j++) {
+            FlowBox child = cast(FlowBox) frags.get(j);
+            if (prevChild !is null && prevChild.getX() + prevChild.width !is child.getX()
+                    && child.getX() + child.width !is prevChild.getX()) {
+                // the boxes are not adjacent, and hence the parent box needs to
+                // be broken up
+                InlineFlow parentFig = parent.owner;
+                // Create and initialize a new line box
+                NestedLine newBox = new NestedLine(parentFig);
+                newBox.setLineRoot(this);
+                // Add all remaining fragments from the current line box to the new one
+                for (int k = j; k < frags.size();)
+                    newBox.fragments.add(frags.remove(k));
+                // Add the new line box to the parent box's list of fragments
+                Result result = findParent(parent, branches, i);
+                result.parent.getFragments().add(result.index + 1, newBox);
+                // Add the new line box to the flow figure's list of fragments
+                parentFig.fragments.add(parentFig.fragments.indexOf(parent) + 1, newBox);
+                branches.add(i + 1, newBox);
+                break;
+            }
+            if (bounds is null)
+                bounds = new Rectangle(child.getX(), 1, child.getWidth(), 1);
+            else
+                bounds.union_(child.getX(), 1, child.getWidth(), 1);
+            prevChild = child;
+        }
+        parent.setX(bounds.x);
+        parent.setWidth(bounds.width);
+    }
+}
+
+/**
+ * Positions the line vertically by settings its baseline.
+ * @param baseline the baseline
+ */
+public void setBaseline(int baseline) {
+    this.baseline = baseline;
+}
+
+/**
+ * @see dwtx.draw2d.text.CompositeBox#setLineTop(int)
+ */
+public void setLineTop(int top) {
+    this.baseline = top + getAscent();
+}
+
+private static class BidiLevelNode : ArrayList
+{
+    int level;
+    final BidiLevelNode parent;
+
+    this() {
+        this(null, 0);
+    }
+
+    this(BidiLevelNode parent, int level) {
+        this.parent = parent;
+        this.level = level;
+    }
+
+    void emit(List list) {
+        if (level % 2 is 1) {
+            for (int i = size() - 1; i >= 0; i--) {
+                Object child = get(i);
+                if (auto bln = cast(BidiLevelNode)child )
+                    bln.emit(list);
+                else
+                    list.add(child);
+            }
+        } else {
+            for (int i = 0; i < size(); i++) {
+                Object child = get(i);
+                if (auto bln = cast(BidiLevelNode)child )
+                    bln.emit(list);
+                else
+                    list.add(child);
+            }
+        }
+    }
+
+    BidiLevelNode pop() {
+        return parent;
+    }
+
+    BidiLevelNode push() {
+        if (!isEmpty()) {
+            Object last = get(size() - 1);
+            if (null !is cast(BidiLevelNode)last && (cast(BidiLevelNode)last).level is level + 1)
+                return cast(BidiLevelNode)last;
+        }
+        BidiLevelNode child = new BidiLevelNode(this, level + 1);
+        add(child);
+        return child;
+    }
+}
+
+private static class Result {
+    private int index;
+    private LineBox parent;
+    private this(LineBox box, int i) {
+        parent = box;
+        index = i;
+    }
+}
+
+}