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.Color;
023: import java.awt.Component;
024: import java.awt.Font;
025: import java.awt.Graphics;
026: import java.awt.Shape;
027: import java.text.BreakIterator;
028: import java.text.CharacterIterator;
029:
030: import javax.swing.event.DocumentEvent;
031: import javax.swing.text.Position.Bias;
032:
033: public class GlyphView extends View implements TabableView, Cloneable {
034: public abstract static class GlyphPainter {
035: public abstract float getSpan(GlyphView v, int startOffset,
036: int endOffset, TabExpander tabExpander, float x);
037:
038: public abstract float getHeight(GlyphView v);
039:
040: public abstract float getAscent(GlyphView v);
041:
042: public abstract float getDescent(GlyphView v);
043:
044: public abstract void paint(GlyphView v, Graphics g,
045: Shape alloc, int startOffset, int endOffset);
046:
047: public abstract Shape modelToView(GlyphView v, int offset,
048: Bias bias, Shape alloc) throws BadLocationException;
049:
050: public abstract int viewToModel(GlyphView v, float x, float y,
051: Shape alloc, Bias[] biasReturn);
052:
053: public abstract int getBoundedPosition(GlyphView v,
054: int startOffset, float x, float len);
055:
056: public GlyphPainter getPainter(final GlyphView v,
057: final int startOffset, final int endOffset) {
058: return this ;
059: }
060:
061: public int getNextVisualPositionFrom(final GlyphView v,
062: final int offset, final Bias bias, final Shape alloc,
063: final int direction, final Bias[] biasReturn)
064: throws BadLocationException {
065:
066: switch (direction) {
067: case EAST:
068: if (offset < v.getEndOffset() - 1) {
069: biasReturn[0] = Bias.Forward;
070: return offset + 1;
071: }
072: return -1;
073:
074: case WEST:
075: if (offset > v.getStartOffset()) {
076: biasReturn[0] = Bias.Forward;
077: return offset - 1;
078: }
079: return -1;
080:
081: case NORTH:
082: case SOUTH:
083: return -1;
084:
085: default:
086: assert false : "Unknown direction. Valid values are "
087: + "EAST, WEST, NORTH, SOUTH";
088: return -1;
089: }
090: }
091: }
092:
093: private GlyphPainter painter;
094: private int partOffset;
095: private int partLength;
096: private TabExpander tabExpander;
097:
098: public GlyphView(final Element element) {
099: super (element);
100: }
101:
102: public void setGlyphPainter(final GlyphPainter p) {
103: painter = p;
104: }
105:
106: public GlyphPainter getGlyphPainter() {
107: return painter;
108: }
109:
110: public float getAlignment(final int axis) {
111: if (axis == X_AXIS) {
112: return super .getAlignment(axis);
113: }
114:
115: checkPainter();
116: final GlyphPainter p = getGlyphPainter();
117: final float height = p.getHeight(this );
118:
119: return (height - p.getDescent(this )) / height;
120: }
121:
122: public float getPreferredSpan(final int axis) {
123: checkPainter();
124: return axis == X_AXIS ? getGlyphPainter().getSpan(this ,
125: getStartOffset(), getEndOffset(), getTabExpander(), 0)
126: : getGlyphPainter().getHeight(this );
127: }
128:
129: public float getPartialSpan(final int startOffset,
130: final int endOffset) {
131: checkPainter();
132: return getGlyphPainter().getSpan(this , startOffset, endOffset,
133: null, 0);
134: }
135:
136: public float getTabbedSpan(final float x,
137: final TabExpander tabExpander) {
138: checkPainter();
139: return getGlyphPainter().getSpan(this , getStartOffset(),
140: getEndOffset(), tabExpander, x);
141: }
142:
143: public TabExpander getTabExpander() {
144: return tabExpander;
145: }
146:
147: public void setParent(final View parent) {
148: if (parent != getParent()) {
149: View p = parent;
150: while (p != null && !(p instanceof TabExpander)) {
151: p = p.getParent();
152: }
153: tabExpander = p != null ? (TabExpander) p : null;
154: }
155: super .setParent(parent);
156: }
157:
158: public Segment getText(final int startOffset, final int endOffset) {
159: final Segment text = new Segment();
160: try {
161: getDocument().getText(startOffset, endOffset - startOffset,
162: text);
163: } catch (BadLocationException e) {
164: throw new Error(e);
165: }
166: return text;
167: }
168:
169: public int getStartOffset() {
170: int result = super .getStartOffset();
171: if (isPart()) {
172: result += partOffset;
173: }
174: return result;
175: }
176:
177: public int getEndOffset() {
178: return isPart() ? super .getStartOffset() + partOffset
179: + partLength : super .getEndOffset();
180: }
181:
182: public void paint(final Graphics g, final Shape alloc) {
183: checkPainter();
184: getGlyphPainter().paint(this , g, alloc, getStartOffset(),
185: getEndOffset());
186: }
187:
188: public Shape modelToView(final int offset, final Shape alloc,
189: final Bias bias) throws BadLocationException {
190: checkPainter();
191: return getGlyphPainter().modelToView(this , offset, bias, alloc);
192: }
193:
194: public int viewToModel(final float x, final float y,
195: final Shape alloc, final Bias[] biasReturn) {
196: checkPainter();
197: return getGlyphPainter().viewToModel(this , x, y, alloc,
198: biasReturn);
199: }
200:
201: public int getNextVisualPositionFrom(final int offset,
202: final Bias bias, final Shape alloc, final int direction,
203: final Bias[] biasReturn) throws BadLocationException {
204:
205: return getGlyphPainter().getNextVisualPositionFrom(this ,
206: offset, bias, alloc, direction, biasReturn);
207: }
208:
209: public int getBreakWeight(final int axis, final float x,
210: final float len) {
211: if (axis == Y_AXIS) {
212: return super .getBreakWeight(axis, x, len);
213: }
214:
215: checkPainter();
216: int breakOffset = getGlyphPainter().getBoundedPosition(this ,
217: getStartOffset(), x, len);
218: if (breakOffset == getStartOffset()) {
219: return BadBreakWeight;
220: }
221: final Segment text = getText(getStartOffset(), breakOffset);
222: boolean hasWhiteSpace;
223: char c = text.last();
224: do {
225: hasWhiteSpace = Character.isWhitespace(c);
226: c = text.previous();
227: } while (c != Segment.DONE && !hasWhiteSpace);
228: return hasWhiteSpace ? ExcellentBreakWeight : GoodBreakWeight;
229: }
230:
231: public View breakView(final int axis, final int startOffset,
232: final float x, final float len) {
233: if (axis == Y_AXIS) {
234: return super .breakView(axis, startOffset, x, len);
235: }
236: final BreakIterator bi = BreakIterator.getWordInstance();
237: final Segment text = getText(startOffset, getEndOffset());
238: bi.setText(text);
239:
240: checkPainter();
241: final GlyphPainter p = getGlyphPainter();
242:
243: float width = 0;
244: float fragmentWidth;
245: int prev;
246: int curr = bi.first() - text.offset;
247: int next;
248: int ws = -1;
249: boolean whitespace;
250: do {
251: prev = curr;
252: next = bi.next();
253: curr = next != BreakIterator.DONE ? next - text.offset
254: : getEndOffset() - startOffset;
255: fragmentWidth = p.getSpan(this , startOffset + prev,
256: startOffset + curr, getTabExpander(), x + width);
257: whitespace = isWhitespace(getText(startOffset + prev,
258: startOffset + curr));
259: if (whitespace) {
260: ws = curr;
261: }
262: width += fragmentWidth;
263: } while (width <= len && next != BreakIterator.DONE);
264:
265: int length;
266: if (ws == -1) {
267: length = p.getBoundedPosition(this , startOffset, x, len)
268: - startOffset;
269: if (length == 0) {
270: // The view cannot be broken at this point
271: return createFragment(getStartOffset(), getEndOffset());
272: }
273: } else {
274: length = whitespace ? curr : prev;
275: }
276:
277: if (startOffset == getStartOffset()
278: && getStartOffset() + length == getEndOffset()) {
279:
280: return this ;
281: }
282: return createFragment(startOffset, startOffset + length);
283: }
284:
285: public View createFragment(final int startOffset,
286: final int endOffset) {
287: GlyphView result = (GlyphView) clone();
288: result.partOffset = startOffset - getElement().getStartOffset();
289: result.partLength = endOffset - startOffset;
290: return result;
291: }
292:
293: public Color getForeground() {
294: final StyledDocument doc = getStyledDocument();
295: if (doc != null) {
296: return doc.getForeground(getAttributes());
297: }
298:
299: final Component component = getComponent();
300: return component != null ? component.getForeground() : null;
301: }
302:
303: public Color getBackground() {
304: if (!getAttributes().isDefined(StyleConstants.Background)) {
305: return null;
306: }
307:
308: final StyledDocument doc = getStyledDocument();
309: return doc != null ? doc.getBackground(getAttributes()) : null;
310: }
311:
312: public Font getFont() {
313: final StyledDocument doc = getStyledDocument();
314: if (doc != null) {
315: return doc.getFont(getAttributes());
316: }
317:
318: final Component component = getComponent();
319: return component != null ? component.getFont() : null;
320: }
321:
322: public boolean isUnderline() {
323: return StyleConstants.isUnderline(getAttributes());
324: }
325:
326: public boolean isStrikeThrough() {
327: return StyleConstants.isStrikeThrough(getAttributes());
328: }
329:
330: public boolean isSubscript() {
331: return StyleConstants.isSubscript(getAttributes());
332: }
333:
334: public boolean isSuperscript() {
335: return StyleConstants.isSuperscript(getAttributes());
336: }
337:
338: public void insertUpdate(final DocumentEvent event,
339: final Shape alloc, final ViewFactory factory) {
340: preferenceChanged(null, true, false);
341: }
342:
343: public void removeUpdate(final DocumentEvent event,
344: final Shape alloc, final ViewFactory factory) {
345: preferenceChanged(null, true, false);
346: }
347:
348: public void changedUpdate(final DocumentEvent event,
349: final Shape alloc, final ViewFactory factory) {
350: preferenceChanged(null, true, true);
351: }
352:
353: protected void checkPainter() {
354: if (getGlyphPainter() == null) {
355: setGlyphPainter(DefaultGlyphPainter.getDefaultPainter());
356: }
357: }
358:
359: protected final Object clone() {
360: Object result = null;
361: try {
362: result = super .clone();
363: } catch (CloneNotSupportedException e) {
364: }
365: return result;
366: }
367:
368: private StyledDocument getStyledDocument() {
369: Document doc = getDocument();
370: return doc instanceof StyledDocument ? (StyledDocument) doc
371: : null;
372: }
373:
374: private boolean isPart() {
375: return partOffset != 0 || partLength != 0;
376: }
377:
378: private static boolean isWhitespace(final CharacterIterator text) {
379: boolean result;
380: char c = text.first();
381: do {
382: result = Character.isWhitespace(c);
383: c = text.next();
384: } while (result && c != CharacterIterator.DONE);
385: return result;
386: }
387: }
|