001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Alexey A. Ivanov
019: * @version $Revision$
020: */package javax.swing.text;
021:
022: import java.awt.Graphics;
023: import java.awt.Rectangle;
024: import java.awt.Shape;
025: import java.util.ArrayList;
026: import java.util.List;
027:
028: import javax.swing.SizeRequirements;
029: import javax.swing.event.DocumentEvent;
030: import javax.swing.event.DocumentEvent.ElementChange;
031: import javax.swing.text.Position.Bias;
032:
033: import org.apache.harmony.awt.text.TextKit;
034: import org.apache.harmony.awt.text.TextUtils;
035:
036: import org.apache.harmony.x.swing.internal.nls.Messages;
037:
038: /**
039: * Represents view for displaying bidirectional text.
040: */
041: class PlainViewI18N extends BoxView implements TabExpander {
042:
043: /**
044: * Represents text fragment with right-to-left orientation.
045: */
046: class RTLTextView extends BidiTextView {
047:
048: public RTLTextView(final Element element, final int start,
049: final int end) {
050: super (element, start, end);
051: }
052:
053: @Override
054: public Shape modelToView(final int pos, final Shape shape,
055: final Bias bias) throws BadLocationException {
056:
057: final int start = getStartOffset();
058: final int end = getEndOffset();
059:
060: if (pos < start || pos > end) {
061: throw new BadLocationException(Messages
062: .getString("swing.98"), pos); //$NON-NLS-1$
063: }
064:
065: getDocument().getText(pos, end - pos, paintParams.buffer);
066: if (paintParams.buffer.last() == '\n') {
067: --paintParams.buffer.count;
068: }
069:
070: Rectangle bounds = shape.getBounds();
071: String text = paintParams.buffer.toString();
072: return new Rectangle(bounds.x
073: + paintParams.metrics.stringWidth(text), bounds.y,
074: 1, paintParams.metrics.getHeight());
075: }
076:
077: @Override
078: public int viewToModel(final float x, final float y,
079: final Shape shape, final Bias[] biasReturn) {
080: biasReturn[0] = Position.Bias.Forward;
081:
082: final Rectangle bounds = shape.getBounds();
083:
084: final int start = getStartOffset();
085: final int end = getEndOffset();
086: final Segment buffer = paintParams.buffer;
087: try {
088: getDocument().getText(start, end - start, buffer);
089: } catch (final BadLocationException e) {
090: }
091:
092: final int bufferCount = buffer.count;
093: buffer.offset += buffer.count;
094: buffer.count = 0;
095: int width = 0;
096: while (bounds.x + width < x && buffer.count < bufferCount) {
097: --buffer.offset;
098: ++buffer.count;
099: width = paintParams.metrics.stringWidth(buffer
100: .toString());
101: }
102: if (buffer.count >= bufferCount && bounds.x + width <= x) {
103: return end;
104: }
105: int resultOffset = end - buffer.count;
106: if (resultOffset == end) {
107: biasReturn[0] = Position.Bias.Backward;
108: }
109: return resultOffset;
110: }
111:
112: void drawLine(final Segment textBuffer,
113: final TextPaintParams tpp, final int start,
114: final int end, final Graphics g, final int x,
115: final int y) {
116: int currX = x;
117:
118: try {
119:
120: if (tpp.selStart == tpp.selEnd || tpp.selStart <= start
121: && end <= tpp.selEnd) {
122:
123: drawUnselectedText(g, currX, y, start, end);
124: return;
125: }
126:
127: if (tpp.selEnd < start || tpp.selStart > end) {
128: drawUnselectedText(g, currX, y, start, end);
129: } else if (start < tpp.selStart) {
130: if (tpp.selEnd < end) {
131: currX = drawUnselectedText(g, currX, y,
132: tpp.selEnd, end);
133: currX = drawSelectedText(g, currX, y,
134: tpp.selStart, tpp.selEnd);
135: } else { // end <= selEnd
136: currX = drawSelectedText(g, currX, y,
137: tpp.selStart, end);
138: }
139: drawUnselectedText(g, currX, y, start, tpp.selStart);
140: } else { // selStart <= start && garanteed selEnd < end
141: currX = drawUnselectedText(g, currX, y, tpp.selEnd,
142: end);
143: drawSelectedText(g, currX, y, start, tpp.selEnd);
144: }
145:
146: } catch (final BadLocationException e) {
147: }
148: }
149: }
150:
151: /**
152: * Represents text fragment with left-to-right orientation.
153: */
154: class LTRTextView extends BidiTextView {
155:
156: public LTRTextView(final Element element, final int start,
157: final int end) {
158: super (element, start, end);
159: }
160:
161: @Override
162: public Shape modelToView(final int pos, final Shape shape,
163: final Bias bias) throws BadLocationException {
164: final int start = getStartOffset();
165:
166: if (pos < start || pos > getEndOffset()) {
167: throw new BadLocationException(Messages
168: .getString("swing.98"), pos); //$NON-NLS-1$
169: }
170:
171: getDocument().getText(start, pos - start,
172: paintParams.buffer);
173: Rectangle bounds = shape.getBounds();
174: return new Rectangle(TextUtils.getTabbedTextWidth(
175: paintParams.buffer, paintParams.metrics, bounds.x,
176: PlainViewI18N.this , pos)
177: + bounds.x, bounds.y, 1, paintParams.metrics
178: .getHeight());
179: }
180:
181: @Override
182: public int viewToModel(final float x, final float y,
183: final Shape shape, final Bias[] biasReturn) {
184: biasReturn[0] = Position.Bias.Forward;
185:
186: final Rectangle bounds = shape.getBounds();
187:
188: final Document doc = getDocument();
189: final int start = getStartOffset();
190: final int end = getEndOffset();
191: try {
192: doc.getText(start, end - start, paintParams.buffer);
193: } catch (final BadLocationException e) {
194: }
195:
196: return start
197: + TextUtils.getTabbedTextOffset(paintParams.buffer,
198: paintParams.metrics, bounds.x, (int) x,
199: PlainViewI18N.this , start);
200: }
201:
202: }
203:
204: /**
205: * Base class for directional run of text.
206: */
207: abstract class BidiTextView extends View {
208: protected Position start;
209: protected Position end;
210: protected Element bidiElement;
211:
212: protected int cachedWidth = -1;
213:
214: public BidiTextView(final Element element, final int start,
215: final int end) {
216: super (element);
217:
218: final Document doc = getDocument();
219: try {
220: this .start = doc.createPosition(start);
221: this .end = doc.createPosition(end);
222: } catch (BadLocationException e) {
223: }
224: }
225:
226: @Override
227: public int getEndOffset() {
228: return end.getOffset();
229: }
230:
231: @Override
232: public int getStartOffset() {
233: return start.getOffset();
234: }
235:
236: @Override
237: public float getAlignment(final int axis) {
238: return ALIGN_LEFT;
239: }
240:
241: protected Segment getText() {
242: final int start = getStartOffset();
243: int end = getEndOffset();
244: if (getParent().getEndOffset() == end) {
245: --end;
246: }
247: try {
248: getDocument().getText(start, end - start,
249: paintParams.buffer);
250: } catch (BadLocationException e) {
251: e.printStackTrace();
252: }
253: return paintParams.buffer;
254: }
255:
256: @Override
257: public float getPreferredSpan(final int axis) {
258: if (axis == Y_AXIS) {
259: return paintParams.metrics.getHeight();
260: }
261:
262: if (cachedWidth == -1) {
263: LineView parent = (LineView) getParent();
264: cachedWidth = TextUtils.getTabbedTextWidth(getText(),
265: paintParams.metrics, parent.accumulatedWidth,
266: PlainViewI18N.this , 0);
267: parent.accumulatedWidth += cachedWidth;
268: }
269: return cachedWidth;
270: }
271:
272: @Override
273: public void paint(final Graphics g, final Shape shape) {
274: Rectangle bounds = shape.getBounds();
275:
276: drawLine(paintParams, getStartOffset(), getEndOffset(), g,
277: bounds.x, bounds.y
278: + paintParams.metrics.getHeight()
279: - paintParams.metrics.getDescent());
280: }
281:
282: @Override
283: protected int drawSelectedText(final Graphics g, final int x,
284: final int y, final int start, final int end)
285: throws BadLocationException {
286:
287: return drawText(g, paintParams.selColor, paintParams, x, y,
288: start, end);
289: }
290:
291: @Override
292: protected int drawUnselectedText(final Graphics g, final int x,
293: final int y, final int start, final int end)
294: throws BadLocationException {
295:
296: return drawText(g, paintParams.color, paintParams, x, y,
297: start, end);
298: }
299:
300: @Override
301: public void preferenceChanged(final View child,
302: final boolean width, final boolean height) {
303: if (width) {
304: cachedWidth = -1;
305: }
306: super .preferenceChanged(child, width, height);
307: }
308:
309: @Override
310: public void insertUpdate(final DocumentEvent event,
311: final Shape shape, final ViewFactory factory) {
312: preferenceChanged(this , true, false);
313: }
314:
315: @Override
316: public void removeUpdate(final DocumentEvent event,
317: final Shape shape, final ViewFactory factory) {
318: preferenceChanged(this , true, false);
319: }
320: }
321:
322: /**
323: * Represents one logical line of text.
324: */
325: class LineView extends BoxView {
326: public LineView(final Element element) {
327: super (element, X_AXIS);
328: }
329:
330: @Override
331: public float getAlignment(final int axis) {
332: return ALIGN_LEFT;
333: }
334:
335: @Override
336: public void paint(final Graphics g, final Shape shape) {
337: TextKit textKit = getTextKit();
338:
339: if (textKit != null) {
340: textKit.paintLayeredHighlights(g, getStartOffset(),
341: getEndOffset() - 1, shape, this );
342: }
343:
344: super .paint(g, shape);
345: }
346:
347: @Override
348: protected boolean updateChildren(final ElementChange change,
349: final DocumentEvent event, final ViewFactory factory) {
350: return false;
351: }
352:
353: @Override
354: protected SizeRequirements calculateMinorAxisRequirements(
355: final int axis, final SizeRequirements r) {
356: SizeRequirements result = super
357: .calculateMinorAxisRequirements(axis, r);
358: // This view is not resizable along Y (minor axis)
359: result.maximum = result.preferred;
360: return result;
361: }
362:
363: int accumulatedWidth;
364:
365: @Override
366: protected SizeRequirements calculateMajorAxisRequirements(
367: final int axis, final SizeRequirements r) {
368: accumulatedWidth = 0;
369: return super .calculateMajorAxisRequirements(axis, r);
370: }
371:
372: @Override
373: protected void loadChildren(final ViewFactory factory) {
374: updateChildren();
375: }
376:
377: private void updateView(final DocumentEvent event,
378: final Shape shape) {
379: final AbstractDocument doc = (AbstractDocument) getDocument();
380: final ElementChange change = event.getChange(doc
381: .getBidiRootElement());
382:
383: if (change != null) {
384: updateChildren();
385: preferenceChanged(this , true, false);
386:
387: if (shape != null) {
388: Rectangle rc = shape.getBounds();
389: getComponent().repaint(rc.x, rc.y, rc.width,
390: rc.height);
391: }
392: } else {
393: forwardUpdate(null, event, shape, null);
394: }
395: }
396:
397: @Override
398: public void insertUpdate(final DocumentEvent event,
399: final Shape shape, final ViewFactory factory) {
400: updateView(event, shape);
401: }
402:
403: @Override
404: public void removeUpdate(final DocumentEvent event,
405: final Shape shape, final ViewFactory factory) {
406: updateView(event, shape);
407: }
408:
409: private void updateChildren() {
410: int startOffset = getStartOffset();
411: final int endOffset = getEndOffset();
412: final Element element = getElement();
413: AbstractDocument doc = (AbstractDocument) getDocument();
414: Element bidiRoot = doc.getBidiRootElement();
415: final int startIndex = bidiRoot
416: .getElementIndex(startOffset);
417: final int endIndex = bidiRoot.getElementIndex(endOffset);
418: List<View> views = new ArrayList<View>();
419: for (int i = startIndex; i <= endIndex
420: && startOffset < endOffset; i++) {
421: Element bidi = bidiRoot.getElement(i);
422: int level = StyleConstants.getBidiLevel(bidi
423: .getAttributes());
424: View child;
425: if (TextUtils.isLTR(level)) {
426: child = new LTRTextView(element, startOffset, Math
427: .min(endOffset, bidi.getEndOffset()));
428: } else {
429: child = new RTLTextView(element, startOffset, Math
430: .min(endOffset, bidi.getEndOffset()));
431: }
432: startOffset = child.getEndOffset();
433: views.add(child);
434: }
435: replace(0, getViewCount(), views.toArray(new View[views
436: .size()]));
437: }
438: }
439:
440: private final TextPaintParams paintParams = new TextPaintParams(
441: this );
442:
443: private ViewFactory lineFactory = new ViewFactory() {
444: public View create(final Element element) {
445: return new LineView(element);
446: }
447: };
448:
449: public PlainViewI18N(final Element element) {
450: super (element, Y_AXIS);
451: }
452:
453: @Override
454: public ViewFactory getViewFactory() {
455: return lineFactory;
456: }
457:
458: @Override
459: public void paint(final Graphics g, final Shape shape) {
460: paintParams.updateFields();
461: super .paint(g, shape);
462: }
463:
464: @Override
465: public void setSize(final float width, final float height) {
466: paintParams.conditionalUpdateMetrics();
467: super .setSize(width, height);
468:
469: final int count = getViewCount();
470: for (int i = 0; i < count; i++) {
471: getView(i).setSize(getSpan(X_AXIS, i), getSpan(Y_AXIS, i));
472: }
473: }
474:
475: public float nextTabStop(final float x, final int tabOffset) {
476: return paintParams.nextTabStop(x);
477: }
478:
479: @Override
480: public float getMaximumSpan(final int axis) {
481: paintParams.conditionalUpdateMetrics();
482: return super .getMaximumSpan(axis);
483: }
484:
485: @Override
486: public float getMinimumSpan(final int axis) {
487: paintParams.conditionalUpdateMetrics();
488: return super .getMinimumSpan(axis);
489: }
490:
491: @Override
492: public float getPreferredSpan(final int axis) {
493: paintParams.conditionalUpdateMetrics();
494: return super .getPreferredSpan(axis);
495: }
496:
497: @Override
498: public void insertUpdate(final DocumentEvent event,
499: final Shape shape, final ViewFactory factory) {
500: super .insertUpdate(event, shape, lineFactory);
501: }
502:
503: @Override
504: public void removeUpdate(final DocumentEvent event,
505: final Shape shape, final ViewFactory factory) {
506: super.removeUpdate(event, shape, lineFactory);
507: }
508: }
|