Mercurial > projects > dwt-addons
view 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 source
/******************************************************************************* * 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; } } }