DSlomer64
DSlomer64

Reputation: 4283

How do I make a grid (with gridlines) inside a panel and have a mouse listener give coordinates relative to the panel when any grid cell is clicked?

I was hoping to make the grid produced by the code below report x and y coordinates of the location of the mouse pointer relative to pnlGrid when clicked. But wherever I clicked (before i added a JLayeredPane), I got no coordinates except for when the mouse pointer was in the red area.

So I added several lines of code to make a JLayeredPane and I get mouse coordinate output, but no grid lines as shown in second screen shot.

How do I get both grid lines AND report of mouse coordinates when any place in the grid is clicked?

enter image description here

package gridcellcoordinator;
import gbl.GBConstraints;
import static java.awt.Color.*;
import java.awt.Color;
import java.awt.Dimension;
import static java.awt.EventQueue.invokeLater;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import static javax.swing.JLayeredPane.DEFAULT_LAYER;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.LineBorder;
import javax.swing.event.MouseInputAdapter;

public class GridCellCoordinator {

  final static int 
      GRID_PANEL_BORDER_WIDTH = 5,
      N = 11,
      CELLSIZE = 40;

  //static final JLayeredPane layer = new JLayeredPane();
  static final JPanel panel = new JPanel();
  static final int SM_CELL_BORDER_WIDTH = 1;
  static LineBorder SMcellBorder = new LineBorder(BLACK,SM_CELL_BORDER_WIDTH);

  static JTextField[][] cells = new JTextField[N][N];

  static JFrame frame = new JFrame();
  public GridCellCoordinator(){
    makeGrid();
  }

  private void makeGrid(){ 
    JPanel pnlGrid = new JPanel();
    pnlGrid.setLayout(new GridLayout(N,N));
    pnlGrid.setBackground(BLUE);
    pnlGrid.setBorder(BorderFactory.createLineBorder(Color.red,GRID_PANEL_BORDER_WIDTH));
    pnlGrid.setLayout(new GridLayout(N, N));

    for(int i = 0 ; i < N ; i++)
      for(int j = 0; j < N; j++){
        cells[i][j] = new JTextField();
        cells[i][j].setText("X");
        cells[i][j].setPreferredSize(new Dimension(CELLSIZE,CELLSIZE));
        cells[i][j].setHorizontalAlignment(JTextField.CENTER);
        cells[i][j].setFocusTraversalKeysEnabled(false);
        cells[i][j].setBorder(SMcellBorder);
        cells[i][j].setOpaque(true);
        pnlGrid.add(cells[i][j], new GBConstraints());
    }

    panel.addMouseListener(new MouseInputAdapter()
    {
      public void mousePressed (MouseEvent e){panelMousePressed (e);}
    });

    pnlGrid.setPreferredSize(new Dimension(N*(CELLSIZE + 1) + 2*GRID_PANEL_BORDER_WIDTH , 
                                           N*(CELLSIZE + 1) + 2*GRID_PANEL_BORDER_WIDTH));
    panel.add(pnlGrid);
    panel.setVisible(true);
    panel.setOpaque(true);
    panel.setPreferredSize(pnlGrid.getPreferredSize());
    panel.setVisible(true);
    //layer.add(pnlGrid, DEFAULT_LAYER);
    //layer.setPreferredSize(pnlGrid.getPreferredSize());
    //layer.setVisible(true);
    frame.add(panel);
    frame.setSize(new Dimension(pnlGrid.getPreferredSize()));
    frame.setVisible(true);
    frame.pack();
    System.out.println("pnlGrid component count: " + pnlGrid.getComponentCount());
    System.out.println("computed dimension: " + (N*(CELLSIZE + 1) + 2*GRID_PANEL_BORDER_WIDTH));
    System.out.println("pnlGrid pref size: " + pnlGrid.getPreferredSize());
    System.out.println("layer pref size: " + layer.getPreferredSize());
    System.out.println("panel pref size: " + panel.getPreferredSize());
  }

