Remembering where a mouse clicked? ArrayLists? HashCodes?

Sorry guys, I deleted my APPLES and CATS example :) Here's the updated version of my question!

I'm losing my sanity here. I need someone who can enlighten me. I've tried a couple of times explaining my problem here. Hopefully, this time, my question will be easier to understand.

Basically I have this frame, and there's an image displayed. There is a JList on the right, and there is another panel for JLabels at the bottom. Here's a screencap of my frame.

When I click on the image, a JOptionPane pops out, like so. And I enter my input. My JList is an ArrayList, so everything I input is added to the JList and the JPanel at the bottom.

Now, when I hover on the the part where I clicked, you noticed that the square disappeared). It only appears when I click the image, and when I hover the label at the bottom. My labels, as of now are LOLZ NOSE and INPUT HERE.

What I want to do is when I hover on the label, for example INPUT HERE, it shows the square again, featuring the part where I clicked. My problem now is when I click on NOSE, which is supposed to be showing a square on the nose part and a the name NOSE with black bg, IT IS NOT SHOWING. Also, only the last label's square is shown, disregarding the other labels' position clicked.

How do I get a label to remember the position of the click I make? People said I should use ArrayLists or HashCodes however I have no idea how to implement them. Thank you to anyone who can help.

Edit: I've already done the rectangle, btw. It's showing only for the last label inputted. Here are some of the code snippets requested!

How I'm setting the text on JLabel and updating the JList:

public void updateLabel(){

        StringBuilder text = new StringBuilder(); //creates empty builder, capacity 16

        for(Object s: tagModel.toArray()) //returns an array containing the elements of the tagModel
            text.append(" " + s);

        repaint();      
        hoverLabel.setText(text.toString()); //returns a String
        hoverLabel.addMouseMotionListener(this);
        hoverPanel.add(hoverLabel);

    }

My mouseListener upon click:

@Override
    public void mouseClicked(MouseEvent event) {
        // TODO Auto-generated method stub

        x = event.getX();
        y = event.getY();

        isRectPresent = true;
        repaint();

        input = JOptionPane.showInputDialog("Enter tag name:");

        if((input != null) && !input.isEmpty()){
            tagModel.addElement(input);
        }
    }

My mouseMotionListener upon hovering:

@Override
    public void mouseMoved(MouseEvent e) {
        // TODO Auto-generated method stub

        xpos = e.getX(); //gets where the mouse moved
        ypos = e.getY(); 

        //checks if the mouse is inside the bounds of the rectangle
        if (xpos > x && xpos < x + 100 && ypos > y && ypos < y + 100)
            isRectPresent = false;

        if(e.getSource() == hoverLabel){
            isRectPresent = true;
            repaint();
        }

        repaint();
    }

How I'm painting:

    public void paintComponent(Graphics g){ 
            Graphics2D g2 = (Graphics2D) g;

            g2.drawImage(image, 0, 0, null);

            if(image != null && isRectPresent){                 
                            Stroke stroke = g2.getStroke();
                g2.setStroke(new BasicStroke(4));
                g2.setColor(Color.WHITE);
                g2.drawRect(x-50, y-50, 100, 100);
                g2.setStroke(stroke);
            }else{
                if(xpos > x && xpos < x + 100 && ypos > y && ypos < y + 100){
                    g.setColor(Color.BLACK);
                    g.fillRect(x-50, y-50, 100, 25);
                    g.setColor(Color.WHITE);
                    g.setFont(new Font("Tahoma", Font.BOLD, 12));
                    g.drawString(input, x-30, y-30);
                }
            }
        }

If you want me to add some more snippets, just tell me! :)

Answers


You should create a HashMap, say something like:

Map linkSet = new HashMap();

And whenever you click on the drawing and create a label, add the JLabel and the point on the image to the set using the put method with the JLabel as the key and the Point as the value. Then in the JLabel's MouseMotionListener, use your label as a key and obtain the corresponding point from the set using the map's get(...) method.

