Java swing popup menu and jlist - java

here is my problem:
I have a jList and a popup menu. When I right click the jList, the popup menu shows. The problem is that the jList item which the mouse is pointing at won't select.
And I want it to do that. When I point my cursor at an item in the list and press the right button, I want two things to happen. Select the item on which I clicked and show the popup menu.
I tried this:
jLists.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
jList.setSelectedIndex(jList.locationToIndex(e.getPoint()));
}
});
jList.setComponentPopupMenu(jPopupMenu);
But it only shows the popup menu.
If I delete this line:
jList.setComponentPopupMenu(jPopupMenu);
then the right-click select works (but the popup menu doesn't show).
So, what do you think is the best way to make these two functions (both) work ?
Thanks and sorry for my english.

Don't do setComponentPopupMenu. In the MouseAdapter do the following:
public void mousePressed(MouseEvent e) {check(e);}
public void mouseReleased(MouseEvent e) {check(e);}
public void check(MouseEvent e) {
if (e.isPopupTrigger()) { //if the event shows the menu
jList.setSelectedIndex(jList.locationToIndex(e.getPoint())); //select the item
jPopupMenu.show(jList, e.getX(), e.getY()); //and show the menu
}
}
This should work.
EDIT: The code now checks both press and release events, because some platforms show popups when mouse presses and some other on release. See the Swing tutorial for more info.

If you want to continue to use setComponentPopupMenu (which is nice because it handles mouse and keyboard invocations of the popup in a cross platform way), you could override JPopupMenu.show(Component, int, int) to select the appropriate row.
JPopupMenu jPopupMenu = new JPopupMenu() {
#Override
public void show(Component invoker, int x, int y) {
int row = jList.locationToIndex(new Point(x, y));
if (row != -1) {
jList.setSelectedIndex(row);
}
super.show(invoker, x, y);
}
};
jList.setComponentPopupMenu(jPopupMenu);
Note that when your popup is invoked via the keyboard (and you don't also override getPopupLocation on your target component), the x, y location you get in JPopupMenu.show will be the midpoint of your component. If there's already a selection in this case you probably don't want to change the selection.
The solution I came up with to solve the keyboard vs. mouse invocation problem was to set a client property on the component in an override of getPopupLocation and then check it when showing the popup. The argument to getPopupLocation will be null when invoked via the keyboard. Here's the core code (perhaps implemented in a utility class available to your component and its popup menu).
private static final String POPUP_TRIGGERED_BY_MOUSE_EVENT = "popupTriggeredByMouseEvent"; // NOI18N
public static Point getPopupLocation(JComponent invoker, MouseEvent event)
{
boolean popupTriggeredByMouseEvent = event != null;
invoker.putClientProperty(POPUP_TRIGGERED_BY_MOUSE_EVENT, Boolean.valueOf(popupTriggeredByMouseEvent));
if (popupTriggeredByMouseEvent)
{
return event.getPoint();
}
return invoker.getMousePosition();
}
public static boolean isPopupTriggeredByMouseEvent(JComponent invoker)
{
return Boolean.TRUE.equals(invoker.getClientProperty(POPUP_TRIGGERED_BY_MOUSE_EVENT));
}
Then override getPopupLocation in your component:
#Override
public Point getPopupLocation(MouseEvent event)
{
return PopupMenuUtils.getPopupLocation(this, event);
}
and call isPopupTriggeredByMouseEvent in an override of JPopupMenu.show to determine whether or not to select the row at the popup location (or whatever action may make sense for the underlying component):
JPopupMenu jPopupMenu = new JPopupMenu() {
#Override
public void show(Component invoker, int x, int y) {
int row = jList.locationToIndex(new Point(x, y));
if (row != -1 && PopupMenuUtils.isPopupTriggeredByMouseEvent((JComponent) invoker)) {
jList.setSelectedIndex(row);
}
super.show(invoker, x, y);
}
};

Related

Java Swing: slightly modify existing keyboard behaviour? (right arrow should initially set cursor to position 0)

If a Java Swing JTextField gets the focus, all its text is marked.
Now, I would like to modify the behaviour, so that if the user now presses the right arrow, the cursor should be set to the texts beginning (position 0).
I'm able to get the RIGHT Key event and set the cursor to position 0,
but I don't know how to pass the RIGHT Key event to the original code so that it can handle the normal behaviour.
JTextField preisFieldEUR = new JTextField(…);
AbstractAction right = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
if (preisFieldEUR.getSelectionEnd() - preisFieldEUR.getSelectionStart() == preisFieldEUR.getText().length()) {
preisFieldEUR.setSelectionStart(0);
preisFieldEUR.setSelectionEnd(0);
} else {
// Todo: How to pass this event to the original keyboard handler to
// keep the normal behaviour?
}
}
};
preisFieldEUR.getInputMap().put(KeyStroke.getKeyStroke("RIGHT"), right);
Thanks a lot for any help!
You need something like this:
Action oldAction = preisFieldEUR.getActionMap().get("caret-forward"); // probably another parameter?
AbstractAction right = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
if (preisFieldEUR.getSelectionEnd() - preisFieldEUR.getSelectionStart() == preisFieldEUR.getText().length()) {
preisFieldEUR.setSelectionStart(0);
preisFieldEUR.setSelectionEnd(0);
} else {
oldAction.actionPeformed(e)
}
}
};

