0

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.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • Well, I guess I'm happy with my workaround, though I'd like to know how to avoid the workaround, which is to change the `JTextField`s to `JLabel`s. I guess I'm close to having it work with `JTextField`s but am too thick to see the problem. – DSlomer64 Mar 23 '15 at 22:19
  • Oh. I also left layered pane out of the picture. – DSlomer64 Mar 23 '15 at 22:27
  • For [example](http://stackoverflow.com/questions/15870608/creating-a-draw-rectangle-filled-with-black-color-function-in-java-for-a-grid/15870637#15870637) and [example](http://stackoverflow.com/questions/15421708/how-to-draw-grid-using-swing-class-java-and-detect-mouse-position-when-click-and/15422801#15422801) – MadProgrammer Mar 23 '15 at 23:23
  • To translate a `MouseEvent` from one component context to another, you can use [`SwingUtilities.convertMouseEvent`](http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#convertMouseEvent(java.awt.Component,%20java.awt.event.MouseEvent,%20java.awt.Component)) or [`SwingUtilities.convertPoint`](http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#convertPoint(java.awt.Component,%20int,%20int,%20java.awt.Component)) – MadProgrammer Mar 23 '15 at 23:24

2 Answers2

1

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

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • @MadProgrammer--thanks for the links, especially to `SwingUtilities`, some of whose wheels I've been reinventing. I've long been assigning doing as you suggest (assigning listeners to each cell). However, in trying to figure out how to drag a `JLabel` from a `JPanel` across the grid (last week), I had to use `JLayeredPane` for that entire grid and another `JPanel`. Dropping was inaccurate, so I wanted a way to get from `e.getX()` and `e.getY()` to grid row and column. Hence the question. Problem solved, though not as described in a previous comment. May post as an answer. – DSlomer64 Mar 24 '15 at 23:19
  • I would then say you are doing your drag/drop process wrong. Generally speaking, you don't wan to transfer the label, but the labels data, it's much simpler to deal with - for [example](http://stackoverflow.com/questions/13855184/drag-and-drop-custom-object-from-jlist-into-jlabel/13856193#13856193) – MadProgrammer Mar 24 '15 at 23:22
  • You might also want to take a look at [Drag and Drop and Data Transfer](http://docs.oracle.com/javase/tutorial/uiswing/dnd/) – MadProgrammer Mar 24 '15 at 23:23
  • @MadProgrammer--thanks for yet more advice and links. BTW, I got my approach from [Trashgod's link to this Chessboard program](http://stackoverflow.com/questions/2561690/placing-component-on-glass-pane/2562685#2562685). Since it did not go smoothly, I'll study your example code and see how that goes. As it happens, dragging the label with an etched border makes it look like you're actually dragging a Scrabble tile across the board, but dealing with data inside a label inside a panel of 7 labels was a nightmare for days. – DSlomer64 Mar 26 '15 at 17:18
0

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.

Will Hartung
  • 115,893
  • 19
  • 128
  • 203
  • Right. That's why I tried layered pane, but I couldn't get both the gridlines AND coordinates. However, see my comment above about my workaround (change 3 or 4 lines, changing text fields to labels). – DSlomer64 Mar 23 '15 at 22:22