257 lines
7.9 KiB
Java
257 lines
7.9 KiB
Java
package ovh.gasser.newshapes.ui;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import ovh.gasser.newshapes.HTMLExporter;
|
|
import ovh.gasser.newshapes.Selection;
|
|
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
|
import ovh.gasser.newshapes.shapes.ResizeHandle;
|
|
import ovh.gasser.newshapes.shapes.SCollection;
|
|
import ovh.gasser.newshapes.shapes.SText;
|
|
import ovh.gasser.newshapes.shapes.Shape;
|
|
import ovh.gasser.newshapes.ui.listeners.SelectionListener;
|
|
|
|
import javax.swing.*;
|
|
import java.awt.*;
|
|
import java.awt.event.KeyAdapter;
|
|
import java.awt.event.KeyEvent;
|
|
import java.awt.event.MouseAdapter;
|
|
import java.awt.event.MouseEvent;
|
|
import java.io.FileNotFoundException;
|
|
import java.util.Optional;
|
|
|
|
public class Controller {
|
|
private final static Logger logger = LoggerFactory.getLogger(Controller.class);
|
|
private final ShapesView view;
|
|
private final SCollection model;
|
|
private final Selection selection;
|
|
|
|
private Point lastMousePos;
|
|
private boolean addingText;
|
|
private boolean resizing;
|
|
private ResizeHandle activeHandle;
|
|
private Point resizeOrigin;
|
|
private boolean resizeMode;
|
|
|
|
Controller(ShapesView view, SCollection model) {
|
|
this.view = view;
|
|
this.model = model;
|
|
this.selection = new Selection();
|
|
|
|
var adapter = new MouseAdapter() {
|
|
@Override
|
|
public void mousePressed(MouseEvent evt) {
|
|
handleMousePressed(evt);
|
|
}
|
|
|
|
@Override
|
|
public void mouseDragged(MouseEvent evt) {
|
|
handleMouseDragged(evt);
|
|
}
|
|
|
|
@Override
|
|
public void mouseReleased(MouseEvent evt) {
|
|
resizing = false;
|
|
activeHandle = null;
|
|
resizeOrigin = null;
|
|
}
|
|
};
|
|
this.view.addMouseMotionListener(adapter);
|
|
this.view.addMouseListener(adapter);
|
|
this.view.addKeyListener(new KeyAdapter() {
|
|
@Override
|
|
public void keyPressed(KeyEvent e) {
|
|
handleKeyPressed(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void handleMouseDragged(MouseEvent evt) {
|
|
if (resizeMode && resizing && activeHandle != null) {
|
|
int dx = evt.getX() - resizeOrigin.x;
|
|
int dy = evt.getY() - resizeOrigin.y;
|
|
for (Shape shape : selection) {
|
|
shape.resize(activeHandle, dx, dy);
|
|
}
|
|
resizeOrigin = evt.getPoint();
|
|
} else if (resizeMode && !selection.isEmpty()) {
|
|
lastMousePos = evt.getPoint();
|
|
ResizeHandle handle = getHandleAt(evt.getPoint());
|
|
if (handle != null) {
|
|
resizing = true;
|
|
activeHandle = handle;
|
|
resizeOrigin = evt.getPoint();
|
|
}
|
|
} else {
|
|
resizing = false;
|
|
activeHandle = null;
|
|
int dx = evt.getX() - lastMousePos.x;
|
|
int dy = evt.getY() - lastMousePos.y;
|
|
for (Shape shape : selection) {
|
|
shape.translate(dx, dy);
|
|
}
|
|
lastMousePos = evt.getPoint();
|
|
}
|
|
view.repaint();
|
|
}
|
|
|
|
private void handleMousePressed(MouseEvent evt) {
|
|
if (addingText) {
|
|
placeTextAt(evt.getPoint());
|
|
return;
|
|
}
|
|
|
|
resizing = false;
|
|
activeHandle = null;
|
|
resizeOrigin = null;
|
|
|
|
if (resizeMode && !selection.isEmpty()) {
|
|
ResizeHandle handle = getHandleAt(evt.getPoint());
|
|
if (handle != null) {
|
|
resizing = true;
|
|
activeHandle = handle;
|
|
resizeOrigin = evt.getPoint();
|
|
view.repaint();
|
|
return;
|
|
}
|
|
}
|
|
|
|
getTarget(evt, this.model)
|
|
.ifPresentOrElse(
|
|
s -> {
|
|
if (!evt.isShiftDown()) {
|
|
resetSelection();
|
|
}
|
|
lastMousePos = evt.getPoint();
|
|
selection.add(s);
|
|
logger.debug("Selecting {}", s);
|
|
},
|
|
this::resetSelection
|
|
);
|
|
view.repaint();
|
|
}
|
|
|
|
<<<<<<< HEAD
|
|
public void enterTextMode() {
|
|
addingText = true;
|
|
}
|
|
|
|
private void placeTextAt(Point point) {
|
|
String input = JOptionPane.showInputDialog(view, "Enter text:", "Add text", JOptionPane.PLAIN_MESSAGE);
|
|
addingText = false;
|
|
if (input == null) {
|
|
return;
|
|
}
|
|
|
|
model.add(SText.create(point.x, point.y, input));
|
|
resetSelection();
|
|
view.repaint();
|
|
}
|
|
|
|
public void enterTextMode() {
|
|
addingText = true;
|
|
}
|
|
|
|
private ResizeHandle getHandleAt(Point point) {
|
|
final int handleSize = 5;
|
|
for (Shape shape : selection) {
|
|
Rectangle bounds = shape.getBounds();
|
|
int cx = bounds.x + bounds.width / 2;
|
|
int cy = bounds.y + bounds.height / 2;
|
|
|
|
if (point.x < cx && point.y < cy) return ResizeHandle.NW;
|
|
if (point.x > cx && point.y < cy) return ResizeHandle.NE;
|
|
if (point.x < cx && point.y > cy) return ResizeHandle.SW;
|
|
if (point.x > cx && point.y > cy) return ResizeHandle.SE;
|
|
if (point.y < cy) return ResizeHandle.N;
|
|
if (point.y > cy) return ResizeHandle.S;
|
|
if (point.x < cx) return ResizeHandle.W;
|
|
if (point.x > cx) return ResizeHandle.E;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void handleKeyPressed(KeyEvent evt) {
|
|
switch (evt.getKeyCode()) {
|
|
case KeyEvent.VK_R -> toggleResizeMode();
|
|
case KeyEvent.VK_DELETE -> deleteSelected();
|
|
case KeyEvent.VK_C -> copySelection();
|
|
case KeyEvent.VK_A -> changeSelectionColor();
|
|
case KeyEvent.VK_H -> exportHtml();
|
|
default -> logger.warn("Pressed unhandled key: {}", evt.getKeyChar());
|
|
}
|
|
}
|
|
|
|
private void toggleResizeMode() {
|
|
if (!selection.isEmpty()) {
|
|
resizeMode = !resizeMode;
|
|
view.setResizeMode(resizeMode);
|
|
view.repaint();
|
|
}
|
|
}
|
|
|
|
private void exportHtml() {
|
|
logger.info("Exporting view to html");
|
|
try {
|
|
new HTMLExporter(this.model).export();
|
|
} catch (FileNotFoundException e) {
|
|
logger.error("Unable to export html: {}", e.getMessage());
|
|
}
|
|
}
|
|
|
|
private void changeSelectionColor(){
|
|
if (selection == null) {
|
|
logger.debug("No selection to change color of");
|
|
return;
|
|
}
|
|
|
|
for (Shape s : selection) {
|
|
if (s instanceof SCollection collection) {
|
|
collection.forEach(shape -> shape.addAttributes(new ColorAttributes(false, true, Color.BLACK, new Color((int) (Math.random() * 0x1000000)))));
|
|
} else {
|
|
s.addAttributes(new ColorAttributes(false, true, Color.BLACK, new Color((int) (Math.random() * 0x1000000))));
|
|
}
|
|
}
|
|
|
|
view.repaint();
|
|
}
|
|
|
|
private void copySelection() {
|
|
if (selection == null) {
|
|
logger.debug("No selection to copy");
|
|
return;
|
|
}
|
|
|
|
for (Shape shape : selection) {
|
|
this.model.add(shape.clone());
|
|
}
|
|
|
|
view.repaint();
|
|
}
|
|
|
|
public void deleteSelected() {
|
|
if (selection == null) return;
|
|
logger.debug("Deleting selected shape(s)");
|
|
for (Shape s : selection) {
|
|
this.model.remove(s);
|
|
}
|
|
resetSelection();
|
|
view.repaint();
|
|
}
|
|
|
|
private void resetSelection() {
|
|
logger.debug("Resetting selection");
|
|
selection.clear();
|
|
}
|
|
|
|
public void addSelectionChangeListener(SelectionListener listener) {
|
|
selection.addListener(listener);
|
|
}
|
|
|
|
private Optional<Shape> getTarget(MouseEvent evt, SCollection sc) {
|
|
return sc.stream()
|
|
.filter(s -> s.getBounds().contains(evt.getPoint()))
|
|
.findFirst();
|
|
}
|
|
}
|