package com.kreative.paint.document.layer; import java.awt.Composite; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.util.ArrayList; import java.util.List; import com.kreative.paint.document.undo.Atom; import com.kreative.paint.document.undo.History; import com.kreative.paint.document.undo.Recordable; public abstract class Layer implements Cloneable, Recordable { protected String name; protected boolean visible; protected boolean locked; protected boolean selected; protected int x; protected int y; protected History history; protected List<LayerListener> listeners; protected Layer(String name) { if (name == null) name = ""; this.name = name; this.visible = true; this.locked = false; this.selected = false; this.x = 0; this.y = 0; this.history = null; this.listeners = new ArrayList<LayerListener>(); } protected Layer(Layer original) { this.name = original.name; this.visible = original.visible; this.locked = original.locked; this.selected = original.selected; this.x = original.x; this.y = original.y; this.history = null; this.listeners = new ArrayList<LayerListener>(); } @Override public History getHistory() { return history; } @Override public void setHistory(History history) { this.history = history; } public void addLayerListener(LayerListener l) { listeners.add(l); } public void removeLayerListener(LayerListener l) { listeners.remove(l); } public LayerListener[] getLayerListeners() { return listeners.toArray(new LayerListener[listeners.size()]); } protected void notifyLayerListeners(int id) { if (listeners.isEmpty()) return; LayerEvent e = new LayerEvent(id, this); switch (id) { case LayerEvent.LAYER_NAME_CHANGED: for (LayerListener l : listeners) l.layerNameChanged(e); break; case LayerEvent.LAYER_VISIBLE_CHANGED: for (LayerListener l : listeners) l.layerVisibleChanged(e); break; case LayerEvent.LAYER_LOCKED_CHANGED: for (LayerListener l : listeners) l.layerLockedChanged(e); break; case LayerEvent.LAYER_SELECTED_CHANGED: for (LayerListener l : listeners) l.layerSelectedChanged(e); break; case LayerEvent.LAYER_LOCATION_CHANGED: for (LayerListener l : listeners) l.layerLocationChanged(e); break; case LayerEvent.LAYER_CONTENT_CHANGED: for (LayerListener l : listeners) l.layerContentChanged(e); break; } } public String getName() { return name; } private static class NameAtom implements Atom { private Layer l; private String oldName; private String newName; public NameAtom(Layer l, String newName) { this.l = l; this.oldName = l.name; this.newName = newName; } @Override public boolean canBuildUpon(Atom prev) { return (prev instanceof NameAtom) && (((NameAtom)prev).l == this.l); } @Override public Atom buildUpon(Atom prev) { this.oldName = ((NameAtom)prev).oldName; return this; } @Override public void undo() { l.name = oldName; l.notifyLayerListeners(LayerEvent.LAYER_NAME_CHANGED); } @Override public void redo() { l.name = newName; l.notifyLayerListeners(LayerEvent.LAYER_NAME_CHANGED); } } public void setName(String name) { if (name == null) name = ""; if (this.name.equals(name)) return; if (history != null) history.add(new NameAtom(this, name)); this.name = name; this.notifyLayerListeners(LayerEvent.LAYER_NAME_CHANGED); } public boolean isVisible() { return visible; } public boolean isLocked() { return locked; } public boolean isSelected() { return selected; } public boolean isEditable() { return visible && selected && !locked; } private static class VisibleAtom implements Atom { private Layer l; private boolean oldVisible; private boolean newVisible; public VisibleAtom(Layer l, boolean newVisible) { this.l = l; this.oldVisible = l.visible; this.newVisible = newVisible; } @Override public boolean canBuildUpon(Atom prev) { return (prev instanceof VisibleAtom) && (((VisibleAtom)prev).l == this.l); } @Override public Atom buildUpon(Atom prev) { this.oldVisible = ((VisibleAtom)prev).oldVisible; return this; } @Override public void undo() { l.visible = oldVisible; l.notifyLayerListeners(LayerEvent.LAYER_VISIBLE_CHANGED); } @Override public void redo() { l.visible = newVisible; l.notifyLayerListeners(LayerEvent.LAYER_VISIBLE_CHANGED); } } private static class LockedAtom implements Atom { private Layer l; private boolean oldLocked; private boolean newLocked; public LockedAtom(Layer l, boolean newLocked) { this.l = l; this.oldLocked = l.locked; this.newLocked = newLocked; } @Override public boolean canBuildUpon(Atom prev) { return (prev instanceof LockedAtom) && (((LockedAtom)prev).l == this.l); } @Override public Atom buildUpon(Atom prev) { this.oldLocked = ((LockedAtom)prev).oldLocked; return this; } @Override public void undo() { l.locked = oldLocked; l.notifyLayerListeners(LayerEvent.LAYER_LOCKED_CHANGED); } @Override public void redo() { l.locked = newLocked; l.notifyLayerListeners(LayerEvent.LAYER_LOCKED_CHANGED); } } private static class SelectedAtom implements Atom { private Layer l; private boolean oldSelected; private boolean newSelected; public SelectedAtom(Layer l, boolean newSelected) { this.l = l; this.oldSelected = l.selected; this.newSelected = newSelected; } @Override public boolean canBuildUpon(Atom prev) { return (prev instanceof SelectedAtom) && (((SelectedAtom)prev).l == this.l); } @Override public Atom buildUpon(Atom prev) { this.oldSelected = ((SelectedAtom)prev).oldSelected; return this; } @Override public void undo() { l.selected = oldSelected; l.notifyLayerListeners(LayerEvent.LAYER_SELECTED_CHANGED); } @Override public void redo() { l.selected = newSelected; l.notifyLayerListeners(LayerEvent.LAYER_SELECTED_CHANGED); } } public void setVisible(boolean visible) { if (this.visible == visible) return; if (history != null) history.add(new VisibleAtom(this, visible)); this.visible = visible; this.notifyLayerListeners(LayerEvent.LAYER_VISIBLE_CHANGED); } public void setLocked(boolean locked) { if (this.locked == locked) return; if (history != null) history.add(new LockedAtom(this, locked)); this.locked = locked; this.notifyLayerListeners(LayerEvent.LAYER_LOCKED_CHANGED); } public void setSelected(boolean selected) { if (this.selected == selected) return; if (history != null) history.add(new SelectedAtom(this, selected)); this.selected = selected; this.notifyLayerListeners(LayerEvent.LAYER_SELECTED_CHANGED); } public int getX() { return x; } public int getY() { return y; } public Point getLocation() { return new Point(x, y); } private static class LocationAtom implements Atom { private Layer l; private int oldX, oldY; private int newX, newY; public LocationAtom(Layer l, int newX, int newY) { this.l = l; this.oldX = l.x; this.oldY = l.y; this.newX = newX; this.newY = newY; } @Override public boolean canBuildUpon(Atom prev) { return (prev instanceof LocationAtom) && (((LocationAtom)prev).l == this.l); } @Override public Atom buildUpon(Atom prev) { this.oldX = ((LocationAtom)prev).oldX; this.oldY = ((LocationAtom)prev).oldY; return this; } @Override public void undo() { l.x = oldX; l.y = oldY; l.notifyLayerListeners(LayerEvent.LAYER_LOCATION_CHANGED); } @Override public void redo() { l.x = newX; l.y = newY; l.notifyLayerListeners(LayerEvent.LAYER_LOCATION_CHANGED); } } public void setX(int x) { if (this.x == x) return; if (history != null) history.add(new LocationAtom(this, x, y)); this.x = x; this.notifyLayerListeners(LayerEvent.LAYER_LOCATION_CHANGED); } public void setY(int y) { if (this.y == y) return; if (history != null) history.add(new LocationAtom(this, x, y)); this.y = y; this.notifyLayerListeners(LayerEvent.LAYER_LOCATION_CHANGED); } public void setLocation(int x, int y) { if (this.x == x && this.y == y) return; if (history != null) history.add(new LocationAtom(this, x, y)); this.x = x; this.y = y; this.notifyLayerListeners(LayerEvent.LAYER_LOCATION_CHANGED); } public void setLocation(Point p) { setLocation(p.x, p.y); } @Override public abstract Layer clone(); protected abstract void paintImpl(Graphics2D g, int gx, int gy, int gw, int gh); public void paint(Graphics2D g, int gx, int gy, int gw, int gh, int tx, int ty) { Paint p = g.getPaint(); Composite c = g.getComposite(); Stroke s = g.getStroke(); Font f = g.getFont(); RenderingHints h = g.getRenderingHints(); Shape gclip = g.getClip(); g.clipRect(gx, gy, gw, gh); AffineTransform gtx = g.getTransform(); g.translate(gx + x + tx, gy + y + ty); paintImpl(g, -x -tx, -y -ty, gw, gh); g.setTransform(gtx); g.setClip(gclip); g.setPaint(p); g.setComposite(c); g.setStroke(s); g.setFont(f); g.setRenderingHints(h); } public void paint(Graphics2D g, int gx, int gy, int gw, int gh) { paint(g, gx, gy, gw, gh, 0, 0); } }