package com.felix.util; import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.KeyEvent; import javax.swing.JTextPane; import javax.swing.plaf.TextUI; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import javax.swing.text.DefaultCaret; import javax.swing.text.JTextComponent; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; /** * A jtextpane that allows for setting the text color in parts, retrieving the * color and overtyping mode, * * @author felix * */ public class ColorPane extends JTextPane { /** */ private static final long serialVersionUID = 1L; private static boolean isOvertypeMode; private Caret defaultCaret; private Caret overtypeCaret; public ColorPane() { super(); defaultCaret = getCaret(); overtypeCaret = new OvertypeCaret(); overtypeCaret.setBlinkRate(defaultCaret.getBlinkRate()); setOvertypeMode(false); setForeground(Color.black, 0, 0); } /** * Append text in some color at end. * * @param c * The color. * @param s * The text. */ public void append(Color c, String s) { StyleContext sc = StyleContext.getDefaultStyleContext(); AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, c); int len = getDocument().getLength(); setCaretPosition(len); setCharacterAttributes(aset, false); replaceSelection(s); } /** * Append a newline and set the foreground color to black. */ public void appendNewLine() { StyleContext sc = StyleContext.getDefaultStyleContext(); AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.black); int len = getDocument().getLength(); setCaretPosition(len); setCharacterAttributes(aset, false); replaceSelection("\n"); } /** * Set the foreground color oof the selected text. * * @param col * The color. */ public void setForegroundForSelection(Color col) { int start = getSelectionStart(); int end = getSelectionEnd(); setForeground(col, start, end); } /** * Set the foreground text color. * * @param col * The color. * @param start * The start index. * @param end * The end index. */ public void setForeground(Color col, int start, int end) { StyleContext sc = StyleContext.getDefaultStyleContext(); AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, col); select(start, end); setCharacterAttributes(aset, true); } /** * Get the color for a part of text. * * @param start * The start index. * @param end * The end index. * @return The color. */ public Color getColor(int start, int end) { select(start, end); AttributeSet aset = getCharacterAttributes(); Color c = (Color) aset.getAttribute(StyleConstants.Foreground); return c; } /** * Get a string where all color changes are tagged. * * @return The string. */ public String getColorTaggedText() { String text = getText(); Color oldColor = getColor(0, 0); String ret = "<color val=\"" + ColorUtil.colorToHex(oldColor) + "\">"; int newlineCounter=0, count=0; boolean startCountdown=false; for (int i = 0; i < text.length(); i++) { Color act = getColor(i, i); if (act != oldColor) { oldColor = act; startCountdown = true; count = newlineCounter; } if (startCountdown) { if (count==0) { ret += "</color><color val=\"" + ColorUtil.colorToHex(oldColor) + "\">"; startCountdown=false; } else { count--; } } char actC = text.charAt(i); if (actC=='\n') { newlineCounter++; } ret += actC; // ret += text.substring(i, i + 1); } return ret += "</color>"; } /* * Return the overtype/insert mode */ public boolean isOvertypeMode() { return isOvertypeMode; } /* * Set the caret to use depending on overtype/insert mode */ public void setOvertypeMode(boolean isOvertypeMode) { ColorPane.isOvertypeMode = isOvertypeMode; int pos = getCaretPosition(); if (isOvertypeMode()) { setCaret(overtypeCaret); } else { setCaret(defaultCaret); } setCaretPosition(pos); } /* * Override method from JComponent */ public void replaceSelection(String text) { // Implement overtype mode by selecting the character at the current // caret position if (isOvertypeMode()) { int pos = getCaretPosition(); if (getSelectedText() == null && pos < getDocument().getLength()) { moveCaretPosition(pos + 1); } } super.replaceSelection(text); } /* * Override method from JComponent */ protected void processKeyEvent(KeyEvent e) { super.processKeyEvent(e); // Handle release of Insert key to toggle overtype/insert mode if (e.getID() == KeyEvent.KEY_RELEASED && e.getKeyCode() == KeyEvent.VK_INSERT) { setOvertypeMode(!isOvertypeMode()); } } /* * Paint a horizontal line the width of a column and 1 pixel high */ class OvertypeCaret extends DefaultCaret { /** */ private static final long serialVersionUID = 1L; /* * The overtype caret will simply be a horizontal line one pixel high * (once we determine where to paint it) */ public void paint(Graphics g) { if (isVisible()) { try { JTextComponent component = getComponent(); TextUI mapper = component.getUI(); Rectangle r = mapper.modelToView(component, getDot()); g.setColor(component.getCaretColor()); int width = g.getFontMetrics().charWidth('w'); int y = r.y + r.height - 2; g.drawLine(r.x, y, r.x + width - 2, y); } catch (BadLocationException e) { } } } /* * Damage must be overridden whenever the paint method is overridden * (The damaged area is the area the caret is painted in. We must * consider the area for the default caret and this caret) */ protected synchronized void damage(Rectangle r) { if (r != null) { JTextComponent component = getComponent(); x = r.x; y = r.y; width = component.getFontMetrics(component.getFont()) .charWidth('w'); height = r.height; repaint(); } } } }