0

This question is related to my previous question : How To create A Large Size Custom Cursor In Java?

If you are curious about what is it used for, you can find a practical use case for an award winning password protection system called GATE [ Graphic Access Tabular Entry ] at : http://gatecybertech.net/

After the previous question I found a way to create a large custom cursor, and the working answer is posted in my previous post. But in order to achieve it, I have to click a checkbox first, now I want to be able to create a large custom cursor without first need to click a checkbox, so I modified my code to look like the following without a checkbox :

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.event.MouseInputAdapter;

public class Demo_Large_Custom_Cursor_Simple
{
  static private MyGlassPane_Simple myGlassPane;

  private static void createAndShowGUI()
  {
    JFrame frame=new JFrame("Demo_Large_Custom_Cursor_Simple");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//    JCheckBox changeButton=new JCheckBox("Custom Cursor \"visible\"");
//    changeButton.setSelected(false);

    Container contentPane=frame.getContentPane();
    contentPane.setLayout(new FlowLayout());
//    contentPane.add(changeButton);

    JButton Button_1=new JButton("<Html><Table Cellpadding=7><Tr><Td>A</Td><Td>B</Td></Tr><Tr><Td>C</Td><Td>D</Td></Tr></Table></Html>");
    Button_1.setPreferredSize(new Dimension(80,80));
    Button_1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Out("Button 1"); } });
    contentPane.add(Button_1);

    JButton Button_2=new JButton("<Html><Table Cellpadding=7><Tr><Td>1</Td><Td>2</Td></Tr><Tr><Td>3</Td><Td>4</Td></Tr></Table></Html>");
    Button_2.setPreferredSize(new Dimension(80,80));
    Button_2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Out("Button 2"); } });
    contentPane.add(Button_2);

    JMenuBar menuBar=new JMenuBar();
    JMenu menu=new JMenu("Menu");
    menu.add(new JMenuItem("Do nothing"));
    menuBar.add(menu);
    frame.setJMenuBar(menuBar);

    // Set up the glass pane, which appears over both menu bar and content pane and is an item listener on the change button.
//    myGlassPane=new MyGlassPane_Simple(changeButton,menuBar,frame.getContentPane());
    myGlassPane=new MyGlassPane_Simple(menuBar,frame.getContentPane());
//    changeButton.addItemListener(myGlassPane);
    frame.setGlassPane(myGlassPane);

    frame.setLocationRelativeTo(null);
    frame.pack();
    frame.setVisible(true);
  }

  private static void out(String message) { System.out.print(message); }

  private static void Out(String message) { System.out.println(message); }

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

// We have to provide our own glass pane so that it can paint.
class MyGlassPane_Simple extends JComponent implements ItemListener
{
  Point point;

//  public MyGlassPane_Simple(AbstractButton aButton,JMenuBar menuBar,Container contentPane)
  public MyGlassPane_Simple(JMenuBar menuBar,Container contentPane)
  {
//    CBListener_Simple listener=new CBListener_Simple(aButton,menuBar,this,contentPane);
    CBListener_Simple listener=new CBListener_Simple(menuBar,this,contentPane);
    addMouseListener(listener);
    addMouseMotionListener(listener);
  }

  // React to change button clicks.
  public void itemStateChanged(ItemEvent e) { setVisible(e.getStateChange()==ItemEvent.SELECTED); }

  protected void paintComponent(Graphics g)
  {
    try
    {
      if (point!=null)
      {
//      g.setColor(Color.red);
//      g.fillOval(point.x-10,point.y-10,20,20);

        BufferedImage image=ImageIO.read(new File("C:/Cursor_Crosshair.PNG"));
        g.drawImage(image,point.x-39,point.y-39,null);
      }
    }
    catch (Exception e) { }
  }

  public void setPoint(Point p) { point=p; }
}

// Listen for all events that our check box is likely to be interested in. Redispatch them to the check box.
class CBListener_Simple extends MouseInputAdapter
{
  Toolkit toolkit;
  Component liveButton;
  JMenuBar menuBar;
  MyGlassPane_Simple glassPane;
  Container contentPane;

//  public CBListener_Simple(Component liveButton,JMenuBar menuBar,MyGlassPane_Simple glassPane,Container contentPane)
  public CBListener_Simple(JMenuBar menuBar,MyGlassPane_Simple glassPane,Container contentPane)
  {
    toolkit=Toolkit.getDefaultToolkit();
    this.liveButton=liveButton;
    this.menuBar=menuBar;
    this.glassPane=glassPane;
    this.contentPane=contentPane;
  }

  public void mouseMoved(MouseEvent e)
  {
//    redispatchMouseEvent(e,false);
    redispatchMouseEvent(e,true);
  }

  public void mouseDragged(MouseEvent e) { redispatchMouseEvent(e,false); }
  public void mouseClicked(MouseEvent e) { redispatchMouseEvent(e,false); }
  public void mouseEntered(MouseEvent e) { redispatchMouseEvent(e,false); }
  public void mouseExited(MouseEvent e) { redispatchMouseEvent(e,false); }
  public void mousePressed(MouseEvent e) { redispatchMouseEvent(e,false); }
  public void mouseReleased(MouseEvent e) { redispatchMouseEvent(e,true); }