edit: Corrected as per alicedimarco's comment. Again, thanks!

edit 2 I think you want again to use a Map. If you have a Map, you can have it retrieve the Point of interest from the JLabel's or the JList's String, and then pass this Point to the class that's drawing the image and let it use the Point to draw a rectangle. For instance you could give the image drawing class a Point field called displayPoint, and a method called setDisplayPoint(Point p). It can be as simple as this:

public void setDisplayPoint(Point p) {
  this.displayPoint  = p;
  repaint();
}

and assuming that the object of interest is centered at that point, use the displayPoint in the paintComponent method:

protected void paintComponent(Graphics g) {
  super.paintComponent(g);
  // draw image
  if (img != null) {
     g.drawImage(img, X_SHIFT, Y_SHIFT, null);
  }

  // if displayPoint not null, draw the surrounding rectangle
  if (displayPoint != null) {
     g.setColor(RECT_COLOR);
     int x = displayPoint.x - RECT_WIDTH / 2;
     int y = displayPoint.y - RECT_WIDTH / 2;
     int width = RECT_WIDTH;
     int height = RECT_WIDTH;
     g.drawRect(x, y, width, height);
  }
}

edit 3: To get mouse clicks, it's quite easy, simply add a MouseListener to the component that holds the image:

     // !! added
     imgRect.addMouseListener(new MouseAdapter() {
        public void mousePressed(MouseEvent e) {
           imgMousePressed(e);
        }
     }); 

And in your code that is called from this mouse listener, use a JOptionPane to get the user's choice of tag name, and add the resulting String to both the listDataModel so that it is seen in the JList and also in the stringPointMap together with the Point obtained from the MouseEvent so that you can map the String to the Point and be able to retrieve it:

// !! added
private void imgMousePressed(MouseEvent e) {
  String result = JOptionPane.showInputDialog(this,
        "Please enter name for this point on image:");
  if (result != null) {
     stringPointMap.put(result, e.getPoint());
     listDataModel.addElement(result);
  }
}

That's it.

Then putting it all together:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;

@SuppressWarnings("serial")
public class ImageRectMain extends JPanel {
   private ImageRect imgRect;
   private DefaultListModel listDataModel = new DefaultListModel();
   private JList list = new JList(listDataModel);
   private Map<String, Point> stringPointMap = new HashMap<String, Point>();

