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.Component;
023: import java.awt.FontMetrics;
024: import java.awt.Graphics;
025: import java.awt.Rectangle;
026: import java.awt.Shape;
027:
028: import javax.swing.event.DocumentEvent;
029: import javax.swing.event.DocumentEvent.ElementChange;
030: import javax.swing.event.DocumentEvent.EventType;
031:
032: import org.apache.harmony.awt.text.TextKit;
033: import org.apache.harmony.awt.text.TextUtils;
034:
035: import org.apache.harmony.x.swing.internal.nls.Messages;
036:
037: public class PlainView extends View implements TabExpander {
038: protected FontMetrics metrics;
039: final TextPaintParams paintParams = new TextPaintParams(this );
040: private Element widestLine;
041: private int widestLineWidth;
042:
043: /**
044: * It is assumed that <code>element</code> is default root of document
045: * this view represents (PlainDocument).
046: */
047: public PlainView(final Element element) {
048: super (element);
049: }
050:
051: public float getPreferredSpan(final int axis) {
052: conditionalUpdateMetrics();
053:
054: switch (axis) {
055: case X_AXIS:
056: return getLineWidth(widestLine);
057:
058: case Y_AXIS:
059: return metrics.getHeight() * getElement().getElementCount();
060:
061: default:
062: throw new IllegalArgumentException(Messages.getString(
063: "swing.00", axis)); //$NON-NLS-1$
064: }
065: }
066:
067: public void insertUpdate(final DocumentEvent event,
068: final Shape shape, final ViewFactory factory) {
069: updateDamage(event, shape, factory);
070: }
071:
072: public void removeUpdate(final DocumentEvent event,
073: final Shape shape, final ViewFactory factory) {
074: updateDamage(event, shape, factory);
075: }
076:
077: public void changedUpdate(final DocumentEvent event,
078: final Shape shape, final ViewFactory factory) {
079: updateDamage(event, shape, factory);
080: }
081:
082: /**
083: * Returns the rectangle where the caret will be painted. This rectangle
084: * is positioned between characters (at position <code>pos</code>).
085: * Its width is 1, and height is the height of the line (of the font used).
086: * <p>For more information about position see
087: * http://java.sun.com/products/jfc/tsc/articles/text/element_buffer/
088: * in the heading <strong>The Position Interface</strong>.)
089: */
090: public Shape modelToView(final int pos, final Shape shape,
091: final Position.Bias bias) throws BadLocationException {
092:
093: if (pos < 0 || pos > getDocument().getLength() + 1) {
094: throw new BadLocationException(Messages
095: .getString("swing.98"), pos); //$NON-NLS-1$
096: }
097:
098: final int lineNo = getElement().getElementIndex(pos);
099: final Element line = getElement().getElement(lineNo);
100:
101: getDocument().getText(line.getStartOffset(),
102: pos - line.getStartOffset(), getLineBuffer());
103: final Rectangle bounds = shape.getBounds();
104: return new Rectangle(TextUtils.getTabbedTextWidth(
105: getLineBuffer(), metrics, bounds.x, this , pos)
106: + bounds.x, bounds.y + metrics.getHeight() * lineNo, 1,
107: metrics.getHeight());
108: }
109:
110: public int viewToModel(final float x, final float y,
111: final Shape shape, final Position.Bias[] biasReturn) {
112: biasReturn[0] = Position.Bias.Forward;
113:
114: final Rectangle bounds = shape.getBounds();
115:
116: if (y >= getPreferredSpan(Y_AXIS) + bounds.y) {
117: return getDocument().getLength();
118: }
119: if (y < bounds.y) {
120: return 0;
121: }
122:
123: final int lineNo = (int) (y - bounds.y) / metrics.getHeight();
124:
125: final Element line = getElement().getElement(lineNo);
126: final int start = line.getStartOffset();
127: final int end = line.getEndOffset() - 1;
128: try {
129: getDocument().getText(start, end - start, getLineBuffer());
130: } catch (final BadLocationException e) {
131: }
132:
133: return start
134: + TextUtils.getTabbedTextOffset(getLineBuffer(),
135: metrics, bounds.x, (int) Math.max(x, bounds.x),
136: this , start);
137: }
138:
139: public float nextTabStop(final float x, final int tabOffset) {
140: conditionalUpdateMetrics();
141: return paintParams.nextTabStop(x);
142: }
143:
144: public void paint(final Graphics g, final Shape shape) {
145: final Rectangle bounds = shape.getBounds();
146: int y = bounds.y + metrics.getAscent();
147:
148: paintParams.updateFields();
149:
150: final Rectangle clipBounds = g.getClipBounds();
151: final int height = metrics.getHeight();
152:
153: final TextKit textKit = getTextKit();
154: for (int i = 0; i < getElement().getElementCount(); i++, y += height) {
155: if (!lineToRect(shape, i).intersects(clipBounds)) {
156: continue;
157: }
158:
159: if (textKit != null) {
160: Element line = getElement().getElement(i);
161: textKit.paintLayeredHighlights(g,
162: line.getStartOffset(), line.getEndOffset() - 1,
163: shape, this );
164: }
165: drawLine(i, g, bounds.x, y);
166: }
167: }
168:
169: public void setSize(final float width, final float height) {
170: conditionalUpdateMetrics();
171: }
172:
173: protected void drawLine(final int lineNo, final Graphics g,
174: final int x, final int y) {
175: final Element line = getElement().getElement(lineNo);
176: drawLine(paintParams, line.getStartOffset(), line
177: .getEndOffset() - 1, g, x, y);
178: }
179:
180: protected int drawSelectedText(final Graphics g, final int x,
181: final int y, final int start, final int end)
182: throws BadLocationException {
183:
184: return drawText(g, paintParams.selColor, paintParams, x, y,
185: start, end);
186: }
187:
188: protected int drawUnselectedText(final Graphics g, final int x,
189: final int y, final int start, final int end)
190: throws BadLocationException {
191:
192: return drawText(g, paintParams.color, paintParams, x, y, start,
193: end);
194: }
195:
196: protected final Segment getLineBuffer() {
197: return paintParams.buffer;
198: }
199:
200: protected int getTabSize() {
201: return paintParams.getTabSize();
202: }
203:
204: protected Rectangle lineToRect(final Shape shape, final int lineNo) {
205: conditionalUpdateMetrics();
206:
207: int height = metrics.getHeight();
208: Rectangle bounds = shape.getBounds();
209: return new Rectangle(bounds.x, bounds.y + height * lineNo,
210: bounds.width, height);
211: }
212:
213: protected void damageLineRange(final int startLine,
214: final int endLine, final Shape shape,
215: final Component component) {
216: Rectangle lineRect;
217: for (int i = startLine; i <= endLine; i++) {
218: lineRect = lineToRect(shape, i);
219: component.repaint(lineRect.x, lineRect.y, lineRect.width,
220: lineRect.height);
221: }
222: }
223:
224: protected void updateDamage(final DocumentEvent event,
225: final Shape shape, final ViewFactory factory) {
226: if (shape == null) {
227: return;
228: }
229:
230: if (metrics == null) {
231: updateMetrics();
232: preferenceChanged(null, true, true);
233: return;
234: }
235:
236: final ElementChange change = event.getChange(getElement());
237:
238: if (event.getType() == EventType.INSERT) {
239: updateDamageOnInsert(event, change, shape);
240: } else {
241: updateDamageOnRemove(event, change, shape);
242: }
243: }
244:
245: protected void updateMetrics() {
246: paintParams.updateMetrics();
247: metrics = paintParams.metrics;
248:
249: updateWidestLine();
250: }
251:
252: final void conditionalUpdateMetrics() {
253: if (paintParams.areMetricsValid()) {
254: updateMetrics();
255: }
256: }
257:
258: private void updateWidestLine() {
259: widestLine = getElement().getElement(0);
260: widestLineWidth = getLineWidth(widestLine);
261:
262: updateWidestLine(1, getElement().getElementCount() - 1);
263: }
264:
265: private void updateWidestLine(final int start, final int end) {
266: for (int i = start; i <= end; i++) {
267: int w = getLineWidth(i);
268: if (w > widestLineWidth) {
269: widestLineWidth = w;
270: widestLine = getElement().getElement(i);
271: }
272: }
273: }
274:
275: private int getLineWidth(final Element line) {
276: try {
277: getDocument().getText(line.getStartOffset(),
278: line.getEndOffset() - line.getStartOffset() - 1,
279: getLineBuffer());
280: } catch (final BadLocationException e) {
281: }
282:
283: return TextUtils.getTabbedTextWidth(getLineBuffer(), metrics,
284: 0, this , line.getStartOffset());
285: }
286:
287: private int getLineWidth(final int lineNo) {
288: return getLineWidth(getElement().getElement(lineNo));
289: }
290:
291: private void updateDamageOnInsert(final DocumentEvent event,
292: final ElementChange change, final Shape shape) {
293: boolean linesAdded = change != null;
294: int start = linesAdded ? change.getIndex() : getElement()
295: .getElementIndex(event.getOffset());
296: int length = linesAdded ? change.getChildrenAdded().length - 1
297: : 0;
298: int width = widestLineWidth;
299: if (widestLine.getEndOffset() < event.getOffset()
300: || widestLine.getStartOffset() > event.getOffset()
301: + event.getLength()) {
302: // The previous longest line was not affected
303: updateWidestLine(start, start + length);
304: } else {
305: updateWidestLine();
306: }
307: preferenceChanged(null, widestLineWidth != width, linesAdded);
308: damageLineRange(start, linesAdded ? getElement()
309: .getElementCount() - 1 : start, shape, getComponent());
310: }
311:
312: private void updateDamageOnRemove(final DocumentEvent event,
313: final ElementChange change, final Shape shape) {
314: int width = widestLineWidth;
315: if (change != null) {
316: updateWidestLine();
317: preferenceChanged(null, widestLineWidth != width, true);
318: getComponent().repaint();
319: } else {
320: int lineNo = getElement()
321: .getElementIndex(event.getOffset());
322: Element line = getElement().getElement(lineNo);
323: if (widestLine == line) {
324: updateWidestLine();
325: preferenceChanged(null, widestLineWidth != width, false);
326: }
327: damageLineRange(lineNo, lineNo, shape, getComponent());
328: }
329: }
330:
331: }
|