commit
ce1a245ca6
@ -0,0 +1,135 @@
|
||||
package controller;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import model.GroupShapes;
|
||||
import model.Shape;
|
||||
import model.AbstractShape;
|
||||
import model.ShapeList;
|
||||
import model.interfaces.IUndoable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
|
||||
/**
|
||||
* Static class that manages commands, that implement IUndoable, performed on JPaint.
|
||||
*
|
||||
* Functionalities:
|
||||
* Undo/Redo commands
|
||||
* Copy/Paste commands
|
||||
* Delete command
|
||||
*/
|
||||
|
||||
class CommandHistory {
|
||||
private static final Stack<IUndoable> UNDO_STACK = new Stack<>();
|
||||
private static final Stack<IUndoable> REDO_STACK = new Stack<>();
|
||||
private static List<AbstractShape> copyShapesBuffer = null;
|
||||
|
||||
public static void add(IUndoable cmd) {
|
||||
UNDO_STACK.push(cmd);
|
||||
REDO_STACK.clear();
|
||||
}
|
||||
|
||||
public static boolean undo() {
|
||||
System.out.println("Undo was pressed!");
|
||||
boolean result = !UNDO_STACK.empty();
|
||||
if (result) {
|
||||
IUndoable c = UNDO_STACK.pop();
|
||||
REDO_STACK.push(c);
|
||||
c.undo();
|
||||
//c.printShapeList();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean redo() {
|
||||
System.out.println("Redo was pressed!");
|
||||
boolean result = !REDO_STACK.empty();
|
||||
if (result) {
|
||||
IUndoable c = REDO_STACK.pop();
|
||||
UNDO_STACK.push(c);
|
||||
c.redo();
|
||||
//c.printShapeList();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void copy(ShapeList shapesListObj) {
|
||||
List<AbstractShape> copiedShapes = shapesListObj.getSelectedShapes();
|
||||
if (copiedShapes.size() == 0) return;
|
||||
Iterator<AbstractShape> iter = copiedShapes.iterator();
|
||||
copyShapesBuffer = new ArrayList<>();
|
||||
while (iter.hasNext()) {
|
||||
AbstractShape cur = iter.next();
|
||||
if (cur instanceof Shape) {
|
||||
Shape copyCur = new Shape(cur.getPrimaryColor(), cur.getSecondaryColor(),
|
||||
cur.getShadingType(), cur.getShapeType(), 0, 0,
|
||||
Math.abs(cur.getEndX() - cur.getStartX()),
|
||||
Math.abs(cur.getEndY() - cur.getStartY()));
|
||||
copyShapesBuffer.add(copyCur);
|
||||
} else if (cur instanceof GroupShapes) {
|
||||
GroupShapes copyCur = new GroupShapes(cur.getPrimaryColor(), cur.getSecondaryColor(),
|
||||
cur.getShadingType(), cur.getShapeType(), 0, 0,
|
||||
Math.abs(cur.getEndX() - cur.getStartX()),
|
||||
Math.abs(cur.getEndY() - cur.getStartY()),
|
||||
copyAbstractShapes(((GroupShapes) cur).getShapes()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<AbstractShape> copyAbstractShapes(List<AbstractShape> shapes) {
|
||||
List<AbstractShape> componentsClone = new ArrayList<>();
|
||||
|
||||
Iterator<AbstractShape> i = shapes.iterator();
|
||||
while (i.hasNext()) {
|
||||
AbstractShape cur = i.next();
|
||||
if (i instanceof GroupShapes) {
|
||||
GroupShapes copyCur = new GroupShapes(cur.getPrimaryColor(), cur.getSecondaryColor(),
|
||||
cur.getShadingType(), cur.getShapeType(), 0, 0,
|
||||
cur.getEndX(), cur.getEndY(), copyAbstractShapes(((GroupShapes) cur).getShapes()));
|
||||
componentsClone.add(copyCur);
|
||||
} else {
|
||||
Shape copyCur = new Shape(cur.getPrimaryColor(), cur.getSecondaryColor(),
|
||||
cur.getShadingType(), cur.getShapeType(), 0, 0,
|
||||
cur.getEndX(), cur.getEndY());
|
||||
componentsClone.add(copyCur);
|
||||
}
|
||||
}
|
||||
|
||||
return componentsClone;
|
||||
}
|
||||
|
||||
public static void paste(ShapeList shapesListObj) {
|
||||
if (copyShapesBuffer != null) {
|
||||
shapesListObj.deselectAllShapes();
|
||||
IUndoable pasteCmd = new PasteCommand(shapesListObj, copyShapesBuffer);
|
||||
UNDO_STACK.push(pasteCmd);
|
||||
REDO_STACK.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void delete(ShapeList shapesListObj) {
|
||||
if (shapesListObj.getNumberOfSelectedShapes() == 0) return;
|
||||
IUndoable deleteCmd = new DeleteCommand(shapesListObj);
|
||||
UNDO_STACK.push(deleteCmd);
|
||||
REDO_STACK.clear();
|
||||
}
|
||||
|
||||
public static void group(ShapeList shapesListObj) {
|
||||
if (shapesListObj.getNumberOfSelectedShapes() == 0) return;
|
||||
IUndoable groupCmd = new GroupCommand(shapesListObj);
|
||||
UNDO_STACK.push(groupCmd);
|
||||
REDO_STACK.clear();
|
||||
}
|
||||
|
||||
public static void ungroup(ShapeList shapesListObj) {
|
||||
if (shapesListObj.getNumberOfSelectedShapes() == 0) return;
|
||||
IUndoable ungroupCmd = new UngroupCommand(shapesListObj);
|
||||
UNDO_STACK.push(ungroupCmd);
|
||||
REDO_STACK.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import model.Shape;
|
||||
import model.AbstractShape;
|
||||
import model.ShapeList;
|
||||
import model.interfaces.IUndoable;
|
||||
|
||||
public class DeleteCommand implements IUndoable {
|
||||
|
||||
private final ShapeList SHAPE_LIST;
|
||||
private List<AbstractShape> deletedShapes;
|
||||
|
||||
public DeleteCommand(ShapeList shapeList) {
|
||||
this.SHAPE_LIST = shapeList;
|
||||
deletedShapes = this.SHAPE_LIST.removeSelectedShape();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
this.SHAPE_LIST.addAllShapes(this.deletedShapes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
deletedShapes = this.SHAPE_LIST.removeSelectedShape();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printShapeList() {
|
||||
System.out.println("Delete CMD");
|
||||
this.SHAPE_LIST.printShapeList();
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package controller;
|
||||
|
||||
import model.Shape;
|
||||
import model.ShapeList;
|
||||
import model.AbstractShape;
|
||||
|
||||
import model.interfaces.IUndoable;
|
||||
|
||||
public class DrawShapeCommand implements IUndoable {
|
||||
|
||||
private final AbstractShape SHAPE;
|
||||
private final ShapeList SHAPE_LIST;
|
||||
|
||||
public DrawShapeCommand(AbstractShape shape, ShapeList shapeList) {
|
||||
this.SHAPE = shape;
|
||||
this.SHAPE_LIST = shapeList;
|
||||
this.SHAPE_LIST.addShape(this.SHAPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
//System.out.println("Undo was pressed!");
|
||||
SHAPE_LIST.removeShape(this.SHAPE);
|
||||
//SHAPE_LIST.printShapeList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
//System.out.println("Redo was pressed!");
|
||||
SHAPE_LIST.addShape(this.SHAPE);
|
||||
//SHAPE_LIST.printShapeList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printShapeList() {
|
||||
System.out.println("DrawShape CMD");
|
||||
this.SHAPE_LIST.printShapeList();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package controller;
|
||||
|
||||
import model.interfaces.IUndoable;
|
||||
import model.ShapeList;
|
||||
import model.AbstractShape;
|
||||
import java.util.List;
|
||||
|
||||
public class GroupCommand implements IUndoable {
|
||||
|
||||
private final ShapeList SHAPE_LIST;
|
||||
private List<AbstractShape> groupedShapes;
|
||||
|
||||
public GroupCommand(ShapeList shapeList) {
|
||||
this.SHAPE_LIST = shapeList;
|
||||
this.groupedShapes = this.SHAPE_LIST.groupShapes(this.SHAPE_LIST.getSelectedShapes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
this.groupedShapes = this.SHAPE_LIST.ungroupShapes(this.groupedShapes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
this.groupedShapes = this.SHAPE_LIST.groupShapes(this.groupedShapes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printShapeList() {
|
||||
System.out.println("Group CMD");
|
||||
this.SHAPE_LIST.printShapeList();
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package controller;
|
||||
|
||||
public interface IJPaintController {
|
||||
void setup();
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package controller;
|
||||
|
||||
import model.ShapeList;
|
||||
import model.interfaces.IApplicationState;
|
||||
import view.EventName;
|
||||
import view.interfaces.IUiModule;
|
||||
|
||||
public class JPaintController implements IJPaintController {
|
||||
private final IUiModule UI_MODULE;
|
||||
private final IApplicationState APP_STATE;
|
||||
private final ShapeList SHAPE_LIST;
|
||||
|
||||
|
||||
public JPaintController(IUiModule uiModule, IApplicationState applicationState, ShapeList shapeList) {
|
||||
this.UI_MODULE = uiModule;
|
||||
this.APP_STATE = applicationState;
|
||||
this.SHAPE_LIST = shapeList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
setupEvents();
|
||||
}
|
||||
|
||||
private void setupEvents() {
|
||||
UI_MODULE.addEvent(EventName.CHOOSE_SHAPE, () -> APP_STATE.setActiveShape());
|
||||
UI_MODULE.addEvent(EventName.CHOOSE_PRIMARY_COLOR, () -> APP_STATE.setActivePrimaryColor());
|
||||
UI_MODULE.addEvent(EventName.CHOOSE_SECONDARY_COLOR, () -> APP_STATE.setActiveSecondaryColor());
|
||||
UI_MODULE.addEvent(EventName.CHOOSE_SHADING_TYPE, () -> APP_STATE.setActiveShadingType());
|
||||
UI_MODULE.addEvent(EventName.CHOOSE_MOUSE_MODE, () -> APP_STATE.setActiveStartAndEndPointMode());
|
||||
UI_MODULE.addEvent(EventName.UNDO, () -> CommandHistory.undo());
|
||||
UI_MODULE.addEvent(EventName.REDO, () -> CommandHistory.redo());
|
||||
UI_MODULE.addEvent(EventName.COPY, () -> CommandHistory.copy(this.SHAPE_LIST));
|
||||
UI_MODULE.addEvent(EventName.PASTE, () -> CommandHistory.paste(this.SHAPE_LIST));
|
||||
UI_MODULE.addEvent(EventName.DELETE, () -> CommandHistory.delete(this.SHAPE_LIST));
|
||||
UI_MODULE.addEvent(EventName.GROUP, () -> CommandHistory.group(this.SHAPE_LIST));
|
||||
UI_MODULE.addEvent(EventName.UNGROUP, () -> CommandHistory.ungroup(this.SHAPE_LIST));
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package controller;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
|
||||
import model.MouseMode;
|
||||
import model.Shape;
|
||||
import model.ShapeList;
|
||||
import model.interfaces.IApplicationState;
|
||||
import model.persistence.ApplicationState;
|
||||
|
||||
/**
|
||||
* MouseAdapter accepts input from the mouse pointer and creates appropriates
|
||||
* commands for JPaint.
|
||||
*/
|
||||
|
||||
public class MouseAdapter implements MouseListener {
|
||||
|
||||
private static MouseAdapter uniqueInstance;
|
||||
|
||||
private int startX;
|
||||
private int startY;
|
||||
private int endX;
|
||||
private int endY;
|
||||
|
||||
private IApplicationState applicationState;
|
||||
private ShapeList shapes;
|
||||
|
||||
private MouseAdapter(ApplicationState appState, ShapeList shapes) {
|
||||
this.applicationState = appState;
|
||||
this.shapes = shapes;
|
||||
}
|
||||
|
||||
public static MouseAdapter getInstance(ApplicationState appState, ShapeList shapes) {
|
||||
if (uniqueInstance == null) {
|
||||
uniqueInstance = new MouseAdapter(appState, shapes);
|
||||
} else {
|
||||
uniqueInstance.applicationState = appState;
|
||||
uniqueInstance.shapes = shapes;
|
||||
}
|
||||
return uniqueInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent arg0) {
|
||||
this.startX = arg0.getX();
|
||||
this.startY = arg0.getY();
|
||||
|
||||
if (applicationState.getActiveMouseMode() == MouseMode.SELECT) {
|
||||
this.shapes.markSelect(this.startX + 1, this.startY + 1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent arg0) { }
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent arg0) { }
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent arg0) {
|
||||
this.startX = arg0.getX();
|
||||
this.startY = arg0.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent arg0) {
|
||||
this.endX = arg0.getX();
|
||||
this.endY = arg0.getY();
|
||||
|
||||
int width = Math.abs(this.endX - this.startX);
|
||||
int height = Math.abs(this.endY - this.startY);
|
||||
|
||||
int x = Math.min(this.startX, this.endX);
|
||||
int y = Math.min(this.startY, this.endY);
|
||||
|
||||
if (applicationState.getActiveMouseMode() == MouseMode.DRAW) {
|
||||
Shape newShape = new Shape(applicationState.getActivePrimaryColor(),
|
||||
applicationState.getActiveSecondaryColor(), applicationState.getActiveShapeShadingType(),
|
||||
applicationState.getActiveShapeType(), x, y, x + width, y + height);
|
||||
|
||||
CommandHistory.add(new DrawShapeCommand(newShape, this.shapes));
|
||||
} else if (applicationState.getActiveMouseMode() == MouseMode.SELECT) {
|
||||
|
||||
this.shapes.markSelect(x, y, width, height);
|
||||
} else if (applicationState.getActiveMouseMode() == MouseMode.MOVE) {
|
||||
int dX = this.endX - this.startX;
|
||||
int dY = this.endY - this.startY;
|
||||
|
||||
CommandHistory.add(new MoveCommand(dX, dY, this.shapes));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import model.Shape;
|
||||
import model.AbstractShape;
|
||||
import model.ShapeList;
|
||||
import model.interfaces.IUndoable;
|
||||
|
||||
public class MoveCommand implements IUndoable {
|
||||
|
||||
private final int DX;
|
||||
private final int DY;
|
||||
private List<AbstractShape> selectedShapes;
|
||||
private final ShapeList SHAPE_LIST;
|
||||
|
||||
public MoveCommand(int dX, int dY, ShapeList shapeListObj) {
|
||||
this.DX = dX;
|
||||
this.DY = dY;
|
||||
this.SHAPE_LIST = shapeListObj;
|
||||
this.selectedShapes = this.SHAPE_LIST.moveShapes(DX, DY, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
this.selectedShapes = SHAPE_LIST.moveShapes(-DX, -DY, selectedShapes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
this.selectedShapes = SHAPE_LIST.moveShapes(DX, DY, selectedShapes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printShapeList() {
|
||||
System.out.println("Move CMD");
|
||||
this.SHAPE_LIST.printShapeList();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import model.Shape;
|
||||
import model.AbstractShape;
|
||||
import model.ShapeList;
|
||||
import model.interfaces.IUndoable;
|
||||
|
||||
public class PasteCommand implements IUndoable {
|
||||
|
||||
private final ShapeList SHAPE_LIST;
|
||||
private List<AbstractShape> pasteShapes;
|
||||
|
||||
public PasteCommand(ShapeList shapeList, List<AbstractShape> pasteShapes) {
|
||||
this.SHAPE_LIST = shapeList;
|
||||
this.pasteShapes = this.SHAPE_LIST.cloneAndAddAllShapes(pasteShapes);
|
||||
this.printShapeList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
this.SHAPE_LIST.removeShapesList(pasteShapes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
this.pasteShapes = this.SHAPE_LIST.cloneAndAddAllShapes(pasteShapes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printShapeList() {
|
||||
System.out.println("Paste CMD");
|
||||
this.SHAPE_LIST.printShapeList();
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package controller;
|
||||
|
||||
import model.ShapeList;
|
||||
import model.AbstractShape;
|
||||
import model.interfaces.IUndoable;
|
||||
import java.util.List;
|
||||
|
||||
public class UngroupCommand implements IUndoable {
|
||||
private final ShapeList SHAPE_LIST;
|
||||
private List<AbstractShape> ungrouped;
|
||||
|
||||
public UngroupCommand(ShapeList shapeList) {
|
||||
this.SHAPE_LIST = shapeList;
|
||||
this.ungrouped = this.SHAPE_LIST.ungroupShapes(this.SHAPE_LIST.getSelectedShapes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
this.ungrouped = this.SHAPE_LIST.groupShapes(this.ungrouped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
this.ungrouped = this.SHAPE_LIST.ungroupShapes(this.ungrouped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printShapeList() {
|
||||
System.out.println("Ungrouped CMD");
|
||||
this.SHAPE_LIST.printShapeList();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package main;
|
||||
|
||||
import controller.IJPaintController;
|
||||
import controller.JPaintController;
|
||||
import model.persistence.ApplicationState;
|
||||
import view.gui.Drawer;
|
||||
import view.gui.Gui;
|
||||
import view.gui.GuiWindow;
|
||||
import view.gui.PaintCanvas;
|
||||
import view.interfaces.IGuiWindow;
|
||||
import view.interfaces.PaintCanvasBase;
|
||||
import view.interfaces.IUiModule;
|
||||
import controller.MouseAdapter;
|
||||
import model.ShapeList;
|
||||
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args){
|
||||
ShapeList shapes = ShapeList.getInstance();
|
||||
PaintCanvasBase paintCanvas = new PaintCanvas();
|
||||
IGuiWindow guiWindow = new GuiWindow(paintCanvas);
|
||||
IUiModule uiModule = new Gui(guiWindow);
|
||||
ApplicationState appState = new ApplicationState(uiModule);
|
||||
MouseAdapter mouseController = MouseAdapter.getInstance(appState, shapes);
|
||||
paintCanvas.addMouseListener(mouseController);
|
||||
Drawer drawer = Drawer.getInstance(paintCanvas);
|
||||
shapes.registerObserver(drawer);
|
||||
IJPaintController controller = new JPaintController(uiModule, appState, shapes);
|
||||
controller.setup();
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package model;
|
||||
|
||||
import view.interfaces.IDrawShape;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractShape {
|
||||
public abstract int getStartX();
|
||||
public abstract int getStartY();
|
||||
public abstract int getEndX();
|
||||
public abstract int getEndY();
|
||||
public abstract boolean isSelected();
|
||||
public abstract void setIsSelected(boolean isSelected);
|
||||
public abstract boolean isBoundary();
|
||||
public abstract boolean isInGroup();
|
||||
public abstract void setIsInGroup(boolean groupFlag);
|
||||
public abstract void move(int dx, int dy);
|
||||
public abstract ShapeType getShapeType();
|
||||
public abstract ShapeColor getPrimaryColor();
|
||||
public abstract ShapeColor getSecondaryColor();
|
||||
public abstract ShapeShadingType getShadingType();
|
||||
public abstract boolean isOverlapping(AbstractShape shape);
|
||||
public abstract List<AbstractShape> getComponents();
|
||||
public abstract boolean isComposite();
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class GroupShapes extends AbstractShape {
|
||||
|
||||
private List<AbstractShape> shapes;
|
||||
private ShapeBoundary groupBoundary;
|
||||
|
||||
private ShapeColor primaryColor;
|
||||
private ShapeColor secondaryColor;
|
||||
private ShapeShadingType shadingType;
|
||||
private ShapeType shapeType;
|
||||
private int startX;
|
||||
private int startY;
|
||||
private int endX;
|
||||
private int endY;
|
||||
private boolean isSelected;
|
||||
private boolean isInGroup;
|
||||
|
||||
private final boolean IS_COMPOSITE = true;
|
||||
private final boolean IS_BOUNDARY = false;
|
||||
|
||||
public GroupShapes(ShapeColor primaryColor, ShapeColor secondaryColor, ShapeShadingType shadingType,
|
||||
ShapeType shapeType, int startX, int startY, int endX, int endY, List<AbstractShape> shapes) {
|
||||
this.primaryColor = primaryColor;
|
||||
this.secondaryColor = secondaryColor;
|
||||
this.shadingType = shadingType;
|
||||
this.shapeType = shapeType;
|
||||
|
||||
this.startX = startX;
|
||||
this.startY = startY;
|
||||
this.endX = endX;
|
||||
this.endY = endY;
|
||||
|
||||
this.shapes = shapes;
|
||||
this.isInGroup = false;
|
||||
this.isSelected = true;
|
||||
|
||||
this.resetBoundary();
|
||||
}
|
||||
|
||||
public void resetBoundary() {
|
||||
this.groupBoundary = new ShapeBoundary(ShapeColor.BLACK, ShapeColor.BLACK, ShapeShadingType.OUTLINE,
|
||||
this.shapeType, startX, startY, endX, endY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComposite() { return this.IS_COMPOSITE; }
|
||||
|
||||
@Override
|
||||
public boolean isBoundary() { return this.IS_BOUNDARY; }
|
||||
|
||||
@Override
|
||||
public boolean isInGroup() { return this.isInGroup; }
|
||||
|
||||
@Override
|
||||
public void setIsInGroup(boolean groupFlag) { this.isInGroup = groupFlag; }
|
||||
|
||||
@Override
|
||||
public boolean isOverlapping(AbstractShape shape) {
|
||||
if ((startX > shape.getEndX()) || (endX < shape.getStartX()) ||
|
||||
startY > shape.getEndY() || endY < shape.getStartY())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartX() { return startX; }
|
||||
|
||||
@Override
|
||||
public int getStartY() { return startY; }
|
||||
|
||||
@Override
|
||||
public int getEndX() { return endX; }
|
||||
|
||||
@Override
|
||||
public int getEndY() { return endY; }
|
||||
|
||||
@Override
|
||||
public boolean isSelected() { return this.isSelected; }
|
||||
|
||||
@Override
|
||||
public void setIsSelected(boolean isSelected) { this.isSelected = isSelected; }
|
||||
|
||||
@Override
|
||||
public void move(int dx, int dy) {
|
||||
this.startX = this.startX + dx;
|
||||
this.startY = this.startY + dy;
|
||||
this.endX = this.endX + dx;
|
||||
this.endY = this.endY + dy;
|
||||
|
||||
Iterator<AbstractShape> shapeIter = this.shapes.iterator();
|
||||
while (shapeIter.hasNext()) {
|
||||
shapeIter.next().move(dx, dy);
|
||||
}
|
||||
|
||||
this.groupBoundary.move(dx,dy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType getShapeType() { return this.shapeType; }
|
||||
|
||||
@Override
|
||||
public ShapeColor getPrimaryColor() { return this.primaryColor; }
|
||||
|
||||
@Override
|
||||
public ShapeColor getSecondaryColor() { return this.secondaryColor; }
|
||||
|
||||
@Override
|
||||
public ShapeShadingType getShadingType() { return this.shadingType; }
|
||||
|
||||
public List<AbstractShape> getShapes() { return this.shapes; }
|
||||
|
||||
@Override
|
||||
public List<AbstractShape> getComponents() {
|
||||
List<AbstractShape> components = new ArrayList<>();
|
||||
components.addAll(this.shapes);
|
||||
if (!this.isInGroup())
|
||||
components.add(groupBoundary);
|
||||
return components;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package model;
|
||||
|
||||
public enum MouseMode {
|
||||
DRAW,
|
||||
SELECT,
|
||||
MOVE
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
package model;
|
||||
|
||||
import view.interfaces.IDrawShape;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Shape Class
|
||||
*
|
||||
* Responsibility: Maintains information/metadata about a generic shape.
|
||||
*
|
||||
* A shape primary's color, secondary color, type, shaping type, selection, and starting and ending coordinates.
|
||||
*
|
||||
*
|
||||
* @author Minh Bui
|
||||
*/
|
||||
|
||||
public class Shape extends AbstractShape {
|
||||
private ShapeColor primaryColor;
|
||||
private ShapeColor secondaryColor;
|
||||
private ShapeShadingType shadingType;
|
||||
private ShapeType shapeType;
|
||||
private int startX;
|
||||
private int startY;
|
||||
private int endX;
|
||||
private int endY;
|
||||
private boolean isSelected;
|
||||
private boolean isInGroup;
|
||||
private final boolean IS_BOUNDARY = false;
|
||||
private final boolean IS_COMPOSITE = false;
|
||||
private ShapeBoundary groupBoundary;
|
||||
private ShapeBoundary boundary;
|
||||
|
||||
public Shape(ShapeColor primaryColor, ShapeColor secondaryColor, ShapeShadingType shadingType, ShapeType shapeType,
|
||||
int startX, int startY, int endX, int endY) {
|
||||
this.primaryColor = primaryColor;
|
||||
this.secondaryColor = secondaryColor;
|
||||
this.shadingType = shadingType;
|
||||
this.shapeType = shapeType;
|
||||
this.startX = startX;
|
||||
this.startY = startY;
|
||||
this.endX = endX;
|
||||
this.endY = endY;
|
||||
this.isSelected = false;
|
||||
this.groupBoundary = null;
|
||||
this.isInGroup = false;
|
||||
resetBoundary();
|
||||
}
|
||||
|
||||
public boolean isBoundary() { return this.IS_BOUNDARY; }
|
||||
|
||||
/* // Uncomment this if use solution with Composite design pattern.
|
||||
|
||||
@Override
|
||||
public boolean isInGroup() { return this.isInGroup; }
|
||||
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void setIsInGroup(boolean groupFlag) { this.isInGroup = groupFlag; }
|
||||
|
||||
public void resetBoundary() {
|
||||
this.boundary = new ShapeBoundary(ShapeColor.BLACK, ShapeColor.BLACK, ShapeShadingType.OUTLINE, this.shapeType,
|
||||
startX, startY, endX, endY);
|
||||
}
|
||||
|
||||
public void setGroupBoundary(int x, int y, int width, int height) {
|
||||
this.groupBoundary = new ShapeBoundary(ShapeColor.BLACK, ShapeColor.BLACK, ShapeShadingType.OUTLINE,
|
||||
ShapeType.RECTANGLE, x, y, x + width, y + height);
|
||||
}
|
||||
|
||||
public void unsetGroupBoundary() { this.groupBoundary = null; }
|
||||
|
||||
public void updateGroupBoundary(int dx, int dy) {
|
||||
int[] groupBoundaryParams = this.getGroupBoundaryParams();
|
||||
|
||||
this.groupBoundary = new ShapeBoundary(ShapeColor.BLACK, ShapeColor.BLACK, ShapeShadingType.OUTLINE,
|
||||
ShapeType.RECTANGLE, groupBoundaryParams[0] + dx, groupBoundaryParams[1] + dy,
|
||||
groupBoundaryParams[0] + groupBoundaryParams[2] + dx,
|
||||
groupBoundaryParams[1] + groupBoundaryParams[3] + dy);
|
||||
}
|
||||
|
||||
public int[] getGroupBoundaryParams() {
|
||||
int[] groupBoundaryParams = new int[4];
|
||||
groupBoundaryParams[0] = this.groupBoundary.getStartX();
|
||||
groupBoundaryParams[1] = this.groupBoundary.getStartY();
|
||||
groupBoundaryParams[2] = this.groupBoundary.getEndX() - this.groupBoundary.getStartX();
|
||||
groupBoundaryParams[3] = this.groupBoundary.getEndY() - this.groupBoundary.getStartY();
|
||||
return groupBoundaryParams;
|
||||
}
|
||||
|
||||
public AbstractShape getBoundary() { return this.boundary; }
|
||||
public AbstractShape getGroupBoundary() { return this.groupBoundary; }
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isInGroup() { return this.groupBoundary != null; }
|
||||
|
||||
|
||||
public boolean isSelected() { return this.isSelected; }
|
||||
|
||||
public void setIsSelected(boolean isSelected) { this.isSelected = isSelected; }
|
||||
|
||||
@Override
|
||||
public int getStartX() { return startX; }
|
||||
|
||||
@Override
|
||||
public int getStartY() { return startY; }
|
||||
|
||||
@Override
|
||||
public int getEndX() { return endX; }
|
||||
|
||||
@Override
|
||||
public int getEndY() { return endY; }
|
||||
|
||||
public ShapeColor getPrimaryColor() { return primaryColor; }
|
||||
|
||||
public ShapeColor getSecondaryColor() { return secondaryColor; }
|
||||
|
||||
public void move(int dx, int dy) {
|
||||
|
||||
// Comment out this block if using solution with Composite design pattern.
|
||||
if (this.isInGroup())
|
||||
this.updateGroupBoundary(dx, dy);
|
||||
|
||||
this.startX = this.startX + dx;
|
||||
this.startY = this.startY + dy;
|
||||
this.endX = this.endX + dx;
|
||||
this.endY = this.endY + dy;
|
||||
|
||||
|
||||
// Comment this if using solution with Composite design pattern.
|
||||
resetBoundary();
|
||||
|
||||
// Uncomment this if using solution with Composite design pattern.
|
||||
//this.boundary.move(dx, dy);
|
||||
}
|
||||
|
||||
public ShapeShadingType getShadingType() { return shadingType; }
|
||||
|
||||
public ShapeType getShapeType() { return shapeType; }
|
||||
|
||||
@Override
|
||||
public boolean isOverlapping(AbstractShape shape) {
|
||||
if (this.groupBoundary != null) {
|
||||
return this.groupBoundary.isOverlapping(shape);
|
||||
} else {
|
||||
return this.boundary.isOverlapping(shape);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
public String toString() {
|
||||
String shapeToStr = "";
|
||||
shapeToStr += "startX: " + this.startX + "\n";
|
||||
shapeToStr += "startY: " + this.startY + "\n";
|
||||
shapeToStr += "endX: " + this.endX + "\n";
|
||||
shapeToStr += "endY: " + this.endY + "\n";
|
||||
shapeToStr += "shadingType: " + this.shadingType + "\n";
|
||||
shapeToStr += "shadeType: " + this.shapeType + "\n";
|
||||
return shapeToStr;
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean isComposite() { return this.IS_COMPOSITE; }
|
||||
|
||||
@Override
|
||||
public List<AbstractShape> getComponents() {
|
||||
List<AbstractShape> components = new ArrayList<>();
|
||||
if (!this.isInGroup())
|
||||
components.add(this.boundary);
|
||||
return components;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package model;
|
||||
|
||||
import view.interfaces.IDrawShape;
|
||||
import java.util.List;
|
||||
|
||||
public class ShapeBoundary extends AbstractShape {
|
||||
|
||||
private ShapeColor primaryColor;
|
||||
private ShapeColor secondaryColor;
|
||||
private ShapeShadingType shadingType;
|
||||
private ShapeType shapeType;
|
||||
private int startX;
|
||||
private int startY;
|
||||
private int endX;
|
||||
private int endY;
|
||||
private boolean isInGroup;
|
||||
private final boolean IS_BOUNDARY = true;
|
||||
private final boolean IS_COMPOSITE = true;
|
||||
|
||||
public ShapeBoundary(ShapeColor primaryColor, ShapeColor secondaryColor, ShapeShadingType shadingType,
|
||||
ShapeType shapeType, int startX, int startY, int endX, int endY) {
|
||||
this.primaryColor = primaryColor;
|
||||
this.secondaryColor = secondaryColor;
|
||||
this.shadingType = shadingType;
|
||||
this.shapeType = shapeType;
|
||||
this.startX = startX;
|
||||
this.startY = startY;
|
||||
this.endX = endX;
|
||||
this.endY = endY;
|
||||
this.isInGroup = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInGroup() { return this.isInGroup; }
|
||||
|
||||
@Override
|
||||
public void setIsInGroup(boolean groupFlag) { this.isInGroup = groupFlag; }
|
||||
|
||||
@Override
|
||||
public boolean isBoundary() { return IS_BOUNDARY; }
|
||||
|
||||
@Override
|
||||
public boolean isComposite() { return this.IS_COMPOSITE; }
|
||||
|
||||
@Override
|
||||
public boolean isOverlapping(AbstractShape shape) {
|
||||
if ((startX > shape.getEndX()) || (endX < shape.getStartX()) ||
|
||||
startY > shape.getEndY() || endY < shape.getStartY())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartX() { return startX; }
|
||||
|
||||
@Override
|
||||
public int getStartY() { return startY; }
|
||||
|
||||
@Override
|
||||
public int getEndX() { return endX; }
|
||||
|
||||
@Override
|
||||
public int getEndY() { return endY; }
|
||||
|
||||
@Override
|
||||
public boolean isSelected() { return false; }
|
||||
|
||||
@Override
|
||||
public void setIsSelected(boolean isSelected) { }
|
||||
|
||||
@Override
|
||||
public void move(int dx, int dy) {
|
||||
this.startX = this.startX + dx;
|
||||
this.startY = this.startY + dy;
|
||||
this.endX = this.endX + dx;
|
||||
this.endY = this.endY + dy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType getShapeType() { return this.shapeType; }
|
||||
|
||||
@Override
|
||||
public ShapeColor getPrimaryColor() { return this.primaryColor; }
|
||||
|
||||
@Override
|
||||
public ShapeColor getSecondaryColor() { return this.secondaryColor; }
|
||||
|
||||
@Override
|
||||
public ShapeShadingType getShadingType() { return this.shadingType; }
|
||||
|
||||
@Override
|
||||
public List<AbstractShape> getComponents() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package model;
|
||||
|
||||
public enum ShapeColor {
|
||||
BLACK,
|
||||
BLUE,
|
||||
CYAN,
|
||||
DARK_GRAY,
|
||||
GRAY,
|
||||
GREEN,
|
||||
LIGHT_GRAY,
|
||||
MAGENTA,
|
||||
ORANGE,
|
||||
PINK,
|
||||
RED,
|
||||
WHITE,
|
||||
YELLOW
|
||||
}
|
@ -0,0 +1,408 @@
|
||||
package model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
|
||||
import model.interfaces.IShapeListObserver;
|
||||
|
||||
/**
|
||||
* ShapeList
|
||||
*
|
||||
* Responsibility: Maintains a list of shapes. It's basically a wrapper class for the List data structure.
|
||||
* ShapeList is a Singleton since we only need 1 instance.
|
||||
*
|
||||
* Note to myself: Might change the class name to ShapeManager instead.
|
||||
*
|
||||
* @author Minh Bui
|
||||
*/
|
||||
|
||||
public class ShapeList {
|
||||
|
||||
private static ShapeList uniqueInstance;
|
||||
|
||||
private final List<AbstractShape> SHAPE_LIST;
|
||||
private final List<IShapeListObserver> OBSERVER_LIST;
|
||||
|
||||
private ShapeList() {
|
||||
this.SHAPE_LIST = new ArrayList<>();
|
||||
this.OBSERVER_LIST = new ArrayList<>();
|
||||
}
|
||||
|
||||
public static ShapeList getInstance() {
|
||||
if (uniqueInstance == null)
|
||||
uniqueInstance = new ShapeList();
|
||||
return uniqueInstance;
|
||||
}
|
||||
|
||||
public void registerObserver(IShapeListObserver observer) {
|
||||
this.OBSERVER_LIST.add(observer);
|
||||
}
|
||||
|
||||
public void removeObserver(IShapeListObserver observer) {
|
||||
Iterator<IShapeListObserver> obsIter = OBSERVER_LIST.iterator();
|
||||
while (obsIter.hasNext()) {
|
||||
IShapeListObserver curObs = obsIter.next();
|
||||
if (curObs.equals(observer)) {
|
||||
obsIter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addShape(AbstractShape newShape) {
|
||||
this.SHAPE_LIST.add(newShape);
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
public void addAllShapes(List<AbstractShape> shapes) {
|
||||
this.SHAPE_LIST.addAll(shapes);
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
public List<AbstractShape> cloneAndAddAllShapes(List<AbstractShape> shapes) {
|
||||
List<AbstractShape> clones = new ArrayList<>();
|
||||
Iterator<AbstractShape> iter = shapes.iterator();
|
||||
while (iter.hasNext()) {
|
||||
AbstractShape cur = iter.next();
|
||||
if (cur instanceof Shape) {
|
||||
Shape copyCur = new Shape(cur.getPrimaryColor(), cur.getSecondaryColor(),
|
||||
cur.getShadingType(), cur.getShapeType(), cur.getStartX(), cur.getStartY(),
|
||||
cur.getEndX(), cur.getEndY());
|
||||
clones.add(copyCur);
|
||||
} else if (cur instanceof GroupShapes) {
|
||||
System.out.println("hello");
|
||||
GroupShapes copyCur = new GroupShapes(cur.getPrimaryColor(), cur.getSecondaryColor(),
|
||||
cur.getShadingType(), cur.getShapeType(), cur.getStartX(), cur.getStartY(),
|
||||
cur.getEndX(), cur.getEndY(), cloneAndAddComponents(((GroupShapes) cur).getShapes()));
|
||||
clones.add(copyCur);
|
||||
}
|
||||
}
|
||||
this.SHAPE_LIST.addAll(clones);
|
||||
this.notifyObservers();
|
||||
return clones;
|
||||
}
|
||||
|
||||
private List<AbstractShape> cloneAndAddComponents(List<AbstractShape> shapes) {
|
||||
List<AbstractShape> componentsClone = new ArrayList<>();
|
||||
|
||||
Iterator<AbstractShape> i = shapes.iterator();
|
||||
while (i.hasNext()) {
|
||||
AbstractShape cur = i.next();
|
||||
if (i instanceof GroupShapes) {
|
||||
GroupShapes copyCur = new GroupShapes(cur.getPrimaryColor(), cur.getSecondaryColor(),
|
||||
cur.getShadingType(), cur.getShapeType(), cur.getStartX(), cur.getStartY(),
|
||||
cur.getEndX(), cur.getEndY(), cloneAndAddComponents(((GroupShapes) cur).getShapes()));
|
||||
componentsClone.add(copyCur);
|
||||
} else {
|
||||
Shape copyCur = new Shape(cur.getPrimaryColor(), cur.getSecondaryColor(),
|
||||
cur.getShadingType(), cur.getShapeType(), cur.getStartX(), cur.getStartY(),
|
||||
cur.getEndX(), cur.getEndY());
|
||||
componentsClone.add(copyCur);
|
||||
}
|
||||
}
|
||||
|
||||
return componentsClone;
|
||||
}
|
||||
|
||||
public void removeShape(AbstractShape shape) {
|
||||
Iterator<AbstractShape> shapeIter = this.SHAPE_LIST.iterator();
|
||||
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
if (curShape.equals(shape))
|
||||
shapeIter.remove();
|
||||
}
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
public List<AbstractShape> removeSelectedShape() {
|
||||
Iterator<AbstractShape> shapeIter = this.SHAPE_LIST.iterator();
|
||||
List<AbstractShape> removedShapes = new ArrayList<>();
|
||||
while(shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
if (curShape.isSelected()) {
|
||||
shapeIter.remove();
|
||||
removedShapes.add(curShape);
|
||||
}
|
||||
}
|
||||
this.notifyObservers();
|
||||
return removedShapes;
|
||||
}
|
||||
|
||||
public void printShapeList() {
|
||||
Iterator<AbstractShape> shapeIter = this.SHAPE_LIST.iterator();
|
||||
|
||||
System.out.println("ShapeList currently has: ");
|
||||
while(shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
System.out.println(curShape + " " + curShape.isBoundary());
|
||||
}
|
||||
|
||||
System.out.println("--------------- END OF SHAPE LIST -----------------");
|
||||
System.out.println("----------- BEGIN PRINTING ALL COMPONENTS -------------");
|
||||
|
||||
shapeIter = this.SHAPE_LIST.iterator();
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
System.out.println(curShape + " " + curShape.isBoundary());
|
||||
|
||||
List<AbstractShape> curComponents = curShape.getComponents();
|
||||
if (curComponents != null) {
|
||||
this.printComponents(curComponents);
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private void printComponents(List<AbstractShape> components) {
|
||||
if (components != null) {
|
||||
System.out.print('\t');
|
||||
|
||||
Iterator<AbstractShape> i = components.iterator();
|
||||
while(i.hasNext()) {
|
||||
AbstractShape aShape = i.next();
|
||||
System.out.println(aShape + " " + aShape.isBoundary());
|
||||
printComponents(aShape.getComponents());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void markSelect(int x, int y, int width, int height) {
|
||||
AbstractShape boundary = new ShapeBoundary(ShapeColor.BLACK, ShapeColor.BLACK, ShapeShadingType.OUTLINE,
|
||||
ShapeType.RECTANGLE, x, y, x + width, y + height);
|
||||
Iterator<AbstractShape> shapeIter = this.SHAPE_LIST.iterator();
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
if (curShape.isOverlapping(boundary)) {
|
||||
curShape.setIsSelected(true);
|
||||
} else {
|
||||
curShape.setIsSelected(false);
|
||||
}
|
||||
}
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
public List<AbstractShape> moveShapes(int dX, int dY, List<AbstractShape> shapesToMove) {
|
||||
List<AbstractShape> movedShapes = new ArrayList<>();
|
||||
Iterator<AbstractShape> shapeIter = this.SHAPE_LIST.iterator();
|
||||
|
||||
if (shapesToMove == null) {
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
if (curShape.isSelected()) {
|
||||
curShape.move(dX, dY);
|
||||
movedShapes.add(curShape);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
if (shapesToMove.contains(curShape)) {
|
||||
curShape.move(dX, dY);
|
||||
movedShapes.add(curShape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.notifyObservers();
|
||||
return movedShapes;
|
||||
}
|
||||
|
||||
public void deselectAllShapes() {
|
||||
Iterator<AbstractShape> shapeIter = this.SHAPE_LIST.iterator();
|
||||
while(shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
curShape.setIsSelected(false);
|
||||
}
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
public void removeShapesList(List<AbstractShape> shapes) {
|
||||
this.SHAPE_LIST.removeAll(shapes);
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
public List<AbstractShape> getSelectedShapes() {
|
||||
List<AbstractShape> selectedShapes = new ArrayList<>();
|
||||
Iterator<AbstractShape> shapeIter = this.SHAPE_LIST.iterator();
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
if (curShape.isSelected())
|
||||
selectedShapes.add(curShape);
|
||||
}
|
||||
return selectedShapes;
|
||||
}
|
||||
|
||||
|
||||
public List<AbstractShape> groupShapes2(List<AbstractShape> shapes) {
|
||||
Iterator<AbstractShape> shapeIter = shapes.iterator();
|
||||
|
||||
int xMax = Integer.MIN_VALUE;
|
||||
int yMax = Integer.MIN_VALUE;
|
||||
int xMin = Integer.MAX_VALUE;
|
||||
int yMin = Integer.MAX_VALUE;
|
||||
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
xMax = max(curShape.getStartX(), curShape.getEndX(), xMax);
|
||||
yMax = max(curShape.getStartY(), curShape.getEndY(), yMax);
|
||||
xMin = min(curShape.getStartX(), curShape.getEndX(), xMin);
|
||||
yMin = min(curShape.getStartY(), curShape.getEndY(), yMin);
|
||||
curShape.setIsInGroup(true);
|
||||
}
|
||||
|
||||
GroupShapes newGroup = new GroupShapes(ShapeColor.BLACK, ShapeColor.BLACK, ShapeShadingType.OUTLINE,
|
||||
ShapeType.RECTANGLE, xMin, yMin, xMax, yMax, shapes);
|
||||
|
||||
this.removeShapesList(shapes);
|
||||
this.addShape(newGroup);
|
||||
this.notifyObservers();
|
||||
|
||||
List<AbstractShape> grouped = new ArrayList<>();
|
||||
grouped.add(newGroup);
|
||||
//this.printShapeList();
|
||||
return grouped;
|
||||
}
|
||||
|
||||
public List<AbstractShape> ungroupShapes2(List<AbstractShape> shapes) {
|
||||
Iterator<AbstractShape> shapeIter = shapes.iterator();
|
||||
|
||||
// List of Shapes to be added back to the ShapeList and returned by this method.
|
||||
List<AbstractShape> ungroup = new ArrayList<>();
|
||||
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
curShape.setIsInGroup(false);
|
||||
if (curShape instanceof GroupShapes) {
|
||||
// Removes the current GroupShapes object and re-add the shapes that are held
|
||||
// by the removed GroupShapes object.
|
||||
|
||||
this.removeShape(curShape);
|
||||
GroupShapes group = (GroupShapes) curShape;
|
||||
|
||||
List<AbstractShape> ushapes = group.getShapes();
|
||||
Iterator<AbstractShape> shapeIter2 = ushapes.iterator();
|
||||
while(shapeIter2.hasNext()) {
|
||||
AbstractShape aShape = shapeIter2.next();
|
||||
aShape.setIsInGroup(false);
|
||||
}
|
||||
|
||||
this.addAllShapes(ushapes);
|
||||
ungroup.addAll(ushapes);
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
//this.printShapeList();
|
||||
this.notifyObservers();
|
||||
return ungroup;
|
||||
}
|
||||
|
||||
public List<AbstractShape> groupSelectedShapes() {
|
||||
List<AbstractShape> selectedShape = this.getSelectedShapes();
|
||||
|
||||
if (selectedShape.isEmpty()) return selectedShape;
|
||||
|
||||
Iterator<AbstractShape> shapeIter = selectedShape.iterator();
|
||||
|
||||
int xMax = Integer.MIN_VALUE;
|
||||
int yMax = Integer.MIN_VALUE;
|
||||
int xMin = Integer.MAX_VALUE;
|
||||
int yMin = Integer.MAX_VALUE;
|
||||
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
if (curShape.isSelected()) {
|
||||
xMax = max(curShape.getStartX(), curShape.getEndX(), xMax);
|
||||
yMax = max(curShape.getStartY(), curShape.getEndY(), yMax);
|
||||
xMin = min(curShape.getStartX(), curShape.getEndX(), xMin);
|
||||
yMin = min(curShape.getStartY(), curShape.getEndY(), yMin);
|
||||
}
|
||||
}
|
||||
|
||||
shapeIter = selectedShape.iterator();
|
||||
while (shapeIter.hasNext()) {
|
||||
Shape curShape = (Shape) shapeIter.next();
|
||||
curShape.setGroupBoundary(xMin, yMin, xMax - xMin, yMax - yMin);
|
||||
}
|
||||
this.notifyObservers();
|
||||
return selectedShape;
|
||||
}
|
||||
|
||||
public List<AbstractShape> groupShapes(List<AbstractShape> shapes) {
|
||||
Iterator<AbstractShape> shapeIter = shapes.iterator();
|
||||
|
||||
int xMax = Integer.MIN_VALUE;
|
||||
int yMax = Integer.MIN_VALUE;
|
||||
int xMin = Integer.MAX_VALUE;
|
||||
int yMin = Integer.MAX_VALUE;
|
||||
|
||||
while (shapeIter.hasNext()) {
|
||||
AbstractShape curShape = shapeIter.next();
|
||||
xMax = max(curShape.getStartX(), curShape.getEndX(), xMax);
|
||||
yMax = max(curShape.getStartY(), curShape.getEndY(), yMax);
|
||||
xMin = min(curShape.getStartX(), curShape.getEndX(), xMin);
|
||||
yMin = min(curShape.getStartY(), curShape.getEndY(), yMin);
|
||||
}
|
||||
|
||||
shapeIter = shapes.iterator();
|
||||
while (shapeIter.hasNext()) {
|
||||
Shape curShape = (Shape) shapeIter.next();
|
||||
curShape.setGroupBoundary(xMin, yMin, xMax - xMin, yMax - yMin);
|
||||
}
|
||||
this.notifyObservers();
|
||||
return shapes;
|
||||
}
|
||||
|
||||
public List<AbstractShape> ungroupSelectedShapes() {
|
||||
Iterator<AbstractShape> shapeIter = this.SHAPE_LIST.iterator();
|
||||
List<AbstractShape> ungrouped = new ArrayList<>();
|
||||
while (shapeIter.hasNext()) {
|
||||
Shape curShape = (Shape) shapeIter.next();
|
||||
if (curShape.isSelected() && curShape.isInGroup()) {
|
||||
curShape.unsetGroupBoundary();
|
||||
ungrouped.add(curShape);
|
||||
}
|
||||
}
|
||||
this.notifyObservers();
|
||||
return ungrouped;
|
||||
}
|
||||
|
||||
public List<AbstractShape> ungroupShapes(List<AbstractShape> shapes) {
|
||||
Iterator<AbstractShape> shapeIter = shapes.iterator();
|
||||
|
||||
while (shapeIter.hasNext()) {
|
||||
|
||||
//AbstractShape curShape = shapeIter.next();
|
||||
Shape curShape = (Shape) shapeIter.next();
|
||||
if (curShape.isInGroup()) {
|
||||
curShape.unsetGroupBoundary();
|
||||
}
|
||||
}
|
||||
this.notifyObservers();
|
||||
return shapes;
|
||||
}
|
||||
|
||||
public int getNumberOfSelectedShapes() {
|
||||
int count = 0;
|
||||
Iterator<AbstractShape> shapeIter = this.SHAPE_LIST.iterator();
|
||||
while (shapeIter.hasNext()) {
|
||||
if (shapeIter.next().isSelected())
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void notifyObservers() {
|
||||
Iterator<IShapeListObserver> obsIter = OBSERVER_LIST.iterator();
|
||||
while (obsIter.hasNext()) {
|
||||
IShapeListObserver curObs = obsIter.next();
|
||||
curObs.update(SHAPE_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
private int max(int a, int b, int c) { return Math.max(a, Math.max(b, c)); }
|
||||
|
||||
private int min(int a, int b, int c) { return Math.min(a, Math.min(b, c)); }
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package model;
|
||||
|
||||
public enum ShapeShadingType {
|
||||
FILLED_IN,
|
||||
OUTLINE,
|
||||
OUTLINE_AND_FILLED_IN
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package model;
|
||||
|
||||
public enum ShapeType {
|
||||
ELLIPSE,
|
||||
RECTANGLE,
|
||||
TRIANGLE
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package model.dialogs;
|
||||
|
||||
import model.ShapeColor;
|
||||
import model.interfaces.IApplicationState;
|
||||
import view.interfaces.IDialogChoice;
|
||||
|
||||
public class ChoosePrimaryColorDialog implements IDialogChoice<ShapeColor> {
|
||||
|
||||
private final IApplicationState APP_STATE;
|
||||
|
||||
public ChoosePrimaryColorDialog(IApplicationState applicationState) {
|
||||
this.APP_STATE = applicationState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogTitle() {
|
||||
return "Primary Color";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogText() {
|
||||
return "Select a primary color from the menu below:";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeColor[] getDialogOptions() {
|
||||
return ShapeColor.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeColor getCurrentSelection() {
|
||||
return APP_STATE.getActivePrimaryColor();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package model.dialogs;
|
||||
|
||||
import model.ShapeColor;
|
||||
import model.interfaces.IApplicationState;
|
||||
import view.interfaces.IDialogChoice;
|
||||
|
||||
public class ChooseSecondaryColorDialog implements IDialogChoice<ShapeColor> {
|
||||
|
||||
private final IApplicationState APP_STATE;
|
||||
|
||||
public ChooseSecondaryColorDialog(IApplicationState applicationState) {
|
||||
this.APP_STATE = applicationState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogTitle() {
|
||||
return "Secondary Color";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogText() {
|
||||
return "Select a secondary color from the menu below:";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeColor[] getDialogOptions() {
|
||||
return ShapeColor.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeColor getCurrentSelection() {
|
||||
return APP_STATE.getActiveSecondaryColor();
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package model.dialogs;
|
||||
|
||||
import model.ShapeShadingType;
|
||||
import model.interfaces.IApplicationState;
|
||||
import view.interfaces.IDialogChoice;
|
||||
|
||||
public class ChooseShadingTypeDialog implements IDialogChoice<ShapeShadingType> {
|
||||
private final IApplicationState APP_STATE;
|
||||
|
||||
public ChooseShadingTypeDialog(IApplicationState applicationState) {
|
||||
this.APP_STATE = applicationState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogTitle() {
|
||||
return "Shading Type";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogText() {
|
||||
return "Select a shading type from the menu below:";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeShadingType[] getDialogOptions() {
|
||||
return ShapeShadingType.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeShadingType getCurrentSelection() {
|
||||
return APP_STATE.getActiveShapeShadingType();
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package model.dialogs;
|
||||
|
||||
import model.ShapeType;
|
||||
import model.interfaces.IApplicationState;
|
||||
import view.interfaces.IDialogChoice;
|
||||
|
||||
public class ChooseShapeDialog implements IDialogChoice<ShapeType> {
|
||||
private final IApplicationState APP_STATE;
|
||||
|
||||
public ChooseShapeDialog(IApplicationState applicationState) {
|
||||
this.APP_STATE = applicationState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogTitle() {
|
||||
return "Shape";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogText() {
|
||||
return "Select a shape from the menu below:";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType[] getDialogOptions() {
|
||||
return ShapeType.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType getCurrentSelection() {
|
||||
return APP_STATE.getActiveShapeType();
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package model.dialogs;
|
||||
|
||||
import model.MouseMode;
|
||||
import model.interfaces.IApplicationState;
|
||||
import view.interfaces.IDialogChoice;
|
||||
|
||||
public class ChooseStartAndEndPointModeDialog implements IDialogChoice<MouseMode> {
|
||||
private final IApplicationState APP_STATE;
|
||||
|
||||
public ChooseStartAndEndPointModeDialog(IApplicationState applicationState) {
|
||||
this.APP_STATE = applicationState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogTitle() {
|
||||
return "Start and End Point Mode";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDialogText() {
|
||||
return "Select a shading type from the menu below:";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MouseMode[] getDialogOptions() {
|
||||
return MouseMode.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MouseMode getCurrentSelection() {
|
||||
return APP_STATE.getActiveMouseMode();
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package model.dialogs;
|
||||
|
||||
import model.ShapeColor;
|
||||
import model.ShapeShadingType;
|
||||
import model.ShapeType;
|
||||
import model.MouseMode;
|
||||
import model.interfaces.IApplicationState;
|
||||
import model.interfaces.IDialogProvider;
|
||||
import view.interfaces.IDialogChoice;
|
||||
|
||||
public class DialogProvider implements IDialogProvider {
|
||||
private final IDialogChoice<ShapeType> CHOOSE_SHAPE_DIALOG;
|
||||
private final IDialogChoice<ShapeColor> CHOOSE_PRIMARY_COLOR_DIALOG;
|
||||
private final IDialogChoice<ShapeColor> CHOOSE_SECONDARY_DIALOG;
|
||||
private final IDialogChoice<ShapeShadingType> CHOOSE_SHADING_TYPE_DIALOG;
|
||||
private final IDialogChoice<MouseMode> CHOOSE_START_END_PT_DIALOG;
|
||||
private final IApplicationState APP_STATE;
|
||||
|
||||
public DialogProvider(IApplicationState applicationState) {
|
||||
this.APP_STATE = applicationState;
|
||||
CHOOSE_SHAPE_DIALOG = new ChooseShapeDialog(this.APP_STATE);
|
||||
CHOOSE_PRIMARY_COLOR_DIALOG = new ChoosePrimaryColorDialog(this.APP_STATE);
|
||||
CHOOSE_SECONDARY_DIALOG = new ChooseSecondaryColorDialog(this.APP_STATE);
|
||||
CHOOSE_SHADING_TYPE_DIALOG = new ChooseShadingTypeDialog(this.APP_STATE);
|
||||
CHOOSE_START_END_PT_DIALOG = new ChooseStartAndEndPointModeDialog(this.APP_STATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDialogChoice<ShapeType> getChooseShapeDialog() {
|
||||
return CHOOSE_SHAPE_DIALOG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDialogChoice<ShapeColor> getChoosePrimaryColorDialog() {
|
||||
return CHOOSE_PRIMARY_COLOR_DIALOG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDialogChoice<ShapeColor> getChooseSecondaryColorDialog() {
|
||||
return CHOOSE_SECONDARY_DIALOG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDialogChoice<ShapeShadingType> getChooseShadingTypeDialog() {
|
||||
return CHOOSE_SHADING_TYPE_DIALOG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDialogChoice<MouseMode> getChooseStartAndEndPointModeDialog() {
|
||||
return CHOOSE_START_END_PT_DIALOG;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package model.interfaces;
|
||||
|
||||
import model.ShapeColor;
|
||||
import model.ShapeShadingType;
|
||||
import model.ShapeType;
|
||||
import model.MouseMode;
|
||||
|
||||
public interface IApplicationState {
|
||||
void setActiveShape();
|
||||
|
||||
void setActivePrimaryColor();
|
||||
|
||||
void setActiveSecondaryColor();
|
||||
|
||||
void setActiveShadingType();
|
||||
|
||||
void setActiveStartAndEndPointMode();
|
||||
|
||||
ShapeType getActiveShapeType();
|
||||
|
||||
ShapeColor getActivePrimaryColor();
|
||||
|
||||
ShapeColor getActiveSecondaryColor();
|
||||
|
||||
ShapeShadingType getActiveShapeShadingType();
|
||||
|
||||
MouseMode getActiveMouseMode();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package model.interfaces;
|
||||
|
||||
import model.ShapeColor;
|
||||
import model.ShapeShadingType;
|
||||
import model.ShapeType;
|
||||
import model.MouseMode;
|
||||
import view.interfaces.IDialogChoice;
|
||||
|
||||
public interface IDialogProvider {
|
||||
|
||||
IDialogChoice<ShapeType> getChooseShapeDialog();
|
||||
|
||||
IDialogChoice<ShapeColor> getChoosePrimaryColorDialog();
|
||||
|
||||
IDialogChoice<ShapeColor> getChooseSecondaryColorDialog();
|
||||
|
||||
IDialogChoice<ShapeShadingType> getChooseShadingTypeDialog();
|
||||
|
||||
IDialogChoice<MouseMode> getChooseStartAndEndPointModeDialog();
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package model.interfaces;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import model.Shape;
|
||||
import model.AbstractShape;
|
||||
|
||||
public interface IShapeListObserver {
|
||||
void update(List<AbstractShape> shapeList);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package model.interfaces;
|
||||
|
||||
/**
|
||||
* IUndoable interface
|
||||
*
|
||||
* Responsibility: An interface that's part of the command pattern.
|
||||
*
|
||||
*/
|
||||
|
||||
public interface IUndoable {
|
||||
void undo();
|
||||
void redo();
|
||||
void printShapeList();
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package model.persistence;
|
||||
|
||||
import model.ShapeColor;
|
||||
import model.ShapeShadingType;
|
||||
import model.ShapeType;
|
||||
import model.MouseMode;
|
||||
import model.dialogs.DialogProvider;
|
||||
import model.interfaces.IApplicationState;
|
||||
import model.interfaces.IDialogProvider;
|
||||
import view.interfaces.IUiModule;
|
||||
|
||||
public class ApplicationState implements IApplicationState {
|
||||
private final IUiModule uiModule;
|
||||
private final IDialogProvider dialogProvider;
|
||||
|
||||
private ShapeType activeShapeType;
|
||||
private ShapeColor activePrimaryColor;
|
||||
private ShapeColor activeSecondaryColor;
|
||||
private ShapeShadingType activeShapeShadingType;
|
||||
private MouseMode activeMouseMode;
|
||||
|
||||
public ApplicationState(IUiModule uiModule) {
|
||||
this.uiModule = uiModule;
|
||||
this.dialogProvider = new DialogProvider(this);
|
||||
setDefaults();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveShape() {
|
||||
activeShapeType = uiModule.getDialogResponse(dialogProvider.getChooseShapeDialog());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActivePrimaryColor() {
|
||||
activePrimaryColor = uiModule.getDialogResponse(dialogProvider.getChoosePrimaryColorDialog());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveSecondaryColor() {
|
||||
activeSecondaryColor = uiModule.getDialogResponse(dialogProvider.getChooseSecondaryColorDialog());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveShadingType() {
|
||||
activeShapeShadingType = uiModule.getDialogResponse(dialogProvider.getChooseShadingTypeDialog());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveStartAndEndPointMode() {
|
||||
activeMouseMode = uiModule.getDialogResponse(dialogProvider.getChooseStartAndEndPointModeDialog());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType getActiveShapeType() {
|
||||
return activeShapeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeColor getActivePrimaryColor() {
|
||||
return activePrimaryColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeColor getActiveSecondaryColor() {
|
||||
return activeSecondaryColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeShadingType getActiveShapeShadingType() {
|
||||
return activeShapeShadingType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MouseMode getActiveMouseMode() {
|
||||
return activeMouseMode;
|
||||
}
|
||||
|
||||
|
||||
private void setDefaults() {
|
||||
activeShapeType = ShapeType.RECTANGLE;
|
||||
activePrimaryColor = ShapeColor.BLUE;
|
||||
activeSecondaryColor = ShapeColor.GREEN;
|
||||
activeShapeShadingType = ShapeShadingType.FILLED_IN;
|
||||
activeMouseMode = MouseMode.DRAW;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package view;
|
||||
|
||||
public enum EventName {
|
||||
CHOOSE_SHAPE{
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CHOOSE SHAPE";
|
||||
}
|
||||
},
|
||||
CHOOSE_PRIMARY_COLOR {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CHOOSE PRIMARY COLOR";
|
||||
}
|
||||
},
|
||||
CHOOSE_SECONDARY_COLOR {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CHOOSE SECONDARY COLOR";
|
||||
}
|
||||
},
|
||||
CHOOSE_SHADING_TYPE {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CHOOSE SHADING TYPE";
|
||||
}
|
||||
},
|
||||
CHOOSE_MOUSE_MODE {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CHOOSE MOUSE MODE";
|
||||
}
|
||||
},
|
||||
UNDO,
|
||||
REDO,
|
||||
COPY,
|
||||
PASTE,
|
||||
DELETE,
|
||||
GROUP,
|
||||
UNGROUP
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package view.gui;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Stroke;
|
||||
import java.util.EnumMap;
|
||||
|
||||
import model.AbstractShape;
|
||||
import model.ShapeColor;
|
||||
import model.ShapeShadingType;
|
||||
import view.interfaces.IDrawShape;
|
||||
import view.interfaces.PaintCanvasBase;
|
||||
|
||||
/**
|
||||
* DrawEllipse class
|
||||
*
|
||||
* Responsibility: Contains strategy for drawing an ellipse.
|
||||
*
|
||||
* @author Minh Bui
|
||||
*/
|
||||
|
||||
public class DrawEllipse implements IDrawShape {
|
||||
|
||||
@Override
|
||||
public void draw(AbstractShape shape, PaintCanvasBase canvas, EnumMap<ShapeColor, Color> colorMap,
|
||||
Stroke selectStroke, boolean offset) {
|
||||
Graphics2D graphics2d = canvas.getGraphics2D();
|
||||
|
||||
graphics2d.setColor(colorMap.get(shape.getPrimaryColor()));
|
||||
|
||||
Stroke customStroke = new BasicStroke(5f);
|
||||
|
||||
int width = Math.abs(shape.getEndX() - shape.getStartX());
|
||||
int height = Math.abs(shape.getEndY() - shape.getStartY());
|
||||
|
||||
int x = Math.min(shape.getStartX(), shape.getEndX());
|
||||
int y = Math.min(shape.getStartY(), shape.getEndY());
|
||||
|
||||
if (offset) {
|
||||
x = x - SELECT_STROKE_OFFSET;
|
||||
y = y - SELECT_STROKE_OFFSET;
|
||||
width = width + SELECT_WIDTH_HEIGHT_OFFSET;
|
||||
height = height + SELECT_WIDTH_HEIGHT_OFFSET;
|
||||
graphics2d.setStroke(selectStroke);
|
||||
} else {
|
||||
graphics2d.setStroke(customStroke);
|
||||
}
|
||||
|
||||
if (shape.getShadingType() == ShapeShadingType.FILLED_IN) {
|
||||
graphics2d.fillOval(x, y, width, height);
|
||||
} else if (shape.getShadingType() == ShapeShadingType.OUTLINE) {
|
||||
graphics2d.drawOval(x, y, width, height);
|
||||
} else {
|
||||
graphics2d.fillOval(x, y, width, height);
|
||||
graphics2d.setColor(colorMap.get(shape.getSecondaryColor()));
|
||||
graphics2d.drawOval(x, y, width, height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package view.gui;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Stroke;
|
||||
import java.util.EnumMap;
|
||||
|
||||
import model.AbstractShape;
|
||||
import model.ShapeColor;
|
||||
import model.ShapeShadingType;
|
||||
import view.interfaces.IDrawShape;
|
||||
import view.interfaces.PaintCanvasBase;
|
||||
|
||||
/**
|
||||
* DrawRectangle class
|
||||
*
|
||||
* Responsibility: Contains strategy for drawing rectangle.
|
||||
*
|
||||
* @author Minh Bui
|
||||
*/
|
||||
|
||||
public class DrawRectangle implements IDrawShape {
|
||||
|
||||
@Override
|
||||
public void draw(AbstractShape shape, PaintCanvasBase canvas, EnumMap<ShapeColor, Color> colorMap,
|
||||
Stroke selectStroke, boolean offset) {
|
||||
Graphics2D graphics2d = canvas.getGraphics2D();
|
||||
|
||||
int width = Math.abs(shape.getEndX() - shape.getStartX());
|
||||
int height = Math.abs(shape.getEndY() - shape.getStartY());
|
||||
|
||||
int x = Math.min(shape.getStartX(), shape.getEndX());
|
||||
int y = Math.min(shape.getStartY(), shape.getEndY());
|
||||
|
||||
Stroke customStroke = new BasicStroke(5f);
|
||||
|
||||
if (offset) {
|
||||
x = x - SELECT_STROKE_OFFSET;
|
||||
y = y - SELECT_STROKE_OFFSET;
|
||||
width = width + SELECT_WIDTH_HEIGHT_OFFSET;
|
||||
height = height + SELECT_WIDTH_HEIGHT_OFFSET;
|
||||
graphics2d.setStroke(selectStroke);
|
||||
} else {
|
||||
graphics2d.setStroke(customStroke);
|
||||
}
|
||||
|
||||
graphics2d.setColor(colorMap.get(shape.getPrimaryColor()));
|
||||
|
||||
if (shape.getShadingType() == ShapeShadingType.FILLED_IN) {
|
||||
graphics2d.fillRect(x, y, width, height);
|
||||
} else if (shape.getShadingType() == ShapeShadingType.OUTLINE) {
|
||||
graphics2d.drawRect(x, y, width, height);
|
||||
} else {
|
||||
graphics2d.fillRect(x, y, width, height);
|
||||
graphics2d.setColor(colorMap.get(shape.getSecondaryColor()));
|
||||
graphics2d.drawRect(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package view.gui;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Polygon;
|
||||
import java.awt.Stroke;
|
||||
import java.util.EnumMap;
|
||||
|
||||
import model.AbstractShape;
|
||||
import model.Shape;
|
||||
import model.ShapeColor;
|
||||
import model.ShapeShadingType;
|
||||
import view.interfaces.IDrawShape;
|
||||
import view.interfaces.PaintCanvasBase;
|
||||
|
||||
/**
|
||||
* DrawTriangle class
|
||||
*
|
||||
* Responsibility: Contains strategy for drawing a triangle.
|
||||
**
|
||||
* @author Minh Bui
|
||||
*/
|
||||
|
||||
public class DrawTriangle implements IDrawShape {
|
||||
|
||||
@Override
|
||||
public void draw(AbstractShape shape, PaintCanvasBase canvas, EnumMap<ShapeColor, Color> colorMap,
|
||||
Stroke selectStroke, boolean offset) {
|
||||
Graphics2D graphics2d = canvas.getGraphics2D();
|
||||
graphics2d.setColor(colorMap.get(shape.getPrimaryColor()));
|
||||
Stroke customStroke = new BasicStroke(5f);
|
||||
|
||||
//int width = Math.abs(shape.getEndX() - shape.getStartX());
|
||||
int height = Math.abs(shape.getEndY() - shape.getStartY());
|
||||
|
||||
int x = Math.min(shape.getStartX(), shape.getEndX());
|
||||
int y = Math.min(shape.getStartY(), shape.getEndY());
|
||||
|
||||
int xMax = Math.max(shape.getStartX(), shape.getEndX());
|
||||
int yMax = Math.max(shape.getStartY(), shape.getEndY());
|
||||
|
||||
int[] xPoints = new int[3];
|
||||
int[] yPoints = new int[3];
|
||||
|
||||
if (!offset) {
|
||||
xPoints[0] = x; xPoints[1] = xMax; xPoints[2] = x;
|
||||
yPoints[0] = y; yPoints[1] = yMax; yPoints[2] = y + height;
|
||||
graphics2d.setStroke(customStroke);
|
||||
} else {
|
||||
xPoints[0] = x - SELECT_STROKE_OFFSET;
|
||||
xPoints[1] = xMax + SELECT_STROKE_OFFSET * 2;
|
||||
xPoints[2] = x - SELECT_STROKE_OFFSET;
|
||||
|
||||
yPoints[0] = y - SELECT_STROKE_OFFSET * 2;
|
||||
yPoints[1] = yMax + SELECT_STROKE_OFFSET;
|
||||
yPoints[2] = y - SELECT_STROKE_OFFSET + height + SELECT_WIDTH_HEIGHT_OFFSET;
|
||||
graphics2d.setStroke(selectStroke);
|
||||
}
|
||||
|
||||
Polygon triangle = new Polygon(xPoints, yPoints, 3);
|
||||
if (shape.getShadingType() == ShapeShadingType.FILLED_IN) {
|
||||
graphics2d.fillPolygon(triangle);
|
||||
} else if (shape.getShadingType() == ShapeShadingType.OUTLINE) {
|
||||
graphics2d.drawPolygon(triangle);
|
||||
} else {
|
||||
graphics2d.fillPolygon(triangle);
|
||||
graphics2d.setColor(colorMap.get(shape.getSecondaryColor()));
|
||||
graphics2d.drawPolygon(triangle);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package view.gui;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Stroke;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import model.*;
|
||||
import model.interfaces.IShapeListObserver;
|
||||
import view.interfaces.IDrawShape;
|
||||
import view.interfaces.PaintCanvasBase;
|
||||
|
||||
/**
|
||||
* Drawer class
|
||||
*
|
||||
* Responsibility: Responsible for selecting specific algorithm to draw shapes and their
|
||||
* corresponding boundaries from a ShapeList object.
|
||||
*
|
||||
* @author Minh Bui
|
||||
*/
|
||||
|
||||
public class Drawer implements IShapeListObserver {
|
||||
|
||||
private static Drawer uniqueInstance;
|
||||
|
||||
private PaintCanvasBase canvas;
|
||||
private final EnumMap<ShapeColor, Color> colorMap;
|
||||
private final EnumMap<ShapeType, IDrawShape> strategies;
|
||||
private IDrawShape strategy;
|
||||
|
||||
private final Stroke selectStroke;
|
||||
|
||||
private Drawer(PaintCanvasBase canvas) {
|
||||
this.canvas = canvas;
|
||||
|
||||
colorMap = new EnumMap<>(ShapeColor.class);
|
||||
colorMap.put(ShapeColor.BLACK, Color.BLACK);
|
||||
colorMap.put(ShapeColor.BLUE, Color.BLUE);
|
||||
colorMap.put(ShapeColor.CYAN, Color.CYAN);
|
||||
colorMap.put(ShapeColor.DARK_GRAY, Color.DARK_GRAY);
|
||||
colorMap.put(ShapeColor.GRAY, Color.GRAY);
|
||||
colorMap.put(ShapeColor.GREEN, Color.GREEN);
|
||||
colorMap.put(ShapeColor.LIGHT_GRAY, Color.LIGHT_GRAY);
|
||||
colorMap.put(ShapeColor.MAGENTA, Color.MAGENTA);
|
||||
colorMap.put(ShapeColor.ORANGE, Color.ORANGE);
|
||||
colorMap.put(ShapeColor.PINK, Color.PINK);
|
||||
colorMap.put(ShapeColor.RED, Color.RED);
|
||||
colorMap.put(ShapeColor.WHITE, Color.WHITE);
|
||||
colorMap.put(ShapeColor.YELLOW, Color.YELLOW);
|
||||
|
||||
strategies = new EnumMap<>(ShapeType.class);
|
||||
|
||||
strategies.put(ShapeType.RECTANGLE, new DrawRectangle());
|
||||
strategies.put(ShapeType.ELLIPSE, new DrawEllipse());
|
||||
strategies.put(ShapeType.TRIANGLE, new DrawTriangle());
|
||||
|
||||
strategy = null;
|
||||
|
||||
selectStroke = new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
|
||||
1, new float[]{9}, 0);
|
||||
}
|
||||
|
||||
public static Drawer getInstance(PaintCanvasBase canvas) {
|
||||
if (uniqueInstance == null)
|
||||
uniqueInstance = new Drawer(canvas);
|
||||
else
|
||||
uniqueInstance.canvas = canvas;
|
||||
return uniqueInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(List<AbstractShape> shapeList) {
|
||||
// Clear the canvas for redraw
|
||||
Graphics2D graphics2d = canvas.getGraphics2D();
|
||||
graphics2d.setColor(canvas.getBackground());
|
||||
graphics2d.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
|
||||
Iterator<AbstractShape> shapeListIter = shapeList.iterator();
|
||||
while (shapeListIter.hasNext()) {
|
||||
AbstractShape curShape = shapeListIter.next();
|
||||
//this.draw2(curShape, curShape.isSelected());
|
||||
this.draw(curShape);
|
||||
}
|
||||
}
|
||||
|
||||
public void draw2(AbstractShape aShape, boolean isSelected) {
|
||||
if (aShape != null) {
|
||||
// Draw the abstract shape first.
|
||||
strategy = strategies.get(aShape.getShapeType());
|
||||
if (strategy != null) {
|
||||
if (!aShape.isBoundary() && !aShape.isComposite())
|
||||
strategy.draw(aShape, canvas, colorMap, selectStroke, false);
|
||||
else
|
||||
if (isSelected && !aShape.isInGroup())
|
||||
strategy.draw(aShape, canvas, colorMap, selectStroke, true);
|
||||
}
|
||||
|
||||
// Draw its children.
|
||||
List<AbstractShape> aShapeComponents = aShape.getComponents();
|
||||
if (aShapeComponents == null) return;
|
||||
Iterator<AbstractShape> componentsIter = aShapeComponents.iterator();
|
||||
|
||||
while(componentsIter.hasNext()) {
|
||||
AbstractShape component = componentsIter.next();
|
||||
draw2(component, isSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(AbstractShape aShape) {
|
||||
strategy = strategies.get(aShape.getShapeType());
|
||||
if (strategy != null) {
|
||||
strategy.draw(aShape, canvas, colorMap, selectStroke, false);
|
||||
|
||||
if (aShape instanceof Shape) {
|
||||
Shape shape = (Shape) aShape;
|
||||
if (shape.isInGroup() && shape.isSelected()) {
|
||||
strategy = strategies.get(ShapeType.RECTANGLE);
|
||||
strategy.draw(shape.getGroupBoundary(), canvas, colorMap, selectStroke, true);
|
||||
} else if (shape.isSelected()) {
|
||||
strategy.draw(shape.getBoundary(), canvas, colorMap, selectStroke, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package view.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import view.EventName;
|
||||
import view.interfaces.IDialogChoice;
|
||||
import view.interfaces.IEventCallback;
|
||||
import view.interfaces.IGuiWindow;
|
||||
import view.interfaces.IUiModule;
|
||||
|
||||
public class Gui implements IUiModule {
|
||||
|
||||
private final IGuiWindow GUI;
|
||||
|
||||
public Gui(IGuiWindow gui) {
|
||||
this.GUI = gui;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEvent(EventName eventName, IEventCallback callback) {
|
||||
JButton button = GUI.getButton(eventName);
|
||||
button.addActionListener((ActionEvent) -> callback.run());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getDialogResponse(IDialogChoice dialogSettings) {
|
||||
Object selectedValue = JOptionPane.showInputDialog(null,
|
||||
dialogSettings.getDialogText(),
|
||||
dialogSettings.getDialogTitle(),
|
||||
JOptionPane.PLAIN_MESSAGE,
|
||||
null,
|
||||
dialogSettings.getDialogOptions(),
|
||||
dialogSettings.getCurrentSelection());
|
||||
return selectedValue == null
|
||||
? (T)dialogSettings.getCurrentSelection()
|
||||
: (T)selectedValue;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package view.gui;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.*;
|
||||
|
||||
import view.interfaces.IGuiWindow;
|
||||
import view.EventName;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public class GuiWindow extends JFrame implements IGuiWindow {
|
||||
private final int DEFAULT_WIDTH = 1250;
|
||||
private final int DEFAULT_HEIGHT = 800;
|
||||
private final String DEFAULT_TITLE = "JPaint";
|
||||
private final Insets defaultButtonDimensions
|
||||
= new Insets(5, 8, 5, 8);
|
||||
private final Map<EventName, JButton> eventButtons = new HashMap<>();
|
||||
|
||||
public GuiWindow(JComponent canvas){
|
||||
setVisible(true);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setTitle(DEFAULT_TITLE);
|
||||
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||
setBackground(new Color(155, 155, 155));
|
||||
setExtendedState(JFrame.MAXIMIZED_BOTH);
|
||||
JPanel window = createWindow();
|
||||
window.add(canvas, BorderLayout.CENTER);
|
||||
validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JButton getButton(EventName eventName) {
|
||||
if(!eventButtons.containsKey(eventName))
|
||||
throw new NoSuchElementException("No button exists for action " + eventName.toString());
|
||||
|
||||
return eventButtons.get(eventName);
|
||||
}
|
||||
|
||||
private JPanel createWindow() {
|
||||
JPanel contentPane = createBackgroundPanel();
|
||||
JPanel buttonPanel = createMenu();
|
||||
contentPane.add(buttonPanel, BorderLayout.NORTH);
|
||||
return contentPane;
|
||||
}
|
||||
|
||||
private JPanel createMenu() {
|
||||
JPanel buttonPanel = createButtonPanel();
|
||||
|
||||
for(EventName eventName : EventName.values()){
|
||||
addButtonToPanel(eventName, buttonPanel);
|
||||
}
|
||||
|
||||
return buttonPanel;
|
||||
}
|
||||
|
||||
private void addButtonToPanel(EventName eventName, JPanel panel) {
|
||||
JButton newButton = createButton(eventName);
|
||||
eventButtons.put(eventName, newButton);
|
||||
panel.add(newButton);
|
||||
}
|
||||
|
||||
private JButton createButton(EventName eventName) {
|
||||
JButton newButton = new JButton(eventName.toString());
|
||||
newButton.setForeground(Color.BLACK);
|
||||
newButton.setBackground(Color.WHITE);
|
||||
newButton.setBorder(createButtonBorder());
|
||||
return newButton;
|
||||
}
|
||||
|
||||
private Border createButtonBorder() {
|
||||
Border line = new LineBorder(Color.BLACK);
|
||||
Border margin = new EmptyBorder(defaultButtonDimensions);
|
||||
return new CompoundBorder(line, margin);
|
||||
}
|
||||
|
||||
private JPanel createButtonPanel() {
|
||||
JPanel panel = new JPanel();
|
||||
FlowLayout flowLayout = (FlowLayout) panel.getLayout();
|
||||
flowLayout.setAlignment(FlowLayout.LEFT);
|
||||
panel.setBackground(Color.lightGray);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createBackgroundPanel() {
|
||||
JPanel contentPane = new JPanel();
|
||||
contentPane.setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
contentPane.setLayout(new BorderLayout(0, 0));
|
||||
contentPane.setBackground(Color.WHITE);
|
||||
setContentPane(contentPane);
|
||||
return contentPane;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package view.gui;
|
||||
|
||||
import view.interfaces.PaintCanvasBase;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import java.awt.*;
|
||||
|
||||
public class PaintCanvas extends PaintCanvasBase {
|
||||
|
||||
public Graphics2D getGraphics2D() {
|
||||
return (Graphics2D)getGraphics();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package view.interfaces;
|
||||
|
||||
public interface IDialogChoice<T> {
|
||||
String getDialogTitle();
|
||||
|
||||
String getDialogText();
|
||||
|
||||
T[] getDialogOptions();
|
||||
|
||||
T getCurrentSelection();
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package view.interfaces;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Stroke;
|
||||
import java.util.EnumMap;
|
||||
|
||||
import model.AbstractShape;
|
||||
import model.ShapeColor;
|
||||
|
||||
/**
|
||||
* IDrawShape interface
|
||||
*
|
||||
* Responsibility: Interface for the strategy pattern for drawing shapes.
|
||||
*
|
||||
* Note that if offset is true, an algorithm for drawing boundary is used instead.
|
||||
*
|
||||
* @author Minh Bui
|
||||
*
|
||||
*/
|
||||
|
||||
public interface IDrawShape {
|
||||
int SELECT_STROKE_OFFSET = 5;
|
||||
int SELECT_WIDTH_HEIGHT_OFFSET = 10;
|
||||
|
||||
void draw(AbstractShape shape, PaintCanvasBase canvas, EnumMap<ShapeColor, Color> colorMap, Stroke selectStroke,
|
||||
boolean offset);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package view.interfaces;
|
||||
|
||||
public interface IEventCallback {
|
||||
void run();
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package view.interfaces;
|
||||
|
||||
import view.EventName;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public interface IGuiWindow {
|
||||
JButton getButton(EventName eventName);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package view.interfaces;
|
||||
|
||||
import view.EventName;
|
||||
|
||||
public interface IUiModule {
|
||||
void addEvent(EventName eventName, IEventCallback command);
|
||||
<T> T getDialogResponse(IDialogChoice dialogChoice);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package view.interfaces;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public abstract class PaintCanvasBase extends JComponent {
|
||||
public abstract Graphics2D getGraphics2D();
|
||||
}
|
Loading…
Reference in new issue