  // A basic implementation of redispatching events.
  private void redispatchMouseEvent(MouseEvent e,boolean repaint)
  {
    Point glassPanePoint=e.getPoint();
    Container container=contentPane;
    Point containerPoint=SwingUtilities.convertPoint(glassPane,glassPanePoint,contentPane);

    if (containerPoint.y<0)
    { // We're not in the content pane
      if (containerPoint.y+menuBar.getHeight()>=0)
      {
        // The mouse event is over the menu bar. Could handle specially.
      }
      else
      {
        // The mouse event is over non-system window decorations, such as the ones provided by the Java look and feel. Could handle specially.
      }
    }
    else
    {
      // The mouse event is probably over the content pane. Find out exactly which component it's over.  
      Component component=SwingUtilities.getDeepestComponentAt(container,containerPoint.x,containerPoint.y);

//      if ((component!=null) && (component.equals(liveButton)))
      if ((component!=null))
      {
        // Forward events over the check box.
        Point componentPoint=SwingUtilities.convertPoint(glassPane,glassPanePoint,component);
        component.dispatchEvent(new MouseEvent(component,e.getID(),e.getWhen(),e.getModifiers(),componentPoint.x,componentPoint.y,e.getClickCount(),e.isPopupTrigger()));
      }
    }

    // Update the glass pane if requested.
    if (repaint)
    {
      glassPane.setPoint(glassPanePoint);
      glassPane.repaint();
    }
  }
}

The 3 classes in this app are :

Demo_Large_Custom_Cursor_Simple.java
MyGlassPane_Simple.java
CBListener_Simple.java

They are all the classes needed to run this demo app.

The Cursor_Crosshair.PNG image looks like this : enter image description here

But, it's not showing large custom cursor any more, I wonder what I did wrong, what should I do based on this existing code to show large custom cursor when mouse enters the app window ?

2
  • It is not possible to run this code without CBListener_Simple and the png file. Please post minimal reproducible example Commented Jul 17, 2018 at 4:04
  • Just added more details, now it's a MCV. Commented Jul 17, 2018 at 14:36

1 Answer 1

0

Thanks to @Radiodef for the inspirational answer to my previous question at : How To create A Large Size Custom Cursor In Java?

Here is a modified version of the answer that fits my requirements :

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;

public class Demo_Large_Custom_Cursor_Simple
{
  static Insets An_Inset=new Insets(0,0,0,0);

