0

The program is an animation that creates a car and/or truck icon on the screen. The way I have it now it isn't working correctly. Specifically, the program is not clicking and dragging right. If one object is not-selected, once clicked on, it will become bolder to show that it is selected. From there we want to be able to drag it and the program will redraw the image wherever the mouse goes. If the image is un-selected, when I click and drag it, it works fine. The problem I am having is if the image is already selected. If the image is already selected, when I move the mouse over to it and click on it in order to move it to a different position, instead of moving, it is deselected instead so no movement occurs. Here is the code for the mousePressed and mouseDragged events. I think that is where the problem is, but I'm not sure what is causing it.

addMouseListener(new
   MouseAdapter()
   {
      public void mousePressed(MouseEvent event)
      {
         mousePoint = event.getPoint();
         for (SceneShape s : shapes)
         {
            if (s.contains(mousePoint))
               s.setSelected(!s.isSelected());
         }
         repaint();
      }
   });

addMouseMotionListener(new
   MouseMotionAdapter()
   {
      public void mouseDragged(MouseEvent event)
      {
         Point lastMousePoint = mousePoint;
         mousePoint = event.getPoint();
         for (SceneShape s : shapes)
         {
            if (s.isSelected())
            {
               double dx
                     = mousePoint.getX() - lastMousePoint.getX();
               double dy
                     = mousePoint.getY() - lastMousePoint.getY();
               s.translate((int) dx, (int) dy);
            }
         }
         repaint();
      }
   });

Can someone help explain to me what is causing the program to deselect an already selected image when I drag it instead of moving it and how to fix this problem? Thanks.

