import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import javax.swing.*;
/**
*
*/
/**
* @author Matt Chun-Lum
*
*/
public class Canvas extends JPanel implements ModelListener{
public static final int SIZE = 400;
public static final int INITIAL_SHAPE_POS = 10;
public static final int INITIAL_SHAPE_SIZE = 20;
private ArrayList<DShape> shapes;
private DShape selected;
private Point movingPoint;
private Point anchorPoint;
private int lastX, lastY;
private Whiteboard whiteboard;
public Canvas(Whiteboard bard) {
setMinimumSize(new Dimension(SIZE, SIZE));
setPreferredSize(getMinimumSize());
setBackground(Color.white);
whiteboard = bard;
shapes = new ArrayList<DShape>();
selected = null;
movingPoint = null;
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if(whiteboard.isNotClient())
selectObjectForClick(e.getPoint());
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
if(whiteboard.isNotClient()) {
int dx = e.getX() - lastX;
int dy = e.getY() - lastY;
lastX = e.getX();
lastY = e.getY();
if(movingPoint != null) {
movingPoint.x += dx;
movingPoint.y += dy;
selected.modifyShapeWithPoints(anchorPoint, movingPoint);
} else if(selected != null) {
selected.move(dx, dy);
}
}
}
});
}
/**
* Adds a shape to the canvas
* @param shape the shape to remove
*/
public void addShape(DShapeModel model) {
if(whiteboard.isNotClient())
model.setID(Whiteboard.getNextIDNumber());
// repaint the area where the last shape was
if(selected != null)
repaintShape(selected);
DShape shape = null;
if(model instanceof DRectModel)
shape = new DRect(model, this);
else if(model instanceof DOvalModel)
shape = new DOval(model, this);
else if(model instanceof DLineModel)
shape = new DLine(model, this);
else if(model instanceof DTextModel) {
shape = new DText(model, this);
DText textShape = (DText) shape;
whiteboard.updateFontGroup(textShape.getText(), textShape.getFontName());
}
model.addListener(this);
shapes.add(shape);
whiteboard.addToTable(shape);
if(whiteboard.isNotClient())
selected = shape;
if(whiteboard.isServer())
whiteboard.doSend(Whiteboard.Message.ADD, model);
repaintShape(shape);
}
/**
* Returns a pointer to the shapes list
* @return a pointer to the shapes list
*/
public ArrayList<DShape> getShapes() {
return shapes;
}
/**
* Returns the shape with the matching ID
* @param ID
* @return
*/
public DShape getShapeWithID(int ID) {
for(DShape shape : shapes)
if(shape.getModelID() == ID)
return shape;
return null;
}
/**
* Returns an array list of all the shape models of the shapes on the canvas
* @return
*/
public ArrayList<DShapeModel> getShapeModels() {
ArrayList<DShapeModel> models = new ArrayList<DShapeModel>();
for(DShape shape : shapes)
models.add(shape.getModel());
return models;
}
/**
* Marks an object for removal
*/
public void markSelectedShapeForRemoval() {
markForRemoval(selected);
selected = null;
}
/**
* Marks the passed shape for removal
*/
public void markForRemoval(DShape shape) {
shape.getModel().removeListener(this);
shape.markForRemoval();
}
public void markAllForRemoval() {
selected = null;
for(int i = shapes.size() - 1; i >= 0; i--)
markForRemoval(shapes.get(i));
}
/**
* removes all of the shapes from the canvas
*/
public void clearCanvas() {
shapes.clear();
selected = null;
whiteboard.clearTable();
repaint();
}
/**
* Removes a specific shape from the canvas, should never be called by whiteboard
* @param shape the shape to remove
*/
public void removeShape(DShape shape) {
shapes.remove(shape);
whiteboard.didRemove(shape);
if(whiteboard.isServer())
whiteboard.doSend(Whiteboard.Message.REMOVE, shape.getModel());
repaintArea(shape.getBigBounds());
}
/**
* Moves the selected shape to the front
*/
public void moveSelectedToFront() {
moveToFront(selected);
}
/**
* moves the specified shape to the front
* @param shape
*/
public void moveToFront(DShape shape) {
if(!shapes.isEmpty() && shapes.remove(shape))
shapes.add(shape);
whiteboard.didMoveToFront(shape);
if(whiteboard.isServer())
whiteboard.doSend(Whiteboard.Message.FRONT, shape.getModel());
repaintShape(shape);
}
/**
* Moves the selected shape to the back
*/
public void moveSelectedToBack() {
moveToBack(selected);
}
/**
* Moves the specified shape to the back
* @param shape
*/
public void moveToBack(DShape shape) {
if(!shapes.isEmpty() && shapes.remove(shape))
shapes.add(0, shape);
whiteboard.didMoveToBack(shape);
if(whiteboard.isServer())
whiteboard.doSend(Whiteboard.Message.BACK, shape.getModel());
repaintShape(shape);
}
/**
* Sets the text for the selected object
* @param text
*/
public void setTextForSelected(String text) {
if(selected instanceof DText)
((DText) selected).setText(text);
}
/**
* Sets the font name for the selected shape
* @param fontName
*/
public void setFontForSelected(String fontName) {
if(selected instanceof DText)
((DText) selected).setFontName(fontName);
}
/**
* Selects the object that contains the given point if it exists.
* If no object was selected, the selected pointer is set to null
* @param pt
*/
public void selectObjectForClick(Point pt) {
lastX = pt.x;
lastY = pt.y;
movingPoint = null;
anchorPoint = null;
// If we already have an object selected, test for a knob selection
if(selected != null) {
for(Point point : selected.getKnobs())
if(selected.selectedKnob(pt, point)) {
movingPoint = new Point(point);
anchorPoint = selected.getAnchorForSelectedKnob(point);
break;
}
}
// If we have no knob selected (or we have not selected object) check
// if the user is clicking on a valid object
if(movingPoint == null) {
selected = null;
for(DShape shape : shapes)
if(shape.containsPoint(pt))
selected = shape;
}
// handle selection of a text shape
if(selected != null && selected instanceof DText) {
DText textShape = (DText) selected;
whiteboard.updateFontGroup(textShape.getText(), textShape.getFontName());
} else {
// clear the textArea and reset the font chooser to the default font
whiteboard.updateFontGroup("", DTextModel.DEFAULT_FONT);
}
// If we are in server mode, let the table know that we have selected
// a different object
if(selected != null && whiteboard.isServer()) {
whiteboard.doSend(Whiteboard.Message.CHANGE, selected.getModel());
}
whiteboard.updateTableSelection(selected);
repaint();
}
/**
* @return true if the canvas has a selected shape
*/
public boolean hasSelected() {
return selected != null;
}
public DShape getSelected() {
return selected;
}
/**
* Sets the color for the selected shape
* @param color
*/
public void setSelectedColor(Color color) {
selected.setColor(color);
}
/**
* repaints the passed shape
*/
public void repaintShape(DShape shape) {
if(shape == selected)
repaint(shape.getBigBounds());
else
repaint(shape.getBounds());
}
/**
* repaints an area specified by the passed bounds
* @param bounds
*/
public void repaintArea(Rectangle bounds) {
repaint(bounds);
}
/**
* Saves the canvas to the passed file
* @param file
*/
public void saveCanvas(File file) {
// From Handout 32
try {
XMLEncoder xmlOut = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(file)));
DShapeModel[] shapeModels = getShapeModels().toArray(new DShapeModel[0]);
xmlOut.writeObject(shapeModels);
xmlOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Opens a canvas from the passed file
* @param file
*/
public void openCanvas(File file) {
markAllForRemoval();
// From Handout 32
try {
XMLDecoder xmlIn = new XMLDecoder(new BufferedInputStream(new FileInputStream(file)));
DShapeModel[] shapeModels = (DShapeModel[]) xmlIn.readObject();
xmlIn.close();
for(DShapeModel shapeModel : shapeModels) {
addShape(shapeModel);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Saves a PNG representation to the passed file
* @param file
*/
public void saveImage(File file) {
// Don't draw knobs
DShape wasSelected = selected;
selected = null;
// From Handout 32
BufferedImage image = (BufferedImage) createImage(getWidth(), getHeight());
Graphics g = image.getGraphics();
paintAll(g);
g.dispose();
try {
javax.imageio.ImageIO.write(image, "PNG", file);
} catch (IOException e) {
e.printStackTrace();
}
selected = wasSelected;
}
/**
* Paints the canvas
*/
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for(DShape shape : shapes)
shape.draw(g, (selected == shape));
}
/* (non-Javadoc)
* @see ModelListener#modelChanged(DShapeModel)
*/
public void modelChanged(DShapeModel model) {
if(whiteboard.isServer() && !model.markedForRemoval())
whiteboard.doSend(Whiteboard.Message.CHANGE, model);
}
}