  private static void createAndShowGUI()
  {
    JFrame frame=new JFrame("Demo_Large_Custom_Cursor_Simple");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    Container contentPane=frame.getContentPane();
    contentPane.setLayout(new FlowLayout());
    contentPane.setPreferredSize(new Dimension(2*80+15,2*80+15));

    int Font_Size=6;
    String Color="blue",Font_Face="Monospaced",
           Token_1="<Html>"+
                   "  <Table Border=0 Cellspacing=3 Cellpadding=3>"+
                   "    <Tr><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">1</Font></Td><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">A</Font></Td></Tr>"+
                   "    <Tr><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">\u2664</Font></Td><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">\u203b</Font></Td></Tr>"+
                   "  </Table>"+
                   "</Html>",
           Token_2="<Html>"+
                   "  <Table Border=0 Cellspacing=3 Cellpadding=3>"+
                   "    <Tr><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">2</Font></Td><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">B</Font></Td></Tr>"+
                   "    <Tr><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">\u2660</Font></Td><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">\u2638</Font></Td></Tr>"+
                   "  </Table>"+
                   "</Html>",
           Token_3="<Html>"+
                   "  <Table Border=0 Cellspacing=3 Cellpadding=3>"+
                   "    <Tr><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">3</Font></Td><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">C</Font></Td></Tr>"+
                   "    <Tr><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">\u2667</Font></Td><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">\u2668</Font></Td></Tr>"+
                   "  </Table>"+
                   "</Html>",
           Token_4="<Html>"+
                   "  <Table Border=0 Cellspacing=3 Cellpadding=3>"+
                   "    <Tr><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">4</Font></Td><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">D</Font></Td></Tr>"+
                   "    <Tr><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">\u2663</Font></Td><Td Align=Center><Font Size="+Font_Size+" Color="+Color+">\u262f</Font></Td></Tr>"+
                   "  </Table>"+
                   "</Html>";
    JButton Button_1=new JButton(Token_1);
    Button_1.setPreferredSize(new Dimension(80,80));
    Button_1.setFont(new Font(Font_Face,0,16));
    Button_1.setMargin(An_Inset);
    Button_1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Out(Button_1.getText().replaceAll("<[^>]*>"," ").replaceAll("( )+"," ")); } });
    contentPane.add(Button_1);

    JButton Button_2=new JButton(Token_2);
    Button_2.setPreferredSize(new Dimension(80,80));
    Button_2.setFont(new Font(Font_Face,0,16));
    Button_2.setMargin(An_Inset);
    Button_2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Out(Button_2.getText().replaceAll("<[^>]*>"," ").replaceAll("( )+"," ")); } });
    contentPane.add(Button_2);

    JButton Button_3=new JButton(Token_3);
    Button_3.setPreferredSize(new Dimension(80,80));
    Button_3.setFont(new Font(Font_Face,0,16));
    Button_3.setMargin(An_Inset);
    Button_3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Out(Button_3.getText().replaceAll("<[^>]*>"," ").replaceAll("( )+"," ")); } });
    contentPane.add(Button_3);

    JButton Button_4=new JButton(Token_4);
    Button_4.setPreferredSize(new Dimension(80,80));
    Button_4.setFont(new Font(Font_Face,0,16));
    Button_4.setMargin(An_Inset);
    Button_4.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Out(Button_4.getText().replaceAll("<[^>]*>"," ").replaceAll("( )+"," ")); } });
    contentPane.add(Button_4);

    JMenuBar menuBar=new JMenuBar();
    JMenu menu=new JMenu("Menu");
    menu.add(new JMenuItem("Do nothing"));
    menuBar.add(menu);
    frame.setJMenuBar(menuBar);

    JPanel glass=new CustomGlassPane();
    glass.add(new CursorPanel(),BorderLayout.CENTER);
    frame.setGlassPane(glass);
    // This next call is necessary because JFrame.setGlassPane delegates to the root pane:
    // - https://docs.oracle.com/javase/9/docs/api/javax/swing/RootPaneContainer.html#setGlassPane-java.awt.Component-
    // - http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/javax/swing/JFrame.java#l738
    // And JRootPane.setGlassPane may call setVisible(false):
    // - https://docs.oracle.com/javase/9/docs/api/javax/swing/JRootPane.html#setGlassPane-java.awt.Component-
    // - http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/javax/swing/JRootPane.java#l663
    glass.setVisible(true);

    frame.setLocationRelativeTo(null);
    frame.pack();
    frame.setVisible(true);
  }

  static class CustomGlassPane extends JPanel
  {
    CustomGlassPane()
    {
      super(new BorderLayout());
      super.setOpaque(false);
    }

    @Override
    public boolean contains(int x,int y) { return false; }
  }

  static class CursorPanel extends JPanel
  {
    final BufferedImage cursorImage;
    Point mouseLocation;

    CursorPanel()
    {
      try { cursorImage=ImageIO.read(new File("C:/Cursor_Crosshair.PNG")); }
      catch (IOException x) { throw new UncheckedIOException(x); }

      setOpaque(false);
      long mask=AWTEvent.MOUSE_EVENT_MASK|AWTEvent.MOUSE_MOTION_EVENT_MASK;
      Toolkit.getDefaultToolkit().addAWTEventListener((AWTEvent e)->
      {
        switch (e.getID())
        {
          case MouseEvent.MOUSE_ENTERED :
          case  MouseEvent.MOUSE_EXITED :
          case   MouseEvent.MOUSE_MOVED :
          case MouseEvent.MOUSE_DRAGGED : capturePoint((MouseEvent)e);
            break;
        }
      },mask);

      // This turned out to be necessary, because the 'mouse exit' events don't always have a Point location which is outside the pane.
      Timer timer=new Timer(100,(ActionEvent e)->
      {
        if (mouseLocation!=null)
        {
          Point p=MouseInfo.getPointerInfo().getLocation();
          SwingUtilities.convertPointFromScreen(p,this);
          if (!contains(p)) setMouseLocation(null);
        }
      });
      timer.setRepeats(true);
      timer.start();
    }

    void capturePoint(MouseEvent e)
    {
      Component comp=e.getComponent();
      Point onThis=SwingUtilities.convertPoint(comp,e.getPoint(),this);
      boolean drawCursor=contains(onThis);

      if (drawCursor)
      {
        Window window=SwingUtilities.windowForComponent(this);
        if (window instanceof JFrame)
        {
          Container content=((JFrame)window).getContentPane();
          Point onContent=SwingUtilities.convertPoint(comp,e.getPoint(),content);
          Component deepest=SwingUtilities.getDeepestComponentAt(content,onContent.x,onContent.y);
          if (deepest==null) drawCursor=false;
        }
      }

      setMouseLocation(drawCursor?onThis:null);
    }

    void setMouseLocation(Point mouseLocation)
    {
      this.mouseLocation=mouseLocation;
      repaint();
    }

    @Override
    protected void paintComponent(Graphics g)
    {
      super.paintComponent(g);

      if (mouseLocation!=null)
      {
        int x=mouseLocation.x-(cursorImage.getWidth()/2)+1;
        int y=mouseLocation.y-(cursorImage.getHeight()/2)+1;

        g.drawImage(cursorImage,x,y,this);
      }
    }
  }

  private static void out(String message) { System.out.print(message); }
  private static void Out(String message) { System.out.println(message); }

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

Cursor_Crosshair.PNG looks like this : enter image description here

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.