1

I have a 2d array of Grids (JPanels) that are added to another JPanel using the GridLayout. That JPanel is added to the JFrame. Whenever a click happens on the JFrame I'm attempting to take the Point of the click and determine if any of those Grids in the 2d array contain that Point.

I'm attempting to do this inside frame.addMouseListener...

I know the frame is registering the mouse clicks. For some reason the Grids don't register that they should be containing that Point. Can anyone explain this? if(theView[i][j].contains(me.getPoint())){ This is the line of code that seems to be failing me.

I originally attempted to have the Grids know when they were clicked on so I wouldn't have to coordinate between the frame and grids, but I couldn't get that to work.

Here's the level designer.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.*;
import javax.swing.*;

public class LevelDesigner extends JPanel implements ButtonListener{
    private final int SIZE = 12;
    private int [][] thePit;    
    private Grid [][] theView;
    private ButtonPanel bp;
    public static int val;
    private int rows, cols;
    private JPanel gridPanel;
    private JFrame frame;

    public LevelDesigner(int r, int c){    
        frame = new JFrame();        
        int h = 10, w = 10;
        setVisible(true);
        setLayout(new BorderLayout());
        setBackground(Color.BLUE);
        rows = r;
        cols = c;
        thePit = new int[r][c];
        theView = new Grid[r][c];   
        gridPanel = new JPanel();
        gridPanel.setVisible(true);
        gridPanel.setBackground(Color.BLACK);
        gridPanel.setPreferredSize(getMaximumSize());
        GridLayout gridLayout = new GridLayout();
        gridLayout.setColumns(cols);
        gridLayout.setRows(rows);
        gridPanel.setLayout(gridLayout);

        for(int i = 0; i < r; i++){
            for(int j = 0; j < c; j++){
                theView[i][j] = new Grid(i, j, SIZE, this);
                gridPanel.add(theView[i][j]);
            }
        }

        String test [] = {"0", "1","2","3","4","save"};
        bp =  new ButtonPanel(test,  this);
        this.add(bp, BorderLayout.SOUTH);
        this.add(gridPanel, BorderLayout.CENTER);

        frame.addMouseListener(new MouseAdapter(){
            public void mousePressed(MouseEvent me) {

                for(int i = 0; i < rows; ++i){
                    for(int j = 0; j < cols; ++j){
                        if(theView[i][j].contains(me.getPoint())){
                            theView[i][j].actionPerformed(null);
                            return;
                        }
                    }
                }
            } 
        });

        frame.setVisible(true);
        frame.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH);
        frame.setTitle("Epic Crawl - Main Menu");
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.repaint();
        frame.add(this);
    }

    public String toString(){
        int noRows = thePit.length;
        int noColumns = thePit[0].length;

        String s="";
        for (int r=0;r<noRows;r++){
            for (int c=0;c<noColumns;c++){
                s=s + thePit[r][c] + " ";
            }
            s=s+"\n";
        }
        return(s);
    }

    public void notify( int i, int j){
        thePit[i][j] = val;
    }

    public void print(){
        final JFileChooser fc = new JFileChooser();
        fc.setCurrentDirectory(new java.io.File("."));
        int returnVal = fc.showSaveDialog( null);

        if( returnVal == JFileChooser.APPROVE_OPTION ){
            try{
                PrintWriter p = new PrintWriter( 
                new File( fc.getSelectedFile().getName() ) );
                System.out.println(" printing");

                p.println( this );
                p.close();   
            }
            catch( Exception e){
                System.out.println("ERROR: file not saved");
            }
        }
    }

    public void buttonPressed(String buttonLabel, int id){
        if(id == 5)
            print();
        else
            val = id;
    }

    public void buttonReleased( String buttonLabel, int buttonId ){}
    public void buttonClicked( String buttonLabel, int buttonId ){}

    public static void main(String arg[]){ 
        LevelDesigner levelDesigner = new LevelDesigner(4, 4);
    }
}

