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.BasicStroke;
023: import java.awt.Color;
024: import java.awt.Component;
025: import java.awt.Font;
026: import java.awt.FontMetrics;
027: import java.awt.Graphics;
028: import java.awt.Graphics2D;
029: import java.awt.Rectangle;
030: import java.awt.Shape;
031: import java.awt.Stroke;
032: import java.awt.Toolkit;
033: import java.awt.font.LineMetrics;
034: import java.util.Arrays;
035: import java.util.Iterator;
036: import java.util.List;
037:
038: import javax.swing.text.GlyphView.GlyphPainter;
039: import javax.swing.text.Position.Bias;
040: import javax.swing.text.TextInterval.TextIntervalPainter;
041:
042: import org.apache.harmony.awt.text.TextKit;
043: import org.apache.harmony.awt.text.TextUtils;
044:
045: import org.apache.harmony.x.swing.internal.nls.Messages;
046:
047: /**
048: * Default GlyphPainter which is used when a GlyphView needs a painter but
049: * none was set.
050: */
051: final class DefaultGlyphPainter extends GlyphPainter {
052:
053: private final class TextPainter implements TextIntervalPainter {
054: final GlyphView view;
055: final Rectangle bounds;
056: final Font font;
057: final boolean advanced;
058:
059: TextPainter(final GlyphView view, final Rectangle bounds,
060: final Font font, final boolean advanced) {
061: this .view = view;
062: this .bounds = bounds;
063: this .font = font;
064: this .advanced = advanced;
065: }
066:
067: public int paintSelected(final Graphics g, final int start,
068: final int end, final int x, final int y)
069: throws BadLocationException {
070:
071: return drawSelectedText(this , g, start, end, x, y);
072: }
073:
074: public int paintUnselected(final Graphics g, final int start,
075: final int end, final int x, final int y)
076: throws BadLocationException {
077:
078: return drawUnselectedText(this , g, start, end, x, y);
079: }
080:
081: public int paintComposed(final Graphics g, final int start,
082: final int end, final int x, final int y)
083: throws BadLocationException {
084:
085: if (true) {
086: throw new UnsupportedOperationException(Messages
087: .getString("swing.87")); //$NON-NLS-1$
088: }
089: return x;
090: }
091: }
092:
093: private static GlyphPainter defaultPainter;
094:
095: public float getSpan(final GlyphView v, final int startOffset,
096: final int endOffset, final TabExpander tabExpander,
097: final float x) {
098: final Segment text = v.getText(startOffset, endOffset);
099: final FontMetrics metrics = getFontMetrics(v);
100: return TextUtils.getTabbedTextWidth(text, metrics, (int) x,
101: tabExpander, startOffset);
102: }
103:
104: public float getHeight(final GlyphView v) {
105: return getFontMetrics(v).getHeight();
106: }
107:
108: public float getAscent(final GlyphView v) {
109: return getFontMetrics(v).getAscent();
110: }
111:
112: public float getDescent(final GlyphView v) {
113: return getFontMetrics(v).getDescent();
114: }
115:
116: public void paint(final GlyphView v, final Graphics g,
117: final Shape alloc, final int startOffset,
118: final int endOffset) {
119: final TextKit textKit = v.getTextKit();
120: textKit.paintLayeredHighlights(g, startOffset, endOffset,
121: alloc, v);
122:
123: final Rectangle bounds = alloc.getBounds();
124: final Font font = v.getFont();
125: final TextPainter painter = new TextPainter(v, bounds, font,
126: g instanceof Graphics2D && v.isUnderline()
127: || v.isStrikeThrough());
128: final UnselectedTextInterval ui = new UnselectedTextInterval(v
129: .getStartOffset(), v.getEndOffset(), painter);
130: final SelectedTextInterval si = new SelectedTextInterval(
131: textKit.getSelectionStart(), textKit.getSelectionEnd(),
132: painter);
133: final List intervals = Arrays.asList(ui.dissect(si));
134:
135: Color oldColor = g.getColor();
136: Font oldFont = g.getFont();
137:
138: g.setFont(font);
139: final FontMetrics metrics = getFontMetrics(v, font);
140: try {
141: int x = bounds.x;
142: final int y = bounds.y + metrics.getAscent();
143: for (Iterator it = intervals.iterator(); it.hasNext();) {
144: TextInterval interval = (TextInterval) it.next();
145: x = interval.paint(g, x, y);
146: }
147: } catch (BadLocationException e) {
148: }
149:
150: g.setFont(oldFont);
151: g.setColor(oldColor);
152: }
153:
154: public Shape modelToView(final GlyphView v, final int offset,
155: final Bias bias, final Shape alloc)
156: throws BadLocationException {
157:
158: if (offset < v.getStartOffset() || offset > v.getEndOffset()) {
159: throw new BadLocationException(Messages.getString(
160: "swing.88", offset), offset); //$NON-NLS-1$
161: }
162:
163: final Segment text = v.getText(v.getStartOffset(), offset);
164: final FontMetrics metrics = getFontMetrics(v);
165: final Rectangle bounds = alloc.getBounds();
166: return new Rectangle(TextUtils.getTabbedTextWidth(text,
167: metrics, bounds.x, v.getTabExpander(), offset)
168: + bounds.x, bounds.y, 0, metrics.getHeight());
169: }
170:
171: public int viewToModel(final GlyphView v, final float x,
172: final float y, final Shape alloc, final Bias[] biasReturn) {
173: biasReturn[0] = Bias.Forward;
174:
175: final Rectangle bounds = alloc.getBounds();
176: if (x < bounds.x || y < bounds.y) {
177: return v.getStartOffset();
178: }
179: if (x > bounds.x + bounds.width || y > bounds.y + bounds.height) {
180: return v.getEndOffset() - 1;
181: }
182: final Segment text = v.getText(v.getStartOffset(), v
183: .getEndOffset());
184: final FontMetrics fm = getFontMetrics(v);
185: return v.getStartOffset()
186: + TextUtils
187: .getTabbedTextOffset(text, fm, bounds.x,
188: (int) x, v.getTabExpander(), v
189: .getStartOffset());
190: }
191:
192: public int getBoundedPosition(final GlyphView v,
193: final int startOffset, final float x, final float len) {
194: final Segment text = v.getText(startOffset, v.getEndOffset());
195: final FontMetrics fm = getFontMetrics(v);
196: return startOffset
197: + TextUtils.getTabbedTextOffset(text, fm, (int) x,
198: (int) (x + len), v.getTabExpander(),
199: startOffset, false);
200: }
201:
202: static GlyphPainter getDefaultPainter() {
203: if (defaultPainter == null) {
204: defaultPainter = new DefaultGlyphPainter();
205: }
206: return defaultPainter;
207: }
208:
209: static FontMetrics getFontMetrics(final GlyphView v) {
210: return getFontMetrics(v, v.getFont());
211: }
212:
213: static FontMetrics getFontMetrics(final GlyphView v, final Font font) {
214: Component c = v.getComponent();
215: return c != null ? c.getFontMetrics(font) : Toolkit
216: .getDefaultToolkit().getFontMetrics(font);
217: }
218:
219: int drawText(final GlyphView v, final Graphics g, final int start,
220: final int end, final int x, final int y)
221: throws BadLocationException {
222: return TextUtils.drawTabbedText(v.getText(start, end), x, y, g,
223: v.getTabExpander(), start);
224: }
225:
226: int drawSelectedText(final TextPainter tp, final Graphics g,
227: final int start, final int end, final int x, final int y)
228: throws BadLocationException {
229: g.setColor(tp.view.getTextKit().getSelectedTextColor());
230: final int result = drawText(tp.view, g, start, end, x, y);
231: if (tp.advanced) {
232: paintAdvancedStyles(tp.view, start, end, (Graphics2D) g,
233: tp.font, tp.view.modelToView(start, Bias.Forward,
234: end, Bias.Backward, tp.bounds).getBounds());
235: }
236: return result;
237: }
238:
239: int drawUnselectedText(final TextPainter tp, final Graphics g,
240: final int start, final int end, final int x, final int y)
241: throws BadLocationException {
242:
243: Rectangle r = null;
244: try {
245: r = tp.view.modelToView(start, Bias.Forward, end,
246: Bias.Backward, tp.bounds).getBounds();
247: } catch (BadLocationException e) {
248: }
249:
250: Color back = tp.view.getBackground();
251: if (back != null && r != null) {
252: g.setColor(back);
253: g.fillRect(r.x, r.y, r.width, r.height);
254: }
255:
256: g.setColor(tp.view.getForeground());
257: final int result = drawText(tp.view, g, start, end, x, y);
258: if (tp.advanced) {
259: paintAdvancedStyles(tp.view, start, end, (Graphics2D) g,
260: tp.font, r);
261: }
262: return result;
263: }
264:
265: private void paintAdvancedStyles(final GlyphView v,
266: final int start, final int end, final Graphics2D g,
267: final Font font, final Rectangle bounds) {
268: final Segment text = v.getText(start, end);
269: final LineMetrics lm = font.getLineMetrics(text.array,
270: text.offset, text.offset + text.count, g
271: .getFontRenderContext());
272: int baseline = bounds.y + Math.round(lm.getAscent());
273:
274: if (v.isStrikeThrough()) {
275: int offset = Math.round(lm.getStrikethroughOffset())
276: + baseline;
277: BasicStroke stroke = new BasicStroke(lm
278: .getStrikethroughThickness());
279: Stroke oldStroke = g.getStroke();
280: g.setStroke(stroke);
281: g.drawLine(bounds.x, offset, bounds.x + bounds.width,
282: offset);
283: g.setStroke(oldStroke);
284: }
285:
286: if (v.isUnderline()) {
287: int offset = Math.round(lm.getUnderlineOffset()) + baseline;
288: BasicStroke stroke = new BasicStroke(lm
289: .getUnderlineThickness());
290: Stroke oldStroke = g.getStroke();
291: g.setStroke(stroke);
292: g.drawLine(bounds.x, offset, bounds.x + bounds.width,
293: offset);
294: g.setStroke(oldStroke);
295: }
296: }
297: }
|