This discussion is archived
0 Replies Latest reply: Jun 23, 2010 1:28 AM by 843807 RSS

Cursor caging

843807 Newbie
Currently Being Moderated
Hello,
I need to implement a functionality in my program. When the user click the arrow of a a conbobox, the mouse must disappear and caged to the popup of the combo.
I have done that by setting an empty cursor for the combobox and by pushing a new EventQueue which replace the mouse each time it goes out of the popup.

The problem is that when the mouse goes out, we can see the pointer that appears before being replace. It lasts a couple of milliseconds but it's ugly.

Have you got an idea to make the cursor totally invisible?

This is my code, if you need it :
import java.awt.AWTEvent;
import java.awt.AWTException;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EmptyStackException;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;

public class CagingMode
{

  private static final class Myui extends MetalComboBoxUI
  {
    //Make the getpopoup function available
    private BasicComboPopup getpopup()
    {
      return (BasicComboPopup)popup;
    }
  }
  
  private final CagedListener CAGING_LISTENER;
  private final Cursor INVISIBLE_CURSOR;
  private Cursor oldCursor;
  private Component c;
  private Robot robot;

  
  ///////// SINGLETON ////
  private static CagingMode instance = new CagingMode();

  private CagingMode()
  {
    Image cursorImage;
    cursorImage = Toolkit.getDefaultToolkit().getImage("image/TransparentCursor.png");
    INVISIBLE_CURSOR = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0, 0), "invisible");
    CAGING_LISTENER  = new CagedListener();
  }

  public static void main(String[] args)
  {
    final JFrame frame = new JFrame();
    frame.setLayout(new FlowLayout());
    frame.setSize(500, 500);
    JComboBox combo = new JComboBox();
    for(int i=0; i<10; i++){
      combo.addItem(i);
    }
    frame.getContentPane().add(combo);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(3);
    
    final Myui ui = new Myui();
    combo.setUI(ui);
    ui.getpopup().addPropertyChangeListener("visible",new PropertyChangeListener() {
      
      @Override
      public void propertyChange(PropertyChangeEvent evt)
      {
        if(evt.getNewValue().equals(Boolean.TRUE))
        {
          CagingMode.getInstance().startCagingMode(ui.getpopup());
        }
        else
        {
          CagingMode.getInstance().stopCagingMode();
        }
      }
    });
  }
  public static CagingMode getInstance()
  {
    return instance;
  }
  
  public void startCagingMode(Component c){
    if(c==null)
      return;
    this.c =c;
    try
    {
      robot = new Robot(c.getGraphicsConfiguration().getDevice());
      oldCursor = c.getCursor();
      CAGING_LISTENER.changeComponent();
      c.setCursor(INVISIBLE_CURSOR);
      Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
      defaultToolkit.getSystemEventQueue().push(CAGING_LISTENER);
    }
    catch (AWTException e)
    {
      e.printStackTrace();
    }
  }
  public void stopCagingMode(){
    this.c =null;
    if(c!=null && oldCursor!=null)
      c.setCursor(oldCursor);
    CAGING_LISTENER.pop();
  }
  
  
  private final class CagedListener extends EventQueue
  {
    private int cursorX;
    private int cursorY;
    private int compX;
    private int compY;
    private int compWidth;
    private int compHeight;

    public CagedListener()
    {
    }
    
    public void changeComponent()
    {
      compX = c.getLocationOnScreen().x;
      compY = c.getLocationOnScreen().y;
      compWidth = c.getWidth();
      compHeight = c.getHeight();
      
    }
    
    @Override
    protected void dispatchEvent(AWTEvent event)
    {
      if(c!=null && event instanceof MouseEvent)
      {
        cursorX = ((MouseEvent)event).getXOnScreen();
        cursorY = ((MouseEvent)event).getYOnScreen();
        boolean changed = false;
        
        if(cursorX < compX || (cursorX > (compX + compWidth-1)&&compWidth>1))
        {
          cursorX = compX+ compWidth/2 - 1;
          changed = true;
        }
          
        if(cursorY < compY)
        {
          cursorY = compY;
          changed = true;
        }
        else if(cursorY > (compY+compHeight-1)&&compHeight>1 )
        {
          changed = true;
          cursorY = compY+compHeight-1;
        }
          
        if(changed)
        {
//          System.out.println("modif "+((MouseEvent)event).getModifiers());
          robot.mouseMove(cursorX, cursorY);
        }
      }
      super.dispatchEvent(event);
    }
    @Override
    protected void pop() throws EmptyStackException
    {
      super.pop();
    }
  }
}