  public void panelMousePressed(MouseEvent e){
    JOptionPane.showMessageDialog(null,"Pressed:" + e.getX() + " " + e.getY());
  }

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

}

P.S. As is, the code produces the grid on the left but only gives coordinates inside the red border. Remove the comment bars on the 4 layer lines to get the coordinates but no gridlines. I'd like BOTH gridlines and coordinates.

Upvotes: 0

Views: 3112

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347314

You could use something like this or this

However, to fix your code, you need to change your focus. Instead of attaching the MouseListener to the pnlGrid, you need to attach it to the text fields. The reason for this is mouse events are like rain drops, any component with an attached MouseListener will act like an umbrella, preventing MouseEvents from occurring on components that are visually below them

To translate a MouseEvent from one component context to another, you can use SwingUtilities.convertMouseEvent or SwingUtilities.convertPoint

For example...

import java.awt.Color;
import static java.awt.Color.BLACK;
import static java.awt.Color.BLUE;
import java.awt.Dimension;
import static java.awt.EventQueue.invokeLater;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

public class GridCellCoordinator {

    final static int GRID_PANEL_BORDER_WIDTH = 5,
                    N = 11,
                    CELLSIZE = 40;

    //static final JLayeredPane layer = new JLayeredPane();
    static final JPanel panel = new JPanel();
    static final int SM_CELL_BORDER_WIDTH = 1;
    static LineBorder SMcellBorder = new LineBorder(BLACK, SM_CELL_BORDER_WIDTH);

    static JTextField[][] cells = new JTextField[N][N];

    static JFrame frame = new JFrame();

    public GridCellCoordinator() {
        makeGrid();
    }

    private void makeGrid() {
        JPanel pnlGrid = new JPanel();
        pnlGrid.setLayout(new GridLayout(N, N));
        pnlGrid.setBackground(BLUE);
        pnlGrid.setBorder(BorderFactory.createLineBorder(Color.red, GRID_PANEL_BORDER_WIDTH));
        pnlGrid.setLayout(new GridLayout(N, N));

        MouseListener mouseHandler = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                doMousePressed(e);
            }
        };

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                cells[i][j] = new JTextField() {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(20, 20);
                    }
                };
                cells[i][j].setText("X");
                cells[i][j].setPreferredSize(new Dimension(CELLSIZE, CELLSIZE));
                cells[i][j].setHorizontalAlignment(JTextField.CENTER);
                cells[i][j].setFocusTraversalKeysEnabled(false);
                cells[i][j].setBorder(SMcellBorder);
                cells[i][j].addMouseListener(mouseHandler);
                pnlGrid.add(cells[i][j]);
            }
        }
        panel.add(pnlGrid);
        panel.setVisible(true);
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
    }

    public void doMousePressed(MouseEvent e) {
        Point p = e.getPoint();
        System.out.println("Source point = " + p + " within " + e.getComponent());
        p = SwingUtilities.convertPoint(e.getComponent(), p, e.getComponent().getParent());
        System.out.println("Converted point = " + p + " within " + e.getComponent().getParent());
    }

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

}

You should avoid using setPreferredSize and rely more overriding getPreferredSize when you need to. See Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? for more details

Upvotes: 1

Will Hartung
Will Hartung

Reputation: 118704

Well, each individual edit widget is what's eating your mouse in the above example.

A normal Java table simply displays the table in a single panel, and then when it's clicked, it then creates a control at that time to handle the editing. So, it's not a large array of edit controls. That turns out to be rather inefficient.

So, the solution is "don't do what you're doing". Or use the layered pane as you've already tried.

Addenda:

The problem is that you have a collection of first class components, each with their own "little" rectangles, rather than a larger, singular component. That's the problem you're encountering. Swing is treating all of those Labels and individual things (because they are). They don't have any global context to readily report compared to their container. You'd have to extrapolate based on their local coordinates converted to a "global" coordinate within the larger panel.

Upvotes: 0

Related Questions