package org.limewire.ui.swing.components; import java.awt.Component; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.text.JTextComponent; import javax.swing.text.Document; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; import org.limewire.i18n.I18nMarker; import org.limewire.ui.swing.util.I18n; /** * Enables clipboard controls on a JTextComponent. */ public class TextFieldClipboardControl { private static String UNDO_MANAGER_FIELD = "limewire.text.undoManager"; /** * The undo action. */ private static Action UNDO_ACTION = new TextFieldAction(I18nMarker.marktr("Undo")) { public void actionPerformed(ActionEvent e) { JTextComponent textField = getTextField(e); UndoManager undoManager = getUndoManager(textField); try { if(undoManager != null) undoManager.undoOrRedo(); } catch(CannotUndoException ignored) { } catch(CannotRedoException ignored) { } } }; /** * The cut action */ private static Action CUT_ACTION = new TextFieldAction(I18nMarker.marktr("Cut")) { public void actionPerformed(ActionEvent e) { getTextField(e).cut(); } }; /** * The copy action. */ private static Action COPY_ACTION = new TextFieldAction(I18nMarker.marktr("Copy")) { public void actionPerformed(ActionEvent e) { getTextField(e).copy(); } }; /** * The paste action. */ private static Action PASTE_ACTION = new TextFieldAction(I18nMarker.marktr("Paste")) { public void actionPerformed(ActionEvent e) { getTextField(e).paste(); } }; /** * The delete action. */ private static Action DELETE_ACTION = new TextFieldAction(I18nMarker.marktr("Delete")) { public void actionPerformed(ActionEvent e) { getTextField(e).replaceSelection(""); } }; /** * The select all action. */ private static Action SELECT_ALL_ACTION = new TextFieldAction(I18nMarker.marktr("Select All")) { public void actionPerformed(ActionEvent e) { getTextField(e).selectAll(); } }; /** * The sole JPopupMenu that's shared among all the text fields. */ private static final JPopupMenu POPUP = createPopup(); public static void install(JTextComponent textField) { textField.setComponentPopupMenu(POPUP); UndoManager undoManager = new UndoManager(); undoManager.setLimit(1); installUndoManager(textField, undoManager); } protected static UndoManager getUndoManager(JTextComponent textField) { return (UndoManager)textField.getClientProperty(UNDO_MANAGER_FIELD); } private static void installUndoManager(JTextComponent textField, final UndoManager undoManager) { textField.getDocument().addUndoableEditListener(undoManager); textField.putClientProperty(UNDO_MANAGER_FIELD, undoManager); textField.addPropertyChangeListener("document", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { Document oldDoc = (Document)evt.getOldValue(); if(oldDoc != null) { oldDoc.removeUndoableEditListener(undoManager); } JTextComponent textField = (JTextComponent)evt.getSource(); textField.removePropertyChangeListener("document", this); } }); } /** * Creates the JPopupMenu that all LimeTextFields will share. */ private static JPopupMenu createPopup() { JPopupMenu popup; // initialize the JPopupMenu with necessary stuff. popup = new JPopupMenu() { @Override public void show(Component invoker, int x, int y) { updateActions((JTextComponent)invoker); super.show(invoker, x, y); } }; popup.add(new JMenuItem(UNDO_ACTION)); popup.addSeparator(); popup.add(new JMenuItem(CUT_ACTION)); popup.add(new JMenuItem(COPY_ACTION)); popup.add(new JMenuItem(PASTE_ACTION)); popup.add(new JMenuItem(DELETE_ACTION)); popup.addSeparator(); popup.add(new JMenuItem(SELECT_ALL_ACTION)); return popup; } /** * Updates the actions in each text just before showing the popup menu. */ private static void updateActions(JTextComponent textField) { String selectedText = textField.getSelectedText(); if(selectedText == null) selectedText = ""; boolean stuffSelected = !selectedText.equals(""); boolean allSelected = selectedText.equals(textField.getText()); UNDO_ACTION.setEnabled(textField.isEnabled() && textField.isEditable() && isUndoAvailable(textField)); CUT_ACTION.setEnabled(textField.isEnabled() && textField.isEditable() && stuffSelected); COPY_ACTION.setEnabled(textField.isEnabled() && stuffSelected); PASTE_ACTION.setEnabled(textField.isEnabled() && textField.isEditable() && isPasteAvailable(textField)); DELETE_ACTION.setEnabled(textField.isEnabled() && textField.isEditable() && stuffSelected); SELECT_ALL_ACTION.setEnabled(textField.isEnabled() && !allSelected); } /** * Determines if an Undo is available. */ private static boolean isUndoAvailable(JTextComponent textField) { UndoManager undoManager = getUndoManager(textField); return undoManager != null && undoManager.canUndoOrRedo(); } /** * Determines if paste is currently available. */ private static boolean isPasteAvailable(JTextComponent textField) { try { Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); return clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor); } catch(UnsupportedOperationException he) { return false; } catch(IllegalStateException ise) { return false; } } private static abstract class TextFieldAction extends AbstractAction { /** * Constructs a new FieldAction looking up the name from the MessagesBundles. */ public TextFieldAction(String name) { super(I18n.tr(name)); } /** * Gets the JTextComponent for the given ActionEvent. */ protected JTextComponent getTextField(ActionEvent e) { JMenuItem source = (JMenuItem)e.getSource(); JPopupMenu menu = (JPopupMenu)source.getParent(); return (JTextComponent)menu.getInvoker(); } } }