/* 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.gui.appear;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.cburch.draw.actions.ModelDeleteHandleAction;
import com.cburch.draw.actions.ModelInsertHandleAction;
import com.cburch.draw.actions.ModelReorderAction;
import com.cburch.draw.canvas.Canvas;
import com.cburch.draw.canvas.Selection;
import com.cburch.draw.canvas.SelectionEvent;
import com.cburch.draw.canvas.SelectionListener;
import com.cburch.draw.model.CanvasModel;
import com.cburch.draw.model.CanvasModelEvent;
import com.cburch.draw.model.CanvasModelListener;
import com.cburch.draw.model.CanvasObject;
import com.cburch.draw.model.Handle;
import com.cburch.draw.util.MatchingSet;
import com.cburch.draw.util.ZOrder;
import com.cburch.logisim.circuit.Circuit;
import com.cburch.logisim.circuit.appear.AppearanceAnchor;
import com.cburch.logisim.circuit.appear.AppearanceElement;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.gui.main.EditHandler;
import com.cburch.logisim.gui.menu.LogisimMenuBar;
import com.cburch.logisim.proj.Project;
import static com.cburch.logisim.util.LocaleString.*;
public class AppearanceEditHandler extends EditHandler
implements SelectionListener, PropertyChangeListener, CanvasModelListener {
private AppearanceCanvas canvas;
AppearanceEditHandler(AppearanceCanvas canvas) {
this.canvas = canvas;
canvas.getSelection().addSelectionListener(this);
CanvasModel model = canvas.getModel();
if (model != null) {
model.addCanvasModelListener(this);
}
canvas.addPropertyChangeListener(Canvas.MODEL_PROPERTY, this);
}
@Override
public void computeEnabled() {
Project proj = canvas.getProject();
Circuit circ = canvas.getCircuit();
Selection sel = canvas.getSelection();
boolean selEmpty = sel.isEmpty();
boolean canChange = proj.getLogisimFile().contains(circ);
boolean clipExists = !Clipboard.isEmpty();
boolean selHasRemovable = false;
for (CanvasObject o : sel.getSelected()) {
if (!(o instanceof AppearanceElement)) {
selHasRemovable = true;
}
}
boolean canRaise;
boolean canLower;
if (!selEmpty && canChange) {
Map<CanvasObject, Integer> zs = ZOrder.getZIndex(sel.getSelected(),
canvas.getModel());
int zmin = Integer.MAX_VALUE;
int zmax = Integer.MIN_VALUE;
int count = 0;
for (Map.Entry<CanvasObject, Integer> entry : zs.entrySet()) {
if (!(entry.getKey() instanceof AppearanceElement)) {
count++;
int z = entry.getValue().intValue();
if (z < zmin) {
zmin = z;
}
if (z > zmax) {
zmax = z;
}
}
}
int maxPoss = AppearanceCanvas.getMaxIndex(canvas.getModel());
if (count > 0 && count <= maxPoss) {
canRaise = zmin <= maxPoss - count;
canLower = zmax >= count;
} else {
canRaise = false;
canLower = false;
}
} else {
canRaise = false;
canLower = false;
}
boolean canAddCtrl = false;
boolean canRemCtrl = false;
Handle handle = sel.getSelectedHandle();
if (handle != null && canChange) {
CanvasObject o = handle.getObject();
canAddCtrl = o.canInsertHandle(handle.getLocation()) != null;
canRemCtrl = o.canDeleteHandle(handle.getLocation()) != null;
}
setEnabled(LogisimMenuBar.CUT, selHasRemovable && canChange);
setEnabled(LogisimMenuBar.COPY, !selEmpty);
setEnabled(LogisimMenuBar.PASTE, canChange && clipExists);
setEnabled(LogisimMenuBar.DELETE, selHasRemovable && canChange);
setEnabled(LogisimMenuBar.DUPLICATE, !selEmpty && canChange);
setEnabled(LogisimMenuBar.SELECT_ALL, true);
setEnabled(LogisimMenuBar.RAISE, canRaise);
setEnabled(LogisimMenuBar.LOWER, canLower);
setEnabled(LogisimMenuBar.RAISE_TOP, canRaise);
setEnabled(LogisimMenuBar.LOWER_BOTTOM, canLower);
setEnabled(LogisimMenuBar.ADD_CONTROL, canAddCtrl);
setEnabled(LogisimMenuBar.REMOVE_CONTROL, canRemCtrl);
}
@Override
public void cut() {
if (!canvas.getSelection().isEmpty()) {
canvas.getProject().doAction(ClipboardActions.cut(canvas));
}
}
@Override
public void copy() {
if (!canvas.getSelection().isEmpty()) {
canvas.getProject().doAction(ClipboardActions.copy(canvas));
}
}
@Override
public void paste() {
ClipboardContents clip = Clipboard.get();
Collection<CanvasObject> contents = clip.getElements();
List<CanvasObject> add = new ArrayList<CanvasObject>(contents.size());
for (CanvasObject o : contents) {
add.add(o.clone());
}
if (add.isEmpty()) {
return;
}
// find how far we have to translate shapes so that at least one of the
// pasted shapes doesn't match what's already in the model
Collection<CanvasObject> raw = canvas.getModel().getObjectsFromBottom();
MatchingSet<CanvasObject> cur = new MatchingSet<CanvasObject>(raw);
int dx = 0;
while (true) {
// if any shapes in "add" aren't in canvas, we are done
boolean allMatch = true;
for (CanvasObject o : add) {
if (!cur.contains(o)) {
allMatch = false;
break;
}
}
if (!allMatch) {
break;
}
// otherwise translate everything by 10 pixels and repeat test
for (CanvasObject o : add) {
o.translate(10, 10);
}
dx += 10;
}
Location anchorLocation = clip.getAnchorLocation();
if (anchorLocation != null && dx != 0) {
anchorLocation = anchorLocation.translate(dx, dx);
}
canvas.getProject().doAction(new SelectionAction(canvas,
getFromLocale("pasteClipboardAction"), null, add, add,
anchorLocation, clip.getAnchorFacing()));
}
@Override
public void delete() {
Selection sel = canvas.getSelection();
int n = sel.getSelected().size();
List<CanvasObject> select = new ArrayList<CanvasObject>(n);
List<CanvasObject> remove = new ArrayList<CanvasObject>(n);
Location anchorLocation = null;
Direction anchorFacing = null;
for (CanvasObject o : sel.getSelected()) {
if (o.canRemove()) {
remove.add(o);
} else {
select.add(o);
if (o instanceof AppearanceAnchor) {
AppearanceAnchor anchor = (AppearanceAnchor) o;
anchorLocation = anchor.getLocation();
anchorFacing = anchor.getFacing();
}
}
}
if (!remove.isEmpty()) {
canvas.getProject().doAction(new SelectionAction(canvas,
getFromLocale("deleteSelectionAction"), remove, null, select,
anchorLocation, anchorFacing));
}
}
@Override
public void duplicate() {
Selection sel = canvas.getSelection();
int n = sel.getSelected().size();
List<CanvasObject> select = new ArrayList<CanvasObject>(n);
List<CanvasObject> clones = new ArrayList<CanvasObject>(n);
for (CanvasObject o : sel.getSelected()) {
if (o.canRemove()) {
CanvasObject copy = o.clone();
copy.translate(10, 10);
clones.add(copy);
select.add(copy);
} else {
select.add(o);
}
}
if (!clones.isEmpty()) {
canvas.getProject().doAction(new SelectionAction(canvas,
getFromLocale("duplicateSelectionAction"), null, clones, select,
null, null));
}
}
@Override
public void selectAll() {
Selection sel = canvas.getSelection();
sel.setSelected(canvas.getModel().getObjectsFromBottom(), true);
canvas.repaint();
}
@Override
public void raise() {
ModelReorderAction act = ModelReorderAction.createRaise(canvas.getModel(),
canvas.getSelection().getSelected());
if (act != null) {
canvas.doAction(act);
}
}
@Override
public void lower() {
ModelReorderAction act = ModelReorderAction.createLower(canvas.getModel(),
canvas.getSelection().getSelected());
if (act != null) {
canvas.doAction(act);
}
}
@Override
public void raiseTop() {
ModelReorderAction act = ModelReorderAction.createRaiseTop(canvas.getModel(),
canvas.getSelection().getSelected());
if (act != null) {
canvas.doAction(act);
}
}
@Override
public void lowerBottom() {
ModelReorderAction act = ModelReorderAction.createLowerBottom(canvas.getModel(),
canvas.getSelection().getSelected());
if (act != null) {
canvas.doAction(act);
}
}
@Override
public void addControlPoint() {
Selection sel = canvas.getSelection();
Handle handle = sel.getSelectedHandle();
canvas.doAction(new ModelInsertHandleAction(canvas.getModel(), handle));
}
@Override
public void removeControlPoint() {
Selection sel = canvas.getSelection();
Handle handle = sel.getSelectedHandle();
canvas.doAction(new ModelDeleteHandleAction(canvas.getModel(), handle));
}
@Override
public void selectionChanged(SelectionEvent e) {
computeEnabled();
}
@Override
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (prop.equals(Canvas.MODEL_PROPERTY)) {
CanvasModel oldModel = (CanvasModel) e.getOldValue();
if (oldModel != null) {
oldModel.removeCanvasModelListener(this);
}
CanvasModel newModel = (CanvasModel) e.getNewValue();
if (newModel != null) {
newModel.addCanvasModelListener(this);
}
}
}
@Override
public void modelChanged(CanvasModelEvent event) {
computeEnabled();
}
}