And here is the Grid.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Grid extends JPanel implements ActionListener{
    LevelDesigner grid;    
    int myI, myJ;
    private String[] imageNames = {"dirt.png", "grass.png", "Door.png", "woodfloor.png", "32x32WoodFloor.png"};
    BufferedImage gridImage;
    private String imagePath;

    public Grid(int i, int j, int size, LevelDesigner m){
        imagePath = "";
        grid = m;
        myI = i;
        myJ = j;
        setBackground(Color.RED);
        this.setBorder(BorderFactory.createLineBorder(Color.black));
        this.setVisible(true);
    }

   @Override
   public void actionPerformed(ActionEvent ae){
        grid.notify(myI, myJ);
        imagePath = "Images/" + imageNames[LevelDesigner.val];
        gridImage = null;
        InputStream input = this.getClass().getClassLoader().getResourceAsStream(imagePath);
        try{
            gridImage = ImageIO.read(input);
        }catch(Exception e){System.err.println("Failed to load image");}
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g); // Important to call super class method
        g.clearRect(0, 0, getWidth(), getHeight()); // Clear the board
        g.drawImage(gridImage, 0, 0, getWidth(), getHeight(), null);
    }
}
Andrew_CS
  • 2,542
  • 1
  • 18
  • 38
  • 1
    Dealing with `Point`s is messy, because you always have to be aware of what coordinate system you're in - in this case it's biting you. Far better to attach listeners to the JPanels that you want the event to happen in. – roippi Jul 19 '13 at 03:19
  • I originally tried to go that route because I wanted to avoid using Point, thanks for the tip! – Andrew_CS Jul 19 '13 at 03:31
  • 1
    I know you've selected a correct answer, but regardless, please see edit to my answer. – Hovercraft Full Of Eels Jul 19 '13 at 03:31

2 Answers2

3

The contains method checks if the Point is within the boundaries of the JPanel but using a coordinate system relative to the JPanel. Instead consider using findComponentAt(Point p).

For example:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;

public class TestMouseListener {
   private static final int SIDE_COUNT = 4;
   private JPanel mainPanel = new JPanel();
   private MyGridCell[][] grid = new MyGridCell[SIDE_COUNT][SIDE_COUNT];

   public TestMouseListener() {
      mainPanel.setLayout(new GridLayout(SIDE_COUNT, SIDE_COUNT));
      for (int i = 0; i < grid.length; i++) {
         for (int j = 0; j < grid[i].length; j++) {
            grid[i][j] = new MyGridCell();
            mainPanel.add(grid[i][j].getMainComponent());
         }
      }

      mainPanel.addMouseListener(new MouseAdapter() {
         @Override
         public void mousePressed(MouseEvent e) {
            Point p = e.getPoint();
            Component c = mainPanel.findComponentAt(p);
            for (MyGridCell[] gridRow : grid) {
               for (MyGridCell myGridCell : gridRow) {
                  if (c == myGridCell.getMainComponent()) {
                     myGridCell.setLabelText("Pressed!");
                  } else {
                     myGridCell.setLabelText("");
                  }
               }
            }
         }
      });
   }

   public Component getMainComponent() {
      return mainPanel;
   }

   private static void createAndShowGui() {
      TestMouseListener mainPanel = new TestMouseListener();

      JFrame frame = new JFrame("TestMouseListener");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel.getMainComponent());
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

class MyGridCell {
   private static final int PREF_W = 200;
   private static final int PREF_H = PREF_W;
   @SuppressWarnings("serial")
   private JPanel mainPanel = new JPanel() {
      public Dimension getPreferredSize() {
         return MyGridCell.this.getPreferredSize();
      };
   };
   private JLabel label = new JLabel();

   public MyGridCell() {
      mainPanel.setBorder(BorderFactory.createLineBorder(Color.black));
      mainPanel.setLayout(new GridBagLayout());
      mainPanel.add(label);
   }

   public Component getMainComponent() {
      return mainPanel;
   }

   public void setLabelText(String text) {
      label.setText(text);
   }

   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
2

Component#contains "Checks whether this component "contains" the specified point, where the point's x and y coordinates are defined to be relative to the coordinate system of this component"

This means that the contains will only return true if the Point is within the bounds of 0 x 0 x width x height.

So if the component is position at 400x200 and is sized at 200x200 (for example). When you click within in, the mouse point will be between 400x200 and 600x400, which is actually out side of the relative position of the component (200x200) - confused yet...

Basically, you either need to convert the click point to a relative coordinate within the context of the component you are checking...

Point p = SwingUtilities.convertPoint(frame, me.getPoint(), theView[i][j])
if (theView[i][j].contains(p)) {...

Or use the components Rectangle bounds...

if (theView[i][j].getBounds().contains(me.getPoint())) {...

So, remember, mouse events are relative to the component that they were generated for

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Used `Point p = SwingUtilities.convertPoint(frame, me.getPoint(), theView[i][j]) if (theView[i][j].contains(p))` and it's working beautifully. Had to add in a call to repaint() inside the Grid's actionPerformed method, but probably should have had that anyways. Thank you! – Andrew_CS Jul 19 '13 at 03:31