Skip to Main Content

Java SE (Java Platform, Standard Edition)

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

JList where mouse click acts like ctrl-mouse click

843807Mar 13 2010 — edited Apr 7 2010
I'd like to create a JList where I can select multiple items as if the control key were down, but without having the control key down. I thought that perhaps I could do this by extending JList and overriding its processMouseEvent method, setting the MouseEvent object to think that control is pressed and then calling the super method like so, but it doesn't work (I still need to press the control key to get my desired effect):
import java.awt.Component;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;

import javax.swing.*;

public class Ctrl_Down_JList {
  private static void createAndShowUI() {
    String[] items = {"Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"};
    
    JList myJList = new JList(items) {
      @Override
      protected void processMouseEvent(MouseEvent e) {
        
        // change the modifiers to believe that control key is down
        int modifiers = e.getModifiers() | InputEvent.CTRL_DOWN_MASK;
        
        // can I use this anywhere?  I don't see how to change the 
        // modifiersEx of the MouseEvent
        int modifiersEx = e.getModifiersEx() | InputEvent.CTRL_DOWN_MASK;
        
        MouseEvent myME = new MouseEvent(
            (Component) e.getSource(), 
            e.getID(), 
            e.getWhen(), 
            modifiers, // my changed modifier
            e.getX(), 
            e.getY(), 
            e.getXOnScreen(), 
            e.getYOnScreen(), 
            e.getClickCount(), 
            e.isPopupTrigger(), 
            e.getButton());
        super.processMouseEvent(myME);
      }
    };
    
    JFrame frame = new JFrame("Ctrl_Down_JList");
    frame.getContentPane().add(new JScrollPane(myJList));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public static void main(String[] args) {
    java.awt.EventQueue.invokeLater(new Runnable() {
      public void run() {
        createAndShowUI();
      }
    });
  }
}
Any ideas would be much appreciated!

Comments

843807 Mar 13 2010 — edited on Mar 13 2010
Any ideas would be much appreciated!
Here is a hack, idea is same as yours, but using Robot:
public static void createAndShowUI2() {
	String[] items = { "Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat" };

	final JList myJList = new JList(items);
	myJList.addMouseListener(new MouseAdapter() {
		Robot robot;
		{
			try {
				robot = new Robot();
			} catch (AWTException ex) {
				ex.printStackTrace();
			}
		}

		@Override
		public void mouseEntered(MouseEvent e) {
			if (robot != null)
				robot.keyPress(KeyEvent.VK_CONTROL);
		}

		@Override
		public void mouseExited(MouseEvent e) {
			if (robot != null)
				robot.keyRelease(KeyEvent.VK_CONTROL);
		}
	});
	JFrame frame = new JFrame("Ctrl_Down_JList");
	frame.getContentPane().add(new JScrollPane(myJList));
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.pack();
	frame.setLocationRelativeTo(null);
	frame.setVisible(true);
}
It does work, but there has to be some cleaner way of doing this, perhaps using SelectionModel but I don't know about it.

Thanks!

Edit: I found that if Control key is pressed manually, then this hack obviously, to handle this, it has to made dirtier:
myJList.addMouseListener(new MouseAdapter() {
	Robot robot;
	{
		try {
			robot = new Robot();
		} catch (AWTException ex) {
			ex.printStackTrace();
		}
	}
	Timer timer = new Timer(20, new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (robot != null)
				robot.keyPress(KeyEvent.VK_CONTROL);
		}
	});
	
        @Override
	public void mouseEntered(MouseEvent e) {
		if (robot != null) {
			robot.keyPress(KeyEvent.VK_CONTROL);
			timer.start();
		}
	}
	
        @Override
	public void mouseExited(MouseEvent e) {
		if (robot != null) {
			robot.keyRelease(KeyEvent.VK_CONTROL);
			timer.stop();
		}
	}
});
Edited by: T.B.M on Mar 13, 2010 10:06 PM
darrylburke Mar 13 2010 — edited on Mar 13 2010
JList's processMouseEvent(...) is inherited form JComponent and doesn't handle selection. That's done in BasicListUI.Handler#mouseClicked(...).

Private classes, private methods ... I'm thinking it might be easier (or not) to implement this via a custom ListSelectionModel.

db

edit Try this:
import javax.swing.*;

public class MultiSelectListDemo {

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

      @Override
      public void run() {
        new MultiSelectListDemo().makeUI();
      }
    });
  }

  public void makeUI() {
    String[] items = {"Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"};
    final JList list = new JList(items);
    list.setSelectionModel(new MultiListSelectionModel());

    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400, 400);
    frame.setLocationRelativeTo(null);
    frame.add(new JScrollPane(list));
    frame.setVisible(true);
  }
}

class MultiListSelectionModel extends DefaultListSelectionModel {

