Results 1 to 12 of 12
Like Tree2Likes
  • 1 Post By DarrylBurke
  • 1 Post By Digital Larry

Thread: Trouble with Mouse Handlers

  1. #1
    Digital Larry is offline Member
    Join Date
    Mar 2013
    Posts
    57
    Rep Power
    0

    Default Trouble with Mouse Handlers

    I'm making a CAD program which allows you to drop blocks onto a panel. Each block has a number of connection points, or "Pins".

    I've written some mouse listeners to pay attention to MousePressed, MouseReleased, MouseClicked, and MouseMoved.

    So for example, I click the mouse.

    I scan through the list of blocks which are part of the drawing and compare the mouse coordinates to the coordinates of the drag handle for each block. If it matches (+/- 3 pixels) then I set dragMode = 1. Now my Mouse Listener for "MouseMoved" drags the block around the screen. If I do a "MouseClicked" again, then that handler sees that dragMode was 1, so it drops the block by setting dragMode to 0.

    This all works fine.

    However, when I am connecting pins, I start with a MouseClick event where the coordinates of the mouse are found to be within +/- 3 pixels of one of the pins of one of the blocks. Now I set dragMode = 2, and the MouseMoved listener rubber-bands a line from the connection point to the current mouse position.

    My problem is that when I click at a destination Pin, if I use MouseRelease, and if dragMode == 2, then I make that connection in the data structures and set dragMode to 0 again. However, immediately the MouseClicked listener triggers again and thinks I am starting a connection on that pin, when I'm not. I would expect MouseReleased to occur after MouseClicked, but it doesn't.

    Any advice? Am I using the mouse events incorrectly? Is there a way to clear a pending MouseClicked when I process MousReleased?

  2. #2
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: Trouble with Mouse Handlers

    It sounds like you've got a bug in your code and that it is likely difficult if not impossible to help without our seeing that code. Consider creating and posting an SSCCE in this forum.

  3. #3
    DarrylBurke's Avatar
    DarrylBurke is offline Forum Police
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,423
    Rep Power
    20

    Default Re: Trouble with Mouse Handlers

    Quote Originally Posted by Digital Larry View Post
    I would expect MouseReleased to occur after MouseClicked, but it doesn't.
    I don't find any support for that assumption in the API. On the contrary, the method description for mouseClicked(...) is (emphasis added)
    Invoked when the mouse button has been clicked (pressed and released) on a component.
    That would seem to imply that mouseClicked(...) is always processed after mouseReleased(...).

    Quote Originally Posted by Digital Larry View Post
    Is there a way to clear a pending MouseClicked when I process MousReleased?
    No, but you conditionally can set a flag in mouseReleased(...) and selectively process mouseClicked(...) to either commence a connection or reset the flag. Since you're already using a numeric flag, you could add a state dragMode = 3 for that. From the design perspective, it would be better to create an enum of possible states, with names that self-document the state.

    If you still need more help you will need to go through the page linked by Fubarable and post that SSCCE.

    db
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  4. #4
    Digital Larry is offline Member
    Join Date
    Mar 2013
    Posts
    57
    Rep Power
    0

    Default Re: Trouble with Mouse Handlers

    Thanks, I'm working on my SSCCE, requires major surgery to extract the core! (sweat, pant)

  5. #5
    DarrylBurke's Avatar
    DarrylBurke is offline Forum Police
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,423
    Rep Power
    20

    Default Re: Trouble with Mouse Handlers

    In fact, on review, I would say you don't need to use mouseReleased(...) and don't need an additional state. Not unless, like SketchUp, you allow press-drag-release as an alternative to click-move-click.

    And since I have a ton of stuff to do (packing up everything to move house in a week) and needed an excuse to not do it, here's my SSCCE that demonstrates that.
    Java Code:
    import java.awt.*;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.Line2D;
    import java.util.ArrayList;
    import java.util.Random;
    import javax.swing.*;
    
    public class PointConnector extends JPanel {
    
      public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {
    
          @Override
          public void run() {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new PointConnector());
            frame.pack();
            frame.setResizable(false);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
          }
        });
      }
    
      private enum State {
    
        INITIAL,
        DRAGGING_BLOCK, // Placeholder, not used in this SSCCE
        DRAWING_LINE
      };
      private State state = State.INITIAL;
      private ArrayList<Point> points = new ArrayList<Point>();
      private ArrayList<Line2D.Double> lines = new ArrayList<Line2D.Double>();
      private Point startPoint;
      private Point endPoint;
      private Point mouseAt;
      private static final int COUNT = 10;
      private static final int SIZE = 400;
      private static int RANGE = 5;
      private Random random = new Random();
    
      public PointConnector() {
        setBackground(Color.DARK_GRAY);
        populatePoints();
        PointListener pointListener = new PointListener();
        addMouseListener(pointListener);
        addMouseMotionListener(pointListener);
      }
    
      private void populatePoints() {
        for (int i = 0; i < COUNT; i++) {
          points.add(new Point(random.nextInt(SIZE), random.nextInt(SIZE)));
        }
      }
    
      @Override
      protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g.setColor(Color.WHITE);
        for (Point point : points) {
          drawPoint(g, point);
        }
        for (Line2D.Double line : lines) {
          g2.draw(line);
        }
    
        g.setColor(Color.RED);
        drawPoint(g, startPoint);
        g.setColor(Color.GREEN);
        drawPoint(g, endPoint);
    
        if (state == State.DRAWING_LINE) {
          g.setColor(Color.CYAN);
          g.drawLine(startPoint.x, startPoint.y, mouseAt.x, mouseAt.y);
        }
      }
    
      private void drawPoint(Graphics g, Point p) {
        if (p != null) {
          g.drawOval(p.x - 1, p.y - 1, 2, 2);
          g.drawOval(p.x - 2, p.y - 2, 4, 4);
        }
      }
    
      @Override
      public Dimension getPreferredSize() {
        return new Dimension(SIZE, SIZE);
      }
    
      private class PointListener extends MouseAdapter {
    
        @Override
        public void mouseMoved(MouseEvent e) {
          mouseAt = e.getPoint();
          switch (state) {
            case INITIAL:
              startPoint = getNearbyPoint();
              break;
            case DRAWING_LINE:
              endPoint = getNearbyPoint();
              break;
          }
          repaint();
        }
    
        @Override
        public void mouseClicked(MouseEvent e) {
          switch (state) {
            case INITIAL:
              if (startPoint != null) {
                state = State.DRAWING_LINE;
              }
              break;
            case DRAWING_LINE:
              state = State.INITIAL;
              if (endPoint != null) {
                lines.add(new Line2D.Double(startPoint, endPoint));
              } else {
                startPoint = null;
              }
              endPoint = null;
              break;
          }
          repaint();
        }
    
        private Point getNearbyPoint() {
          for (Point point : points) {
            if (Math.abs(point.x - mouseAt.x) <= RANGE
                    && Math.abs(point.y - mouseAt.y) <= RANGE) {
              mouseAt = point;
              return point;
            }
          }
          return null;
        }
      }
    }
    Oh, and you can't usually create a SSCCE by stripping down a working program; you need to start afresh and add only what's relevant.

    edit: Note that this code doesn't prevent creating two or more connections between the same pair of points, but you probably already have logic in place for that. Or if you don't, then you need to.
    Last edited by DarrylBurke; 03-23-2013 at 05:39 PM.
    Fubarable likes this.
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  6. #6
    Digital Larry is offline Member
    Join Date
    Mar 2013
    Posts
    57
    Rep Power
    0

    Default Re: Trouble with Mouse Handlers

    Thanks Darryl! I will look at your stuff more closely.

    [Edit - does exactly what I want - and I like the "snap when nearby" feature! Let me print this out and absorb it over some coffee.]

    Well I did manage to strip down my program and get it to manifest the problem - which even more fun for all of you, is not consistent!

    If you watch the console, sometimes you see it report "Stop Pin!" when you click on the second connection, and much more regularly, it shows "Stop Pin!" followed immediately by "Connect Pin!".

    Java Code:
    package com.holycityaudio.CADTest;
    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.border.EmptyBorder;
    
    
    
    public class CADTest extends JFrame {
    
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private JPanel contentPane;
    	/**
    	 * Launch the application.
    	 */
    	public static void main(String[] args) {
    		EventQueue.invokeLater(new Runnable() {
    			public void run() {
    				try {
    					CADTest dspFrame = new CADTest();
    					dspFrame.setVisible(true);
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    			}
    		});
    	}
    
    	/**
    	 * Create the frame.
    	 */
    	public CADTest() {
    		setTitle("Connection Bug Demo");
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		setBounds(100, 100, 800, 500);
    		
    		final CADPanelTest panel = new CADPanelTest(null);
    		contentPane = new JPanel();
    		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    		contentPane.setLayout(new BorderLayout(0, 0));
    		setContentPane(contentPane);
    	
    		contentPane.add(panel, BorderLayout.CENTER);
    		
    	}
    }
    and also...

    Java Code:
    package com.holycityaudio.CADTest;
    
    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Graphics2D;
    import java.awt.geom.Ellipse2D;
    
    public class Pin {
    	private int x_pos = -1;
    	private int y_pos = -1;
    	private Color pinColor = Color.black;
    
    	Pin(int x, int y) {
    		x_pos = x;
    		y_pos = y;
    	}
    
    	public void drawPin(Graphics2D g2) {
    		int size = 8;
    		Ellipse2D boundingRect = new Ellipse2D.Double(x_pos - size/2, y_pos - size/2, size, size);
    		g2.setColor(pinColor);
    		g2.setStroke(new BasicStroke(3));
    		g2.draw(boundingRect);
    	}
    
    	public int getX() {
    		return x_pos;
    	}
    
    	public int getY() {
    		return y_pos;
    	}
    }
    Not to mention...

    Java Code:
    package com.holycityaudio.CADTest;
    
    import com.holycityaudio.CADTest.Pin;
    
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import javax.swing.JPanel;
    
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.Line2D;
    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class CADPanelTest extends JPanel {
    
    	private static final long serialVersionUID = 1L;
    	Line2D dragLine = null;
    
    	public enum dragMode { NODRAG, MOVE, CONNECT };
    
    	public dragMode dm;
    
    	// following 4 variables are for pin to pin connections
    	Pin startPin;
    	Pin stopPin;
    	Pin currentPin;
    
    	ArrayList<Pin> pinList = new ArrayList<Pin>();
    
    	public CADPanelTest (CADTest spdFrame) {
    
    		addPin(20,40);
    		addPin(300,90);
    		addPin(140,400);
    		addPin(560, 220);
    		repaint();
    
    		addMouseMotionListener(new MouseAdapter() {
    			@Override
    			public void mouseMoved(MouseEvent e) {
    				if(dm == dragMode.CONNECT) {
    					int startX = startPin.getX();
    					int startY = startPin.getY();
    					dragLine = new Line2D.Double(startX, startY, e.getX(), e.getY());
    					repaint();
    				}
    			}
    		});
    
    		addMouseListener(new MouseAdapter() {
    			@Override
    			public void mouseClicked(MouseEvent arg0) {
    
    				Iterator<Pin> itrPin = pinList.iterator();
    				Pin currentPin = null;
    				while(itrPin.hasNext()) {
    					currentPin = itrPin.next();
    					// hit a block pin, so connect it
    					if(hitPin(arg0, currentPin)) {
    						if(dm != dragMode.CONNECT) {
    							System.out.println("Connect start!");
    							dm = dragMode.CONNECT;	// now we're going to connect a wire
    							startPin = currentPin;
    						}
    						else {		// we already were dragging a wire, now place it
    							stopPin = currentPin;
    							dm = dragMode.NODRAG;
    						}
    						repaint();
    						return;
    					}
    				}
    			}
    			public void mouseReleased(MouseEvent arg0) {
    				if(dm == dragMode.CONNECT)	 {	// connect mode, released
    					Iterator<Pin> itrPin = pinList.iterator();
    					while(itrPin.hasNext()) {
    						currentPin = itrPin.next();
    						if(hitPin(arg0, currentPin) && currentPin != startPin) {
    							System.out.println("Stop Pin!");
    							stopPin = currentPin;
    							dragLine = null;	// get rid of that
    
    							dm = dragMode.NODRAG;
    							repaint();
    							return;
    						}
    					}
    				}
    			}
    		}); 
    	}
    
    	public void paintComponent( Graphics g ) {
    		super.paintComponent(g);
    		Graphics2D g2 = (Graphics2D)g;   
    		Iterator<Pin> itrPin = pinList.iterator();
    		Pin currentPin;
    		while(itrPin.hasNext()) {
    			currentPin = itrPin.next();
    			currentPin.drawPin(g2);
    		}		 
    
    		if(dragLine != null) {
    			g2.setColor(Color.BLACK);
    			g2.draw(dragLine);
    		}
    	}
    
    	private boolean hitPin(MouseEvent arg0, Pin p) {
    
    		int deltaX = Math.abs(arg0.getX() - (int) p.getX());
    		int deltaY = Math.abs(arg0.getY() - (int) p.getY());
    		if(deltaX < 3 && deltaY < 3 ) {
    			return true;
    		}
    		else {
    			return false;
    		}
    	}
    
    	public void addPin(int x, int y) {
    
    		Pin p = new Pin(x, y);
    		pinList.add(p);
    	}
    
    }
    I am really sorry if this exceeds protocol for how short it should be... at least it bears some similarity to my actual program still.

    Thanks!

    DL
    Last edited by Digital Larry; 03-23-2013 at 06:18 PM. Reason: took out unused variables and old comments

  7. #7
    Digital Larry is offline Member
    Join Date
    Mar 2013
    Posts
    57
    Rep Power
    0

    Default Re: Trouble with Mouse Handlers

    As usual, I solved my problem by exploiting Murphy's fifth corollary of public technical humiliation...

    Looks like I had a MouseReleased and MouseClicked handlers for the same thing. Got rid of the MouseReleased handler and it works much better!
    Fubarable likes this.

  8. #8
    DarrylBurke's Avatar
    DarrylBurke is offline Forum Police
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,423
    Rep Power
    20

    Default Re: Trouble with Mouse Handlers

    Quote Originally Posted by DarrylBurke View Post
    I would say you don't need to use mouseReleased(...)
    Quote Originally Posted by Digital Larry View Post
    Looks like I had a MouseReleased and MouseClicked handlers for the same thing. Got rid of the MouseReleased handler and it works much better!
    What I said. But have you ever played with SketchUp?
    Quote Originally Posted by DarrylBurke View Post
    ... unless, like SketchUp, you allow press-drag-release as an alternative to click-move-click.
    db
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  9. #9
    DarrylBurke's Avatar
    DarrylBurke is offline Forum Police
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,423
    Rep Power
    20

    Default Re: Trouble with Mouse Handlers

    I needed a break after two straight hours of packing boxes :)

    This allows both click-move-click and press-drag-release.
    Java Code:
    import java.awt.*;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.Line2D;
    import java.util.ArrayList;
    import java.util.Random;
    import javax.swing.*;
    
    public class PointConnector extends JPanel {
    
      public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {
    
          @Override
          public void run() {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new PointConnector());
            frame.pack();
            frame.setResizable(false);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
          }
        });
      }
    
      private enum State {
    
        INITIAL,
        CLICK_MOVE_BLOCK, DRAG_MOVE_BLOCK, // placeholders, not used in this SSCCE
        CLICK_DRAW_LINE, DRAG_DRAW_LINE;
      };
      private State state = State.INITIAL;
      private ArrayList<Point> points = new ArrayList<Point>();
      private ArrayList<Line2D.Double> lines = new ArrayList<Line2D.Double>();
      private Point startPoint;
      private Point endPoint;
      private Point mouseAt;
      private static final int COUNT = 10;
      private static final int SIZE = 400;
      private static int RANGE = 5;
      private Random random = new Random();
    
      public PointConnector() {
        setBackground(Color.DARK_GRAY);
        populatePoints();
        PointListener pointListener = new PointListener();
        addMouseListener(pointListener);
        addMouseMotionListener(pointListener);
      }
    
      private void populatePoints() {
        for (int i = 0; i < COUNT; i++) {
          points.add(new Point(random.nextInt(SIZE), random.nextInt(SIZE)));
        }
      }
    
      @Override
      protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g.setColor(Color.WHITE);
        for (Point point : points) {
          drawPoint(g, point);
        }
        for (Line2D.Double line : lines) {
          g2.draw(line);
        }
    
        g.setColor(Color.RED);
        drawPoint(g, startPoint);
        g.setColor(Color.GREEN);
        drawPoint(g, endPoint);
    
        if (state == State.CLICK_DRAW_LINE || state == State.DRAG_DRAW_LINE) {
          g.setColor(Color.CYAN);
          g.drawLine(startPoint.x, startPoint.y, mouseAt.x, mouseAt.y);
        }
      }
    
      private void drawPoint(Graphics g, Point p) {
        if (p != null) {
          g.drawOval(p.x - 1, p.y - 1, 2, 2);
          g.drawOval(p.x - 2, p.y - 2, 4, 4);
        }
      }
    
      @Override
      public Dimension getPreferredSize() {
        return new Dimension(SIZE, SIZE);
      }
    
      private class PointListener extends MouseAdapter {
    
        @Override
        public void mousePressed(MouseEvent e) {
          if (state == State.INITIAL && startPoint != null) {
            state = State.DRAG_DRAW_LINE;
          }
        }
    
        @Override
        public void mouseReleased(MouseEvent e) {
          switch (state) {
            case DRAG_DRAW_LINE:
              Point nearbyPoint = getNearbyPoint();
              if (nearbyPoint != null && nearbyPoint.equals(startPoint)) {
                state = State.CLICK_DRAW_LINE;
                break;
              }
            // else fall-through
            case CLICK_DRAW_LINE:
              if (endPoint != null) {
                lines.add(new Line2D.Double(startPoint, endPoint));
              }
              state = State.INITIAL;
              startPoint = null;
              endPoint = null;
              repaint();
              break;
          }
        }
    
        @Override
        public void mouseDragged(MouseEvent e) {
          mouseAt = e.getPoint();
          Point nearbyPoint = getNearbyPoint();
          if (nearbyPoint != null && !nearbyPoint.equals(startPoint)) {
            endPoint = getNearbyPoint();
          }
          repaint();
        }
    
        @Override
        public void mouseMoved(MouseEvent e) {
          mouseAt = e.getPoint();
          switch (state) {
            case INITIAL:
              startPoint = getNearbyPoint();
              break;
            case DRAG_DRAW_LINE:
              state = State.CLICK_DRAW_LINE;
            // fall-through
            case CLICK_DRAW_LINE:
              endPoint = getNearbyPoint();
              break;
          }
          repaint();
        }
    
        private Point getNearbyPoint() {
          for (Point point : points) {
            if (Math.abs(point.x - mouseAt.x) <= RANGE
                    && Math.abs(point.y - mouseAt.y) <= RANGE) {
              mouseAt = point;
              return point;
            }
          }
          return null;
        }
      }
    }
    db
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  10. #10
    DarrylBurke's Avatar
    DarrylBurke is offline Forum Police
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,423
    Rep Power
    20

    Default Re: Trouble with Mouse Handlers

    Disclaimer: the above code has not been tested as thoroughly as it should have been.

    db
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  11. #11
    Steve11235's Avatar
    Steve11235 is offline Senior Member
    Join Date
    Dec 2008
    Posts
    1,046
    Rep Power
    7

    Default Re: Trouble with Mouse Handlers

    A bit off topic, but a technique that has worked for me is to use a Timer started/stopped by mouse down/up in place of mouse moves. It looks at the mouse position every 30 millis and adjusts. Visually, the effect is the same, but the overhead is much less.
    The Java Tutorial. Read it.

  12. #12
    Digital Larry is offline Member
    Join Date
    Mar 2013
    Posts
    57
    Rep Power
    0

    Default Re: Trouble with Mouse Handlers

    Thanks for all the help. I've found the best results using MousePressed and MouseReleased events only. Trying to use MouseClicked seemed to have many side effects.

Similar Threads

  1. Fun With the Mouse
    By CuppaCoffee in forum New To Java
    Replies: 3
    Last Post: 06-13-2012, 05:47 PM
  2. multiple event handlers
    By Bimz in forum New To Java
    Replies: 12
    Last Post: 09-24-2011, 08:36 AM
  3. Replies: 9
    Last Post: 01-20-2011, 05:01 PM
  4. Mouse Listener for mouse floating over object?
    By Krooger in forum AWT / Swing
    Replies: 1
    Last Post: 11-18-2009, 05:34 AM
  5. Help with Event Handlers
    By Daniel in forum AWT / Swing
    Replies: 2
    Last Post: 07-04-2007, 07:16 AM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •