package net.roarsoftware.gui;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Vector;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.Utilities;
import net.roarsoftware.gui.event.GutterEvent;
import net.roarsoftware.gui.event.GutterListener;
/**
* Ein <code>JGutter</code> ist eine Leiste, die links an einer
* <code>JTextComponent</code> dargestellt wird. Sie zeigt Zeilennummern an und
* benutzerdefinierte Symbole usw.
*
* @version 1.0
* @author Janni Kovacs
*/
public class JGutter extends JLabel {
public class LineNumberIcon implements Icon {
public void paintIcon(Component c, Graphics g, int x, int y) {
if(buffer == null || buffer.getHeight() != Math.max(1, owner.getHeight() )) {
paintGutter();
}
g.drawImage( buffer, x, 0, c );
}
public int getIconWidth() {
return buffer != null ? buffer.getWidth(): 20;
}
public int getIconHeight() {
return buffer != null ? buffer.getHeight() : owner.getHeight();
}
}
private int spaceLeft = 10;
private int spaceRight = 5;
private boolean selectLineOnClick = true;
private JTextComponent owner;
private BufferedImage buffer = null;
private List<GutterListener> listeners = new Vector<GutterListener>();
public JGutter(JTextComponent owner) {
setIcon(new LineNumberIcon());
this.owner = owner;
owner.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) {
paintGutter();
}
public void removeUpdate(DocumentEvent e) {
paintGutter();
}
public void changedUpdate(DocumentEvent e) { }
});
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
int x = e.getX(), y = e.getY();
int line = x/getRowHeight();
fireLineSelected(new GutterEvent(JGutter.this, line));
if(selectLineOnClick) {
JTextComponent tc = JGutter.this.owner;
int offset = tc.viewToModel(e.getPoint());
try {
tc.select(Utilities.getRowStart(tc, offset),
Utilities.getRowEnd(tc, offset));
} catch (BadLocationException ex) {
// nix
}
}
}
});
paintGutter();
}
// --------------------------------------- Listener methods
public void addGutterListener(GutterListener listener) {
listeners.add(listener);
}
public void fireLineSelected(GutterEvent ev) {
for(GutterListener l : listeners) {
l.lineSelected(ev);
}
}
// ---------------------------------------- Gutter methods
public int getSpaceLeft() {
return spaceLeft;
}
public int getSpaceRight() {
return spaceRight;
}
public JTextComponent getOwner() {
return owner;
}
public void setSelectLineOnClick(boolean select) {
selectLineOnClick = select;
}
public boolean isSelectLineOnClick(){
return selectLineOnClick;
}
protected int getRowHeight() {
FontMetrics metrics = getFontMetrics(owner.getFont());
return metrics.getHeight();
}
protected void paintGutter() {
int lines = owner.getDocument().getDefaultRootElement().getElementCount();
int width = Math.max(20, getFontMetrics(owner.getFont()).stringWidth(String.valueOf(lines)));
width += getSpaceLeft() + getSpaceRight();
buffer = new BufferedImage(width, Math.max(1, owner.getHeight()), BufferedImage.TYPE_INT_RGB);
paintLineNumbers(buffer.getGraphics());
repaint();
}
protected void paintLineNumbers(Graphics g) {
int rowHeight = getRowHeight();
Document doc = owner.getDocument();
int lines = doc.getDefaultRootElement().getElementCount();
g.setColor(getBackground());
g.fillRect(0, 0, buffer.getWidth(), buffer.getHeight());
// Font f = g.getFont();
// f = f.deriveFont(owner.getFont().getSize2D());
g.setFont(owner.getFont());
g.setColor(getForeground());
FontMetrics fm = g.getFontMetrics();
for(int i = 1; i <= lines; i++) {
String line = String.valueOf(i);
g.drawString(line, buffer.getWidth()-getSpaceRight()-fm.stringWidth(line), i*rowHeight);
}
}
}