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 getTarget(MouseEvent evt, SCollection sc) { return sc.stream() .filter(s -> s.getBounds().contains(evt.getPoint())) .findFirst(); } }