|
|
||
Scott Violet's BlogAugust 2006 ArchivesCut, Copy and PastePosted by zixle on August 07, 2006 at 09:27 PM | Permalink | Comments (6)After a long hiatus I'm returning to a series of blogs on architecting applications. This time around I'm covering a simple way to provide rich cut, copy and paste behavior in an application. As usual, for those wishing to cut to the chase, here's the app:
Many Swing components provide cut, copy and paste support. For example, JTextField supports cut, copy and paste out the box. This is hardly revolutionary; todays developers expect such behavior out of the box. Swing's cut, copy and paste support is provided by javax.swing.TransferHandler. TransferHandler provides Actions for cut, copy and paste. Unfortunately the enabled state of the actions is not updated based on context, and the actions target the source of the event. In other words, the actions provided by TransferHandler are not appropriate for use on menus or toolbars. While the Actions provided by TransferHandler have limited usage, the remaining portions of TransferHandler are imminently more useful (even more so in 1.6). Rather than reinvent the wheel, I'll provide new Actions that target TransferHandler. Here's the list of requirements for cut, copy and paste actions:
Targetting the focused component is easily done with the KeyboardFocusManager. The KeyboardFocusManager maintains a bound property for the permanent focus owner. To listen for changes one need only install a PropertyChangeListener. The following code illustrates this:
PropertyChangeListener focusListener = new PropertyChangeListener {
public void propertyChange(PropertyChangeEvent e) {
if (e.getPropertyName() == "permanentFocusOwner") {
// The permanent focus owner has changed.
newPermanentFocusOwner((Component)e.getNewValue());
}
}
};
KeyboardFocusManager.getCurrentKeyboardFocusManager().
addPropertyChangeListener(focusListener);
The paste action is the trickiest. The paste action is enabled if the
paste action is enabled on the focus component, and a data flavor on
the clipboard matches the one supplied for the focused
component. Tracking the set of data flavors on the clipboard is easily
done using a FlavorListener. The following code illustrates this:
FlavorListener flavorListener = new FlavorListener {
public void flavorsChanged(FlavorEvent e) {
flavorsChanged();
}
};
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.addFlavorListener(flavorListener);
The set of data flavors a component supports for paste must be
provided by the developer. This is done with the following method:
CutCopyPasteHelper.registerDataFlavors(component, DataFlavor...dataFlavors);In addition to setting the set of flavors, you must explicitly enable the ability for a component to paste. This is done with the setPasteEnabled method. Seperating the set of flavors
from the enable state makes it easier to operate on each
independently. For example, once you've registered the flavors, if you
subsequently need to change the paste enabled state you need only
invoke setPasteEnabled.
Once you've enabled paste on the component and registered the set of flavors, CutCopyPasteHelper has all the information needed to update the paste action appropriately for you. The cut and copy actions are simpler. These are updated based on the focused component, and whether or not you've enabled cut and copy. For example, a text field with an empty selection would disable the cut and copy actions. On the other hand, a text field with a non-empty selection would enable the cut and copy actions. CutCopyPasteHelper provides the setCutEnabled and setCopyEnabled methods for this. Many Swing components provide keyboard bindings that target the actions provided by TransferHandler. CutCopyPasteHelper provides replacements for these actions. For a component to use CutCopyPasteHelper actions you must change the bindings registered on each component to target CutCopyPasteHelper. This is done using the ActionMap and InputMap. CutCopyPasteHelper provides the registerCutCopyPasteBindings that takes care of this. Here's a cheat sheet for using these actions with a component:
// Step 1
// Install the TransferHandler.
entryList.setTransferHandler(new ListTransferHandler());
// Step 2
// Register key bindings that target the cut, copy, and paste actions
// provided by CutCopyPasteHelper.
CutCopyPasteHelper.registerCutCopyPasteBindings(entryList, true);
// Step 3
// Register the data flavors on the list:
CutCopyPasteHelper.registerDataFlavors(entryList,
PASSWORD_ENTRY_DATA_FLAVOR);
// Enable paste
CutCopyPasteHelper.setPasteEnabled(entryList, true);
// Step 4
// Install listener to update cut/copy state based on selection
ListSelectionListener selectionListener = new ListSelectionListener {
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
boolean hasSelection = (entryList.getMinSelectionIndex() != -1);
CutCopyPasteHelper.setCopyEnabled(entryList, hasSelection);
CutCopyPasteHelper.setCutEnabled(entryList, hasSelection);
}
}
};
entryList.addListSelectionListener(new ListSelectionHandler());
To use the actions provided by CutCopyPasteHelper in a menu is no
different than any other action, invoke setAction or use a constructor
that takes an Action. Please note that the name, accelerator,
mnemonic, and icon are not provided. It is expected you'll set these
directly on the menu or button using the action.
* SelectionYou'll notice these actions target the focused component. Targetting the focused component is problematic if you need another component to get focus while firing the action. For example, if a button wired to the cut action gets focus, the cut action targets the button; this is not what you want. This is trivially fixed by invoking setFocusable(false) on the button, but that's a stop-gap. The larger issue is how to determine the target of these actions. In nearly all cases targetting the permanent focus owner is what you want, but there are exceptions. This is a larger issue, to which many folks have proposed big solutions. It's something that needs more study, and has been bothering both Hans and myself...The SandboxFor security reasons a sandboxed app can't get at the system clipboard. This is most definitely a major pain! I've primarily concerned myself with apps that have full access to the clipboard. I suspect there are some things that could be done to make support of these actions better when in the sandbox. That's for another day though.Early on I mentioned I wanted it to be trivial to wire these actions up to existing components. I've done the work for the text components. As this blog is now gargantuan in size, I'll save that for a later blog. In the mean time, here's the latest source for the password store app, and source for fabric. For those that look at the source, you'll see a bunch of stuff I haven't gotten to. That's for a later day as well... Oh, and yes, CutCopyPasteHelper will eventually makes it's way into Swing in some form or another.
-Scott
| ||
|
|