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 Evgeniya G. Maenkova
019: * @version $Revision$
020: */package javax.swing.text;
021:
022: import java.awt.Color;
023: import java.awt.Graphics;
024: import java.awt.Rectangle;
025: import java.awt.Shape;
026: import java.util.ArrayList;
027: import javax.swing.plaf.TextUI;
028:
029: import org.apache.harmony.awt.text.TextKit;
030: import org.apache.harmony.awt.text.TextUtils;
031:
032: import org.apache.harmony.x.swing.internal.nls.Messages;
033:
034: public class DefaultHighlighter extends LayeredHighlighter {
035:
036: // This vector contains all highlights
037: private final HighlightList highlights = new HighlightList();;
038:
039: // Defines paint procedure
040: private boolean drawsLayeredHighlights;
041:
042: // JTextComponent bound to the highlighter
043: private JTextComponent component;
044:
045: private TextKit textKit;
046:
047: /**
048: * Provides more convinient interface for DefaultHighlighter
049: */
050: private class HighlightList extends ArrayList {
051:
052: public HighlightImpl getElem(final Object obj) {
053: int index = indexOf(obj);
054: return index >= 0 ? (HighlightImpl) get(index) : null;
055: }
056:
057: public HighlightImpl getElem(final int index) {
058: return (HighlightImpl) get(index);
059: }
060: }
061:
062: /**
063: * Stores all Highlights information.
064: *
065: */
066: private class HighlightImpl implements Highlight {
067: // highlight start position
068: private Position startPosition;
069:
070: // highlight end position
071: private Position endPosition;
072:
073: // This variable is used for painting. This is true, if
074: // Highlight painter is instanceof
075: // LayeredHighlight.LayeredHighlightPainter,otherwise - false.
076: private final boolean instanceLayerPainter;
077:
078: // Defines paint order for highlight.
079: // Equals DefaultHighlighter.drawsLayeredHighlight, when Highlight
080: // is added.
081: private final boolean drawsLayered;
082:
083: // Highlight painter
084: private final Highlighter.HighlightPainter painter;
085:
086: /**
087: * New highlight constructor. Sets startPosition, endPosition,
088: * drawsLayered, instanceLayerPainter and bounds. Parameters: p0 - start
089: * position, p1 - end position, pnt - painter, r - bounds
090: */
091: HighlightImpl(final Position start, final Position end,
092: final HighlightPainter pnt) {
093: startPosition = start;
094: endPosition = end;
095: painter = pnt;
096: instanceLayerPainter = (pnt instanceof LayerPainter);
097: drawsLayered = getDrawsLayeredHighlights();
098: }
099:
100: /**
101: * Changes highlight. Changes startPosition, endPosition, bounds.
102: */
103: public void changeHighlight(final Position start,
104: final Position end) {
105: startPosition = start;
106: endPosition = end;
107: }
108:
109: /**
110: * Returns the endPosition offset
111: */
112: public int getEndOffset() {
113: return endPosition.getOffset();
114: }
115:
116: /**
117: * Returns the painter
118: */
119: public Highlighter.HighlightPainter getPainter() {
120: return painter;
121: }
122:
123: /**
124: * Returns the startPosition Offset
125: */
126: public int getStartOffset() {
127: return startPosition.getOffset();
128: }
129:
130: final boolean needSimplePaint() {
131: return !drawsLayered || !instanceLayerPainter;
132: }
133: }
134:
135: public static class DefaultHighlightPainter extends LayerPainter {
136: private final Color color;
137:
138: public DefaultHighlightPainter(final Color c) {
139: color = c;
140: }
141:
142: /**
143: * @return color, defined by constructor parameter.
144: */
145: public Color getColor() {
146: return color;
147: }
148:
149: /**
150: * It supposes, that p0 and p1 have Position.Bias.Forward bias.
151: * Calculates appropriate rectangles by call TextUI.modelToView. If some
152: * rectangles equals null, do nothing.
153: *
154: */
155: public void paint(final Graphics g, final int p0, final int p1,
156: final Shape shape, final JTextComponent jtc) {
157: TextUI textUI = jtc.getUI();
158: if (textUI == null) {
159: return;
160: }
161:
162: int start = Math.min(p0, p1);
163: int end = Math.max(p0, p1);
164: Rectangle startRect = null;
165: Rectangle endRect = null;
166: Rectangle shapeBounds = shape.getBounds();
167:
168: try {
169: startRect = textUI.modelToView(jtc, start,
170: Position.Bias.Forward);
171: endRect = textUI.modelToView(jtc, end,
172: Position.Bias.Backward);
173: } catch (final BadLocationException e) {
174: }
175:
176: if (startRect == null || endRect == null) {
177: return;
178: }
179: int startRectMaxY = startRect.y + startRect.height;
180: boolean isLineGap = (startRectMaxY < endRect.y);
181: boolean isDifferentLine = (startRectMaxY <= endRect.y);
182: g.setColor(getRealColor(jtc));
183: //It need to add 1 to width and height as that requires fillRect
184: if (isLineGap) {
185: g.fillRect(shapeBounds.x, startRectMaxY,
186: shapeBounds.width + 1, endRect.y
187: - startRectMaxY + 1);
188: }
189:
190: if (isDifferentLine) {
191: g.fillRect(startRect.x, startRect.y, shapeBounds.width
192: - startRect.x + 1 + shapeBounds.x,
193: startRect.height + 1);
194: g.fillRect(shapeBounds.x, endRect.y, endRect.x
195: - shapeBounds.x + 1, endRect.height + 1);
196: } else {
197: g.fillRect(Math.min(startRect.x, endRect.x),
198: startRect.y, Math.abs(endRect.x - startRect.x
199: + 1), startRect.height + 1);
200:
201: }
202: }
203:
204: /**
205: * It supposes, that start has Position.Bias.Forward bias, end has
206: * Position.Bias.Backward bias. Calculates appropriate rectangles by
207: * call view.modelToView. If some rectangles equals null, do nothing.
208: * This method is called by DefaultHighlighter paint method, when
209: * painter is instance of LayeredHighlighter.LayeredHighlightPainter and
210: * drawsLayered of the current Highlight equals true.
211: */
212: public Shape paintLayer(final Graphics g, final int start,
213: final int end, final Shape shape,
214: final JTextComponent jtc, final View view) {
215: return TextUtils.paintLayer(g, start, end, shape,
216: getRealColor(jtc), view, true);
217: }
218:
219: private Color getRealColor(final JTextComponent c) {
220: return color == null ? c.getSelectionColor() : color;
221: }
222: }
223:
224: /**
225: * DefaultHighlighter.DefaultHighlighterPainter with color equal to null
226: */
227: public static final LayerPainter DefaultPainter = new DefaultHighlightPainter(
228: null);
229:
230: /**
231: * Calls setDrawLayeredHighlights(true). Initializes internal vector
232: * variable to store highlights in future.
233: */
234: public DefaultHighlighter() {
235: setDrawsLayeredHighlights(true);
236: }
237:
238: public Object addHighlight(final int start, final int end,
239: final Highlighter.HighlightPainter pnt)
240: throws BadLocationException {
241: checkBoundaries(start, end);
242: Highlight h = new HighlightImpl(createPosition(start),
243: createPosition(end), pnt);
244: highlights.add(h);
245: repaintComponent(getBoundsByOffsets(start, end));
246: return h;
247: }
248:
249: public void changeHighlight(final Object obj, final int start,
250: final int end) throws BadLocationException {
251: checkBoundaries(start, end);
252:
253: HighlightImpl h = highlights.getElem(obj);
254: if (h == null) {
255: return;
256: }
257:
258: int oldStart = h.getStartOffset();
259: int oldEnd = h.getEndOffset();
260: h.changeHighlight(createPosition(start), createPosition(end));
261: updateHighlights(oldStart, oldEnd, start, end);
262: }
263:
264: public void deinstall(final JTextComponent c) {
265: }
266:
267: public boolean getDrawsLayeredHighlights() {
268: return drawsLayeredHighlights;
269: }
270:
271: public Highlighter.Highlight[] getHighlights() {
272: return (Highlighter.Highlight[]) highlights
273: .toArray(new Highlighter.Highlight[highlights.size()]);
274: }
275:
276: public void install(final JTextComponent c) {
277: component = c;
278: removeAllHighlights();
279: textKit = TextUtils.getTextKit(c);
280: }
281:
282: /**
283: * Looks trough highlights vector (down) to calls paint of Highlight
284: * painter. Method paint of highlight painter is called, if the Highlight is
285: * added at getDrawsLayeredHighlights() equals false or the Highlght painter
286: * doesn't instance of LayeredHighlighter.LayeredHighlightPainter.
287: */
288:
289: public void paint(final Graphics g) {
290: Rectangle r = TextUtils.getEditorRect(component);
291: for (int i = 0; i < highlights.size(); i++) {
292: HighlightImpl hElem = highlights.getElem(i);
293: int start = hElem.getStartOffset();
294: int end = hElem.getEndOffset();
295: if (hElem.needSimplePaint()) {
296: hElem.getPainter().paint(g, start, end, r, component);
297: }
298: }
299: }
300:
301: /**
302: * Look through highlights vector (up) to calls paintLayer of Highlight
303: * painter. Method paintLayer of Highlight painter is called, if
304: * getStartOffet() and getEndOffset() of the Highlight corresponds to view,
305: * painter is instance of LayeredHighlight.LayeredHightPainter, and the
306: * Highlight is added at getDrawsLayeredHighlights() equals true.
307: *
308: */
309: public void paintLayeredHighlights(final Graphics g, final int p0,
310: final int p1, final Shape viewBounds,
311: final JTextComponent editor, final View view) {
312:
313: Document doc = editor.getDocument();
314: int length = doc.getLength();
315:
316: for (int i = highlights.size() - 1; i >= 0; i--) {
317: HighlightImpl hElem = highlights.getElem(i);
318: int start = hElem.getStartOffset();
319: int end = hElem.getEndOffset();
320:
321: if (hElem.needSimplePaint() || end > length || start > p1
322: || end < p0) {
323: continue;
324: }
325: ((LayerPainter) (hElem.getPainter())).paintLayer(g, Math
326: .max(p0, start), Math.min(p1, end), viewBounds,
327: editor, view);
328: }
329: }
330:
331: /**
332: * Calls repaint of component with parameters corresponding to bounds of
333: * deleted highlight (for each highlight).
334: */
335: public void removeAllHighlights() {
336: while (!highlights.isEmpty()) {
337: HighlightImpl hElem = highlights.getElem(0);
338: Rectangle highlightBounds = getBoundsByHighlight(hElem);
339: highlights.remove(0);
340: repaintComponent(highlightBounds);
341: }
342: }
343:
344: /**
345: * Calls repaint of component with parameters corresponding to bounds of
346: * deleted highlight.
347: *
348: */
349: public void removeHighlight(final Object obj) {
350: Highlight highlight = highlights.getElem(obj);
351: if (highlight != null) {
352: Rectangle highlightBounds = getBoundsByHighlight(highlight);
353: highlights.remove(highlight);
354: repaintComponent(highlightBounds);
355: }
356: }
357:
358: /**
359: * Defines paint order for Highlight is added at this setting. If b equals
360: * false, added highlight will be painted only by call paint method of
361: * highlight painter. Otherwise, if added highlighter is instance of
362: * LayeredHighlighter.LayeredHighlightPainter, method paintLayer of
363: * HighlightPainter will be used.
364: *
365: * @param b
366: */
367: public void setDrawsLayeredHighlights(final boolean b) {
368: drawsLayeredHighlights = b;
369: }
370:
371: private void checkBoundaries(final int start, final int end)
372: throws BadLocationException {
373: if (start < 0) {
374: throw new BadLocationException(Messages.getString(
375: "swing.89", start), end); //$NON-NLS-1$
376: }
377: if (end < start) {
378: throw new BadLocationException(Messages.getString(
379: "swing.89", start), end); //$NON-NLS-1$
380: }
381: }
382:
383: /**
384: * Return Position by Offset, if document of component bound to this
385: * instance of current Highlight throws BadLocationException - returns null.
386: */
387:
388: private Position createPosition(final int offset)
389: throws BadLocationException {
390: return component.getDocument().createPosition(offset);
391: }
392:
393: private void updateHighlights(final int oldStart, final int oldEnd,
394: final int newStart, final int newEnd) {
395: if (oldEnd < newStart || oldStart > oldEnd) {
396: repaintComponent(oldStart, oldEnd, newStart, newEnd);
397: } else {
398: repaintComponent(Math.min(oldStart, newStart), Math.max(
399: oldStart, newStart), Math.min(oldEnd, newEnd), Math
400: .max(oldEnd, newEnd));
401: }
402: }
403:
404: private void repaintComponent(final int p0, final int p1,
405: final int p2, final int p3) {
406: repaintComponent(getBoundsByOffsets(p0, p1));
407: repaintComponent(getBoundsByOffsets(p2, p3));
408: }
409:
410: private void repaintComponent(final Rectangle r) {
411: if (r != null) {
412: component.repaint(r.x, r.y, r.width + 1, r.height + 1);
413: }
414: }
415:
416: private Rectangle getBoundsByHighlight(final Highlight highlight) {
417: return TextUtils.getBoundsByOffsets(textKit, highlight
418: .getStartOffset(), highlight.getEndOffset());
419: }
420:
421: private Rectangle getBoundsByOffsets(final int p0, final int p1) {
422: return TextUtils.getBoundsByOffsets(textKit, p0, p1);
423: }
424:
425: }
|