One Action for one clipboard
In our app at work you can copy data to the clipboard from text fields, tables, graphs and probably more. The problem was that we needed 'cut', 'copy' and 'paste' actions in the Edit menu on the menu bar, and those actions needed to act upon different components when invoked at different times.Our solution: track the most recently focused component (that supports the clipboard), and direct the action to that component. It exploits the fact that
TransferHandler.getXXXAction().actionPerformed()
performs its work on the source of the received ActionEvent
.Here's the code:
/**
* This class provides cut, copy and paste actions that act
* upon the most recently focused component that supports these
* actions.
*/
public class ClipboardActionsProvider
{
/** use the focus manager to track the most recently focused component */
private KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
/** The clipboard-capable JComponent that had the focus most recently */
private JComponent latestSupported = null;
/** a single instance for each clipboard action */
private final Action cutAction = new CutAction();
private final Action copyAction = new CopyAction();
private final Action pasteAction = new PasteAction();
public ClipboardActionsProvider() {
focusManager.addPropertyChangeListener("focusOwner", new FocusOwnerListener());
}
public Action cutAction() { return cutAction; }
public Action copyAction() { return copyAction; }
public Action pasteAction() { return pasteAction; }
private void handleFocusOwnerChange() {
Component component = focusManager.getFocusOwner();
if(component == latestSupported) return;
// bail if this component can't participate in cut & paste
if(!(component instanceof JComponent)) return;
JComponent jComponent = (JComponent)component;
if(jComponent.getTransferHandler() == null) return;
// save this component for cut, copy and paste
latestSupported = jComponent;
}
/**
* When the focus is changed, update the latest supported component.
*/
private class FocusOwnerListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
handleFocusOwnerChange();
}
}
/**
* Invokes a cut, copy or paste as if it originated from
* the most recent focus owner.
*/
private class CutAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
TransferHandler.getCutAction().actionPerformed(new ActionEvent(latestSupported, e.getID(), e.getActionCommand(), e.getWhen(), e.getModifiers()));
}
}
private class CopyAction extends AbstractAction {
/** omitted, code is similar to CutAction */
}
private class PasteAction extends AbstractAction {
/** omitted, code is similar to CutAction */
}
}