Now, having completed part 1, let's add a popup menu to the JTable. However, the menu item in the popup menu should invoke the same Action as invoked from the toolbar button created yesterday.
Add this to the constructor created yesterday:
Collection<? extends Action> stockActions =
Lookups.forPath("Actions/Stock").lookupAll(Action.class);
for (Action action : stockActions) {
popupMenu.add(new JMenuItem(action));
}
MouseListener popupListener = new PopupListener();
// Add the listener to the JTable:
table.addMouseListener(popupListener);
// Add the listener specifically to the header:
table.getTableHeader().addMouseListener(popupListener);
And here's the standard popup enablement code:
private JPopupMenu popupMenu = new JPopupMenu();
class PopupListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
showPopup(e);
}
@Override
public void mouseReleased(MouseEvent e) {
showPopup(e);
}
private void showPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
}
This is wrong. You should use Utilities.actionsForPath then Utilities.actionsToPopup, which is simpler than the above code yet correctly handles Presenter.Popup, separators, and so on.
More subtly, the above code fails to check for ContextAwareAction. (The proxy for ShowStockAction would implement this interface.) It may happen to work if as a side effect of right-clicking the table you also happen to (first) select the TopComponent containing the table and activate its row, thus activating its corresponding nodes, and said TC contains just this one table with a possible selection: the Action with no specified context will generally fall back to using actionsGlobalContext. But that involves a number of assumptions about the contents of the window, the behavior of the native window manager and focus system, the order of delivery of selection events, etc. Such assumptions are not acceptable for any explorer-like view displaying a popup menu.
By contrast, actionsToPopup looks for a selection on the actual component being right-clicked, regardless of what the application's global selection might be. You can explicitly pass in a Lookup context if that is convenient; but you may also pass in the Component being clicked if it or an ancestor is a Lookup.Provider, which MyWindowTopComponent would be in this case.
(In case you had multiple tables with independent selections in the same TC, you would want each to implement L.P or be wrapped in a panel which did, so that aTP given the specific JTable would pay attention just to the selection in that table, regardless of which table was currently considered "active" by the TC.)
I have to thank you for this cool example. This may have some problems as pointed out by Jesse, but the idea behind all this is awesome. And i don't see why you couldn't do it for other components, like JTrees or JLists for instance. It should also ease the task of migrating a Swing application to a Netbeans application.
Regards
Thanks for the cool example.
Is it possible to disable the action when the table is empty and enabled when the table is not empty?
Hi Willy. That's already the case. The Action is context-sensitive, so if there is no Stock object selected, i.e., the table is empty, the Action will be disabled.
I think I have found it.
i have to put a : 'content.set(Collections.EMPTY_LIST, null);'
in the 'resultChanged(LookupEvent lookupEvent)' from my 'EditorTopComponent' where i view the jTabel.
This is the right way?
@ Jesse,
You say Utilities.actionsToPopup "correctly handles Presenter.Popup" but in my experience, using Utilities.actionsToPopup does not get the icons (16x16) associated with the actions to show up in the resulting popup menu - or am I missing something? I believe this defect is related to http://netbeans.org/bugzilla/show_bug.cgi?id=208081. Using Geertjan's approach, you can get the icons to show in the popup, but I agree you will have to do a little bit extra to handle things like separators etc.
if its's the same popup for each row, it's clearly not context sensitive