I have a situation where I have a popup menu created when a JTable is right clicked on. Standard way of creating the popup menu:
aJTable.setComponentPopupMenu(rightClickMenu);
Now afterwards in the action that gets registered, I am unable to find out which cell was right clicked on to get that popup menu to appear.
rightClickMenuItem.addActionListener(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Work out what cell was right clicked to generate the menu
}
});
Any ideas on how you do this?
Astonishing fact: with a componentPopupMenu installed, a mouseListener never sees the mouseEvent that is the popupTrigger (reason is that showing the componentPopup is handled globally by a AWTEventListener installed by BasicLookAndFeel, and that listener consumes the event).
The only place which sees the mousePosition of that trigger is the getPopupLocation(MouseEvent), so the only reliable way to get hold of it (for doing location dependent config/actions) is #Mad's suggestion to override that method and store the value somewhere for later use.
The snippet below uses a clientProperty as storage location:
final JTable table = new JTable(new AncientSwingTeam()) {
#Override
public Point getPopupLocation(MouseEvent event) {
setPopupTriggerLocation(event);
return super.getPopupLocation(event);
}
protected void setPopupTriggerLocation(MouseEvent event) {
putClientProperty("popupTriggerLocation",
event != null ? event.getPoint() : null);
}
};
JPopupMenu popup = new JPopupMenu();
Action action = new AbstractAction("show trigger location") {
#Override
public void actionPerformed(ActionEvent e) {
JPopupMenu parent = (JPopupMenu) SwingUtilities.getAncestorOfClass(
JPopupMenu.class, (Component) e.getSource());
JTable invoker = (JTable) parent.getInvoker();
Point p = (Point) invoker.getClientProperty("popupTriggerLocation");
String output = p != null ? "row/col: "
+ invoker.rowAtPoint(p) + "/" + invoker.columnAtPoint(p) : null;
System.out.println(output);
}
};
popup.add(action);
popup.add("dummy2");
table.setComponentPopupMenu(popup);
#MadProgrammer's suggestion of getPopupLocation looked promising, but I couldn't work out how to get the information across between the table and the actionEvent...
I got around this by making sure that the row was selected when you rightclicked on it -> since the popup menu prevents the selection of the row, you can add in a mouse listener that makes sure the row gets selected no matter what click (left or right) is pressed.
aTable.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
int r = aTable.rowAtPoint(e.getPoint());
if (r >= 0 && r < clt.getRowCount()) {
aTable.setRowSelectionInterval(r, r);
} else {
aTable.clearSelection();
}
}
});
This means that in the rightClickMenuItem's action listener, you can grab the table's selected cell / row
rightClickMenuItem.addActionListener(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
aTable.get details about the selected one....
}
});
Too easy! Thanks everyone for the help.
JTable has methods
int row = rowAtPoint(p);
int col = columnAtPoint(p);
So pass the MouseEvent's point and use the values
Add a MouseListener and store the last right click point somewhere.
Related
I have an editable JCombobox . I have already done whats required for loading data from database.
After loading the data, I add some extra data like .next. or - for a specific reason. .next. or - which only work when they are highlighted in the popup menu.I have designed their working already.
However I don't want to view/selected .next. or - in JTextField of JComboBox.
For this purpose, I override JCombobox,
searchCBX.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED
&& (".next.".equals(e.getItem()) || "-".equals(e.getItem()))) {
searchTF.setText("");
}
}
});
Here, searchCBX is my required combobox and searchTF is my textfield of searchCBX. It works fine when I try to select .next. or - by scrolling the JPopupmenu from keyboard, searchTF automatically goes to empty.
Now the problem arises when I try to select .next. or - by mouse click from visible popup menu. It gets selected automatically.
I am trying to override mouseListener but it's not working.
searchCBX.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
if ((".next.".equals(searchTF.getText()) || "-".equals(searchTF.getText()))) {
searchTF.setText("");
}
}
});
So how can I remove the selected data from searchTF after mouse click of a jComboBox popup menu where value is .next. or - . Any help would be really appreciated.
To my understand you need to remove the text of the searchTF once you select the .next. or - from the dropdown of the searchCBX. If its the case , you dont need to worry about a MouseListener here. Just the ItemStageChage event can do the work.
Here is a required part of the code :
public class Example extends JFrame {
private JComboBox searchCBX;
private JTextField searchTF;
/**
* Creates new form Example
*/
public Example() {
initComponents();
}
private void initComponents() {
searchCBX = new JComboBox();
searchTF = new JTextField();
searchCBX.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent evt) {
searchCBXItemStateChanged(evt);
}
});
}
private void searchCBXItemStateChanged(ItemEvent evt) {
if (evt.getStateChange() == ItemEvent.SELECTED
&& (".next.".equals(evt.getItem()) || "-".equals(evt.getItem()))) {
searchTF.setText("");
} else {
searchTF.setText(searchCBX.getSelectedItem().toString());
}
}
}
When attempting to click on an item in a submenu, it is natural to quickly draw your mouse across the menu items below it. Both Windows and Mac natively handle this by putting a small delay before the a menu is opened. Swing JMenus do not handle this, and the menu the mouse briefly hovers over would be opened before the mouse reaches the intended menu item.
For example, in the image below, if I tried to select Item 3, but in the process my mouse briefly slid across Menu 2, the Menu 1 submenu would disappear before I got to it.
Does anyone have any tips or suggestions for getting around this? My idea was to define a custom MenuUI that added a timer to its mouse handler.
Here is some simple example code that illustrates my problem:
public class Thing extends JFrame {
public Thing()
{
super();
this.setSize(new Dimension(500, 500));
final JPopupMenu pMenu = new JPopupMenu();
for (int i = 0; i < 5; i++)
{
JMenu menu = new JMenu("Menu " + i);
pMenu.add(menu);
for (int j = 0; j < 10; j++)
{
menu.add(new JMenuItem("Item " + j));
}
}
this.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
pMenu.show(Thing.this, e.getX(), e.getY());
}
});
}
public static void main(String[] args)
{
Thing t = new Thing();
t.setVisible(true);
}
}
Call setDelay(delay) on your menu variable, where the delay parameter is the amount of milliseconds to wait for the menu to show, as an int.
This following line of code will set the delay to 1 second, so the user has to mouseover the menu item "Menu n" 1 second, before the submenu is displayed: menu.setDelay(1000);
Here's a snippet of the edited code:
for (int i = 0; i < 5; i++)
{
JMenu menu = new JMenu("Menu " + i);
pMenu.add(menu);
for (int j = 0; j < 10; j++)
{
menu.add(new JMenuItem("Item " + j));
}
menu.setDelay(1000);
}
I came up with a very hacky solution.
I made a UI class that extends BasicMenuUI. I override the createMouseInputListener method to return a custom MouseInputListener instead of the private handler object inside BasicMenuUI.
I then got the code for the MouseInputListener implementation in handler from GrepCode[1], and copied it into my custom listener. I made one change, putting a timer in mouseEntered. My final code for mouseEntered looks like this:
public void mouseEntered(MouseEvent e) {
timer.schedule(new TimerTask() {
#Override
public void run() {
if (menuItem.isShowing())
{
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
Point menuLoc = menuItem.getLocationOnScreen();
if (mouseLoc.x >= menuLoc.x && mouseLoc.x <= menuLoc.x + menuItem.getWidth() &&
mouseLoc.y >= menuLoc.y && mouseLoc.y <= menuLoc.y + menuItem.getHeight())
{
originalMouseEnteredStuff();
}
}
}
}, 100);
}
Before calling the the original code that was in mouseEntered, I check to make sure the mouse is still within this menu's area. I don't want all the menus my mouse brushes over to pop up after 100 ms.
Please let me know if anyone has discovered a better solution for this.
[1] http://www.grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/7-b147/javax/swing/plaf/basic/BasicMenuUI.java/?v=source
Thank you very much, you saved my day! The solution works as expected but I recommend using the Swing timer to ensure the code is executed by the EDT.
Additionally you should temporary set the menus delay to zero before calling the original stuff. Otherwise the user has to wait twice the delay time.
#Override
public void mouseEntered(MouseEvent e) {
if (menu.isTopLevelMenu() || menu.getDelay() == 0) {
originalMouseEnteredStuff(e);
} else {
final javax.swing.Timer timer = new javax.swing.Timer(menu.getDelay(), new DelayedMouseEnteredAction(e));
timer.setRepeats(false);
timer.start();
}
}
class DelayedMouseEnteredAction implements ActionListener
{
private final MouseEvent mouseEnteredEvent;
private DelayedMouseEnteredAction(MouseEvent mouseEnteredEvent) {
this.mouseEnteredEvent = mouseEnteredEvent;
}
#Override
public void actionPerformed(ActionEvent actionEvent) {
if (menu.isShowing()) {
final Point mouseLocationOnScreen = MouseInfo.getPointerInfo().getLocation();
final Rectangle menuBoundsOnScreen = new Rectangle(menu.getLocationOnScreen(), menu.getSize());
if (menuBoundsOnScreen.contains(mouseLocationOnScreen)) {
/*
* forward the mouse event only if the mouse cursor is yet
* located in the menus area.
*/
int menuDelay = menu.getDelay();
try {
/*
* Temporary remove the delay. Otherwise the delegate would wait the
* delay a second time e.g. before highlighting the menu item.
*/
menu.setDelay(0);
originalMouseEnteredStuff(mouseEnteredEvent);
} finally {
// reset the delay
menu.setDelay(menuDelay);
}
}
}
}
}
I have a JTable with editable cells. When I click in a cell, it enters edit mode; the same happens when I'm moving through cell using the directional arrows.
Now I want to select the cell instead of start editing, and edit the cell only when the Enter key is pressed.
If any other information is needed, please just ask for it.
Edit: Action for Enter key
class EnterAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
JTable tbl = (JTable) e.getSource();
tbl.editCellAt(tbl.getSelectedRow(), tbl.getSelectedColumn());
if (tbl.getEditorComponent() != null) {
tbl.getEditorComponent().requestFocus();
}
}
}
Now this is for left arrow action the rest of 3 are not hard to deduce from this one:
class LeftAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
JTable tbl = (JTable)e.getSource();
tbl.requestFocus();
tbl.changeSelection(tbl.getSelectedRow(), tbl.getSelectedColumn() > 0 ? tbl.getSelectedColumn()-1:tbl.getSelectedColumn(), false, false);
if(tbl.getCellEditor()!=null)
tbl.getCellEditor().stopCellEditing();
}
}
And this is how you bind this actions:
final String solve = "Solve";
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
table.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(enter, solve);
table.getActionMap().put(solve, new EnterAction());
final String sel = "Sel";
KeyStroke arrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
table.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(arrow, sel);
table.getActionMap().put(sel, new LeftAction());
Oh,i almost forgot,to select the cell instead of edit on Mouse Click:
public static MouseListener mAdapterTable = new MouseListener()
{
#Override
public void mousePressed(MouseEvent e)
{
JTable tbl=((JTable)e.getComponent());
if(tbl.isEditing())
{
tbl.getCellEditor().stopCellEditing();
}
}
#Override
public void mouseClicked(MouseEvent e) {
JTable tbl=((JTable)e.getComponent());
if(tbl.isEditing() )
tbl.getCellEditor().stopCellEditing();
}
#Override
public void mouseReleased(MouseEvent e) {
JTable tbl=((JTable)e.getComponent());
if(tbl.isEditing() )
tbl.getCellEditor().stopCellEditing();
}
};
The EventListner must be added to table like so:
table.addMouseListener(mAdapterTable);
Use Key Bindings for this. Most Look & Feel implementations already bind F2 to the table's startEditing action, but you add a different binding:
tree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "startEditing");
This will effectively replace the previous binding of Enter to the table's selectNextRowCell action.
Here is what i would do:
First enable the single cell selection for the JTable
Create a KeyAdapter or KeyListener for the JTable or for the JPanel,
what contains your table.
In the KeyAdapter's keyPressed() method enter the edit mode of the
selected cell, something like this:
http://www.exampledepot.com/egs/javax.swing.table/StopEdit.html
You can check in the keyPressed() method, if the user pressed the right button for editing. I'm not sure, if the normal (double click) editing is disabled in your table, then what happens, if you try to edit it programmatically, but if it doesn't work, then you can enable the editing on the selected cell, when the user presses the edit button, then when he/she finished, disable it again.
Let's say I have JTabbedPane with a ChangeListener
JTabbedPane tabbedPane = new JTabbedPane();
// Add few tabs
.....
.....
tabbedPane.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent changeEvent) {
// How to determine if the changeEvent was fired because of a tab remove/add ?
}
});
and somewhere I am doing a
tabbedPane.removeTabAt(2);
and somewhere else
tabbedPane.add(panel, 0);
The ChangeListener should get fired now, is there any way to determine within the listener if it was called due to a tab remove/add ?
EDIT: I am basically trying to do some actions only when the user switches between tabs and not when adding or removing.
If I remember correctly, JTabbedPane will fire a componentAdded() event (defined in Container) when a new tab is added and a componentRemoved() event if a tab is removed.
You should be able to listen for adding or removal of a tab by registering a ContainerListener
http://docs.oracle.com/javase/7/docs/api/java/awt/Container.html#addContainerListener(java.awt.event.ContainerListener)
The stateChanged() event is just a side-effect of the add because the JTabbedPanel automatically switches to the new tab.
Depending on the exact requirement, you might keep track of the selected component and only do stuff if that has changed:
ChangeListener l = new ChangeListener() {
Component lastSelected = tabbedPane.getSelectedComponent();
#Override
public void stateChanged(ChangeEvent e) {
if (lastSelected != tabbedPane.getSelectedComponent()) {
LOG.info("changed: " + tabbedPane.getSelectedIndex());
lastSelected = tabbedPane.getSelectedComponent();
}
}
};
tabbedPane.addChangeListener(l);
Might not be good enough, though, as it will trigger if the selected tab itself is removed.
You might also want to examine the client property __index_to_remove__, which is set by removeTabAt().
By keeping track of the current number of tabs, you can detect a selection change based on add or delete
ChangeListener l = new ChangeListener() {
int lastTabCount = tabbedPane.getTabCount();
Component lastSelected = tabbedPane.getSelectedComponent();
#Override
public void stateChanged(ChangeEvent e) {
if (lastSelected != tabbedPane.getSelectedIndex())
{
int currentTabCount = tabbedPane.getTabCount();
if (lastTabCount == currentTabCount ) {
LOG.info("changed: " + tabbedPane.getSelectedIndex());
} else if (lastTabCount < currentTabCount)
LOG.info("changed due to delete: " + tabbedPane.getSelectedIndex());
} else if (lastTabCount > currentTabCount)
LOG.info("changed due to add: " + tabbedPane.getSelectedIndex());
}
lastTabCount = tabbedPane.getTabCount();
lastSelected = tabbedPane.getSelectedComponent();
}
}
};
tabbedPane.addChangeListener(l);
How can I enable hyperlink for every record in the JTable?
What I want to do is such that a user can click on the hyperlink which will then display the information they can edit/update.
Alternatively how can I enable in place editing of the table data?
Another question is i am currently using the following way to display different screen. But this is not an elegant way i understand we should use cardlayout but how exactly to go about it?
mainPanel.setVisible(false);
createBlogEntryPanel.setVisible(true);
setComponent(createBlogEntryPanel);
To address the problem with JTable consuming the events, you can add your own MouseListener (or a MouseAdapter) to the JTable and do your manipulation inside this listener. Here is an example of what you can achieve:
public class Main extends JFrame {
public Main() {
super();
DefaultTableModel dt = new DefaultTableModel(
new String[][] { { "http://google.com" }, { "http://gmail.com" } }, new String[] { "Url" });
final JTable t = new JTable(dt);
t.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
int row = t.rowAtPoint(new Point(e.getX(), e.getY()));
int col = t.columnAtPoint(new Point(e.getX(), e.getY()));
System.out.println(row + " " + col);
String url = (String) t.getModel().getValueAt(row, col);
System.out.println(url + " was clicked");
// DO here what you want to do with your url
}
#Override
public void mouseEntered(MouseEvent e) {
int col = t.columnAtPoint(new Point(e.getX(), e.getY()));
if (col == 0) {
t.setCursor(new Cursor(Cursor.HAND_CURSOR));
}
}
#Override
public void mouseExited(MouseEvent e) {
int col = t.columnAtPoint(new Point(e.getX(), e.getY()));
if (col != 0) {
t.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
});
add(new JScrollPane(t));
t.getColumnModel().getColumn(0).setCellRenderer(new TableCellRenderer() {
#Override
public Component getTableCellRendererComponent(JTable table, final Object value, boolean arg2,
boolean arg3, int arg4, int arg5) {
final JLabel lab = new JLabel("<html>" + value + "");
return lab;
}
});
setSize(700, 500);
setLocationRelativeTo(null);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
new Main();
}
}
(The question is way too vague to answer it concisely/completely - this answer assume the main part to be the hyperlink in JTable)
Consider using a framework that fully supports hyperlinks in collection components (table, list, tree ..) - like f.i. SwingX
For a code example in SwingX, see a recent answer
Edit
But what if only part of the cell is a hyperlink, or worse, if there are 2 different links in a cell?
As mentioned in my comment: that's not supported in SwingX (it's cell-based).
So it's back to square one with several options
The ol' style: (mis-)use a cellEditor
have a editingComponent which is/contains a hyperlink
have a mouseListener which starts editing on rollover the cell
from now on the live-component takes care of mouseEvents and can trigger the appropriate actions on presses/clicks at the hyperlink/s
Extending SwingX style: enrich the rollover interface (the incubator may have examples, but that wasn't added to core SwingX for the reason of not yet being ready :-) The basic idea is to
add "hot-spots" the rollover interface
enhance (or replace) the rolloverProducer to produce finer-grained notifications once rollover-aware cell is detected
translate the mousePosition to the cell (rendering component) coordinates and query the cell if or not that corresponds to a hot-spot, if (and only if) so, arm and on click trigger the action
The second can be implemented independently of SwingX, of course: a plain ol' custom mouseListener and the mapping logic should do the trick (sorry, no code handy)