| /* 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); |
| } |
| } |
| } |