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