/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.comp;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.LinkedList;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.tools.Caret;
import com.cburch.logisim.tools.CaretEvent;
import com.cburch.logisim.tools.CaretListener;
class TextFieldCaret implements Caret, TextFieldListener {
private LinkedList<CaretListener> listeners = new LinkedList<CaretListener>();
private TextField field;
private Graphics g;
private String oldText;
private String curText;
private int pos;
public TextFieldCaret(TextField field, Graphics g, int pos) {
this.field = field;
this.g = g;
this.oldText = field.getText();
this.curText = field.getText();
this.pos = pos;
field.addTextFieldListener(this);
}
public TextFieldCaret(TextField field, Graphics g, int x, int y) {
this(field, g, 0);
moveCaret(x, y);
}
@Override
public void addCaretListener(CaretListener l) {
listeners.add(l);
}
@Override
public void removeCaretListener(CaretListener l) {
listeners.remove(l);
}
@Override
public String getText() { return curText; }
@Override
public void commitText(String text) {
curText = text;
pos = curText.length();
field.setText(text);
}
@Override
public void draw(Graphics g) {
if (field.getFont() != null) {
g.setFont(field.getFont());
}
// draw boundary
Bounds bds = getBounds(g);
g.setColor(Color.white);
g.fillRect(bds.getX(), bds.getY(),
bds.getWidth(), bds.getHeight());
g.setColor(Color.black);
g.drawRect(bds.getX(), bds.getY(),
bds.getWidth(), bds.getHeight());
// draw text
int x = field.getX();
int y = field.getY();
FontMetrics fm = g.getFontMetrics();
int width = fm.stringWidth(curText);
int ascent = fm.getAscent();
int descent = fm.getDescent();
switch (field.getHAlign()) {
case TextField.H_CENTER: x -= width / 2; break;
case TextField.H_RIGHT: x -= width; break;
default: break;
}
switch (field.getVAlign()) {
case TextField.V_TOP: y += ascent; break;
case TextField.V_CENTER: y += (ascent - descent) / 2; break;
case TextField.V_BOTTOM: y -= descent; break;
default: break;
}
g.drawString(curText, x, y);
// draw cursor
if (pos > 0) {
x += fm.stringWidth(curText.substring(0, pos));
}
g.drawLine(x, y, x, y - ascent);
}
@Override
public Bounds getBounds(Graphics g) {
int x = field.getX();
int y = field.getY();
Font font = field.getFont();
FontMetrics fm;
if (font == null) {
fm = g.getFontMetrics();
}
else {
fm = g.getFontMetrics(font);
}
int width = fm.stringWidth(curText);
int ascent = fm.getAscent();
int descent = fm.getDescent();
int height = ascent + descent;
switch (field.getHAlign()) {
case TextField.H_CENTER: x -= width / 2; break;
case TextField.H_RIGHT: x -= width; break;
default: break;
}
switch (field.getVAlign()) {
case TextField.V_TOP: y += ascent; break;
case TextField.V_CENTER: y += (ascent - descent) / 2; break;
case TextField.V_BOTTOM: y -= descent; break;
default: break;
}
return Bounds.create(x, y - ascent, width, height)
.add(field.getBounds(g))
.expand(3);
}
@Override
public void cancelEditing() {
CaretEvent e = new CaretEvent(this, oldText, oldText);
curText = oldText;
pos = curText.length();
for (CaretListener l : new ArrayList<CaretListener>(listeners)) {
l.editingCanceled(e);
}
field.removeTextFieldListener(this);
}
@Override
public void stopEditing() {
CaretEvent e = new CaretEvent(this, oldText, curText);
field.setText(curText);
for (CaretListener l : new ArrayList<CaretListener>(listeners)) {
l.editingStopped(e);
}
field.removeTextFieldListener(this);
}
@Override
public void mousePressed(MouseEvent e) {
//TODO: enhance label editing
moveCaret(e.getX(), e.getY());
}
@Override
public void mouseDragged(MouseEvent e) {
//TODO: enhance label editing
}
@Override
public void mouseReleased(MouseEvent e) {
//TODO: enhance label editing
moveCaret(e.getX(), e.getY());
}
@Override
public void keyPressed(KeyEvent e) {
int ign = InputEvent.ALT_MASK | InputEvent.CTRL_MASK
| InputEvent.META_MASK;
if ((e.getModifiers() & ign) != 0) {
return;
}
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
case KeyEvent.VK_KP_LEFT:
if (pos > 0) {
--pos;
}
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_KP_RIGHT:
if (pos < curText.length()) {
++pos;
}
break;
case KeyEvent.VK_HOME:
pos = 0;
break;
case KeyEvent.VK_END:
pos = curText.length();
break;
case KeyEvent.VK_ESCAPE:
case KeyEvent.VK_CANCEL:
cancelEditing();
break;
case KeyEvent.VK_CLEAR:
curText = "";
pos = 0;
break;
case KeyEvent.VK_ENTER:
stopEditing();
break;
case KeyEvent.VK_BACK_SPACE:
if (pos > 0) {
curText = curText.substring(0, pos - 1)
+ curText.substring(pos);
--pos;
}
break;
case KeyEvent.VK_DELETE:
if (pos < curText.length()) {
curText = curText.substring(0, pos)
+ curText.substring(pos + 1);
}
break;
case KeyEvent.VK_INSERT:
case KeyEvent.VK_COPY:
case KeyEvent.VK_CUT:
case KeyEvent.VK_PASTE:
//TODO: enhance label editing
break;
default:
// ignore
;
}
}
@Override
public void keyReleased(KeyEvent e) { }
@Override
public void keyTyped(KeyEvent e) {
int ign = InputEvent.ALT_MASK | InputEvent.CTRL_MASK
| InputEvent.META_MASK;
if ((e.getModifiers() & ign) != 0) {
return;
}
char c = e.getKeyChar();
if (c == '\n') {
stopEditing();
} else if (c != KeyEvent.CHAR_UNDEFINED
&& !Character.isISOControl(c)) {
if (pos < curText.length()) {
curText = curText.substring(0, pos) + c
+ curText.substring(pos);
} else {
curText += c;
}
++pos;
}
}
private void moveCaret(int x, int y) {
Bounds bds = getBounds(g);
FontMetrics fm = g.getFontMetrics();
x -= bds.getX();
int last = 0;
for (int i = 0; i < curText.length(); i++) {
int cur = fm.stringWidth(curText.substring(0, i + 1));
if (x <= (last + cur) / 2) {
pos = i;
return;
}
last = cur;
}
pos = curText.length();
}
@Override
public void textChanged(TextFieldEvent e) {
curText = field.getText();
oldText = curText;
pos = curText.length();
}
}