History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/text/TextLayoutStrategy.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/text/TextLayoutStrategy.java+555−0
@@ -0,0 +1,555 @@
1/*
2 * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package javax.swing.text;
26
27import java.util.*;
28import java.awt.*;
29import java.text.AttributedCharacterIterator;
30import java.text.BreakIterator;
31import java.awt.font.*;
32import java.awt.geom.AffineTransform;
33import javax.swing.JComponent;
34import javax.swing.event.DocumentEvent;
35import sun.font.BidiUtils;
36
37/**
38 * A flow strategy that uses java.awt.font.LineBreakMeasureer to
39 * produce java.awt.font.TextLayout for i18n capable rendering.
40 * If the child view being placed into the flow is of type
41 * GlyphView and can be rendered by TextLayout, a GlyphPainter
42 * that uses TextLayout is plugged into the GlyphView.
43 *
44 * @author Timothy Prinzing
45 */
46class TextLayoutStrategy extends FlowView.FlowStrategy {
47
48 /**
49 * Constructs a layout strategy for paragraphs based
50 * upon java.awt.font.LineBreakMeasurer.
51 */
52 public TextLayoutStrategy() {
53 text = new AttributedSegment();
54 }
55
56 // --- FlowStrategy methods --------------------------------------------
57
58 /**
59 * Gives notification that something was inserted into the document
60 * in a location that the given flow view is responsible for. The
61 * strategy should update the appropriate changed region (which
62 * depends upon the strategy used for repair).
63 *
64 * @param e the change information from the associated document
65 * @param alloc the current allocation of the view inside of the insets.
66 * This value will be null if the view has not yet been displayed.
67 * @see View#insertUpdate
68 */
69 public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
70 sync(fv);
71 super.insertUpdate(fv, e, alloc);
72 }
73
74 /**
75 * Gives notification that something was removed from the document
76 * in a location that the given flow view is responsible for.
77 *
78 * @param e the change information from the associated document
79 * @param alloc the current allocation of the view inside of the insets.
80 * @see View#removeUpdate
81 */
82 public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
83 sync(fv);
84 super.removeUpdate(fv, e, alloc);
85 }
86
87 /**
88 * Gives notification from the document that attributes were changed
89 * in a location that this view is responsible for.
90 *
91 * @param changes the change information from the associated document
92 * @param a the current allocation of the view
93 * @param f the factory to use to rebuild if the view has children
94 * @see View#changedUpdate
95 */
96 public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
97 sync(fv);
98 super.changedUpdate(fv, e, alloc);
99 }
100
101 /**
102 * Does a a full layout on the given View. This causes all of
103 * the rows (child views) to be rebuilt to match the given
104 * constraints for each row. This is called by a FlowView.layout
105 * to update the child views in the flow.
106 *
107 * @param fv the view to reflow
108 */
109 public void layout(FlowView fv) {
110 super.layout(fv);
111 }
112
113 /**
114 * Creates a row of views that will fit within the
115 * layout span of the row. This is implemented to execute the
116 * superclass functionality (which fills the row with child
117 * views or view fragments) and follow that with bidi reordering
118 * of the unidirectional view fragments.
119 *
120 * @param row the row to fill in with views. This is assumed
121 * to be empty on entry.
122 * @param pos The current position in the children of
123 * this views element from which to start.
124 * @return the position to start the next row
125 */
126 protected int layoutRow(FlowView fv, int rowIndex, int p0) {
127 int p1 = super.layoutRow(fv, rowIndex, p0);
128 View row = fv.getView(rowIndex);
129 Document doc = fv.getDocument();
130 Object i18nFlag = doc.getProperty(AbstractDocument.I18NProperty);
131 if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
132 int n = row.getViewCount();
133 if (n > 1) {
134 AbstractDocument d = (AbstractDocument)fv.getDocument();
135 Element bidiRoot = d.getBidiRootElement();
136 byte[] levels = new byte[n];
137 View[] reorder = new View[n];
138
139 for( int i=0; i<n; i++ ) {
140 View v = row.getView(i);
141 int bidiIndex =bidiRoot.getElementIndex(v.getStartOffset());
142 Element bidiElem = bidiRoot.getElement( bidiIndex );
143 levels[i] = (byte)StyleConstants.getBidiLevel(bidiElem.getAttributes());
144 reorder[i] = v;
145 }
146
147 BidiUtils.reorderVisually( levels, reorder );
148 row.replace(0, n, reorder);
149 }
150 }
151 return p1;
152 }
153
154 /**
155 * Adjusts the given row if possible to fit within the
156 * layout span. Since all adjustments were already
157 * calculated by the LineBreakMeasurer, this is implemented
158 * to do nothing.
159 *
160 * @param r the row to adjust to the current layout
161 * span.
162 * @param desiredSpan the current layout span >= 0
163 * @param x the location r starts at.
164 */
165 protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
166 }
167
168 /**
169 * Creates a unidirectional view that can be used to represent the
170 * current chunk. This can be either an entire view from the
171 * logical view, or a fragment of the view.
172 *
173 * @param fv the view holding the flow
174 * @param startOffset the start location for the view being created
175 * @param spanLeft the about of span left to fill in the row
176 * @param rowIndex the row the view will be placed into
177 */
178 protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {
179 // Get the child view that contains the given starting position
180 View lv = getLogicalView(fv);
181 View row = fv.getView(rowIndex);
182 boolean requireNextWord = (viewBuffer.size() == 0) ? false : true;
183 int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
184 View v = lv.getView(childIndex);
185
186 int endOffset = getLimitingOffset(v, startOffset, spanLeft, requireNextWord);
187 if (endOffset == startOffset) {
188 return null;
189 }
190
191 View frag;
192 if ((startOffset==v.getStartOffset()) && (endOffset == v.getEndOffset())) {
193 // return the entire view
194 frag = v;
195 } else {
196 // return a unidirectional fragment.
197 frag = v.createFragment(startOffset, endOffset);
198 }
199
200 if ((frag instanceof GlyphView) && (measurer != null)) {
201 // install a TextLayout based renderer if the view is responsible
202 // for glyphs. If the view represents a tab, the default
203 // glyph painter is used (may want to handle tabs differently).
204 boolean isTab = false;
205 int p0 = frag.getStartOffset();
206 int p1 = frag.getEndOffset();
207 if ((p1 - p0) == 1) {
208 // check for tab
209 Segment s = ((GlyphView)frag).getText(p0, p1);
210 char ch = s.first();
211 if (ch == '\t') {
212 isTab = true;
213 }
214 }
215 TextLayout tl = (isTab) ? null :
216 measurer.nextLayout(spanLeft, text.toIteratorIndex(endOffset),
217 requireNextWord);
218 if (tl != null) {
219 ((GlyphView)frag).setGlyphPainter(new GlyphPainter2(tl));
220 }
221 }
222 return frag;
223 }
224
225 /**
226 * Calculate the limiting offset for the next view fragment.
227 * At most this would be the entire view (i.e. the limiting
228 * offset would be the end offset in that case). If the range
229 * contains a tab or a direction change, that will limit the
230 * offset to something less. This value is then fed to the
231 * LineBreakMeasurer as a limit to consider in addition to the
232 * remaining span.
233 *
234 * @param v the logical view representing the starting offset.
235 * @param startOffset the model location to start at.
236 */
237 int getLimitingOffset(View v, int startOffset, int spanLeft, boolean requireNextWord) {
238 int endOffset = v.getEndOffset();
239
240 // check for direction change
241 Document doc = v.getDocument();
242 if (doc instanceof AbstractDocument) {
243 AbstractDocument d = (AbstractDocument) doc;
244 Element bidiRoot = d.getBidiRootElement();
245 if( bidiRoot.getElementCount() > 1 ) {
246 int bidiIndex = bidiRoot.getElementIndex( startOffset );
247 Element bidiElem = bidiRoot.getElement( bidiIndex );
248 endOffset = Math.min( bidiElem.getEndOffset(), endOffset );
249 }
250 }
251
252 // check for tab
253 if (v instanceof GlyphView) {
254 Segment s = ((GlyphView)v).getText(startOffset, endOffset);
255 char ch = s.first();
256 if (ch == '\t') {
257 // if the first character is a tab, create a dedicated
258 // view for just the tab
259 endOffset = startOffset + 1;
260 } else {
261 for (ch = s.next(); ch != Segment.DONE; ch = s.next()) {
262 if (ch == '\t') {
263 // found a tab, don't include it in the text
264 endOffset = startOffset + s.getIndex() - s.getBeginIndex();
265 break;
266 }
267 }
268 }
269 }
270
271 // determine limit from LineBreakMeasurer
272 int limitIndex = text.toIteratorIndex(endOffset);
273 if (measurer != null) {
274 int index = text.toIteratorIndex(startOffset);
275 if (measurer.getPosition() != index) {
276 measurer.setPosition(index);
277 }
278 limitIndex = measurer.nextOffset(spanLeft, limitIndex, requireNextWord);
279 }
280 int pos = text.toModelPosition(limitIndex);
281 return pos;
282 }
283
284 /**
285 * Synchronize the strategy with its FlowView. Allows the strategy
286 * to update its state to account for changes in that portion of the
287 * model represented by the FlowView. Also allows the strategy
288 * to update the FlowView in response to these changes.
289 */
290 void sync(FlowView fv) {
291 View lv = getLogicalView(fv);
292 text.setView(lv);
293
294 Container container = fv.getContainer();
295 FontRenderContext frc = sun.swing.SwingUtilities2.
296 getFontRenderContext(container);
297 BreakIterator iter;
298 Container c = fv.getContainer();
299 if (c != null) {
300 iter = BreakIterator.getLineInstance(c.getLocale());
301 } else {
302 iter = BreakIterator.getLineInstance();
303 }
304
305 Object shaper = null;
306 if (c instanceof JComponent) {
307 shaper = ((JComponent) c).getClientProperty(
308 TextAttribute.NUMERIC_SHAPING);
309 }
310 text.setShaper(shaper);
311
312 measurer = new LineBreakMeasurer(text, iter, frc);
313
314 // If the children of the FlowView's logical view are GlyphViews, they
315 // need to have their painters updated.
316 int n = lv.getViewCount();
317 for( int i=0; i<n; i++ ) {
318 View child = lv.getView(i);
319 if( child instanceof GlyphView ) {
320 int p0 = child.getStartOffset();
321 int p1 = child.getEndOffset();
322 measurer.setPosition(text.toIteratorIndex(p0));
323 TextLayout layout
324 = measurer.nextLayout( Float.MAX_VALUE,
325 text.toIteratorIndex(p1), false );
326 ((GlyphView)child).setGlyphPainter(new GlyphPainter2(layout));
327 }
328 }
329
330 // Reset measurer.
331 measurer.setPosition(text.getBeginIndex());
332
333 }
334
335 // --- variables -------------------------------------------------------
336
337 private LineBreakMeasurer measurer;
338 private AttributedSegment text;
339
340 /**
341 * Implementation of AttributedCharacterIterator that supports
342 * the GlyphView attributes for rendering the glyphs through a
343 * TextLayout.
344 */
345 static class AttributedSegment extends Segment implements AttributedCharacterIterator {
346
347 AttributedSegment() {
348 }
349
350 View getView() {
351 return v;
352 }
353
354 void setView(View v) {
355 this.v = v;
356 Document doc = v.getDocument();
357 int p0 = v.getStartOffset();
358 int p1 = v.getEndOffset();
359 try {
360 doc.getText(p0, p1 - p0, this);
361 } catch (BadLocationException bl) {
362 throw new IllegalArgumentException("Invalid view");
363 }
364 first();
365 }
366
367 /**
368 * Get a boundary position for the font.
369 * This is implemented to assume that two fonts are
370 * equal if their references are equal (i.e. that the
371 * font came from a cache).
372 *
373 * @return the location in model coordinates. This is
374 * not the same as the Segment coordinates.
375 */
376 int getFontBoundary(int childIndex, int dir) {
377 View child = v.getView(childIndex);
378 Font f = getFont(childIndex);
379 for (childIndex += dir; (childIndex >= 0) && (childIndex < v.getViewCount());
380 childIndex += dir) {
381 Font next = getFont(childIndex);
382 if (next != f) {
383 // this run is different
384 break;
385 }
386 child = v.getView(childIndex);
387 }
388 return (dir < 0) ? child.getStartOffset() : child.getEndOffset();
389 }
390
391 /**
392 * Get the font at the given child index.
393 */
394 Font getFont(int childIndex) {
395 View child = v.getView(childIndex);
396 if (child instanceof GlyphView) {
397 return ((GlyphView)child).getFont();
398 }
399 return null;
400 }
401
402 int toModelPosition(int index) {
403 return v.getStartOffset() + (index - getBeginIndex());
404 }
405
406 int toIteratorIndex(int pos) {
407 return pos - v.getStartOffset() + getBeginIndex();
408 }
409
410 private void setShaper(Object shaper) {
411 this.shaper = shaper;
412 }
413
414 // --- AttributedCharacterIterator methods -------------------------
415
416 /**
417 * Returns the index of the first character of the run
418 * with respect to all attributes containing the current character.
419 */
420 public int getRunStart() {
421 int pos = toModelPosition(getIndex());
422 int i = v.getViewIndex(pos, Position.Bias.Forward);
423 View child = v.getView(i);
424 return toIteratorIndex(child.getStartOffset());
425 }
426
427 /**
428 * Returns the index of the first character of the run
429 * with respect to the given attribute containing the current character.
430 */
431 public int getRunStart(AttributedCharacterIterator.Attribute attribute) {
432 if (attribute instanceof TextAttribute) {
433 int pos = toModelPosition(getIndex());
434 int i = v.getViewIndex(pos, Position.Bias.Forward);
435 if (attribute == TextAttribute.FONT) {
436 return toIteratorIndex(getFontBoundary(i, -1));
437 }
438 }
439 return getBeginIndex();
440 }
441
442 /**
443 * Returns the index of the first character of the run
444 * with respect to the given attributes containing the current character.
445 */
446 public int getRunStart(Set<? extends Attribute> attributes) {
447 int index = getBeginIndex();
448 Object[] a = attributes.toArray();
449 for (int i = 0; i < a.length; i++) {
450 TextAttribute attr = (TextAttribute) a[i];
451 index = Math.max(getRunStart(attr), index);
452 }
453 return Math.min(getIndex(), index);
454 }
455
456 /**
457 * Returns the index of the first character following the run
458 * with respect to all attributes containing the current character.
459 */
460 public int getRunLimit() {
461 int pos = toModelPosition(getIndex());
462 int i = v.getViewIndex(pos, Position.Bias.Forward);
463 View child = v.getView(i);
464 return toIteratorIndex(child.getEndOffset());
465 }
466
467 /**
468 * Returns the index of the first character following the run
469 * with respect to the given attribute containing the current character.
470 */
471 public int getRunLimit(AttributedCharacterIterator.Attribute attribute) {
472 if (attribute instanceof TextAttribute) {
473 int pos = toModelPosition(getIndex());
474 int i = v.getViewIndex(pos, Position.Bias.Forward);
475 if (attribute == TextAttribute.FONT) {
476 return toIteratorIndex(getFontBoundary(i, 1));
477 }
478 }
479 return getEndIndex();
480 }
481
482 /**
483 * Returns the index of the first character following the run
484 * with respect to the given attributes containing the current character.
485 */
486 public int getRunLimit(Set<? extends Attribute> attributes) {
487 int index = getEndIndex();
488 Object[] a = attributes.toArray();
489 for (int i = 0; i < a.length; i++) {
490 TextAttribute attr = (TextAttribute) a[i];
491 index = Math.min(getRunLimit(attr), index);
492 }
493 return Math.max(getIndex(), index);
494 }
495
496 /**
497 * Returns a map with the attributes defined on the current
498 * character.
499 */
500 public Map<Attribute, Object> getAttributes() {
501 Object[] ka = keys.toArray();
502 Hashtable<Attribute, Object> h = new Hashtable<Attribute, Object>();
503 for (int i = 0; i < ka.length; i++) {
504 TextAttribute a = (TextAttribute) ka[i];
505 Object value = getAttribute(a);
506 if (value != null) {
507 h.put(a, value);
508 }
509 }
510 return h;
511 }
512
513 /**
514 * Returns the value of the named attribute for the current character.
515 * Returns null if the attribute is not defined.
516 * @param attribute the key of the attribute whose value is requested.
517 */
518 public Object getAttribute(AttributedCharacterIterator.Attribute attribute) {
519 int pos = toModelPosition(getIndex());
520 int childIndex = v.getViewIndex(pos, Position.Bias.Forward);
521 if (attribute == TextAttribute.FONT) {
522 return getFont(childIndex);
523 } else if( attribute == TextAttribute.RUN_DIRECTION ) {
524 return
525 v.getDocument().getProperty(TextAttribute.RUN_DIRECTION);
526 } else if (attribute == TextAttribute.NUMERIC_SHAPING) {
527 return shaper;
528 }
529 return null;
530 }
531
532 /**
533 * Returns the keys of all attributes defined on the
534 * iterator's text range. The set is empty if no
535 * attributes are defined.
536 */
537 public Set<Attribute> getAllAttributeKeys() {
538 return keys;
539 }
540
541 View v;
542
543 static Set<Attribute> keys;
544
545 static {
546 keys = new HashSet<Attribute>();
547 keys.add(TextAttribute.FONT);
548 keys.add(TextAttribute.RUN_DIRECTION);
549 keys.add(TextAttribute.NUMERIC_SHAPING);
550 }
551
552 private Object shaper = null;
553 }
554
555}