   public ImageRectMain() {
      String nose = "Nose";
      String ear = "Ear";
      String rightEye = "Right Eye";
      String leftEye = "Left Eye";
      listDataModel.addElement(ear);
      listDataModel.addElement(nose);
      listDataModel.addElement(rightEye);
      listDataModel.addElement(leftEye);
      stringPointMap.put(nose, new Point(480, 500));
      stringPointMap.put(ear, new Point(270, 230));
      stringPointMap.put(rightEye, new Point(380, 390));
      stringPointMap.put(leftEye, new Point(662, 440));

      MouseAdapter listMouseAdapter = new MouseAdapter() {
         @Override
         public void mouseMoved(MouseEvent e) {
            listMouseMoved(e);
         }

         @Override
         public void mouseExited(MouseEvent e) {
            listMouseExited(e);
         }

      };
      list.addMouseMotionListener(listMouseAdapter);
      list.addMouseListener(listMouseAdapter);

      try {
         imgRect = new ImageRect();

         // !! added
         imgRect.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
               imgMousePressed(e);
            }
         }); 

         JPanel eastPanel = new JPanel();
         eastPanel.setLayout(new BoxLayout(eastPanel, BoxLayout.PAGE_AXIS));
         eastPanel.add(new JLabel("You have tagged the following:"));
         eastPanel.add(new JScrollPane(list));
         eastPanel.add(Box.createVerticalGlue());
         eastPanel.add(Box.createVerticalGlue());
         eastPanel.add(Box.createVerticalGlue());
         eastPanel.add(Box.createVerticalGlue());
         setLayout(new BorderLayout());
         add(imgRect, BorderLayout.CENTER);
         add(eastPanel, BorderLayout.EAST);
      } catch (MalformedURLException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   // !! added
   private void imgMousePressed(MouseEvent e) {
      String result = JOptionPane.showInputDialog(this,
            "Please enter name for this point on image:");
      if (result != null) {
         stringPointMap.put(result, e.getPoint());
         listDataModel.addElement(result);
      }
   }

   private void listMouseExited(MouseEvent e) {
      imgRect.setDisplayPoint(null);
   }

   private void listMouseMoved(MouseEvent e) {
      int index = list.locationToIndex(e.getPoint());
      Object value = listDataModel.get(index);
      if (value != null) {
         Point point = stringPointMap.get(value.toString());
         if (point != null) {
            imgRect.setDisplayPoint(point);
         }
      }
   }

   private static void createAndShowGui() {
      JFrame frame = new JFrame("ImageRectMain");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new ImageRectMain());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

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

@SuppressWarnings("serial")
class ImageRect extends JPanel {
   public static final String IMAGE_PATH = "http://i.stack.imgur.com/7oNzg.jpg";
   private static final int DEFAULT_W = 687;
   private static final int DEFAULT_H = 636;
   private static final int X_SHIFT = -6;
   private static final int Y_SHIFT = -26;
   private static final Color RECT_COLOR = Color.pink;
   private static final int RECT_WIDTH = 40;
   private BufferedImage img;
   private Point displayPoint = null;

   public ImageRect() throws MalformedURLException, IOException {
      img = ImageIO.read(new URL(IMAGE_PATH));
   }

   public void setDisplayPoint(Point p) {
      this.displayPoint = p;
      repaint();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (img != null) {
         g.drawImage(img, X_SHIFT, Y_SHIFT, null);
      }
      if (displayPoint != null) {
         g.setColor(RECT_COLOR);
         int x = displayPoint.x - RECT_WIDTH / 2;
         int y = displayPoint.y - RECT_WIDTH / 2;
         int width = RECT_WIDTH;
         int height = RECT_WIDTH;
         g.drawRect(x, y, width, height);
      }
   }

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

One nice feature of a JList is that you can story any object in it. You're not limited to strings. When objects are stored in JLists, swing will call the object's toString() method, and display it in the list.

Knowing this, you can now write your own class that stores the name of your selection label and the coordinates of the box. This object's toString() method will return the name of the label, which will make the right thing appear in the JList.

Then, in the selection event handler for the JList, you can get your custom object out, and retrieve the box coordinates stored in it, and draw them on the screen. No need to fuss with other containers (although knowing how to use them is a good thing to).

Ok, Create a class like this...

public class MyLabel {
    private int x;
    private int y;
    private String text;

    public MyLabel (String text, int x, int y) {
        this.text = text;
        // assign x and y too...
    }

    @Override
    public String toString() {
        return label;
    }

    public int getX() {
        return x;
    }

    // similar function to getY()
}

The class above overrides toString(), so when you put it in a JList, it will use the call to toString() to determine what to display, and since this toString implementation returns the label name you'll see that in the list.

And add those into your JList instead of Strings. Then at some point in your code you'll do something like this...

// this is pseudocode, method names may not be correct...

MyLabel ml = (MyLabel)jList.getSelectedItem();
int x = ml.getX();
int y = ml.getY();
// draw the box...

Hope that helps.


Need Your Help

DOM (Events) in GWT

javascript dom gwt dom-events

I have a bit of a trouble understanding GWT's architecture. I can see two packages: com.google.gwt.dom.client and com.google.gwt.xml.client. As far as I understand, the first gives a wrapper for th...

Can not load FragmentActivity, google maps v 2

android nullpointerexception google-maps-api-2

I am trying to display googlemaps v2, on my Android phone LG-E400, with Android OS v 2.3.6.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.