  @Override
  public void setSelectionInterval(int index0, int index1) {
    if (index0 != index1) {
      super.setSelectionInterval(index0, index1);
      return;
    }
    if (isSelectedIndex(index0)) {
      super.removeSelectionInterval(index0, index1);
    } else {
      super.addSelectionInterval(index0, index1);
    }
  }
}
Edited by: DarrylBurke
843807 Mar 13 2010
DarrylBurke wrote:
JList's processMouseEvent(...) is inherited form JComponent and doesn't handle selection. That's done in BasicListUI.Handler#mouseClicked(...).
I didn't know this, but it makes sense and explains quite a bit of why it's not working. Thanks.
Private classes, private methods ... I'm thinking it might be easier (or not) to implement this via a custom ListSelectionModel.
I'll look into this. Thanks again Darryl

/Pete
843807 Mar 14 2010 — edited on Mar 14 2010
The problem is you are using the wrong mask when doing your hack.

InputEvent.CTRL_DOWN_MASK is used by ModifiersEx while InputEvent.CTRL_MASK is used by plain old Modifiers, sent to the MouseEvent constructor (for backwards compatibility). Change
int modifiers = e.getModifiers() | InputEvent.CTRL_DOWN_MASK;
to
int modifiers = e.getModifiers() | InputEvent.CTRL_MASK;
and it works.


Edit: it also seems to work if you simply pass in the modifiersEx instead of your modifiers variable.
darrylburke Mar 14 2010
and it works.
An honest question, not a nitpick. Wouldn't that depend on processMouseEvent being executed before BasicListUI.Handler#mouseClicked(...) ? And is that behavior documented somewhere, or at least possible to infer from the API?

db
843807 Mar 14 2010
DarrylBurke wrote:
and it works.
An honest question, not a nitpick. Wouldn't that depend on processMouseEvent being executed before BasicListUI.Handler#mouseClicked(...) ? And is that behavior documented somewhere, or at least possible to infer from the API?

db
It is my understanding that JComponent.processMouseEvent notifies all the listeners. As to whether the behavior is documented - don't know but it would probably break a lot of people's code if it were to be implemented differently somehow. I've overriden processMouseEvent to filter out events I don't want the UI to process for instance.
darrylburke Mar 14 2010
Thanks, Aephyr.

db
843807 Mar 14 2010
Aephyr wrote:
The problem is you are using the wrong mask when doing your hack.
That's it! Thanks!

I'm out of Dukes stars, but if you want some, I'll figure out a way to get you some. Again, thanks!
kleopatra-JavaNet Apr 6 2010
hope this thread is not regarded as so old that a comment is regarded as resurrection <g>
DarrylBurke wrote:
An honest question, not a nitpick. Wouldn't that depend on processMouseEvent being executed before BasicListUI.Handler#mouseClicked(...) ? And is that behavior documented somewhere, or at least possible to infer from the API?
Well, I think it's documented: all processXXEvent methods are documented as dispatching the XXEvents as they deem appropriate, f.i to registered XXlisteners. So it's one level-of-control above the listeners - which is what all ui-delegate installed Handlers are.

CU
Jeanette
darrylburke Apr 6 2010 — edited on Apr 6 2010
Thanks Jeanette, I had gathered that from looking at the sources.

And since this has already been resurrected, I'll hijack it to ask a question that's been on my mind: which would you consider the 'cleaner' way to achiever the desired result: a customized selection model or a synthetic mouse event? Or is it an even draw?

db
kleopatra-JavaNet Apr 6 2010
Darryl,

not sure if it's possible to do completely on the selection model level - once tried on SwingX JXDatePicker without success, felt like not enough information at the time of the setSelection to decide what exactly to do. So dropped the idea without further digging ;-)

CU
Jeanette
darrylburke Apr 6 2010
not sure if it's possible to do completely on the selection model level
Isn't that what I showed at #2? or what did I miss this time?

Thanks, db
kleopatra-JavaNet Apr 6 2010 — edited on Apr 6 2010
could be - didn't try your code snippet :-) Just saying that from my experience it turned out to be a dead end. Forgot the details, though.

CU
Jeanette
kleopatra-JavaNet Apr 7 2010
after some sleep: the answer to the "cleaner" might be "depends" - on what exactly is the requirement. The base question is if you want to change the overall behaviour of the selectionModel's setSelectionInterval or if you want to change the effect of individual user gestures. Doing the first will effect all clients - ui delegates in their key and mouseListeners plus all application code which programatically sets the selection. Personally, would stick to plug into the event level until it turns out that I really need a generally changed model behaviour. Which might be violating super's contract (or not, too lazy to check right now :-)

CU
Jeanette
1 - 14
Locked Post
New comments cannot be posted to this locked post.

Post Details

Locked on May 5 2010
Added on Mar 13 2010
14 comments
2,647 views