javaswinggraphicsjpaneljcomponent

Drawing a custom Line component over another Line component, what am I doing wrong?


I am working on a project that involves the need for a custom Line component (so that additional functionality can be included later - i.e., Lines can be removed and moved about), that draws a new Line on a JPanel (i.e., LinePanel) wherever the user presses the mouse (start point) and drags to (end point). The idea is that the user can click and drag to any point on the panel to create a line (I have included a border in the code to display the bounded rectangle - for feedback). The below code works fine when clicking directly on the LinePanel, however if the user clicks over another Line component the start position is altered - this is the part that is driving me crazy (I suspect that the problem is occurring inside the mousePressed() logic. I'm relatively new to Graphics with Java and would really love some feedback. I'm sure that there is a better way to do this.

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class LineFun {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(new LinePanel());
        frame.pack();
        frame.setVisible(true);
    }
}

class LinePanel extends JPanel {
    Point point1 = null, point2 = null;
    Line line = null;

    public LinePanel() {
        LineListener listener = new LineListener();
        addMouseListener(listener);
        addMouseMotionListener(listener);

        setLayout(null);

        setBackground(Color.white);
        setPreferredSize(new Dimension(400, 400));
    }

    public void paintComponent(Graphics page) {
        super.paintComponent(page);
        page.setColor(Color.BLACK);
    }

    private class LineListener extends MouseAdapter {

        /***
         * It feels like this is where the problem is occuring - it works fine when I don't click on another Line
         * component.
         * @param e - event.
         */
        @Override
        public void mousePressed(MouseEvent e) {
            super.mousePressed(e);
            point1 = e.getPoint();
            if ((e.getSource() instanceof Line)) {
                Line lineComponent = (Line)e.getSource();
                Point componentPoint = lineComponent.point2;

                Point newStart = new Point(point1.x + componentPoint.x, point1.y + componentPoint.y);

                line = new Line(newStart, newStart);
                line.addMouseListener(new LineListener());
                line.addMouseMotionListener(new LineListener());
                add(line);
            }
            else {
                line = new Line(point1, point1);
                line.addMouseListener(new LineListener());
                line.addMouseMotionListener(new LineListener());
                add(line);
            }
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            super.mouseDragged(e);
            point2 = e.getPoint();
            line.setPoint2(point2);

                if ((point2.x - point1.x) >= 0 && (point2.y - point1.y) >= 0) {
                    line.setBounds(point1.x, point1.y, point2.x - point1.x, point2.y - point1.y);
                } else if ((point2.x - point1.x) < 0 && (point2.y - point1.y) < 0) {
                    line.setBounds(point2.x, point2.y, point1.x - point2.x, point1.y - point2.y);
                } else if ((point2.x - point1.x) < 0) {
                    line.setBounds(point2.x, point1.y, point1.x - point2.x, point2.y - point1.y);
                } else {
                    line.setBounds(point1.x, point2.y, point2.x - point1.x, point1.y - point2.y);
                }
                repaint();
        }

    }
}

/***
 * Line class - I've created a border so that I can see the Bounded Rectangle.
 */
    class Line extends JComponent {
        Point point1 = null, point2 = null;

        public Line(Point point, Point point2) {
            point1 = point;
            this.point2 = point2;
            setBorder(BorderFactory.createEtchedBorder());
        }

        public void setPoint2(Point point) {
            point2 = point;
        }

    /***
     * The below logic is so that the line originates from the origin (where the mouse press occurs).
     * I feel like there must be a better way to do this.
     * @param page
     */
    public void paintComponent(Graphics page) {
            super.paintComponent(page);
            page.setColor(Color.black);

        if (point2.x >= point1.x && point2.y >= point1.y) {
            page.drawLine(0,0, point2.x - point1.x,point2.y - point1.y);
        } else if ((point2.x - point1.x) < 0 && (point2.y - point1.y) < 0){
            page.drawLine(0,0, point1.x - point2.x, point1.y - point2.y);
        } else if (point2.x < point1.x){
            page.drawLine(0, point2.y - point1.y, point1.x - point2.x, 0);
        } else {
            page.drawLine(point2.x - point1.x, 0, 0, point1.y - point2.y);
        }


        }
    }





Solution

  • It feels like this is where the problem is occuring - it works fine when I don't click on another Line component.

    Correct. The mouse point is generated relative to the component you click on, since you are adding the mouse listener to all your components.

    So in the case where the component is an instance of Line, I would guess that you need to convert the mouse points to be relative to your LinePanel which would be the parent of the Line component.

    You can use the SwingUtiltities.convertPoint(...) method to do the conversion.

    And you can use the Container.getParent() method to get the reference to your LinePanel