Java SWT get bounds of custom element in TreeView

I´ve created a TreeView with a ContentProvider and custom tree elements.
I also have a ISelectionChangedListener added to the TreeView.
I want to add an MouseListener, do detect if an element of the tree is right-clicked and show a popup menu.
If the white area around the tree is clicked, i don´t want to display the popup menu.
The menu is added via Extensions in the plugin.xml.
How can I now evaluate if a tree element is right-clicked, so I can show the popup menu (maybe with visibleWhen in the plugin.xml) ?
I also want to clear the selection, if the right-click is detected in the white area of the TreeView.
Ok, i did not realized that i can still use tree.getItem(...).
So here is my full MouseListener:
treeOPCUA.addMouseListener(new MouseListener()
{
#Override
public void mouseUp(MouseEvent e)
{
if(e.button == 3 && rightMouseClicked == true)
rightMouseClicked = false;
}
#Override
public void mouseDown(MouseEvent e)
{
if(e.button == 3 && rightMouseClicked == false)
rightMouseClicked = true;
if(treeOPCUA.getItem(new Point(e.x, e.y)) == null)
viewer.setSelection(null);
}
#Override
public void mouseDoubleClick(MouseEvent e)
{
viewer.setExpandedState(e.getSource(), true);
}
});
With the boolean variable "rightMouseClicked" I detect in my ISelectionChangedListener if the right mouse is clicked:
if(event.getSelection() instanceof IStructuredSelection && !rightMouseClicked)
I hope this answer helps anyone.

JTabbedPane track previous tab selection

I have a class that extends BasicTabbedPaneUI and does some paint component overriding.
I want to be able to add a addMouseListener to the class I use it in to check when the user selects a tab the current tab index and the previous tab index.
NOTE: The user is able to navigate to tabs via the keyboard and not just clicking on a tab and I want to be able to make sure the previous index tracks this. So in the example below preIndex would equal the previous index regardless to whether the user navigated to it via the keyboard or mouse.
Any ideas please?
tabbedPane.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JTabbedPane tabP = (JTabbedPane) e.getSource();
int currIndex = tabP.indexAtLocation(e.getX(), e.getY());
int prevIndex = ?????
}
});
Many thanks!!!!
I would use the change listener instead of mouse listener (it's called in both cases: for mouse and key event triggered tab change). If you cannot determine previously selected tab you can use following approach: save currently selected tab index as client property of the tabbed pane.
private static final String OLD_TAB_INDEX_PROPERTY = "oldTabIdx";
tabbedPane.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JTabbedPane tabP = (JTabbedPane) e.getSource();
int currIndex = tabP.getSelectedIndex();
int oldIdx = 0;
Object old = tabP.getClientProperty(OLD_TAB_INDEX_PROPERTY);
if (old instanceof Integer) {
oldIdx = (Integer) old;
}
tabP.putClientProperty(OLD_TAB_INDEX_PROPERTY, currIndex);
// now we can use old and current index
}
});

JPopupMenu not showing text of JMenuItem

I'm trying to create a JPopupMenu, but for some reason, it doesn't show the text I've set on the JMenuItems. The menu itself works, there are menuitems in it and they are responsive, but the text is not showing. I'm creating the menu like this:
private void createPopupMenu() {
this.popupMenu = new JPopupMenu();
this.addMouseListener(new PopupListener(this));
JMenuItem addPlaceMenuItem = new JMenuItem(SketchPad.ADD_PLACE_POPUP_TEXT);
addPlaceMenuItem.setAction(new PopupAction(ActionType.AddPlace));
this.popupMenu.add(addPlaceMenuItem);
JMenuItem addTransitionMenuItem = new JMenuItem(SketchPad.ADD_TRANSITION_POPUP_TEXT);
addTransitionMenuItem.setAction(new PopupAction(ActionType.AddTransition));
this.popupMenu.add(addTransitionMenuItem);
}
In case it matters, here is the PopupListener:
class PopupListener extends MouseAdapter {
SketchPad pad;
public PopupListener(SketchPad pad)
{
this.pad = pad;
}
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1)
{
this.pad.getController().deselectAllNodes();
}
else
{
maybeShowPopup(e);
}
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
pad.popupPosition = new Point(e.getX(), e.getY());
pad.popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
}
What am I missing here?
but for some reason, it doesn't show the text I've set on the JMenuItems.
addPlaceMenuItem.setAction(new PopupAction(ActionType.AddPlace));
The setAction(...) method reset the properties of the menu item with the properties of the Action. So you need to make sure you set the NAME property of the Action to set the text of the menu item.
So in your case it looks like the value of the NAME property should be:
SketchPad.ADD_PLACE_POPUP_TEXT
Or the other approach is to reset the text of the menu item after you set the Action
JMenuItem addPlaceMenuItem = new JMenuItem( new PopupAction(ActionType.AddPlace) );
addPlaceMenuItem.setText(SketchPad.ADD_PLACE_POPUP_TEXT);
The effect is platform specific. In particular, "In Microsoft Windows, the user by convention brings up a popup menu by releasing the right mouse button while the cursor is over a component that is popup-enabled." Your implementation of mouseReleased() precludes even checking isPopupTrigger(). Instead, handle the selection and check the trigger. A similar approach is shown in GraphPanel in order to handle multiple selection and a context menu.

