/*******************************************************************************
* 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.draw.model;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.cburch.draw.canvas.Selection;
import com.cburch.draw.shapes.Text;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.util.EventSourceWeakSupport;
public class Drawing implements CanvasModel {
private EventSourceWeakSupport<CanvasModelListener> listeners;
private ArrayList<CanvasObject> canvasObjects;
private DrawingOverlaps overlaps;
public Drawing() {
listeners = new EventSourceWeakSupport<CanvasModelListener>();
canvasObjects = new ArrayList<CanvasObject>();
overlaps = new DrawingOverlaps();
}
public void addCanvasModelListener(CanvasModelListener l) {
listeners.add(l);
}
public void addObjects(int index, Collection<? extends CanvasObject> shapes) {
Map<CanvasObject, Integer> indexes;
indexes = new LinkedHashMap<CanvasObject, Integer>();
int i = index;
for (CanvasObject shape : shapes) {
indexes.put(shape, Integer.valueOf(i));
i++;
}
addObjectsHelp(indexes);
}
public void addObjects(Map<? extends CanvasObject, Integer> shapes) {
addObjectsHelp(shapes);
}
private void addObjectsHelp(Map<? extends CanvasObject, Integer> shapes) {
// this is separate method so that subclass can call super.add to either
// of the add methods, and it won't get redirected into the subclass
// in calling the other add method
CanvasModelEvent e = CanvasModelEvent.forAdd(this, shapes.keySet());
if (!shapes.isEmpty() && isChangeAllowed(e)) {
for (Map.Entry<? extends CanvasObject, Integer> entry : shapes
.entrySet()) {
CanvasObject shape = entry.getKey();
int index = entry.getValue().intValue();
canvasObjects.add(index, shape);
overlaps.addShape(shape);
}
fireChanged(e);
}
}
public Handle deleteHandle(Handle handle) {
CanvasModelEvent e = CanvasModelEvent.forDeleteHandle(this, handle);
if (isChangeAllowed(e)) {
CanvasObject o = handle.getObject();
Handle ret = o.deleteHandle(handle);
overlaps.invalidateShape(o);
fireChanged(e);
return ret;
} else {
return null;
}
}
private void fireChanged(CanvasModelEvent e) {
for (CanvasModelListener listener : listeners) {
listener.modelChanged(e);
}
}
public List<CanvasObject> getObjectsFromBottom() {
return Collections.unmodifiableList(canvasObjects);
}
public List<CanvasObject> getObjectsFromTop() {
List<CanvasObject> ret = new ArrayList<CanvasObject>(
getObjectsFromBottom());
Collections.reverse(ret);
return ret;
}
public Collection<CanvasObject> getObjectsIn(Bounds bds) {
List<CanvasObject> ret = null;
for (CanvasObject shape : getObjectsFromBottom()) {
if (bds.contains(shape.getBounds())) {
if (ret == null)
ret = new ArrayList<CanvasObject>();
ret.add(shape);
}
}
if (ret == null) {
return Collections.emptyList();
} else {
return ret;
}
}
public Collection<CanvasObject> getObjectsOverlapping(CanvasObject shape) {
return overlaps.getObjectsOverlapping(shape);
}
public void insertHandle(Handle desired, Handle previous) {
CanvasObject obj = desired.getObject();
CanvasModelEvent e = CanvasModelEvent.forInsertHandle(this, desired);
if (isChangeAllowed(e)) {
obj.insertHandle(desired, previous);
overlaps.invalidateShape(obj);
fireChanged(e);
}
}
protected boolean isChangeAllowed(CanvasModelEvent e) {
return true;
}
public Handle moveHandle(HandleGesture gesture) {
CanvasModelEvent e = CanvasModelEvent.forMoveHandle(this, gesture);
CanvasObject o = gesture.getHandle().getObject();
if (canvasObjects.contains(o)
&& (gesture.getDeltaX() != 0 || gesture.getDeltaY() != 0)
&& isChangeAllowed(e)) {
Handle moved = o.moveHandle(gesture);
gesture.setResultingHandle(moved);
overlaps.invalidateShape(o);
fireChanged(e);
return moved;
} else {
return null;
}
}
public void paint(Graphics g, Selection selection) {
Set<CanvasObject> suppressed = selection.getDrawsSuppressed();
for (CanvasObject shape : getObjectsFromBottom()) {
Graphics dup = g.create();
if (suppressed.contains(shape)) {
selection.drawSuppressed(dup, shape);
} else {
shape.paint(dup, null);
}
dup.dispose();
}
}
public void removeCanvasModelListener(CanvasModelListener l) {
listeners.remove(l);
}
public void removeObjects(Collection<? extends CanvasObject> shapes) {
List<CanvasObject> found = restrict(shapes);
CanvasModelEvent e = CanvasModelEvent.forRemove(this, found);
if (!found.isEmpty() && isChangeAllowed(e)) {
for (CanvasObject shape : found) {
canvasObjects.remove(shape);
overlaps.removeShape(shape);
}
fireChanged(e);
}
}
public void reorderObjects(List<ReorderRequest> requests) {
boolean hasEffect = false;
for (ReorderRequest r : requests) {
if (r.getFromIndex() != r.getToIndex()) {
hasEffect = true;
}
}
CanvasModelEvent e = CanvasModelEvent.forReorder(this, requests);
if (hasEffect && isChangeAllowed(e)) {
for (ReorderRequest r : requests) {
if (canvasObjects.get(r.getFromIndex()) != r.getObject()) {
throw new IllegalArgumentException("object not present"
+ " at indicated index: " + r.getFromIndex());
}
canvasObjects.remove(r.getFromIndex());
canvasObjects.add(r.getToIndex(), r.getObject());
}
fireChanged(e);
}
}
private ArrayList<CanvasObject> restrict(
Collection<? extends CanvasObject> shapes) {
ArrayList<CanvasObject> ret;
ret = new ArrayList<CanvasObject>(shapes.size());
for (CanvasObject shape : shapes) {
if (canvasObjects.contains(shape)) {
ret.add(shape);
}
}
return ret;
}
public void setAttributeValues(Map<AttributeMapKey, Object> values) {
Map<AttributeMapKey, Object> oldValues;
oldValues = new HashMap<AttributeMapKey, Object>();
for (AttributeMapKey key : values.keySet()) {
@SuppressWarnings("unchecked")
Attribute<Object> attr = (Attribute<Object>) key.getAttribute();
Object oldValue = key.getObject().getValue(attr);
oldValues.put(key, oldValue);
}
CanvasModelEvent e = CanvasModelEvent.forChangeAttributes(this,
oldValues, values);
if (isChangeAllowed(e)) {
for (Map.Entry<AttributeMapKey, Object> entry : values.entrySet()) {
AttributeMapKey key = entry.getKey();
CanvasObject shape = key.getObject();
@SuppressWarnings("unchecked")
Attribute<Object> attr = (Attribute<Object>) key.getAttribute();
shape.setValue(attr, entry.getValue());
overlaps.invalidateShape(shape);
}
fireChanged(e);
}
}
public void setText(Text text, String value) {
String oldValue = text.getText();
CanvasModelEvent e = CanvasModelEvent.forChangeText(this, text,
oldValue, value);
if (canvasObjects.contains(text) && !oldValue.equals(value)
&& isChangeAllowed(e)) {
text.setText(value);
overlaps.invalidateShape(text);
fireChanged(e);
}
}
public void translateObjects(Collection<? extends CanvasObject> shapes,
int dx, int dy) {
List<CanvasObject> found = restrict(shapes);
CanvasModelEvent e = CanvasModelEvent.forTranslate(this, found, dx, dy);
if (!found.isEmpty() && (dx != 0 || dy != 0) && isChangeAllowed(e)) {
for (CanvasObject shape : shapes) {
shape.translate(dx, dy);
overlaps.invalidateShape(shape);
}
fireChanged(e);
}
}
}