GenericUser01
  • 337
  • 3
  • 11
  • 27
  • You'll want to create and post a functional example program, one that is much smaller than your current program but just contains enough code so that it compiles, runs, and demonstrates your problem for us, a [mcve]. – Hovercraft Full Of Eels Oct 28 '15 at 22:11
  • Also, use **one** single class that extends from MouseAdapter and one single object of this class for both your MouseListener and your MouseMotionListener. For example, as I demonstrate in [this answer](http://stackoverflow.com/a/4894516/522444). – Hovercraft Full Of Eels Oct 28 '15 at 22:12
  • 1
    Use `mouseClicked` (or `mouseReleased) to deselect something and `mousePressed` to select it. `mouseClicked` won't be triggered if the x/y position of the `mouseReleased` event is not the same as the x/y position of the `mousePressed` event (ie, it was dragged) – MadProgrammer Oct 28 '15 at 22:20
  • @MadProgrammer: I don't see that he's using `mouseClicked`. – Hovercraft Full Of Eels Oct 28 '15 at 22:25
  • @HovercraftFullOfEels That's kind of the point ;) `mouseClicked` won't be called if a drag operation occurs, SO, the OP "could" select the component that was clicked using `mousePressed`, allow the drag operation to occur and the component won't be unselected at the end OR the start of the next drag operation. It's a neat little side effect ;) – MadProgrammer Oct 28 '15 at 22:28
  • I thought you were chiding them for using mouseClicked, when he's obviously not. – Hovercraft Full Of Eels Oct 28 '15 at 22:51
  • @MadProgrammer - I am just confused on having a mouseClicked and mousePressed. Wouldn't they just do the exact same thing? In other words, when I click on an icon, how do I know whether mouseClicked or mousePressed is being used? – GenericUser01 Oct 28 '15 at 23:10
  • @GenericUser01 No, `mouseClicked` WON'T be called if a drag operation occurs. This means that you can use `mousePressed` to select the object and `mouseClicked` to deselect, but only when a no drag operation occurs. – MadProgrammer Oct 28 '15 at 23:13
  • @HovercraftFullOfEels It's early morning, who knows what I'm talking about :P – MadProgrammer Oct 28 '15 at 23:15
  • @MadProgrammer Okay that makes sense. I am just having a hard time thinking how to implement that given the code I have above. So the way I have it now, `mousePressed` does both selecting and deselecting. Should I just change `mousePressed` to `mouseClicked` and then add another MouseListener and add a `mousePressed` to that? – GenericUser01 Oct 28 '15 at 23:21
  • @MadProgrammer: My bad, I retract it and sit in the corner, facing the corner. – Hovercraft Full Of Eels Oct 28 '15 at 23:43
  • @HovercraftFullOfEels Don't worry, I'll be there to join you shortly :P – MadProgrammer Oct 28 '15 at 23:44

1 Answers1

1

One of the side effects of a drag operation is the fact that mouseClicked won't be called. Why is this important? Basically you can use this fact to make a decision about whether a object should be deselected or not within the mouseClicked event instead of something like mousePressed or mouseReleased.

It does require you to maintain some information about the current and previous states, so you know whether the object was just selected or was previously selected, but the basic idea works well.

Drag me

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class WhatADrag {

    public static void main(String[] args) {
        new WhatADrag();
    }

    public WhatADrag() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private List<Rectangle> boxes;
        private Rectangle selected;

        public TestPane() {
            boxes = new ArrayList<>(25);
            int x = 0;
            int y = 0;
            for (int index = 0; index < 10; index++) {
                boxes.add(new Rectangle(x, y, 100, 100));
                x += 25;
                y += 25;
            }

            MouseAdapter ma = new MouseAdapter() {

                private Rectangle previous;
                private Point delta;

                @Override
                public void mousePressed(MouseEvent e) {
                    List<Rectangle> reversed = new ArrayList<>(boxes);
                    Collections.reverse(reversed);
                    previous = selected;
                    if (selected == null || !selected.contains(e.getPoint())) {
                        for (Rectangle box : reversed) {
                            if (box.contains(e.getPoint())) {
                                selected = box;
                                delta = new Point(e.getX() - selected.x, e.getY() - selected.y);
                                repaint();
                                break;
                            }
                        }
                        if (selected != null) {
                            boxes.remove(selected);
                            boxes.add(boxes.size() - 1, selected);
                        }
                    } else if (selected != null) {
                        delta = new Point(e.getX() - selected.x, e.getY() - selected.y);
                    }
                }

                @Override
                public void mouseClicked(MouseEvent e) {
                    if (selected == previous && selected != null && selected.contains(e.getPoint())) {
                        selected = null;
                        repaint();
                    }
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    if (selected != null) {
                        int x = e.getX() - delta.x;
                        int y = e.getY() - delta.y;
                        selected.x = x;
                        selected.y = y;
                        repaint();
                    }
                }

            };

            addMouseListener(ma);
            addMouseMotionListener(ma);
        }

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

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            for (Rectangle box : boxes) {
                if (box != selected) {
                    g2d.setColor(Color.BLUE);
                    g2d.fill(box);
                    g2d.setColor(Color.BLACK);
                    g2d.draw(box);
                }
            }
            if (selected != null) {
                g2d.setColor(Color.CYAN);
                g2d.fill(selected);
                g2d.setColor(Color.BLUE);
                g2d.draw(selected);
            }
            g2d.dispose();
        }

    }

}

I am just so lost. I am looking at all this code and don't even understand what it is doing.

Yeah, I feel like this about my code a lot.

Basically...

First: mousePressed is called, I reverse my list of objects, because the top most component will be the last in the list (this is my requirement), I store the currently selected object in the previous variable, I use this to determine if there has been a change in selection or not. I check to see if the user clicked the selected object or not, if they did, we can basically skip every thing else. If not, we determine what they clicked, if anything. The delta is simply the difference between the place they clicked and the location of the object, this is used to make the drag more natural

If no drag occurs: mouseClicked is called. We test to see if the selected object is equal to the previous object and if the mouse was clicked within the selected object, if these are true, then the currently selected object should be deselected. Otherwise the user has basically just changed the selection, so we don't want to immediately deselect it.

Else if a drag occurs: mouseDragged is called. We simply check to see if something is selected, we calculate the difference between the current mouse position and the "click offset" and update the position of the selected object

Clear as mud :P

One thing to also remember is mouseReleased will always be called after mousePressed, even if mouseClicked isn't (which is called after mouseReleased when no dragging occurs).

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I am just so lost. I am looking at all this code and don't even understand what it is doing. Our professor hasn't done any examples of what mouse listeners and mouse adapters are. Just gave us a program and told us we need MouseListeners. I am trying to follow your code and understand what it is doing, but I am completely lost. – GenericUser01 Oct 28 '15 at 23:59
  • Maybe you should have a look at [How to Write a Mouse Listener](http://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html). `MouseAdapter` is just a "default" implementation of `MouseListener`, `MouseWheelListener` and `MouseMotionListener`, with all the methods filled out as empty, this makes it so much simpler to implement only the logic which you want to use – MadProgrammer Oct 29 '15 at 00:01
  • I read that article and did some of the demos listed, and those make sense, but my segment of code that I have does not and I have tried many many things but can't seem to get it to work :/. – GenericUser01 Oct 29 '15 at 01:09
  • I am confused where you say `previous = selected`. I am confused because in your program, I never see `previous` or `selected` initialized to anything. – GenericUser01 Oct 29 '15 at 01:48
  • `selected = box;` ? In my code, I simply use `selected` and `previous` as pointers to other objects (in particular `Rectangle`s from the `boxes` `List`) – MadProgrammer Oct 29 '15 at 01:51