/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution 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 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution 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 logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.logisim.tools;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import com.cburch.logisim.circuit.Circuit;
import com.cburch.logisim.circuit.CircuitEvent;
import com.cburch.logisim.circuit.CircuitListener;
import com.cburch.logisim.circuit.CircuitMutation;
import com.cburch.logisim.comp.Component;
import com.cburch.logisim.comp.ComponentDrawContext;
import com.cburch.logisim.comp.ComponentUserEvent;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.gui.main.Canvas;
import com.cburch.logisim.proj.Action;
import com.cburch.logisim.proj.Project;
import com.cburch.logisim.std.base.Text;
public class TextTool extends Tool {
private class MyListener implements CaretListener, CircuitListener {
public void circuitChanged(CircuitEvent event) {
if (event.getCircuit() != caretCircuit) {
event.getCircuit().removeCircuitListener(this);
return;
}
int action = event.getAction();
if (action == CircuitEvent.ACTION_REMOVE) {
if (event.getData() == caretComponent) {
caret.cancelEditing();
}
} else if (action == CircuitEvent.ACTION_CLEAR) {
if (caretComponent != null) {
caret.cancelEditing();
}
}
}
public void editingCanceled(CaretEvent e) {
if (e.getCaret() != caret) {
e.getCaret().removeCaretListener(this);
return;
}
caret.removeCaretListener(this);
caretCircuit.removeCircuitListener(this);
caretCircuit = null;
caretComponent = null;
caretCreatingText = false;
caret = null;
}
public void editingStopped(CaretEvent e) {
if (e.getCaret() != caret) {
e.getCaret().removeCaretListener(this);
return;
}
caret.removeCaretListener(this);
caretCircuit.removeCircuitListener(this);
String val = caret.getText();
boolean isEmpty = (val == null || val.equals(""));
Action a;
Project proj = caretCanvas.getProject();
if (caretCreatingText) {
if (!isEmpty) {
CircuitMutation xn = new CircuitMutation(caretCircuit);
xn.add(caretComponent);
a = xn.toAction(Strings.getter("addComponentAction",
Text.FACTORY.getDisplayGetter()));
} else {
a = null; // don't add the blank text field
}
} else {
if (isEmpty && caretComponent.getFactory() instanceof Text) {
CircuitMutation xn = new CircuitMutation(caretCircuit);
xn.add(caretComponent);
a = xn.toAction(Strings.getter("removeComponentAction",
Text.FACTORY.getDisplayGetter()));
} else {
Object obj = caretComponent.getFeature(TextEditable.class);
if (obj == null) { // should never happen
a = null;
} else {
TextEditable editable = (TextEditable) obj;
a = editable.getCommitAction(caretCircuit,
e.getOldText(), e.getText());
}
}
}
caretCircuit = null;
caretComponent = null;
caretCreatingText = false;
caret = null;
if (a != null)
proj.doAction(a);
}
}
private static Cursor cursor = Cursor
.getPredefinedCursor(Cursor.TEXT_CURSOR);
private MyListener listener = new MyListener();
private AttributeSet attrs;
private Caret caret = null;
private boolean caretCreatingText = false;
private Canvas caretCanvas = null;
private Circuit caretCircuit = null;
private Component caretComponent = null;
public TextTool() {
attrs = Text.FACTORY.createAttributeSet();
}
@Override
public void deselect(Canvas canvas) {
if (caret != null) {
caret.stopEditing();
caret = null;
}
}
@Override
public void draw(Canvas canvas, ComponentDrawContext context) {
if (caret != null)
caret.draw(context.getGraphics());
}
@Override
public boolean equals(Object other) {
return other instanceof TextTool;
}
@Override
public AttributeSet getAttributeSet() {
return attrs;
}
@Override
public Cursor getCursor() {
return cursor;
}
@Override
public String getDescription() {
return Strings.get("textToolDesc");
}
@Override
public String getDisplayName() {
return Strings.get("textTool");
}
@Override
public String getName() {
return "Text Tool";
}
@Override
public int hashCode() {
return TextTool.class.hashCode();
}
@Override
public void keyPressed(Canvas canvas, KeyEvent e) {
if (caret != null) {
caret.keyPressed(e);
canvas.getProject().repaintCanvas();
}
}
@Override
public void keyReleased(Canvas canvas, KeyEvent e) {
if (caret != null) {
caret.keyReleased(e);
canvas.getProject().repaintCanvas();
}
}
@Override
public void keyTyped(Canvas canvas, KeyEvent e) {
if (caret != null) {
caret.keyTyped(e);
canvas.getProject().repaintCanvas();
}
}
@Override
public void mouseDragged(Canvas canvas, Graphics g, MouseEvent e) {
// TODO: enhance label editing
}
@Override
public void mousePressed(Canvas canvas, Graphics g, MouseEvent e) {
Project proj = canvas.getProject();
Circuit circ = canvas.getCircuit();
if (!proj.getLogisimFile().contains(circ)) {
if (caret != null)
caret.cancelEditing();
canvas.setErrorMessage(Strings.getter("cannotModifyError"));
return;
}
// Maybe user is clicking within the current caret.
if (caret != null) {
if (caret.getBounds(g).contains(e.getX(), e.getY())) { // Yes
caret.mousePressed(e);
proj.repaintCanvas();
return;
} else { // No. End the current caret.
caret.stopEditing();
}
}
// caret will be null at this point
// Otherwise search for a new caret.
int x = e.getX();
int y = e.getY();
Location loc = Location.create(x, y);
ComponentUserEvent event = new ComponentUserEvent(canvas, x, y);
// First search in selection.
for (Component comp : proj.getSelection().getComponentsContaining(loc,
g)) {
TextEditable editable = (TextEditable) comp
.getFeature(TextEditable.class);
if (editable != null) {
caret = editable.getTextCaret(event);
if (caret != null) {
proj.getFrame().viewComponentAttributes(circ, comp);
caretComponent = comp;
caretCreatingText = false;
break;
}
}
}
// Then search in circuit
if (caret == null) {
for (Component comp : circ.getAllContaining(loc, g)) {
TextEditable editable = (TextEditable) comp
.getFeature(TextEditable.class);
if (editable != null) {
caret = editable.getTextCaret(event);
if (caret != null) {
proj.getFrame().viewComponentAttributes(circ, comp);
caretComponent = comp;
caretCreatingText = false;
break;
}
}
}
}
// if nothing found, create a new label
if (caret == null) {
if (loc.getX() < 0 || loc.getY() < 0)
return;
AttributeSet copy = (AttributeSet) attrs.clone();
caretComponent = Text.FACTORY.createComponent(loc, copy);
caretCreatingText = true;
TextEditable editable = (TextEditable) caretComponent
.getFeature(TextEditable.class);
if (editable != null) {
caret = editable.getTextCaret(event);
proj.getFrame().viewComponentAttributes(circ, caretComponent);
}
}
if (caret != null) {
caretCanvas = canvas;
caretCircuit = canvas.getCircuit();
caret.addCaretListener(listener);
caretCircuit.addCircuitListener(listener);
}
proj.repaintCanvas();
}
@Override
public void mouseReleased(Canvas canvas, Graphics g, MouseEvent e) {
// TODO: enhance label editing
}
@Override
public void paintIcon(ComponentDrawContext c, int x, int y) {
Text.FACTORY.paintIcon(c, x, y, null);
}
}