-1

I'm writing a utility that has port objects and target objects. I would like it to have the following characteristics:

  • Port icons can be created on the fly and then linked to any or all existing target icons.
  • User shift-clicks on a port icon and drags to a target icon to create a connection.
  • A line representing the connection grows and snaps into place as user drags mouse from port to target.
  • Right-clicking on a connection line disconnects the port from the target.
  • Port and Target icons can be dragged around freely.
  • Port objects can be destroyed

Like this

I'd like to do this using Swing, but I'm open to other suggestions.

Here's an SO thread that gets me part of the way there in Swing

My questions are

  • how to have 'growing' lines that snap into place
  • how to make lines clickable
Community
  • 1
  • 1
TOZ
  • 49
  • 1
  • 7
  • 3
    I suggest that you work on creating your model separate from your view, and that you experiment with the view to see what works for you. It's this experimenting that makes coding fun. – Hovercraft Full Of Eels Aug 31 '14 at 16:34

1 Answers1

2

"how to make lines clickable"

You should use an abstraction. Create say a SelectableLine class where you have attributes like Line2D and isSelected. In a MouseListener get the Point from the MouseEvent. You may want to created a small rectangle region from the point just so you don't have to click the line exactly. Use that Rectangle to see if the line intersects(). If it does, setSelected on the SelectableRectangle. You can choose to do whatever you wish to with the line that isSelected. In the example below, I just change the color to show it's selected.

Here's an example (Note: there are many ways to accomplish this. This is just my "put something together in 10 minutes" version)

enter image description here

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class SelectLineDemo {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new SelectLineDemo();
            }
        });
    }

    public SelectLineDemo() {
        JFrame frame = new JFrame();
        frame.add(new SelectLinePanel());
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    class SelectableLine {
        boolean selected = false;
        Line2D line;

        public SelectableLine(Line2D line) {
            this.line = line;
        }

        public boolean intersects(Rectangle rectangle) {
            return line.intersects(rectangle);
        }

        public void setSelected(boolean selected) {
            this.selected = selected;
        }

        public boolean isSelected() {
            return selected;
        }

        public Line2D getLine() {
            return line;
        }
    }

    class SelectLinePanel extends JPanel {
        private int selectionRadius = 5;
        private final Color SELECTED_COLOR = Color.BLUE;
        private final Color UNSELECTED_COLOR = Color.BLACK;
        private final Stroke STROKE = new BasicStroke(5.0f);

        private List<SelectableLine> lines = new ArrayList<SelectableLine>();

        public SelectLinePanel() {
            initLines();

            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    Point p = e.getPoint();
                    Rectangle selectionRect = getRectangleFromPoint(p);
                    checkIfLinesAreSelected(selectionRect);
                    repaint();
                }
            });
        }

        private void checkIfLinesAreSelected(Rectangle rectangle) {
            for (SelectableLine line : lines) {
                if (line.intersects(rectangle)) {
                    line.setSelected(true);
                } else {
                    line.setSelected(false);
                }
            }
        }

        private Rectangle getRectangleFromPoint(Point p) {
            int x = p.x - selectionRadius;
            int y = p.y - selectionRadius;
            int size = selectionRadius * 2;
            Rectangle selectionRect = new Rectangle(x, y, size, size);
            return selectionRect;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setStroke(STROKE);
            for (SelectableLine line : lines) {
                if (line.isSelected()) {
                    g2d.setColor(SELECTED_COLOR);
                } else {
                    g2d.setColor(UNSELECTED_COLOR);
                }
                g2d.draw(line.getLine());
            }
        }

        private void initLines() {
            for (int i = 50; i <= 350; i += 50) {
                lines.add(new SelectableLine(new Line2D.Double(50, i, 350, i)));
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    }
}

"how to have 'growing' lines that snap into place"

This on the other hand is not such a trivial task (i.e. not something I could whip up in ten minutes). You might want to create some kind of anchor point at the end of the line. Maybe add a small Rectangle at each end point. As you drag the rectangle, the line will follow it. As far as snapping the rectangle, you'll need an algorithm to determine the nearest point of the edge of the shape you want to anchor it to. When you release the mouse, snap it in place.


Some resource you may want to look at for some coding idea.

See GraphPanel from Dr.John.B.Matthews (link obtained from this SO answer)

GraphPanel image

See answer from @MadProgrammer in Move an Oval in java

MadProgrammer1

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720