Copy cell value of JTable with right click

I am showing some results in a JTable that consists of 2 columns.
File - Result
I implemented a JPopupMenu which displays a copy entry, and I try to copy the value of the cell, where I right-clicked.
filelistTable.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if(SwingUtilities.isRightMouseButton(e))
{
TablePopupMenu popup = new TablePopupMenu(filelistTable, e.getPoint());
filelistTable.setComponentPopupMenu(popup);
}
}
});
--
public TablePopupMenu(JTable table, Point p) {
this.table = table;
this.p = p;
JMenuItem mntmKopieren = new JMenuItem("Kopieren");
mntmKopieren.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
copyCellToClipboard();
}
});
add(mntmKopieren);
}
public void copyCellToClipboard()
{
int r = table.rowAtPoint(p);
int c = table.columnAtPoint(p);
System.out.println(table.getValueAt(table.convertRowIndexToView(r),
table.convertRowIndexToView(c)));
StringSelection entry = new StringSelection(table.getValueAt(table.convertRowIndexToView(r),
table.convertRowIndexToView(c)).toString());
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents( entry, this );
}
Anyhow, this only works for a small number of tests.
Did I do something wrong or something missing? It looks to me, as if the cell will not even get choosen correctly.
Two thingies are slightly off:
setting the componentPopup in the clicked is too late in the sequence of mouseEvents (popups are typically triggered on pressed or released which happen before the click)
the value is taken from the incorrect cell: all coordinates in a JTable are in view coordinate system, converting them to view coordinates will be completely off
That said: getting cell-coordinate related context is poorly supported. Often, the best bet is to (code snippet below)
override getPopupLocation(MouseEvent) and store the location somewhere
implement a popup/action to access the location
Fails if (as should be done in a well-behaved application), the popup could be triggered by keyboard: if that's the case, you'll need to provide some other marker (f.i. the focused cell) to act on.
final String popupLocation = "table.popupLocation";
final JTable table = new JXTable(new AncientSwingTeam()) {
#Override
public Point getPopupLocation(MouseEvent event) {
// event may be null if triggered by keyboard, f.i.
// thanks to #Mad for the heads up!
((JComponent) event.getComponent()).putClientProperty(
popupLocation, event != null ? event.getPoint() : null);
return super.getPopupLocation(event);
}
};
JPopupMenu popup = new JPopupMenu();
Action printLocation = new AbstractAction("print cell") {
#Override
public void actionPerformed(ActionEvent e) {
Point p = (Point) table.getClientProperty(popupLocation);
if (p != null) { // popup triggered by mouse
int row = table.rowAtPoint(p);
int column = table.columnAtPoint(p);
LOG.info("" + table.getValueAt(row, column));
} else { // popup triggered otherwise
// could choose f.i. by leadRow/ColumnSelection
...
}
}
};
popup.add(printLocation);
table.setComponentPopupMenu(popup);
Edit (triggered by Mad's comment):
You should be checking MouseEvent.isPopupTrigger as the trigger point is platform dependent. This does mean you need to monitor mousePressed, mouseReleased and mouseClicked
No, that's not needed (just checked :-): the mechanism that shows the componentPopup in response to a mouseEvent - happens in BasicLookAndFeel.AWTEventHelper - only does so if it is a popupTrigger.
By reading the api doc (should have done yesterday ;-) again, it turns out that the method is called always before showing the componentPopup, that is also if triggered by other means, f.i. keyboard. In that case the event param is null - and the original code would blow. On the bright side, with that guarantee, all the logic of finding the target cell/s could be moved into that method. Didn't try though, so it might not be feasable (f.i. if then the location should be based on the leadRow/ColumnSelection that might not yet be fully handled at that time)

Categories