package com.limegroup.gnutella.gui;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import javax.swing.Action;
import javax.swing.AbstractAction;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.text.Document;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.DataFlavor;
import javax.swing.undo.UndoManager;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CannotRedoException;
/**
* A better JTextField.
*/
public class LimeTextField extends JTextField {
/**
* The undo action.
*/
private static Action UNDO_ACTION = new FieldAction("UNDO") {
public void actionPerformed(ActionEvent e) {
getField(e).undo();
}
};
/**
* The cut action
*/
private static Action CUT_ACTION = new FieldAction("CUT") {
public void actionPerformed(ActionEvent e) {
getField(e).cut();
}
};
/**
* The copy action.
*/
private static Action COPY_ACTION = new FieldAction("COPY") {
public void actionPerformed(ActionEvent e) {
getField(e).copy();
}
};
/**
* The paste action.
*/
private static Action PASTE_ACTION = new FieldAction("PASTE") {
public void actionPerformed(ActionEvent e) {
getField(e).paste();
}
};
/**
* The delete action.
*/
private static Action DELETE_ACTION = new FieldAction("DELETE") {
public void actionPerformed(ActionEvent e) {
getField(e).replaceSelection("");
}
};
/**
* The select all action.
*/
private static Action SELECT_ALL_ACTION = new FieldAction("SELECT_ALL") {
public void actionPerformed(ActionEvent e) {
getField(e).selectAll();
}
};
/**
* The sole JPopupMenu that's shared among all the text fields.
*/
private static final JPopupMenu POPUP = createPopup();
/**
* Our UndoManager.
*/
private UndoManager undoManager;
/**
* Constructs a new LimeTextField.
*/
public LimeTextField() {
super();
init();
}
/**
* Constructs a new LimeTextField with the given text.
*/
public LimeTextField(String text) {
super(text);
init();
}
/**
* Constructs a new LimeTextField with the given amount of columns.
*/
public LimeTextField(int columns) {
super(columns);
init();
}
/**
* Constructs a new LimeTextField with the given text & number of columns.
*/
public LimeTextField(String text, int columns) {
super(text, columns);
init();
}
/**
* Constructs a new LimeTextField with the given document, text, and columns.
*/
public LimeTextField(Document doc, String text, int columns) {
super(doc, text, columns);
init();
}
/**
* Undoes the last action.
*/
public void undo() {
try {
if(undoManager != null)
undoManager.undoOrRedo();
} catch(CannotUndoException ignored) {
} catch(CannotRedoException ignored) {
}
}
/**
* Sets the UndoManager (but does NOT add it to the document).
*/
public void setUndoManager(UndoManager undoer) {
undoManager = undoer;
}
/**
* Gets the undo manager.
*/
public UndoManager getUndoManager() {
return undoManager;
}
/**
* Intercept the 'setDocument' so that we can null out our manager
* and possibly assign a new one.
*/
public void setDocument(Document doc) {
if(doc != getDocument())
undoManager = null;
super.setDocument(doc);
}
/**
* Initialize the necessary events.
*/
private void init() {
// TODO: when we compile with java 1.5, do this.
// if(CommonUtils.isJava15OrLater())
// setComponentPopupMenu(POPUP);
// else
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
undoManager = new UndoManager();
undoManager.setLimit(1);
getDocument().addUndoableEditListener(undoManager);
}
/**
* Intercept mouse events if we're below Java 1.5 (which doesn't have
* internal PopupMenu support), so we can show the popup menu without
* having to add a listener.
*/
protected void processMouseEvent(MouseEvent e) {
super.processMouseEvent(e);
// TODO: do not do if we compile with java 1.5
// if(!CommonUtils.isJava15OrLater()) {
if(!e.isConsumed() && POPUP.isPopupTrigger(e)) {
e.consume();
POPUP.show(this, e.getX(), e.getY());
}
// }
}
/**
* Creates the JPopupMenu that all LimeTextFields will share.
*/
private static JPopupMenu createPopup() {
JPopupMenu popup;
// initialize the JPopupMenu with necessary stuff.
popup = new JPopupMenu() {
public void show(Component invoker, int x, int y) {
((LimeTextField)invoker).updateActions();
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 void updateActions() {
String selectedText = getSelectedText();
if(selectedText == null)
selectedText = "";
boolean stuffSelected = !selectedText.equals("");
boolean allSelected = selectedText.equals(getText());
UNDO_ACTION.setEnabled(isEnabled() && isEditable() && isUndoAvailable());
CUT_ACTION.setEnabled(isEnabled() && isEditable() && stuffSelected);
COPY_ACTION.setEnabled(isEnabled() && stuffSelected);
PASTE_ACTION.setEnabled(isEnabled() && isEditable() && isPasteAvailable());
DELETE_ACTION.setEnabled(isEnabled() && stuffSelected);
SELECT_ALL_ACTION.setEnabled(isEnabled() && !allSelected);
}
/**
* Determines if an Undo is available.
*/
private boolean isUndoAvailable() {
return getUndoManager() != null && getUndoManager().canUndoOrRedo();
}
/**
* Determines if paste is currently available.
*/
private boolean isPasteAvailable() {
try {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
// TODO: When we compile with Java 1.5, do this:
// if(CommonUtils.isJava15OrLater()) {
// return clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor);
// } else {
Transferable contents = clipboard.getContents(this);
return contents != null && contents.isDataFlavorSupported(DataFlavor.stringFlavor);
// }
} catch(UnsupportedOperationException he) {
return false;
} catch(IllegalStateException ise) {
return false;
}
}
/**
* Base Action that all LimeTextField actions extend.
*/
private static abstract class FieldAction extends AbstractAction {
/**
* Constructs a new FieldAction looking up the name from the MessagesBundles.
*/
public FieldAction(String name) {
super(GUIMediator.getStringResource("CONTEXT_MENU_" + name));
}
/**
* Gets the LimeTextField for the given ActionEvent.
*/
protected LimeTextField getField(ActionEvent e) {
JMenuItem source = (JMenuItem)e.getSource();
JPopupMenu menu = (JPopupMenu)source.getParent();
return (LimeTextField)menu.getInvoker();
}
}
}