Mercurial > projects > dwt-addons
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; + } +} + +}