comparison 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
comparison
equal deleted inserted replaced
96:b492ba44e44d 98:95307ad235d9
1 /*******************************************************************************
2 * Copyright (c) 2004, 2005 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13
14 module dwtx.draw2d.text.LineRoot;
15
16 import dwt.dwthelper.utils;
17 import dwtx.dwtxhelper.Collection;
18
19 import dwtx.draw2d.geometry.Rectangle;
20 import dwtx.draw2d.text.LineBox;
21 import dwtx.draw2d.text.FlowBox;
22 import dwtx.draw2d.text.NestedLine;
23 import dwtx.draw2d.text.ContentBox;
24 import dwtx.draw2d.text.InlineFlow;
25
26 /**
27 * LineRoot is the top-most container on a line of text displayed in Draw2d. Hence, a
28 * LineRoot can tell you of things like the highest ascent or descent on a line, which
29 * is required to display selection and such. All
30 * {@link dwtx.draw2d.text.ContentBox fragments} know of the LineRoot they belong
31 * to.
32 * @author Randy Hudson
33 * @author Pratik Shah
34 * @since 3.1
35 */
36 public class LineRoot
37 : LineBox
38 {
39
40 private int baseline;
41 private bool isMirrored;
42
43 /**
44 * Constructor
45 * @param isMirrored <code>true</code> if the line is to be displayed in a mirrored control
46 */
47 public this(bool isMirrored) {
48 this.isMirrored = isMirrored;
49 }
50
51 /**
52 * @see dwtx.draw2d.text.CompositeBox#add(dwtx.draw2d.text.FlowBox)
53 */
54 public void add(FlowBox child) {
55 super.add(child);
56 child.setLineRoot(this);
57 }
58
59 private void bidiCommit() {
60 int xLocation = getX();
61 BidiLevelNode root = new BidiLevelNode();
62 List branches = new ArrayList();
63 // branches does not include this LineRoot; all the non-leaf child fragments of a
64 // parent will be listed before the parent itself in this list
65 buildBidiTree(this, root, branches);
66 List result = new ArrayList();
67 root.emit(result);
68 int i = isMirrored ? result.size() - 1 : 0;
69 while (i >= 0 && i < result.size()) {
70 FlowBox box = cast(FlowBox)result.get(i);
71 box.setX(xLocation);
72 xLocation += box.getWidth();
73 i += isMirrored ? -1 : 1;
74 }
75 // set the bounds of the composite boxes, and break overlapping ones into two
76 layoutNestedLines(branches);
77 }
78
79 private void buildBidiTree(FlowBox box, BidiLevelNode node, List branches) {
80 if ( auto lb = cast(LineBox)box ) {
81 List children = lb.getFragments();
82 for (int i = 0; i < children.size(); i++)
83 buildBidiTree(cast(FlowBox)children.get(i), node, branches);
84 if (box !is this)
85 branches.add(box);
86 } else {
87 ContentBox leafBox = cast(ContentBox)box;
88 while (leafBox.getBidiLevel() < node.level)
89 node = node.pop();
90 while (leafBox.getBidiLevel() > node.level)
91 node = node.push();
92 node.add(leafBox);
93 }
94 }
95
96 /**
97 * Committing a LineRoot will position its children correctly. All children boxes are made
98 * to have the same baseline, and are laid out according to the Unicode BiDi Algorithm,
99 * or left-to-right if Bidi is not necessary.
100 */
101 public void commit() {
102 if (requiresBidi())
103 bidiCommit();
104 else
105 contiguousCommit(this, getX());
106 }
107
108 /**
109 * A LineRoot cannot be targetted.
110 * @see dwtx.draw2d.text.FlowBox#containsPoint(int, int)
111 */
112 public bool containsPoint(int x, int y) {
113 return false;
114 }
115
116 /*
117 * Simply lays out all fragments from left-to-right in the order in which they're
118 * contained.
119 */
120 private void contiguousCommit(FlowBox box, int x) {
121 box.setX(x);
122 if (auto lb = cast(LineBox)box ) {
123 List fragments = lb.getFragments();
124 int i = isMirrored ? fragments.size() - 1 : 0;
125 while (i >= 0 && i < fragments.size()) {
126 FlowBox child = cast(FlowBox)fragments.get(i);
127 contiguousCommit(child, x);
128 x += child.getWidth();
129 i += isMirrored ? -1 : 1;
130 }
131 }
132 }
133
134 private Result findParent(NestedLine line, List branches, int afterIndex) {
135 for (int i = afterIndex + 1; i < branches.size(); i++) {
136 NestedLine box = cast(NestedLine)branches.get(i);
137 int index = box.getFragments().indexOf(line);
138 if (index >= 0)
139 return new Result(box, index);
140 }
141 return new Result(this, getFragments().indexOf(line));
142 }
143
144 /**
145 * @see dwtx.draw2d.text.FlowBox#getBaseline()
146 */
147 public int getBaseline() {
148 return baseline;
149 }
150
151 LineRoot getLineRoot() {
152 return this;
153 }
154
155 int getVisibleBottom() {
156 return baseline + contentDescent;
157 }
158
159 int getVisibleTop() {
160 return baseline - contentAscent;
161 }
162
163 private void layoutNestedLines(List branches) {
164 for (int i = 0; i < branches.size(); i++) {
165 NestedLine parent = cast(NestedLine)branches.get(i);
166 FlowBox prevChild = null;
167 Rectangle bounds = null;
168 List frags = parent.getFragments();
169 for (int j = 0; j < frags.size(); j++) {
170 FlowBox child = cast(FlowBox) frags.get(j);
171 if (prevChild !is null && prevChild.getX() + prevChild.width !is child.getX()
172 && child.getX() + child.width !is prevChild.getX()) {
173 // the boxes are not adjacent, and hence the parent box needs to
174 // be broken up
175 InlineFlow parentFig = parent.owner;
176 // Create and initialize a new line box
177 NestedLine newBox = new NestedLine(parentFig);
178 newBox.setLineRoot(this);
179 // Add all remaining fragments from the current line box to the new one
180 for (int k = j; k < frags.size();)
181 newBox.fragments.add(frags.remove(k));
182 // Add the new line box to the parent box's list of fragments
183 Result result = findParent(parent, branches, i);
184 result.parent.getFragments().add(result.index + 1, newBox);
185 // Add the new line box to the flow figure's list of fragments
186 parentFig.fragments.add(parentFig.fragments.indexOf(parent) + 1, newBox);
187 branches.add(i + 1, newBox);
188 break;
189 }
190 if (bounds is null)
191 bounds = new Rectangle(child.getX(), 1, child.getWidth(), 1);
192 else
193 bounds.union_(child.getX(), 1, child.getWidth(), 1);
194 prevChild = child;
195 }
196 parent.setX(bounds.x);
197 parent.setWidth(bounds.width);
198 }
199 }
200
201 /**
202 * Positions the line vertically by settings its baseline.
203 * @param baseline the baseline
204 */
205 public void setBaseline(int baseline) {
206 this.baseline = baseline;
207 }
208
209 /**
210 * @see dwtx.draw2d.text.CompositeBox#setLineTop(int)
211 */
212 public void setLineTop(int top) {
213 this.baseline = top + getAscent();
214 }
215
216 private static class BidiLevelNode : ArrayList
217 {
218 int level;
219 final BidiLevelNode parent;
220
221 this() {
222 this(null, 0);
223 }
224
225 this(BidiLevelNode parent, int level) {
226 this.parent = parent;
227 this.level = level;
228 }
229
230 void emit(List list) {
231 if (level % 2 is 1) {
232 for (int i = size() - 1; i >= 0; i--) {
233 Object child = get(i);
234 if (auto bln = cast(BidiLevelNode)child )
235 bln.emit(list);
236 else
237 list.add(child);
238 }
239 } else {
240 for (int i = 0; i < size(); i++) {
241 Object child = get(i);
242 if (auto bln = cast(BidiLevelNode)child )
243 bln.emit(list);
244 else
245 list.add(child);
246 }
247 }
248 }
249
250 BidiLevelNode pop() {
251 return parent;
252 }
253
254 BidiLevelNode push() {
255 if (!isEmpty()) {
256 Object last = get(size() - 1);
257 if (null !is cast(BidiLevelNode)last && (cast(BidiLevelNode)last).level is level + 1)
258 return cast(BidiLevelNode)last;
259 }
260 BidiLevelNode child = new BidiLevelNode(this, level + 1);
261 add(child);
262 return child;
263 }
264 }
265
266 private static class Result {
267 private int index;
268 private LineBox parent;
269 private this(LineBox box, int i) {
270 parent = box;
271 index = i;
272 }
273 }
274
275 }