| /* DefaultHighlighter.java -- The default highlight for Swing | |
| Copyright (C) 2004, 2006 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package javax.swing.text; | |
| import java.awt.Color; | |
| import java.awt.Graphics; | |
| import java.awt.Insets; | |
| import java.awt.Rectangle; | |
| import java.awt.Shape; | |
| import java.util.ArrayList; | |
| import java.util.Iterator; | |
| import javax.swing.SwingUtilities; | |
| import javax.swing.plaf.TextUI; | |
| /** | |
| * The default highlight for Swing text components. It highlights text | |
| * by filling the background with a rectangle. | |
| */ | |
| public class DefaultHighlighter extends LayeredHighlighter | |
| { | |
| public static class DefaultHighlightPainter | |
| extends LayerPainter | |
| { | |
| private Color color; | |
| public DefaultHighlightPainter(Color c) | |
| { | |
| super(); | |
| color = c; | |
| } | |
| public Color getColor() | |
| { | |
| return color; | |
| } | |
| public void paint(Graphics g, int p0, int p1, Shape bounds, | |
| JTextComponent t) | |
| { | |
| if (p0 == p1) | |
| return; | |
| Rectangle rect = bounds.getBounds(); | |
| Color col = getColor(); | |
| if (col == null) | |
| col = t.getSelectionColor(); | |
| g.setColor(col); | |
| TextUI ui = t.getUI(); | |
| try | |
| { | |
| Rectangle l0 = ui.modelToView(t, p0, null); | |
| Rectangle l1 = ui.modelToView(t, p1, null); | |
| // Note: The computed locations may lie outside of the allocation | |
| // area if the text is scrolled. | |
| if (l0.y == l1.y) | |
| { | |
| SwingUtilities.computeUnion(l0.x, l0.y, l0.width, l0.height, l1); | |
| // Paint only inside the allocation area. | |
| SwingUtilities.computeIntersection(rect.x, rect.y, rect.width, | |
| rect.height, l1); | |
| g.fillRect(l1.x, l1.y, l1.width, l1.height); | |
| } | |
| else | |
| { | |
| // 1. The line of p0 is painted from the position of p0 | |
| // to the right border. | |
| // 2. All lines between the ones where p0 and p1 lie on | |
| // are completely highlighted. The allocation area is used to find | |
| // out the bounds. | |
| // 3. The final line is painted from the left border to the | |
| // position of p1. | |
| int firstLineWidth = rect.x + rect.width - l0.x; | |
| g.fillRect(l0.x, l0.y, firstLineWidth, l0.height); | |
| if (l0.y + l0.height != l1.y) | |
| { | |
| g.fillRect(rect.x, l0.y + l0.height, rect.width, | |
| l1.y - l0.y - l0.height); | |
| } | |
| g.fillRect(rect.x, l1.y, l1.x - rect.x, l1.height); | |
| } | |
| } | |
| catch (BadLocationException ex) | |
| { | |
| // Can't render. Comment out for debugging. | |
| // ex.printStackTrace(); | |
| } | |
| } | |
| public Shape paintLayer(Graphics g, int p0, int p1, Shape bounds, | |
| JTextComponent c, View view) | |
| { | |
| Color col = getColor(); | |
| if (col == null) | |
| col = c.getSelectionColor(); | |
| g.setColor(col); | |
| Rectangle rect = null; | |
| if (p0 == view.getStartOffset() && p1 == view.getEndOffset()) | |
| { | |
| // Paint complete bounds region. | |
| rect = bounds instanceof Rectangle ? (Rectangle) bounds | |
| : bounds.getBounds(); | |
| } | |
| else | |
| { | |
| // Only partly inside the view. | |
| try | |
| { | |
| Shape s = view.modelToView(p0, Position.Bias.Forward, | |
| p1, Position.Bias.Backward, | |
| bounds); | |
| rect = s instanceof Rectangle ? (Rectangle) s : s.getBounds(); | |
| } | |
| catch (BadLocationException ex) | |
| { | |
| // Can't render the highlight. | |
| } | |
| } | |
| if (rect != null) | |
| { | |
| g.fillRect(rect.x, rect.y, rect.width, rect.height); | |
| } | |
| return rect; | |
| } | |
| } | |
| private class HighlightEntry implements Highlighter.Highlight | |
| { | |
| Position p0; | |
| Position p1; | |
| Highlighter.HighlightPainter painter; | |
| public HighlightEntry(Position p0, Position p1, | |
| Highlighter.HighlightPainter painter) | |
| { | |
| this.p0 = p0; | |
| this.p1 = p1; | |
| this.painter = painter; | |
| } | |
| public int getStartOffset() | |
| { | |
| return p0.getOffset(); | |
| } | |
| public int getEndOffset() | |
| { | |
| return p1.getOffset(); | |
| } | |
| public Highlighter.HighlightPainter getPainter() | |
| { | |
| return painter; | |
| } | |
| } | |
| /** | |
| * A HighlightEntry that is used for LayerPainter painters. In addition | |
| * to the info maintained by the HighlightEntry, this class maintains | |
| * a painting rectangle. This is used as repaint region when the | |
| * highlight changes and the text component needs repainting. | |
| */ | |
| private class LayerHighlightEntry | |
| extends HighlightEntry | |
| { | |
| /** | |
| * The paint rectangle. | |
| */ | |
| Rectangle paintRect = new Rectangle(); | |
| LayerHighlightEntry(Position p0, Position p1, | |
| Highlighter.HighlightPainter p) | |
| { | |
| super(p0, p1, p); | |
| } | |
| /** | |
| * Paints the highlight by calling the LayerPainter. This | |
| * restricts the area to be painted by startOffset and endOffset | |
| * and manages the paint rectangle. | |
| */ | |
| void paintLayeredHighlight(Graphics g, int p0, int p1, Shape bounds, | |
| JTextComponent tc, View view) | |
| { | |
| p0 = Math.max(getStartOffset(), p0); | |
| p1 = Math.min(getEndOffset(), p1); | |
| Highlighter.HighlightPainter painter = getPainter(); | |
| if (painter instanceof LayerPainter) | |
| { | |
| LayerPainter layerPainter = (LayerPainter) painter; | |
| Shape area = layerPainter.paintLayer(g, p0, p1, bounds, tc, view); | |
| Rectangle rect; | |
| if (area instanceof Rectangle && paintRect != null) | |
| rect = (Rectangle) area; | |
| else | |
| rect = area.getBounds(); | |
| if (paintRect.width == 0 || paintRect.height == 0) | |
| paintRect = rect.getBounds(); | |
| else | |
| paintRect = SwingUtilities.computeUnion(rect.x, rect.y, rect.width, | |
| rect.height, paintRect); | |
| } | |
| } | |
| } | |
| /** | |
| * @specnote final as of 1.4 | |
| */ | |
| public static final LayeredHighlighter.LayerPainter DefaultPainter = | |
| new DefaultHighlightPainter(null); | |
| private JTextComponent textComponent; | |
| private ArrayList highlights = new ArrayList(); | |
| private boolean drawsLayeredHighlights = true; | |
| public DefaultHighlighter() | |
| { | |
| // Nothing to do here. | |
| } | |
| public boolean getDrawsLayeredHighlights() | |
| { | |
| return drawsLayeredHighlights; | |
| } | |
| public void setDrawsLayeredHighlights(boolean newValue) | |
| { | |
| drawsLayeredHighlights = newValue; | |
| } | |
| private void checkPositions(int p0, int p1) | |
| throws BadLocationException | |
| { | |
| if (p0 < 0) | |
| throw new BadLocationException("DefaultHighlighter", p0); | |
| if (p1 < p0) | |
| throw new BadLocationException("DefaultHighlighter", p1); | |
| } | |
| public void install(JTextComponent c) | |
| { | |
| textComponent = c; | |
| removeAllHighlights(); | |
| } | |
| public void deinstall(JTextComponent c) | |
| { | |
| textComponent = null; | |
| } | |
| public Object addHighlight(int p0, int p1, | |
| Highlighter.HighlightPainter painter) | |
| throws BadLocationException | |
| { | |
| checkPositions(p0, p1); | |
| HighlightEntry entry; | |
| Document doc = textComponent.getDocument(); | |
| Position pos0 = doc.createPosition(p0); | |
| Position pos1 = doc.createPosition(p1); | |
| if (getDrawsLayeredHighlights() && painter instanceof LayerPainter) | |
| entry = new LayerHighlightEntry(pos0, pos1, painter); | |
| else | |
| entry = new HighlightEntry(pos0, pos1, painter); | |
| highlights.add(entry); | |
| textComponent.getUI().damageRange(textComponent, p0, p1); | |
| return entry; | |
| } | |
| public void removeHighlight(Object tag) | |
| { | |
| HighlightEntry entry = (HighlightEntry) tag; | |
| if (entry instanceof LayerHighlightEntry) | |
| { | |
| LayerHighlightEntry lEntry = (LayerHighlightEntry) entry; | |
| Rectangle paintRect = lEntry.paintRect; | |
| textComponent.repaint(paintRect.x, paintRect.y, paintRect.width, | |
| paintRect.height); | |
| } | |
| else | |
| { | |
| textComponent.getUI().damageRange(textComponent, | |
| entry.getStartOffset(), | |
| entry.getEndOffset()); | |
| } | |
| highlights.remove(tag); | |
| } | |
| public void removeAllHighlights() | |
| { | |
| // Repaint damaged region. | |
| int minX = 0; | |
| int maxX = 0; | |
| int minY = 0; | |
| int maxY = 0; | |
| int p0 = -1; | |
| int p1 = -1; | |
| for (Iterator i = highlights.iterator(); i.hasNext();) | |
| { | |
| HighlightEntry e = (HighlightEntry) i.next(); | |
| if (e instanceof LayerHighlightEntry) | |
| { | |
| LayerHighlightEntry le = (LayerHighlightEntry) e; | |
| Rectangle r = le.paintRect; | |
| minX = Math.min(r.x, minX); | |
| maxX = Math.max(r.x + r.width, maxX); | |
| minY = Math.min(r.y, minY); | |
| maxY = Math.max(r.y + r.height, maxY); | |
| } | |
| else | |
| { | |
| if (p0 == -1 || p1 == -1) | |
| { | |
| p0 = e.getStartOffset(); | |
| p1 = e.getEndOffset(); | |
| } | |
| else | |
| { | |
| p0 = Math.min(p0, e.getStartOffset()); | |
| p1 = Math.max(p1, e.getEndOffset()); | |
| } | |
| } | |
| if (minX != maxX && minY != maxY) | |
| textComponent.repaint(minX, minY, maxX - minX, maxY - minY); | |
| if (p0 != -1 && p1 != -1) | |
| { | |
| TextUI ui = textComponent.getUI(); | |
| ui.damageRange(textComponent, p0, p1); | |
| } | |
| } | |
| highlights.clear(); | |
| } | |
| public Highlighter.Highlight[] getHighlights() | |
| { | |
| return (Highlighter.Highlight[]) | |
| highlights.toArray(new Highlighter.Highlight[highlights.size()]); | |
| } | |
| public void changeHighlight(Object tag, int n0, int n1) | |
| throws BadLocationException | |
| { | |
| Document doc = textComponent.getDocument(); | |
| TextUI ui = textComponent.getUI(); | |
| if (tag instanceof LayerHighlightEntry) | |
| { | |
| LayerHighlightEntry le = (LayerHighlightEntry) tag; | |
| Rectangle r = le.paintRect; | |
| if (r.width > 0 && r.height > 0) | |
| textComponent.repaint(r.x, r.y, r.width, r.height); | |
| r.width = 0; | |
| r.height = 0; | |
| le.p0 = doc.createPosition(n0); | |
| le.p1 = doc.createPosition(n1); | |
| ui.damageRange(textComponent, Math.min(n0, n1), Math.max(n0, n1)); | |
| } | |
| else if (tag instanceof HighlightEntry) | |
| { | |
| HighlightEntry e = (HighlightEntry) tag; | |
| int p0 = e.getStartOffset(); | |
| int p1 = e.getEndOffset(); | |
| if (p0 == n0) | |
| { | |
| ui.damageRange(textComponent, Math.min(p1, n1), | |
| Math.max(p1, n1)); | |
| } | |
| else if (n1 == p1) | |
| { | |
| ui.damageRange(textComponent, Math.min(p0, n0), | |
| Math.max(p0, n0)); | |
| } | |
| else | |
| { | |
| ui.damageRange(textComponent, p0, p1); | |
| ui.damageRange(textComponent, n0, n1); | |
| } | |
| e.p0 = doc.createPosition(n0); | |
| e.p1 = doc.createPosition(n1); | |
| } | |
| } | |
| public void paintLayeredHighlights(Graphics g, int p0, int p1, | |
| Shape viewBounds, JTextComponent editor, | |
| View view) | |
| { | |
| for (Iterator i = highlights.iterator(); i.hasNext();) | |
| { | |
| Object o = i.next(); | |
| if (o instanceof LayerHighlightEntry) | |
| { | |
| LayerHighlightEntry entry = (LayerHighlightEntry) o; | |
| int start = entry.getStartOffset(); | |
| int end = entry.getEndOffset(); | |
| if ((p0 < start && p1 > start) || (p0 >= start && p0 < end)) | |
| entry.paintLayeredHighlight(g, p0, p1, viewBounds, editor, view); | |
| } | |
| } | |
| } | |
| public void paint(Graphics g) | |
| { | |
| int size = highlights.size(); | |
| // Check if there are any highlights. | |
| if (size == 0) | |
| return; | |
| // Prepares the rectangle of the inner drawing area. | |
| Insets insets = textComponent.getInsets(); | |
| Shape bounds = | |
| new Rectangle(insets.left, | |
| insets.top, | |
| textComponent.getWidth() - insets.left - insets.right, | |
| textComponent.getHeight() - insets.top - insets.bottom); | |
| for (int index = 0; index < size; ++index) | |
| { | |
| HighlightEntry entry = (HighlightEntry) highlights.get(index); | |
| if (! (entry instanceof LayerHighlightEntry)) | |
| entry.painter.paint(g, entry.getStartOffset(), entry.getEndOffset(), | |
| bounds, textComponent); | |
| } | |
| } | |
| } |