I've figured out how to do this application-wide, including on JFileChoosers and showInputDialog and things! I'm not sure how sane and proper it is but it works. It (ab)uses the pluggable look and feel system. JTextComponent calls updateUI during its constructor, which provides the opportunity to call setComponentPopupMenu when the L&F gets asked for its UI delegate.
If you change the look and feel for already-open windows, each component's updateUI method will be called again. To prevent setting the default menu again, the code below stores the property of whether a text box has already been initialized or not using JComponent.putClientProperty.
The net effect is it behaves just as though each JTextComponent itself was calling setComponentPopupMenu just one time during its constructor. Thus, it is easy to override this for special text boxes which want no menu or want a different menu: just call setComponentPopupMenu again. E.g, from a textfield subclass constructor or from the calling code that creates a window and its widgets.
This is the code to run once at application startup:
UIManager.addAuxiliaryLookAndFeel(new LookAndFeel() {
private final UIDefaults defaults = new UIDefaults() {
@Override
public javax.swing.plaf.ComponentUI getUI(JComponent c) {
if (c instanceof javax.swing.text.JTextComponent) {
if (c.getClientProperty(this) == null) {
c.setComponentPopupMenu(TextContextMenu.INSTANCE);
c.putClientProperty(this, Boolean.TRUE);
}
}
return null;
}
};
@Override public UIDefaults getDefaults() { return defaults; };
@Override public String getID() { return "TextContextMenu"; }
@Override public String getName() { return getID(); }
@Override public String getDescription() { return getID(); }
@Override public boolean isNativeLookAndFeel() { return false; }
@Override public boolean isSupportedLookAndFeel() { return true; }
});