package de.usd.cstchef.view; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Point; import java.awt.Rectangle; import java.awt.dnd.DragSource; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Objects; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JWindow; import javax.swing.SwingUtilities; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; /* * Based on: * https://stackoverflow.com/questions/27245283/java-drag-and-drop-to-change-the-order-of-panels */ public abstract class OperationMouseAdapter extends MouseAdapter { private static final Rectangle R1 = new Rectangle(); private static final Rectangle R2 = new Rectangle(); private static Rectangle prevRect; private final JWindow window = new JWindow(); private Container panelPreview; private JLabel windowPreviewLbl; private JLabel panelPreviewLbl; private Point startPt; private Point dragOffset; private final int gestureMotionThreshold = DragSource.getDragThreshold(); protected Container source; protected Container target; private RecipeStepPanel currentTargetPanel; private Operation draggedOperation; public OperationMouseAdapter(Container source, Container target) { super(); this.source = source; this.target = target; window.setSize(300, 35); window.setLocationRelativeTo(null); window.setBackground(new Color(0, true)); window.setVisible(false); Container windowPreview = createPreview("My Preview"); windowPreviewLbl = (JLabel) windowPreview.getComponent(0); window.add(windowPreview); panelPreview = createPreview("My Preview"); panelPreviewLbl = (JLabel) panelPreview.getComponent(0); dragOffset = new Point(window.getWidth() / 3, window.getHeight() / 2); } private Container createPreview(String title) { Box previewBox = Box.createHorizontalBox(); previewBox.setOpaque(true); previewBox.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); previewBox.setBackground(new Color(127, 237, 247, 255)); JLabel previewLbl = new JLabel(title); previewLbl.setForeground(new Color(58, 135, 173)); previewBox.add(previewLbl); return previewBox; } private void startDragging(Point pt) { OperationInfos opInfos = this.draggedOperation.getClass().getAnnotation(OperationInfos.class); if (opInfos == null) { return; } String name = opInfos.name(); windowPreviewLbl.setText(name); panelPreviewLbl.setText(name); updateWindowLocation(pt, this.source); window.setVisible(true); } private void updateWindowLocation(Point pt, Container parent) { Point p = new Point(pt.x - dragOffset.x, pt.y - dragOffset.y); SwingUtilities.convertPointToScreen(p, parent); window.setLocation(p); } private int getTargetIndex(Rectangle r, Point pt, int i, boolean previewIndexSmaller) { int ht2 = (int) (0.5 + r.height * 0.5); R1.setBounds(r.x, r.y, r.width, ht2); R2.setBounds(r.x, r.y + ht2, r.width, ht2); if (R1.contains(pt)) { prevRect = R1; return previewIndexSmaller ? i - 1 : i - 1 > 0 ? i : 0; } else if (R2.contains(pt)) { prevRect = R2; return i; } return -1; } private void addComponent(RecipeStepPanel line, Component comp, int idx) { line.removeComponent(comp); line.addComponent(comp, idx); } @Override public void mousePressed(MouseEvent e) { this.startPt = e.getPoint(); } protected abstract Operation getDraggedOperation(int x, int y); @Override public void mouseDragged(MouseEvent e) { Point pt = e.getPoint(); JComponent parent = (JComponent) e.getComponent(); // not yet dragging and motion > threshold if (this.draggedOperation == null && startPt != null) { double a = Math.pow(pt.x - startPt.x, 2); double b = Math.pow(pt.y - startPt.y, 2); if (Math.sqrt(a + b) > gestureMotionThreshold) { this.draggedOperation = this.getDraggedOperation(startPt.x, startPt.y); if (this.draggedOperation != null) { startDragging(pt); } } return; } // dragging, but no component was created if (!window.isVisible() || draggedOperation == null) { return; } pt = SwingUtilities.convertPoint(parent, e.getPoint(), this.target); updateWindowLocation(pt, this.target); Component targetLine = this.target.getComponentAt(pt); // changed the target, remove the old preview if (currentTargetPanel != null) { if (targetLine == null || !targetLine.equals(currentTargetPanel)) { this.currentTargetPanel.removeComponent(panelPreview); this.currentTargetPanel = null; } } // we have no valid target if (targetLine == null || !(targetLine instanceof RecipeStepPanel)) { return; } RecipeStepPanel targetPanel = (RecipeStepPanel) this.target.getComponentAt(pt); this.currentTargetPanel = targetPanel; JPanel operationsPanel = currentTargetPanel.getOperationsPanel(); pt = SwingUtilities.convertPoint(this.target, pt, operationsPanel); if (prevRect != null && prevRect.contains(pt)) { return; } boolean gotPreview = false; for (int i = 0; i < operationsPanel.getComponentCount(); i++) { Component comp = operationsPanel.getComponent(i); Rectangle r = comp.getBounds(); // inside our gap, do nothing if (Objects.equals(comp, panelPreview)) { if (r.contains(pt)) { return; } else { gotPreview = true; continue; } } int tgt; if (!(comp instanceof Operation)) { //this is the dummy panel int count = operationsPanel.getComponentCount(); tgt = count > 1 ? operationsPanel.getComponentCount() - 2 : 0; } else { tgt = getTargetIndex(r, pt, i, gotPreview); } if (tgt >= 0) { addComponent(currentTargetPanel, panelPreview, tgt); return; } } } @Override public void mouseReleased(MouseEvent e) { startPt = null; // no dragging if (!window.isVisible() || draggedOperation == null) { return; } int addIndex = -1; // get the index of the preview element if (currentTargetPanel != null) { JPanel operationsPanel = this.currentTargetPanel.getOperationsPanel(); for (int i = 0; i < operationsPanel.getComponentCount(); i++) { Component comp = operationsPanel.getComponent(i); if (comp.equals(this.panelPreview)) { addIndex = i; break; } } // remove preview from panel currentTargetPanel.removeComponent(this.panelPreview); } if (addIndex != -1) { currentTargetPanel.addComponent(this.draggedOperation, addIndex); } this.draggedOperation = null; prevRect = null; this.startPt = null; this.window.setVisible(false); this.currentTargetPanel = null; } }